Upgrade to Pro — share decks privately, control downloads, hide ads and more …

10 Things You Didn't Know Rails Could do

jeg2
April 25, 2012

10 Things You Didn't Know Rails Could do

My talk from RailsConf 2012, in Austin, TX. It's a collection of random tricks you can do with Rails.

jeg2

April 25, 2012
Tweet

More Decks by jeg2

Other Decks in Programming

Transcript

  1. James Edward Gray II I am a regular on the

    Ruby Rogues podcast I have been a Rubyist for eight years now I have written a lot of documentation and code for Ruby, including CSV
  2. Remind You of Things 3 class UsersController < ApplicationController #

    TODO: Make it possible to create new users. end
  3. Remind You of Things 3 class UsersController < ApplicationController #

    TODO: Make it possible to create new users. end class User < ActiveRecord::Base # FIXME: Should token really be accessible? attr_accessible :bio, :email, :name, :token end
  4. <%# OPTIMIZE: Paginate this listing. %> <%= render Article.all %>

    Remind You of Things 3 class UsersController < ApplicationController # TODO: Make it possible to create new users. end class User < ActiveRecord::Base # FIXME: Should token really be accessible? attr_accessible :bio, :email, :name, :token end
  5. $ rake notes app/controllers/users_controller.rb: * [ 2] [TODO] Make it

    possible to create new users. app/models/user.rb: * [ 2] [FIXME] Should token really be accessible? app/views/articles/index.html.erb: * [ 1] [OPTIMIZE] Paginate this listing. <%# OPTIMIZE: Paginate this listing. %> <%= render Article.all %> Remind You of Things 3 class UsersController < ApplicationController # TODO: Make it possible to create new users. end class User < ActiveRecord::Base # FIXME: Should token really be accessible? attr_accessible :bio, :email, :name, :token end
  6. $ rake notes app/controllers/users_controller.rb: * [ 2] [TODO] Make it

    possible to create new users. app/models/user.rb: * [ 2] [FIXME] Should token really be accessible? app/views/articles/index.html.erb: * [ 1] [OPTIMIZE] Paginate this listing. <%# OPTIMIZE: Paginate this listing. %> <%= render Article.all %> Remind You of Things 3 class UsersController < ApplicationController # TODO: Make it possible to create new users. end class User < ActiveRecord::Base # FIXME: Should token really be accessible? attr_accessible :bio, :email, :name, :token end
  7. <%# OPTIMIZE: Paginate this listing. %> <%= render Article.all %>

    Remind You of Things 3 class UsersController < ApplicationController # TODO: Make it possible to create new users. end class User < ActiveRecord::Base # FIXME: Should token really be accessible? attr_accessible :bio, :email, :name, :token end $ rake notes:todo app/controllers/users_controller.rb: * [ 2] Make it possible to create new users. $ rake notes:fixme app/models/user.rb: * [ 2] Should token really be accessible?
  8. <%# OPTIMIZE: Paginate this listing. %> <%= render Article.all %>

    Remind You of Things 3 class UsersController < ApplicationController # TODO: Make it possible to create new users. end class User < ActiveRecord::Base # FIXME: Should token really be accessible? attr_accessible :bio, :email, :name, :token end $ rake notes:todo app/controllers/users_controller.rb: * [ 2] Make it possible to create new users. $ rake notes:fixme app/models/user.rb: * [ 2] Should token really be accessible?
  9. Remind You of Things 3 class Article < ActiveRecord::Base belongs_to

    :user attr_accessible :body, :subject # JEG2: Add that code from your blog here. end $ rake notes:custom ANNOTATION=JEG2 app/models/article.rb: * [ 4] Add that Code from your blog here.
  10. Remind You of Things 3 class Article < ActiveRecord::Base belongs_to

    :user attr_accessible :body, :subject # JEG2: Add that code from your blog here. end $ rake notes:custom ANNOTATION=JEG2 app/models/article.rb: * [ 4] Add that Code from your blog here.
  11. Sandbox Your Console 4 $ rails r 'p [Article, Comment,

    User].map(&:count)' [0, 0, 0] $ rails c --sandbox Loading development environment in sandbox (Rails 3.2.3) Any modifications you make will be rolled back on exit >> jeg2 = User.create!(name: "James Edward Gray II") => #<User id: 1, name: "James Edward Gray II", …> >> article = Article.new(subject: "First Post").tap { |a| a.user = jeg2; a.save! } => #<Article id: 1, user_id: 1, subject: "First Post", …> >> Comment.new(body: "I need to add this.").tap { |c| c.user, c.article = jeg2, article; c.save! } => #<Comment id: 1, user_id: 1, article_id: 1, body: "I need to add this.", …> >> [Article, Comment, User].map(&:count) => [1, 1, 1] >> exit $ rails r 'p [Article, Comment, User].map(&:count)' [0, 0, 0]
  12. Sandbox Your Console 4 $ rails r 'p [Article, Comment,

    User].map(&:count)' [0, 0, 0] $ rails c --sandbox Loading development environment in sandbox (Rails 3.2.3) Any modifications you make will be rolled back on exit >> jeg2 = User.create!(name: "James Edward Gray II") => #<User id: 1, name: "James Edward Gray II", …> >> article = Article.new(subject: "First Post").tap { |a| a.user = jeg2; a.save! } => #<Article id: 1, user_id: 1, subject: "First Post", …> >> Comment.new(body: "I need to add this.").tap { |c| c.user, c.article = jeg2, article; c.save! } => #<Comment id: 1, user_id: 1, article_id: 1, body: "I need to add this.", …> >> [Article, Comment, User].map(&:count) => [1, 1, 1] >> exit $ rails r 'p [Article, Comment, User].map(&:count)' [0, 0, 0]
  13. Sandbox Your Console 4 $ rails r 'p [Article, Comment,

    User].map(&:count)' [0, 0, 0] $ rails c --sandbox Loading development environment in sandbox (Rails 3.2.3) Any modifications you make will be rolled back on exit >> jeg2 = User.create!(name: "James Edward Gray II") => #<User id: 1, name: "James Edward Gray II", …> >> article = Article.new(subject: "First Post").tap { |a| a.user = jeg2; a.save! } => #<Article id: 1, user_id: 1, subject: "First Post", …> >> Comment.new(body: "I need to add this.").tap { |c| c.user, c.article = jeg2, article; c.save! } => #<Comment id: 1, user_id: 1, article_id: 1, body: "I need to add this.", …> >> [Article, Comment, User].map(&:count) => [1, 1, 1] >> exit $ rails r 'p [Article, Comment, User].map(&:count)' [0, 0, 0]
  14. Sandbox Your Console 4 $ rails r 'p [Article, Comment,

    User].map(&:count)' [0, 0, 0] $ rails c --sandbox Loading development environment in sandbox (Rails 3.2.3) Any modifications you make will be rolled back on exit >> jeg2 = User.create!(name: "James Edward Gray II") => #<User id: 1, name: "James Edward Gray II", …> >> article = Article.new(subject: "First Post").tap { |a| a.user = jeg2; a.save! } => #<Article id: 1, user_id: 1, subject: "First Post", …> >> Comment.new(body: "I need to add this.").tap { |c| c.user, c.article = jeg2, article; c.save! } => #<Comment id: 1, user_id: 1, article_id: 1, body: "I need to add this.", …> >> [Article, Comment, User].map(&:count) => [1, 1, 1] >> exit $ rails r 'p [Article, Comment, User].map(&:count)' [0, 0, 0]
  15. Sandbox Your Console 4 $ rails r 'p [Article, Comment,

    User].map(&:count)' [0, 0, 0] $ rails c --sandbox Loading development environment in sandbox (Rails 3.2.3) Any modifications you make will be rolled back on exit >> jeg2 = User.create!(name: "James Edward Gray II") => #<User id: 1, name: "James Edward Gray II", …> >> article = Article.new(subject: "First Post").tap { |a| a.user = jeg2; a.save! } => #<Article id: 1, user_id: 1, subject: "First Post", …> >> Comment.new(body: "I need to add this.").tap { |c| c.user, c.article = jeg2, article; c.save! } => #<Comment id: 1, user_id: 1, article_id: 1, body: "I need to add this.", …> >> [Article, Comment, User].map(&:count) => [1, 1, 1] >> exit $ rails r 'p [Article, Comment, User].map(&:count)' [0, 0, 0]
  16. Run Helper Methods in the Console 5 $ rails c

    Loading development environment (Rails 3.2.3) >> helper.number_to_currency(100) => "$100.00" >> helper.time_ago_in_words(3.days.ago) => "3 days"
  17. Run Helper Methods in the Console 5 $ rails c

    Loading development environment (Rails 3.2.3) >> helper.number_to_currency(100) => "$100.00" >> helper.time_ago_in_words(3.days.ago) => "3 days"
  18. Use Non-WEBrick Servers in Development 6 source 'https://rubygems.org' # ...

    group :development do gem "thin" end $ rails s thin => Booting Thin => Rails 3.2.3 application starting in development on http://0.0.0.0:3000 => Call with -d to detach => Ctrl-C to shutdown server >> Thin web server (v1.3.1 codename Triple Espresso) >> Maximum connections set to 1024 >> Listening on 0.0.0.0:3000, CTRL+C to stop
  19. Use Non-WEBrick Servers in Development 6 source 'https://rubygems.org' # ...

    group :development do gem "thin" end $ rails s thin => Booting Thin => Rails 3.2.3 application starting in development on http://0.0.0.0:3000 => Call with -d to detach => Ctrl-C to shutdown server >> Thin web server (v1.3.1 codename Triple Espresso) >> Maximum connections set to 1024 >> Listening on 0.0.0.0:3000, CTRL+C to stop
  20. Allow You to Tap Into its Configuration From Josh Susser

    7 # lib/custom/railtie.rb module Custom class Railtie < Rails::Railtie config.custom = ActiveSupport::OrderedOptions.new end end
  21. Allow You to Tap Into its Configuration From Josh Susser

    7 # lib/custom/railtie.rb module Custom class Railtie < Rails::Railtie config.custom = ActiveSupport::OrderedOptions.new end end
  22. Allow You to Tap Into its Configuration From Josh Susser

    7 # lib/custom/railtie.rb module Custom class Railtie < Rails::Railtie config.custom = ActiveSupport::OrderedOptions.new end end # config/application.rb # ... require_relative "../lib/custom/railtie" module Blog class Application < Rails::Application # ... config.custom.setting = 42 end end
  23. Allow You to Tap Into its Configuration From Josh Susser

    7 # lib/custom/railtie.rb module Custom class Railtie < Rails::Railtie config.custom = ActiveSupport::OrderedOptions.new end end # config/application.rb # ... require_relative "../lib/custom/railtie" module Blog class Application < Rails::Application # ... config.custom.setting = 42 end end
  24. Allow You to Tap Into its Configuration From Josh Susser

    7 # lib/custom/railtie.rb module Custom class Railtie < Rails::Railtie config.custom = ActiveSupport::OrderedOptions.new end end # config/application.rb # ... require_relative "../lib/custom/railtie" module Blog class Application < Rails::Application # ... config.custom.setting = 42 end end
  25. Understand Shorthand Migrations $ rails g resource user name:string email:string

    token:string bio:text $ rails g resource user name email token:string{6} bio:text 9 From José Valim
  26. Understand Shorthand Migrations $ rails g resource user name:string email:string

    token:string bio:text $ rails g resource user name email token:string{6} bio:text class CreateUsers < ActiveRecord::Migration def change create_table :users do |t| t.string :name t.string :email t.string :token, :limit => 6 t.text :bio t.timestamps end end end 9 From José Valim
  27. Understand Shorthand Migrations $ rails g resource user name:string email:string

    token:string bio:text $ rails g resource user name email token:string{6} bio:text class CreateUsers < ActiveRecord::Migration def change create_table :users do |t| t.string :name t.string :email t.string :token, :limit => 6 t.text :bio t.timestamps end end end 9 From José Valim
  28. Understand Shorthand Migrations $ rails g resource user name:string email:string

    token:string bio:text $ rails g resource user name email token:string{6} bio:text class CreateUsers < ActiveRecord::Migration def change create_table :users do |t| t.string :name t.string :email t.string :token, :limit => 6 t.text :bio t.timestamps end end end 9 From José Valim
  29. Add Indexes to Migrations $ rails g resource user name:index

    email:uniq token:string{6} bio:text 10 From José Valim
  30. Add Indexes to Migrations $ rails g resource user name:index

    email:uniq token:string{6} bio:text class CreateUsers < ActiveRecord::Migration def change create_table :users do |t| t.string :name t.string :email t.string :token, :limit => 6 t.text :bio t.timestamps end add_index :users, :name add_index :users, :email, :unique => true end end 10 From José Valim
  31. Add Indexes to Migrations $ rails g resource user name:index

    email:uniq token:string{6} bio:text class CreateUsers < ActiveRecord::Migration def change create_table :users do |t| t.string :name t.string :email t.string :token, :limit => 6 t.text :bio t.timestamps end add_index :users, :name add_index :users, :email, :unique => true end end 10 From José Valim
  32. Add Indexes to Migrations $ rails g resource user name:index

    email:uniq token:string{6} bio:text class CreateUsers < ActiveRecord::Migration def change create_table :users do |t| t.string :name t.string :email t.string :token, :limit => 6 t.text :bio t.timestamps end add_index :users, :name add_index :users, :email, :unique => true end end 10 From José Valim
  33. Add Associations to a Migration $ rails g resource article

    user:references subject body:text 11 From José Valim
  34. Add Associations to a Migration $ rails g resource article

    user:references subject body:text class CreateArticles < ActiveRecord::Migration def change create_table :articles do |t| t.references :user t.string :subject t.text :body t.timestamps end add_index :articles, :user_id end end class Article < ActiveRecord::Base belongs_to :user attr_accessible :body, :subject end 11 From José Valim
  35. Add Associations to a Migration $ rails g resource article

    user:references subject body:text class CreateArticles < ActiveRecord::Migration def change create_table :articles do |t| t.references :user t.string :subject t.text :body t.timestamps end add_index :articles, :user_id end end class Article < ActiveRecord::Base belongs_to :user attr_accessible :body, :subject end 11 From José Valim
  36. Add Associations to a Migration $ rails g resource article

    user:references subject body:text class CreateArticles < ActiveRecord::Migration def change create_table :articles do |t| t.references :user t.string :subject t.text :body t.timestamps end add_index :articles, :user_id end end class Article < ActiveRecord::Base belongs_to :user attr_accessible :body, :subject end 11 From José Valim
  37. Add Associations to a Migration $ rails g resource article

    user:references subject body:text class CreateArticles < ActiveRecord::Migration def change create_table :articles do |t| t.references :user t.string :subject t.text :body t.timestamps end add_index :articles, :user_id end end class Article < ActiveRecord::Base belongs_to :user attr_accessible :body, :subject end 11 From José Valim
  38. Add Associations to a Migration $ rails g resource article

    user:references subject body:text class CreateArticles < ActiveRecord::Migration def change create_table :articles do |t| t.references :user t.string :subject t.text :body t.timestamps end add_index :articles, :user_id end end class Article < ActiveRecord::Base belongs_to :user attr_accessible :body, :subject end $ rails g resource comment user:belongs_to article:belongs_to body:text 11 From José Valim
  39. Add Associations to a Migration $ rails g resource article

    user:references subject body:text class CreateArticles < ActiveRecord::Migration def change create_table :articles do |t| t.references :user t.string :subject t.text :body t.timestamps end add_index :articles, :user_id end end class Article < ActiveRecord::Base belongs_to :user attr_accessible :body, :subject end $ rails g resource comment user:belongs_to article:belongs_to body:text 11 From José Valim
  40. Show You the Status of the Database $ rake db:migrate:status

    database: db/development.sqlite3 Status Migration ID Migration Name -------------------------------------------------- up 20120414155612 Create users up 20120414160528 Create articles down 20120414161355 Create comments 12
  41. Show You the Status of the Database $ rake db:migrate:status

    database: db/development.sqlite3 Status Migration ID Migration Name -------------------------------------------------- up 20120414155612 Create users up 20120414160528 Create articles down 20120414161355 Create comments 12
  42. Import Your CSV Data 13 Name,Email James,[email protected] Dana,[email protected] Summer,[email protected] require

    "csv" namespace :users do desc "Import users from a CSV file" task :import => :environment do path = ENV.fetch("CSV_FILE") { File.join(File.dirname(__FILE__), *%w[.. .. db data users.csv]) } CSV.foreach(path, headers: true, header_converters: :symbol) do |row| User.create(row.to_hash) end end end
  43. Import Your CSV Data 13 Name,Email James,[email protected] Dana,[email protected] Summer,[email protected] require

    "csv" namespace :users do desc "Import users from a CSV file" task :import => :environment do path = ENV.fetch("CSV_FILE") { File.join(File.dirname(__FILE__), *%w[.. .. db data users.csv]) } CSV.foreach(path, headers: true, header_converters: :symbol) do |row| User.create(row.to_hash) end end end
  44. Import Your CSV Data 13 Name,Email James,[email protected] Dana,[email protected] Summer,[email protected] require

    "csv" namespace :users do desc "Import users from a CSV file" task :import => :environment do path = ENV.fetch("CSV_FILE") { File.join(File.dirname(__FILE__), *%w[.. .. db data users.csv]) } CSV.foreach(path, headers: true, header_converters: :symbol) do |row| User.create(row.to_hash) end end end
  45. Store CSV in Your Database 14 class Article < ActiveRecord::Base

    require "csv" module CSVSerializer module_function def load(field) field.to_s.parse_csv end def dump(object) Array(object).to_csv end end serialize :categories, CSVSerializer # ... attr_accessible :body, :subject, :categories end
  46. Store CSV in Your Database 14 class Article < ActiveRecord::Base

    require "csv" module CSVSerializer module_function def load(field) field.to_s.parse_csv end def dump(object) Array(object).to_csv end end serialize :categories, CSVSerializer # ... attr_accessible :body, :subject, :categories end
  47. Store CSV in Your Database 14 class Article < ActiveRecord::Base

    require "csv" module CSVSerializer module_function def load(field) field.to_s.parse_csv end def dump(object) Array(object).to_csv end end serialize :categories, CSVSerializer # ... attr_accessible :body, :subject, :categories end $ rails c Loading development environment (Rails 3.2.3) >> Article.create!(subject: "JEG2's Rails Hacks", categories: ["Rails", "Gray, James", "hacks"]) => #<Article id: 1, …> >> Article.last.categories => ["Rails", "Gray, James", "hacks"] >> exit $ sqlite3 db/development.sqlite3 'SELECT categories FROM articles ORDER BY created_at DESC LIMIT 1' -- Loading resources from /Users/james/.sqliterc categories -------------------------- Rails,"Gray, James",hacks
  48. Store CSV in Your Database 14 class Article < ActiveRecord::Base

    require "csv" module CSVSerializer module_function def load(field) field.to_s.parse_csv end def dump(object) Array(object).to_csv end end serialize :categories, CSVSerializer # ... attr_accessible :body, :subject, :categories end $ rails c Loading development environment (Rails 3.2.3) >> Article.create!(subject: "JEG2's Rails Hacks", categories: ["Rails", "Gray, James", "hacks"]) => #<Article id: 1, …> >> Article.last.categories => ["Rails", "Gray, James", "hacks"] >> exit $ sqlite3 db/development.sqlite3 'SELECT categories FROM articles ORDER BY created_at DESC LIMIT 1' -- Loading resources from /Users/james/.sqliterc categories -------------------------- Rails,"Gray, James",hacks
  49. Store CSV in Your Database 14 class Article < ActiveRecord::Base

    require "csv" module CSVSerializer module_function def load(field) field.to_s.parse_csv end def dump(object) Array(object).to_csv end end serialize :categories, CSVSerializer # ... attr_accessible :body, :subject, :categories end $ rails c Loading development environment (Rails 3.2.3) >> Article.create!(subject: "JEG2's Rails Hacks", categories: ["Rails", "Gray, James", "hacks"]) => #<Article id: 1, …> >> Article.last.categories => ["Rails", "Gray, James", "hacks"] >> exit $ sqlite3 db/development.sqlite3 'SELECT categories FROM articles ORDER BY created_at DESC LIMIT 1' -- Loading resources from /Users/james/.sqliterc categories -------------------------- Rails,"Gray, James",hacks
  50. “Pluck” Fields Out of Your Database 15 From Ryan Bates

    $ rails c Loading development environment (Rails 3.2.3) >> User.select(:email).map(&:email) User Load (0.1ms) SELECT email FROM "users" => ["[email protected]", "[email protected]", "[email protected]"] >> User.pluck(:email) (0.2ms) SELECT email FROM "users" => ["[email protected]", "[email protected]", "[email protected]"] >> User.uniq.pluck(:email) (0.2ms) SELECT DISTINCT email FROM "users" => ["[email protected]", "[email protected]", "[email protected]"]
  51. “Pluck” Fields Out of Your Database 15 From Ryan Bates

    $ rails c Loading development environment (Rails 3.2.3) >> User.select(:email).map(&:email) User Load (0.1ms) SELECT email FROM "users" => ["[email protected]", "[email protected]", "[email protected]"] >> User.pluck(:email) (0.2ms) SELECT email FROM "users" => ["[email protected]", "[email protected]", "[email protected]"] >> User.uniq.pluck(:email) (0.2ms) SELECT DISTINCT email FROM "users" => ["[email protected]", "[email protected]", "[email protected]"]
  52. “Pluck” Fields Out of Your Database 15 From Ryan Bates

    $ rails c Loading development environment (Rails 3.2.3) >> User.select(:email).map(&:email) User Load (0.1ms) SELECT email FROM "users" => ["[email protected]", "[email protected]", "[email protected]"] >> User.pluck(:email) (0.2ms) SELECT email FROM "users" => ["[email protected]", "[email protected]", "[email protected]"] >> User.uniq.pluck(:email) (0.2ms) SELECT DISTINCT email FROM "users" => ["[email protected]", "[email protected]", "[email protected]"]
  53. Count Records in Groups 16 $ rails g resource event

    article:belongs_to trigger $ rails c Loading development environment (Rails 3.2.3) >> article = Article.last => #<Article id: 1, …> >> {edit: 3, view: 10}.each do |trigger, count| ?> count.times do ?> Event.new(trigger: trigger).tap { |e| e.article = article; e.save! } >> end >> end => {:edit=>3, :view=>10} >> Event.count => 13 >> Event.group(:trigger).count => {"edit"=>3, "view"=>10}
  54. Count Records in Groups 16 $ rails g resource event

    article:belongs_to trigger $ rails c Loading development environment (Rails 3.2.3) >> article = Article.last => #<Article id: 1, …> >> {edit: 3, view: 10}.each do |trigger, count| ?> count.times do ?> Event.new(trigger: trigger).tap { |e| e.article = article; e.save! } >> end >> end => {:edit=>3, :view=>10} >> Event.count => 13 >> Event.group(:trigger).count => {"edit"=>3, "view"=>10}
  55. Count Records in Groups 16 $ rails g resource event

    article:belongs_to trigger $ rails c Loading development environment (Rails 3.2.3) >> article = Article.last => #<Article id: 1, …> >> {edit: 3, view: 10}.each do |trigger, count| ?> count.times do ?> Event.new(trigger: trigger).tap { |e| e.article = article; e.save! } >> end >> end => {:edit=>3, :view=>10} >> Event.count => 13 >> Event.group(:trigger).count => {"edit"=>3, "view"=>10}
  56. Allow You to Override Associations 17 From Josh Susser class

    Car < ActiveRecord::Base belongs_to :owner belongs_to :previous_owner, class_name: "Owner" def owner=(new_owner) self.previous_owner = owner super end end
  57. Allow You to Override Associations 17 From Josh Susser class

    Car < ActiveRecord::Base belongs_to :owner belongs_to :previous_owner, class_name: "Owner" def owner=(new_owner) self.previous_owner = owner super end end
  58. Instantiate Records Without a Database 18 $ rails c Loading

    development environment (Rails 3.2.3) >> User.find(1) => #<User id: 1, name: "James", email: "[email protected]", …> >> jeg2 = User.instantiate("id" => 1, "email" => "[email protected]") => #<User id: 1, email: "[email protected]"> >> jeg2.name = "James Edward Gray II" => "James Edward Gray II" >> jeg2.save! => true >> User.find(1) => #<User id: 1, name: "James Edward Gray II", email: "[email protected]", …>
  59. Instantiate Records Without a Database 18 $ rails c Loading

    development environment (Rails 3.2.3) >> User.find(1) => #<User id: 1, name: "James", email: "[email protected]", …> >> jeg2 = User.instantiate("id" => 1, "email" => "[email protected]") => #<User id: 1, email: "[email protected]"> >> jeg2.name = "James Edward Gray II" => "James Edward Gray II" >> jeg2.save! => true >> User.find(1) => #<User id: 1, name: "James Edward Gray II", email: "[email protected]", …>
  60. Instantiate Records Without a Database 18 $ rails c Loading

    development environment (Rails 3.2.3) >> User.find(1) => #<User id: 1, name: "James", email: "[email protected]", …> >> jeg2 = User.instantiate("id" => 1, "email" => "[email protected]") => #<User id: 1, email: "[email protected]"> >> jeg2.name = "James Edward Gray II" => "James Edward Gray II" >> jeg2.save! => true >> User.find(1) => #<User id: 1, name: "James Edward Gray II", email: "[email protected]", …>
  61. Use Limitless Strings in PostgreSQL 19 From Josh Susser $

    rails g resource user bio # ... module PsqlApp class Application < Rails::Application # ... # Switch to limitless strings initializer "postgresql.no_default_string_limit" do ActiveSupport.on_load(:active_record) do adapter = ActiveRecord::ConnectionAdapters::PostgreSQLAdapter adapter::NATIVE_DATABASE_TYPES[:string].delete(:limit) end end end end
  62. Use Limitless Strings in PostgreSQL 19 From Josh Susser $

    rails g resource user bio $ rails c Loading development environment (Rails 3.2.3) >> very_long_bio = "X" * 10_000; :set => :set >> User.create!(bio: very_long_bio) => #<User id: 1, bio: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XX...", created_at: "2012-04-14 23:02:08", updated_at: "2012-04-14 23:02:08"> >> User.last.bio.size => 10000
  63. Use Limitless Strings in PostgreSQL 19 From Josh Susser $

    rails g resource user bio $ rails c Loading development environment (Rails 3.2.3) >> very_long_bio = "X" * 10_000; :set => :set >> User.create!(bio: very_long_bio) => #<User id: 1, bio: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XX...", created_at: "2012-04-14 23:02:08", updated_at: "2012-04-14 23:02:08"> >> User.last.bio.size => 10000
  64. Use Full Text Search in PostgreSQL 20 $ rails g

    resource article subject body:text From PeepCode
  65. Use Full Text Search in PostgreSQL 20 class CreateArticles <

    ActiveRecord::Migration def change create_table :articles do |t| t.string :subject t.text :body t.column :search, "tsvector" t.timestamps end execute <<-END_SQL CREATE INDEX articles_search_index ON articles USING gin(search); CREATE TRIGGER articles_search_update BEFORE INSERT OR UPDATE ON articles FOR EACH ROW EXECUTE PROCEDURE tsvector_update_trigger( search, 'pg_catalog.english', subject, body ); END_SQL end end From PeepCode
  66. Use Full Text Search in PostgreSQL 20 class CreateArticles <

    ActiveRecord::Migration def change create_table :articles do |t| t.string :subject t.text :body t.column :search, "tsvector" t.timestamps end execute <<-END_SQL CREATE INDEX articles_search_index ON articles USING gin(search); CREATE TRIGGER articles_search_update BEFORE INSERT OR UPDATE ON articles FOR EACH ROW EXECUTE PROCEDURE tsvector_update_trigger( search, 'pg_catalog.english', subject, body ); END_SQL end end From PeepCode
  67. Use Full Text Search in PostgreSQL 20 class CreateArticles <

    ActiveRecord::Migration def change create_table :articles do |t| t.string :subject t.text :body t.column :search, "tsvector" t.timestamps end execute <<-END_SQL CREATE INDEX articles_search_index ON articles USING gin(search); CREATE TRIGGER articles_search_update BEFORE INSERT OR UPDATE ON articles FOR EACH ROW EXECUTE PROCEDURE tsvector_update_trigger( search, 'pg_catalog.english', subject, body ); END_SQL end end From PeepCode
  68. Use Full Text Search in PostgreSQL 20 class CreateArticles <

    ActiveRecord::Migration def change create_table :articles do |t| t.string :subject t.text :body t.column :search, "tsvector" t.timestamps end execute <<-END_SQL CREATE INDEX articles_search_index ON articles USING gin(search); CREATE TRIGGER articles_search_update BEFORE INSERT OR UPDATE ON articles FOR EACH ROW EXECUTE PROCEDURE tsvector_update_trigger( search, 'pg_catalog.english', subject, body ); END_SQL end end From PeepCode
  69. Use Full Text Search in PostgreSQL 20 From PeepCode class

    Article < ActiveRecord::Base attr_accessible :body, :subject def self.search(query) sql = sanitize_sql_array(["plainto_tsquery('english', ?)", query]) where( "search @@ #{sql}" ).order( "ts_rank_cd(search, #{sql}) DESC" ) end end
  70. Use Full Text Search in PostgreSQL 20 From PeepCode class

    Article < ActiveRecord::Base attr_accessible :body, :subject def self.search(query) sql = sanitize_sql_array(["plainto_tsquery('english', ?)", query]) where( "search @@ #{sql}" ).order( "ts_rank_cd(search, #{sql}) DESC" ) end end
  71. Use Full Text Search in PostgreSQL 20 From PeepCode $

    rails c Loading development environment (Rails 3.2.3) >> Article.create!(subject: "Full Text Search") => #<Article id: 1, …> >> Article.create!(body: "A stemmed search.") => #<Article id: 2, …> >> Article.create!(body: "You won't find me!") => #<Article id: 3, …> >> Article.search("search").map { |a| a.subject || a.body } => ["Full Text Search", "A stemmed search."] >> Article.search("stemming").map { |a| a.subject || a.body } => ["A stemmed search."]
  72. Use Full Text Search in PostgreSQL 20 From PeepCode $

    rails c Loading development environment (Rails 3.2.3) >> Article.create!(subject: "Full Text Search") => #<Article id: 1, …> >> Article.create!(body: "A stemmed search.") => #<Article id: 2, …> >> Article.create!(body: "You won't find me!") => #<Article id: 3, …> >> Article.search("search").map { |a| a.subject || a.body } => ["Full Text Search", "A stemmed search."] >> Article.search("stemming").map { |a| a.subject || a.body } => ["A stemmed search."]
  73. Use Full Text Search in PostgreSQL 20 From PeepCode $

    rails c Loading development environment (Rails 3.2.3) >> Article.create!(subject: "Full Text Search") => #<Article id: 1, …> >> Article.create!(body: "A stemmed search.") => #<Article id: 2, …> >> Article.create!(body: "You won't find me!") => #<Article id: 3, …> >> Article.search("search").map { |a| a.subject || a.body } => ["Full Text Search", "A stemmed search."] >> Article.search("stemming").map { |a| a.subject || a.body } => ["A stemmed search."]
  74. Use Different a Database for Each User 21 def connect_to_user_database(name)

    config = ActiveRecord::Base.configurations["development"] .merge("database" => "db/#{name}.sqlite3") ActiveRecord::Base.establish_connection(config) end
  75. Use Different a Database for Each User 21 def connect_to_user_database(name)

    config = ActiveRecord::Base.configurations["development"] .merge("database" => "db/#{name}.sqlite3") ActiveRecord::Base.establish_connection(config) end
  76. Use Different a Database for Each User 21 def connect_to_user_database(name)

    config = ActiveRecord::Base.configurations["development"] .merge("database" => "db/#{name}.sqlite3") ActiveRecord::Base.establish_connection(config) end
  77. Use Different a Database for Each User 21 def connect_to_user_database(name)

    config = ActiveRecord::Base.configurations["development"] .merge("database" => "db/#{name}.sqlite3") ActiveRecord::Base.establish_connection(config) end require "user_database"
  78. Use Different a Database for Each User 21 namespace :db

    do desc "Add a new user database" task :add => %w[environment load_config] do name = ENV.fetch("DB_NAME") { fail "DB_NAME is required" } connect_to_user_database(name) ActiveRecord::Base.connection end namespace :migrate do desc "Migrate all user databases" task :all => %w[environment load_config] do ActiveRecord::Migration.verbose = ENV.fetch("VERBOSE", "true") == "true" Dir.glob("db/*.sqlite3") do |file| next if file == "db/test.sqlite3" connect_to_user_database(File.basename(file, ".sqlite3")) ActiveRecord::Migrator.migrate( ActiveRecord::Migrator.migrations_paths, ENV["VERSION"] && ENV["VERSION"].to_i ) do |migration| ENV["SCOPE"].blank? || (ENV["SCOPE"] == migration.scope) end end end end end
  79. Use Different a Database for Each User 21 namespace :db

    do desc "Add a new user database" task :add => %w[environment load_config] do name = ENV.fetch("DB_NAME") { fail "DB_NAME is required" } connect_to_user_database(name) ActiveRecord::Base.connection end namespace :migrate do desc "Migrate all user databases" task :all => %w[environment load_config] do ActiveRecord::Migration.verbose = ENV.fetch("VERBOSE", "true") == "true" Dir.glob("db/*.sqlite3") do |file| next if file == "db/test.sqlite3" connect_to_user_database(File.basename(file, ".sqlite3")) ActiveRecord::Migrator.migrate( ActiveRecord::Migrator.migrations_paths, ENV["VERSION"] && ENV["VERSION"].to_i ) do |migration| ENV["SCOPE"].blank? || (ENV["SCOPE"] == migration.scope) end end end end end
  80. Use Different a Database for Each User 21 $ rails

    g resource user name $ rake db:add DB_NAME=ruby_rogues $ rake db:add DB_NAME=grays $ rake db:migrate:all == CreateUsers: migrating ================================== -- create_table(:users) -> 0.0008s == CreateUsers: migrated (0.0008s) ========================= == CreateUsers: migrating ================================== -- create_table(:users) -> 0.0007s == CreateUsers: migrated (0.0008s) =========================
  81. Use Different a Database for Each User 21 $ rails

    g resource user name $ rake db:add DB_NAME=ruby_rogues $ rake db:add DB_NAME=grays $ rake db:migrate:all == CreateUsers: migrating ================================== -- create_table(:users) -> 0.0008s == CreateUsers: migrated (0.0008s) ========================= == CreateUsers: migrating ================================== -- create_table(:users) -> 0.0007s == CreateUsers: migrated (0.0008s) =========================
  82. Use Different a Database for Each User 21 $ rails

    g resource user name $ rake db:add DB_NAME=ruby_rogues $ rake db:add DB_NAME=grays $ rake db:migrate:all == CreateUsers: migrating ================================== -- create_table(:users) -> 0.0008s == CreateUsers: migrated (0.0008s) ========================= == CreateUsers: migrating ================================== -- create_table(:users) -> 0.0007s == CreateUsers: migrated (0.0008s) =========================
  83. Use Different a Database for Each User 21 $ rails

    c >> connect_to_user_database("ruby_rogues") => #<ActiveRecord::ConnectionAdapters::ConnectionPool…> >> User.create!(name: "Chuck") => #<User id: 1, name: "Chuck", …> >> User.create!(name: "Josh") => #<User id: 2, name: "Josh", …> >> User.create!(name: "Avdi") => #<User id: 3, name: "Avdi", …> … >> connect_to_user_database("grays") => #<ActiveRecord::ConnectionAdapters::ConnectionPool…> >> User.create!(name: "James") => #<User id: 1, name: "James", …> >> User.create!(name: "Dana") => #<User id: 2, name: "Dana", …> >> User.create!(name: "Summer") => #<User id: 3, name: "Summer", …>
  84. Use Different a Database for Each User 21 $ rails

    c >> connect_to_user_database("ruby_rogues") => #<ActiveRecord::ConnectionAdapters::ConnectionPool…> >> User.create!(name: "Chuck") => #<User id: 1, name: "Chuck", …> >> User.create!(name: "Josh") => #<User id: 2, name: "Josh", …> >> User.create!(name: "Avdi") => #<User id: 3, name: "Avdi", …> … >> connect_to_user_database("grays") => #<ActiveRecord::ConnectionAdapters::ConnectionPool…> >> User.create!(name: "James") => #<User id: 1, name: "James", …> >> User.create!(name: "Dana") => #<User id: 2, name: "Dana", …> >> User.create!(name: "Summer") => #<User id: 3, name: "Summer", …>
  85. Use Different a Database for Each User 21 $ rails

    c >> connect_to_user_database("ruby_rogues") => #<ActiveRecord::ConnectionAdapters::ConnectionPool…> >> User.create!(name: "Chuck") => #<User id: 1, name: "Chuck", …> >> User.create!(name: "Josh") => #<User id: 2, name: "Josh", …> >> User.create!(name: "Avdi") => #<User id: 3, name: "Avdi", …> … >> connect_to_user_database("grays") => #<ActiveRecord::ConnectionAdapters::ConnectionPool…> >> User.create!(name: "James") => #<User id: 1, name: "James", …> >> User.create!(name: "Dana") => #<User id: 2, name: "Dana", …> >> User.create!(name: "Summer") => #<User id: 3, name: "Summer", …>
  86. Use Different a Database for Each User 21 class ApplicationController

    < ActionController::Base protect_from_forgery before_filter :connect_to_database private def connect_to_database connect_to_user_database(request.subdomains.first) end end
  87. Use Different a Database for Each User 21 class ApplicationController

    < ActionController::Base protect_from_forgery before_filter :connect_to_database private def connect_to_database connect_to_user_database(request.subdomains.first) end end
  88. Why Do This? I’m trying to give you new ideas

    Studies show this will help you solve Rails problems Also, it’s fun! Remember to have fun
  89. Write Files Atomically 23 class Comment < ActiveRecord::Base # ...

    Q_DIR = (Rails.root + "comment_queue").tap(&:mkpath) after_save :queue_comment def queue_comment File.atomic_write(Q_DIR + "#{id}.txt") do |f| f.puts "Article: #{article.subject}" f.puts "User: #{user.name}" f.puts body end end end
  90. Write Files Atomically 23 class Comment < ActiveRecord::Base # ...

    Q_DIR = (Rails.root + "comment_queue").tap(&:mkpath) after_save :queue_comment def queue_comment File.atomic_write(Q_DIR + "#{id}.txt") do |f| f.puts "Article: #{article.subject}" f.puts "User: #{user.name}" f.puts body end end end
  91. Write Files Atomically 23 $ ls comment_queue ls: comment_queue: No

    such file or directory $ rails r 'Comment.new(body: "Queue me.").tap { |c| c.article, c.user = Article.first, User.first; c.save! }' $ ls comment_queue 1.txt $ cat comment_queue/1.txt Article: JEG2's Rails Hacks User: James Edward Gray II Queue me.
  92. Write Files Atomically 23 $ ls comment_queue ls: comment_queue: No

    such file or directory $ rails r 'Comment.new(body: "Queue me.").tap { |c| c.article, c.user = Article.first, User.first; c.save! }' $ ls comment_queue 1.txt $ cat comment_queue/1.txt Article: JEG2's Rails Hacks User: James Edward Gray II Queue me.
  93. Merge Nested Hashes 24 $ rails c Loading development environment

    (Rails 3.2.3) >> {nested: {one: 1}}.merge(nested: {two: 2}) => {:nested=>{:two=>2}} >> {nested: {one: 1}}.deep_merge(nested: {two: 2}) => {:nested=>{:one=>1, :two=>2}}
  94. Merge Nested Hashes 24 $ rails c Loading development environment

    (Rails 3.2.3) >> {nested: {one: 1}}.merge(nested: {two: 2}) => {:nested=>{:two=>2}} >> {nested: {one: 1}}.deep_merge(nested: {two: 2}) => {:nested=>{:one=>1, :two=>2}}
  95. Remove Specific Keys From a Hash 25 $ rails c

    Loading development environment (Rails 3.2.3) >> params = {controller: "home", action: "index", from: "Google"} => {:controller=>"home", :action=>"index", :from=>"Google"} >> params.except(:controller, :action) => {:from=>"Google"}
  96. Remove Specific Keys From a Hash 25 $ rails c

    Loading development environment (Rails 3.2.3) >> params = {controller: "home", action: "index", from: "Google"} => {:controller=>"home", :action=>"index", :from=>"Google"} >> params.except(:controller, :action) => {:from=>"Google"}
  97. Add Defaults to Hash 26 $ rails c Loading development

    environment (Rails 3.2.3) >> {required: true}.merge(optional: true) => {:required=>true, :optional=>true} >> {required: true}.reverse_merge(optional: true) => {:optional=>true, :required=>true} >> {required: true, optional: false}.merge(optional: true) => {:required=>true, :optional=>true} >> {required: true, optional: false}.reverse_merge(optional: true) => {:optional=>false, :required=>true}
  98. Add Defaults to Hash 26 $ rails c Loading development

    environment (Rails 3.2.3) >> {required: true}.merge(optional: true) => {:required=>true, :optional=>true} >> {required: true}.reverse_merge(optional: true) => {:optional=>true, :required=>true} >> {required: true, optional: false}.merge(optional: true) => {:required=>true, :optional=>true} >> {required: true, optional: false}.reverse_merge(optional: true) => {:optional=>false, :required=>true}
  99. Add Defaults to Hash 26 $ rails c Loading development

    environment (Rails 3.2.3) >> {required: true}.merge(optional: true) => {:required=>true, :optional=>true} >> {required: true}.reverse_merge(optional: true) => {:optional=>true, :required=>true} >> {required: true, optional: false}.merge(optional: true) => {:required=>true, :optional=>true} >> {required: true, optional: false}.reverse_merge(optional: true) => {:optional=>false, :required=>true}
  100. Add Defaults to Hash 26 $ rails c Loading development

    environment (Rails 3.2.3) >> {required: true}.merge(optional: true) => {:required=>true, :optional=>true} >> {required: true}.reverse_merge(optional: true) => {:optional=>true, :required=>true} >> {required: true, optional: false}.merge(optional: true) => {:required=>true, :optional=>true} >> {required: true, optional: false}.reverse_merge(optional: true) => {:optional=>false, :required=>true}
  101. Answer Questions About Strings 27 $ rails g migration add_status_to_articles

    status class AddStatusToArticles < ActiveRecord::Migration def change add_column :articles, :status, :string, default: "Draft", null: false end end
  102. Answer Questions About Strings 27 $ rails g migration add_status_to_articles

    status class AddStatusToArticles < ActiveRecord::Migration def change add_column :articles, :status, :string, default: "Draft", null: false end end
  103. Answer Questions About Strings 27 $ rails c Loading development

    environment (Rails 3.2.3) >> env = Rails.env => "development" >> env.development? => true >> env.test? => false >> "magic".inquiry.magic? => true >> article = Article.first => #<Article id: 1, …, status: "Draft"> >> article.draft? => true >> article.published? => false
  104. Answer Questions About Strings 27 $ rails c Loading development

    environment (Rails 3.2.3) >> env = Rails.env => "development" >> env.development? => true >> env.test? => false >> "magic".inquiry.magic? => true >> article = Article.first => #<Article id: 1, …, status: "Draft"> >> article.draft? => true >> article.published? => false
  105. Answer Questions About Strings 27 $ rails c Loading development

    environment (Rails 3.2.3) >> env = Rails.env => "development" >> env.development? => true >> env.test? => false >> "magic".inquiry.magic? => true >> article = Article.first => #<Article id: 1, …, status: "Draft"> >> article.draft? => true >> article.published? => false
  106. Answer Questions About Strings 27 $ rails c Loading development

    environment (Rails 3.2.3) >> env = Rails.env => "development" >> env.development? => true >> env.test? => false >> "magic".inquiry.magic? => true >> article = Article.first => #<Article id: 1, …, status: "Draft"> >> article.draft? => true >> article.published? => false
  107. Answer Questions About Strings 27 class Article < ActiveRecord::Base #

    ... STATUSES = %w[Draft Published] validates_inclusion_of :status, in: STATUSES def method_missing(method, *args, &block) if method =~ /\A#{STATUSES.map(&:downcase).join("|")}\?\z/ status.downcase.inquiry.send(method) else super end end end
  108. Answer Questions About Strings 27 class Article < ActiveRecord::Base #

    ... STATUSES = %w[Draft Published] validates_inclusion_of :status, in: STATUSES def method_missing(method, *args, &block) if method =~ /\A#{STATUSES.map(&:downcase).join("|")}\?\z/ status.downcase.inquiry.send(method) else super end end end
  109. Answer Questions About Strings 27 class Article < ActiveRecord::Base #

    ... STATUSES = %w[Draft Published] validates_inclusion_of :status, in: STATUSES def method_missing(method, *args, &block) if method =~ /\A#{STATUSES.map(&:downcase).join("|")}\?\z/ status.downcase.inquiry.send(method) else super end end end
  110. Get you on the cover of a magazine The hairdo

    may be required http://www.rubyinside.com/rubys-popularity-scales-new-heights-19.html 28
  111. Hide Comments From Your Users 30 <!-- HTML comments stay

    in the rendered content --> <%# ERb comments do not %> <h1>Home Page</h1> From The Rails View
  112. Hide Comments From Your Users 30 <!-- HTML comments stay

    in the rendered content --> <%# ERb comments do not %> <h1>Home Page</h1> From The Rails View
  113. Hide Comments From Your Users 30 <!-- HTML comments stay

    in the rendered content --> <%# ERb comments do not %> <h1>Home Page</h1> <body> <!-- HTML comments stay in the rendered content --> <h1>Home Page</h1> </body> From The Rails View
  114. Understand a Shorter ERb Syntax 31 # ... module Blog

    class Application < Rails::Application # ... # Broken: config.action_view.erb_trim_mode = '%' ActionView::Template::Handlers::ERB.erb_implementation = Class.new(ActionView::Template::Handlers::Erubis) do include ::Erubis::PercentLineEnhancer end end end % if current_user.try(:admin?) <%= render "edit_links" %> % end
  115. Understand a Shorter ERb Syntax 31 # ... module Blog

    class Application < Rails::Application # ... # Broken: config.action_view.erb_trim_mode = '%' ActionView::Template::Handlers::ERB.erb_implementation = Class.new(ActionView::Template::Handlers::Erubis) do include ::Erubis::PercentLineEnhancer end end end % if current_user.try(:admin?) <%= render "edit_links" %> % end
  116. Understand a Shorter ERb Syntax 31 # ... module Blog

    class Application < Rails::Application # ... # Broken: config.action_view.erb_trim_mode = '%' ActionView::Template::Handlers::ERB.erb_implementation = Class.new(ActionView::Template::Handlers::Erubis) do include ::Erubis::PercentLineEnhancer end end end % if current_user.try(:admin?) <%= render "edit_links" %> % end
  117. Understand a Shorter ERb Syntax 31 # ... module Blog

    class Application < Rails::Application # ... # Broken: config.action_view.erb_trim_mode = '%' ActionView::Template::Handlers::ERB.erb_implementation = Class.new(ActionView::Template::Handlers::Erubis) do include ::Erubis::PercentLineEnhancer end end end % if current_user.try(:admin?) <%= render "edit_links" %> % end
  118. Use Blocks to Avoid Assignments in Views 32 <table> <%

    @cart.products.each do |product| %> <tr> <td><%= product.name %></td> <td><%= number_to_currency product.price %></td> </tr> <% end %> <tr> <td>Subtotal</td> <td><%= number_to_currency @cart.total %></td> </tr> <tr> <td>Tax</td> <td><%= number_to_currency(tax = calculate_tax(@cart.total)) %></td> </tr> <tr> <td>Total</td> <td><%= number_to_currency(@cart.total + tax) %></td> </tr> </table> From The Rails View
  119. Use Blocks to Avoid Assignments in Views 32 <table> <%

    @cart.products.each do |product| %> <tr> <td><%= product.name %></td> <td><%= number_to_currency product.price %></td> </tr> <% end %> <tr> <td>Subtotal</td> <td><%= number_to_currency @cart.total %></td> </tr> <tr> <td>Tax</td> <td><%= number_to_currency(tax = calculate_tax(@cart.total)) %></td> </tr> <tr> <td>Total</td> <td><%= number_to_currency(@cart.total + tax) %></td> </tr> </table> From The Rails View
  120. Use Blocks to Avoid Assignments in Views 32 <table> <%

    @cart.products.each do |product| %> <tr> <td><%= product.name %></td> <td><%= number_to_currency product.price %></td> </tr> <% end %> <tr> <td>Subtotal</td> <td><%= number_to_currency @cart.total %></td> </tr> <tr> <td>Tax</td> <td><%= number_to_currency(tax = calculate_tax(@cart.total)) %></td> </tr> <tr> <td>Total</td> <td><%= number_to_currency(@cart.total + tax) %></td> </tr> </table> From The Rails View
  121. <table> <% @cart.products.each do |product| %> <tr> <td><%= product.name %></td>

    <td><%= number_to_currency product.price %></td> </tr> <% end %> <tr> <td>Subtotal</td> <td><%= number_to_currency @cart.total %></td> </tr> <% calculate_tax @cart.total do |tax| %> <tr> <td>Tax</td> <td><%= number_to_currency tax %></td> </tr> <tr> <td>Total</td> <td><%= number_to_currency(@cart.total + tax) %></td> </tr> <% end %> </table> Use Blocks to Avoid Assignments in Views 32 From The Rails View
  122. <table> <% @cart.products.each do |product| %> <tr> <td><%= product.name %></td>

    <td><%= number_to_currency product.price %></td> </tr> <% end %> <tr> <td>Subtotal</td> <td><%= number_to_currency @cart.total %></td> </tr> <% calculate_tax @cart.total do |tax| %> <tr> <td>Tax</td> <td><%= number_to_currency tax %></td> </tr> <tr> <td>Total</td> <td><%= number_to_currency(@cart.total + tax) %></td> </tr> <% end %> </table> Use Blocks to Avoid Assignments in Views 32 From The Rails View
  123. Use Blocks to Avoid Assignments in Views 32 module CartHelper

    def calculate_tax(total, user = current_user) tax = TaxTable.for(user).calculate(total) if block_given? yield tax else tax end end end From The Rails View
  124. Use Blocks to Avoid Assignments in Views 32 module CartHelper

    def calculate_tax(total, user = current_user) tax = TaxTable.for(user).calculate(total) if block_given? yield tax else tax end end end From The Rails View
  125. Generate Multiple Tags at Once 33 From José Valim <h1>Articles</h1>

    <% @articles.each do |article| %> <%= content_tag_for(:div, article) do %> <h2><%= article.subject %></h2> <% end %> <% end %>
  126. Generate Multiple Tags at Once 33 From José Valim <h1>Articles</h1>

    <% @articles.each do |article| %> <%= content_tag_for(:div, article) do %> <h2><%= article.subject %></h2> <% end %> <% end %>
  127. Generate Multiple Tags at Once 33 From José Valim <h1>Articles</h1>

    <% @articles.each do |article| %> <%= content_tag_for(:div, article) do %> <h2><%= article.subject %></h2> <% end %> <% end %> <h1>Articles</h1> <%= content_tag_for(:div, @articles) do |article| %> <h2><%= article.subject %></h2> <% end %>
  128. Generate Multiple Tags at Once 33 From José Valim <h1>Articles</h1>

    <% @articles.each do |article| %> <%= content_tag_for(:div, article) do %> <h2><%= article.subject %></h2> <% end %> <% end %> <h1>Articles</h1> <%= content_tag_for(:div, @articles) do |article| %> <h2><%= article.subject %></h2> <% end %>
  129. Render Any Object 34 From José Valim class Event <

    ActiveRecord::Base # ... def to_partial_path "events/#{trigger}" # events/edit or events/view end end
  130. Render Any Object 34 From José Valim class Event <

    ActiveRecord::Base # ... def to_partial_path "events/#{trigger}" # events/edit or events/view end end <%= render partial: @events, as: :event %>
  131. Render Any Object 34 From José Valim class Event <

    ActiveRecord::Base # ... def to_partial_path "events/#{trigger}" # events/edit or events/view end end <%= render partial: @events, as: :event %>
  132. Render Any Object 34 From José Valim class Event <

    ActiveRecord::Base # ... def to_partial_path "events/#{trigger}" # events/edit or events/view end end <%= render partial: @events, as: :event %>
  133. Group Menu Entries 35 From The Rails View <%= select_tag(

    :grouped_menu, grouped_options_for_select( "Group A" => %w[One Two Three], "Group B" => %w[One Two Three] ) ) %>
  134. Group Menu Entries 35 From The Rails View <%= select_tag(

    :grouped_menu, grouped_options_for_select( "Group A" => %w[One Two Three], "Group B" => %w[One Two Three] ) ) %>
  135. Build Forms the Way You Like Them 36 From The

    Rails View class LabeledFieldsWithErrors < ActionView::Helpers::FormBuilder def errors_for(attribute) if (errors = object.errors[attribute]).any? @template.content_tag(:span, errors.to_sentence, class: "error") end end def method_missing(method, *args, &block) if %r{ \A (?<labeled>labeled_)? (?<wrapped>\w+?) (?<with_errors>_with_errors)? \z }x =~ method and respond_to?(wrapped) and [labeled, with_errors].any?(&:present?) attribute, tags = args.first, [ ] tags << label(attribute) if labeled.present? tags << send(wrapped, *args, &block) tags << errors_for(attribute) if with_errors.present? tags.join(" ").html_safe else super end end end
  136. Build Forms the Way You Like Them 36 From The

    Rails View class LabeledFieldsWithErrors < ActionView::Helpers::FormBuilder def errors_for(attribute) if (errors = object.errors[attribute]).any? @template.content_tag(:span, errors.to_sentence, class: "error") end end def method_missing(method, *args, &block) if %r{ \A (?<labeled>labeled_)? (?<wrapped>\w+?) (?<with_errors>_with_errors)? \z }x =~ method and respond_to?(wrapped) and [labeled, with_errors].any?(&:present?) attribute, tags = args.first, [ ] tags << label(attribute) if labeled.present? tags << send(wrapped, *args, &block) tags << errors_for(attribute) if with_errors.present? tags.join(" ").html_safe else super end end end
  137. Build Forms the Way You Like Them 36 From The

    Rails View class LabeledFieldsWithErrors < ActionView::Helpers::FormBuilder def errors_for(attribute) if (errors = object.errors[attribute]).any? @template.content_tag(:span, errors.to_sentence, class: "error") end end def method_missing(method, *args, &block) if %r{ \A (?<labeled>labeled_)? (?<wrapped>\w+?) (?<with_errors>_with_errors)? \z }x =~ method and respond_to?(wrapped) and [labeled, with_errors].any?(&:present?) attribute, tags = args.first, [ ] tags << label(attribute) if labeled.present? tags << send(wrapped, *args, &block) tags << errors_for(attribute) if with_errors.present? tags.join(" ").html_safe else super end end end
  138. Build Forms the Way You Like Them 36 From The

    Rails View class LabeledFieldsWithErrors < ActionView::Helpers::FormBuilder def errors_for(attribute) if (errors = object.errors[attribute]).any? @template.content_tag(:span, errors.to_sentence, class: "error") end end def method_missing(method, *args, &block) if %r{ \A (?<labeled>labeled_)? (?<wrapped>\w+?) (?<with_errors>_with_errors)? \z }x =~ method and respond_to?(wrapped) and [labeled, with_errors].any?(&:present?) attribute, tags = args.first, [ ] tags << label(attribute) if labeled.present? tags << send(wrapped, *args, &block) tags << errors_for(attribute) if with_errors.present? tags.join(" ").html_safe else super end end end
  139. Build Forms the Way You Like Them 36 From The

    Rails View # ... module Blog class Application < Rails::Application # ... require "labeled_fields_with_errors" config.action_view.default_form_builder = LabeledFieldsWithErrors config.action_view.field_error_proc = ->(field, _) { field } end end
  140. Build Forms the Way You Like Them 36 From The

    Rails View # ... module Blog class Application < Rails::Application # ... require "labeled_fields_with_errors" config.action_view.default_form_builder = LabeledFieldsWithErrors config.action_view.field_error_proc = ->(field, _) { field } end end
  141. Build Forms the Way You Like Them 36 From The

    Rails View # ... module Blog class Application < Rails::Application # ... require "labeled_fields_with_errors" config.action_view.default_form_builder = LabeledFieldsWithErrors config.action_view.field_error_proc = ->(field, _) { field } end end
  142. Build Forms the Way You Like Them 36 From The

    Rails View <%= form_for @article do |f| %> <p><%= f.text_field :subject %></p> <p><%= f.labeled_text_field :subject %></p> <p><%= f.text_field_with_errors :subject %></p> <p><%= f.labeled_text_field_with_errors :subject %></p> <%= f.submit %> <% end %>
  143. Build Forms the Way You Like Them 36 From The

    Rails View <%= form_for @article do |f| %> <p><%= f.text_field :subject %></p> <p><%= f.labeled_text_field :subject %></p> <p><%= f.text_field_with_errors :subject %></p> <p><%= f.labeled_text_field_with_errors :subject %></p> <%= f.submit %> <% end %>
  144. Build Forms the Way You Like Them 36 From The

    Rails View <!-- ... --> <p><input id="article_subject" name="article[subject]" size="30" type="text" value="" /></p> <p><label for="article_subject">Subject</label> <input id="article_subject" name="article[subject]" size="30" type="text" value="" /></p> <p><input id="article_subject" name="article[subject]" size="30" type="text" value="" /> <span class="error">can't be blank</span></p> <p><label for="article_subject">Subject</label> <input id="article_subject" name="article[subject]" size="30" type="text" value="" /> <span class="error">can't be blank</span></p> <!-- ... -->
  145. Build Forms the Way You Like Them 36 From The

    Rails View <!-- ... --> <p><input id="article_subject" name="article[subject]" size="30" type="text" value="" /></p> <p><label for="article_subject">Subject</label> <input id="article_subject" name="article[subject]" size="30" type="text" value="" /></p> <p><input id="article_subject" name="article[subject]" size="30" type="text" value="" /> <span class="error">can't be blank</span></p> <p><label for="article_subject">Subject</label> <input id="article_subject" name="article[subject]" size="30" type="text" value="" /> <span class="error">can't be blank</span></p> <!-- ... -->
  146. Build Forms the Way You Like Them 36 From The

    Rails View <!-- ... --> <p><input id="article_subject" name="article[subject]" size="30" type="text" value="" /></p> <p><label for="article_subject">Subject</label> <input id="article_subject" name="article[subject]" size="30" type="text" value="" /></p> <p><input id="article_subject" name="article[subject]" size="30" type="text" value="" /> <span class="error">can't be blank</span></p> <p><label for="article_subject">Subject</label> <input id="article_subject" name="article[subject]" size="30" type="text" value="" /> <span class="error">can't be blank</span></p> <!-- ... -->
  147. Route Exceptions From José Valim 38 # ... module Blog

    class Application < Rails::Application # ... config.exceptions_app = routes end end
  148. Route Exceptions From José Valim 38 # ... module Blog

    class Application < Rails::Application # ... config.exceptions_app = routes end end
  149. Route Exceptions From José Valim 38 # ... module Blog

    class Application < Rails::Application # ... config.exceptions_app = routes end end Blog::Application.routes.draw do # ... match "/404", to: "errors#not_found" end
  150. Route Exceptions From José Valim 38 # ... module Blog

    class Application < Rails::Application # ... config.exceptions_app = routes end end Blog::Application.routes.draw do # ... match "/404", to: "errors#not_found" end
  151. Route to Sinatra From Crafting Rails Applications 39 source 'https://rubygems.org'

    # ... gem "resque", require: "resque/server" module AdminValidator module_function def matches?(request) if (id = request.env["rack.session"]["user_id"]) current_user = User.find_by_id(id) current_user.try(:admin?) else false end end end
  152. Route to Sinatra From Crafting Rails Applications 39 source 'https://rubygems.org'

    # ... gem "resque", require: "resque/server" module AdminValidator module_function def matches?(request) if (id = request.env["rack.session"]["user_id"]) current_user = User.find_by_id(id) current_user.try(:admin?) else false end end end
  153. Route to Sinatra From Crafting Rails Applications 39 source 'https://rubygems.org'

    # ... gem "resque", require: "resque/server" module AdminValidator module_function def matches?(request) if (id = request.env["rack.session"]["user_id"]) current_user = User.find_by_id(id) current_user.try(:admin?) else false end end end
  154. Route to Sinatra From Crafting Rails Applications 39 Blog::Application.routes.draw do

    # ... require "admin_validator" constraints AdminValidator do mount Resque::Server, at: "/admin/resque" end end
  155. Route to Sinatra From Crafting Rails Applications 39 Blog::Application.routes.draw do

    # ... require "admin_validator" constraints AdminValidator do mount Resque::Server, at: "/admin/resque" end end
  156. Route to Sinatra From Crafting Rails Applications 39 Blog::Application.routes.draw do

    # ... require "admin_validator" constraints AdminValidator do mount Resque::Server, at: "/admin/resque" end end
  157. Route to Sinatra From Crafting Rails Applications 39 Blog::Application.routes.draw do

    # ... require "admin_validator" constraints AdminValidator do mount Resque::Server, at: "/admin/resque" end end
  158. Stream CSV to Users 40 class ArticlesController < ApplicationController def

    index respond_to do |format| format.html do @articles = Article.all end format.csv do headers["Content-Disposition"] = %Q{attachment; filename="articles.csv"} self.response_body = Enumerator.new do |response| csv = CSV.new(response, row_sep: "\n") csv << %w[Subject Created Status] Article.find_each do |article| csv << [ article.subject, article.created_at.to_s(:long), article.status ] end end end end end # ... end
  159. Stream CSV to Users 40 class ArticlesController < ApplicationController def

    index respond_to do |format| format.html do @articles = Article.all end format.csv do headers["Content-Disposition"] = %Q{attachment; filename="articles.csv"} self.response_body = Enumerator.new do |response| csv = CSV.new(response, row_sep: "\n") csv << %w[Subject Created Status] Article.find_each do |article| csv << [ article.subject, article.created_at.to_s(:long), article.status ] end end end end end # ... end
  160. Stream CSV to Users 40 class ArticlesController < ApplicationController def

    index respond_to do |format| format.html do @articles = Article.all end format.csv do headers["Content-Disposition"] = %Q{attachment; filename="articles.csv"} self.response_body = Enumerator.new do |response| csv = CSV.new(response, row_sep: "\n") csv << %w[Subject Created Status] Article.find_each do |article| csv << [ article.subject, article.created_at.to_s(:long), article.status ] end end end end end # ... end
  161. Stream CSV to Users 40 class ArticlesController < ApplicationController def

    index respond_to do |format| format.html do @articles = Article.all end format.csv do headers["Content-Disposition"] = %Q{attachment; filename="articles.csv"} self.response_body = Enumerator.new do |response| csv = CSV.new(response, row_sep: "\n") csv << %w[Subject Created Status] Article.find_each do |article| csv << [ article.subject, article.created_at.to_s(:long), article.status ] end end end end end # ... end
  162. Stream CSV to Users 40 class ArticlesController < ApplicationController def

    index respond_to do |format| format.html do @articles = Article.all end format.csv do headers["Content-Disposition"] = %Q{attachment; filename="articles.csv"} self.response_body = Enumerator.new do |response| csv = CSV.new(response, row_sep: "\n") csv << %w[Subject Created Status] Article.find_each do |article| csv << [ article.subject, article.created_at.to_s(:long), article.status ] end end end end end # ... end
  163. Stream CSV to Users 40 class ArticlesController < ApplicationController def

    index respond_to do |format| format.html do @articles = Article.all end format.csv do headers["Content-Disposition"] = %Q{attachment; filename="articles.csv"} self.response_body = Enumerator.new do |response| csv = CSV.new(response, row_sep: "\n") csv << %w[Subject Created Status] Article.find_each do |article| csv << [ article.subject, article.created_at.to_s(:long), article.status ] end end end end end # ... end
  164. Do Some Work in the Background From Crafting Rails Applications

    41 $ rails g migration add_stats_to_articles stats:text
  165. Do Some Work in the Background From Crafting Rails Applications

    41 $ rails g migration add_stats_to_articles stats:text
  166. Do Some Work in the Background From Crafting Rails Applications

    41 class Article < ActiveRecord::Base # ... serialize :stats def calculate_stats words = Hash.new(0) body.to_s.scan(/\S+/) { |word| words[word] += 1 } sleep 10 # simulate a lot of work self.stats = {words: words} end end $ rails g migration add_stats_to_articles stats:text
  167. Do Some Work in the Background From Crafting Rails Applications

    41 class Article < ActiveRecord::Base # ... serialize :stats def calculate_stats words = Hash.new(0) body.to_s.scan(/\S+/) { |word| words[word] += 1 } sleep 10 # simulate a lot of work self.stats = {words: words} end end $ rails g migration add_stats_to_articles stats:text
  168. Do Some Work in the Background From Crafting Rails Applications

    41 class Article < ActiveRecord::Base # ... require "thread" def self.queue; @queue ||= Queue.new end def self.thread @thread ||= Thread.new do while job = queue.pop job.call end end end thread # start the Thread end
  169. Do Some Work in the Background From Crafting Rails Applications

    41 class Article < ActiveRecord::Base # ... require "thread" def self.queue; @queue ||= Queue.new end def self.thread @thread ||= Thread.new do while job = queue.pop job.call end end end thread # start the Thread end
  170. Do Some Work in the Background From Crafting Rails Applications

    41 class Article < ActiveRecord::Base # ... require "thread" def self.queue; @queue ||= Queue.new end def self.thread @thread ||= Thread.new do while job = queue.pop job.call end end end thread # start the Thread end
  171. Do Some Work in the Background From Crafting Rails Applications

    41 class Article < ActiveRecord::Base # ... require "thread" def self.queue; @queue ||= Queue.new end def self.thread @thread ||= Thread.new do while job = queue.pop job.call end end end thread # start the Thread end
  172. Do Some Work in the Background From Crafting Rails Applications

    41 class Article < ActiveRecord::Base # ... require "thread" def self.queue; @queue ||= Queue.new end def self.thread @thread ||= Thread.new do while job = queue.pop job.call end end end thread # start the Thread end
  173. Do Some Work in the Background From Crafting Rails Applications

    41 class Article < ActiveRecord::Base # ... after_create :add_stats def add_stats self.class.queue << -> { calculate_stats; save } end end
  174. Do Some Work in the Background From Crafting Rails Applications

    41 class Article < ActiveRecord::Base # ... after_create :add_stats def add_stats self.class.queue << -> { calculate_stats; save } end end
  175. Do Some Work in the Background From Crafting Rails Applications

    41 $ rails c Loading development environment (Rails 3.2.3) >> Article.create!(subject: "Stats", body: "Lorem ipsum…"); Time.now.strftime("%H:%M:%S") => "15:24:10" >> [Article.last.stats, Time.now.strftime("%H:%M:%S")] => [nil, "15:24:13"] >> [Article.last.stats, Time.now.strftime("%H:%M:%S")] =>[{:words=>{"Lorem"=>1, "ipsum"=>1, …}, "15:24:26"]
  176. Do Some Work in the Background From Crafting Rails Applications

    41 $ rails c Loading development environment (Rails 3.2.3) >> Article.create!(subject: "Stats", body: "Lorem ipsum…"); Time.now.strftime("%H:%M:%S") => "15:24:10" >> [Article.last.stats, Time.now.strftime("%H:%M:%S")] => [nil, "15:24:13"] >> [Article.last.stats, Time.now.strftime("%H:%M:%S")] =>[{:words=>{"Lorem"=>1, "ipsum"=>1, …}, "15:24:26"]
  177. Do Some Work in the Background From Crafting Rails Applications

    41 $ rails c Loading development environment (Rails 3.2.3) >> Article.create!(subject: "Stats", body: "Lorem ipsum…"); Time.now.strftime("%H:%M:%S") => "15:24:10" >> [Article.last.stats, Time.now.strftime("%H:%M:%S")] => [nil, "15:24:13"] >> [Article.last.stats, Time.now.strftime("%H:%M:%S")] =>[{:words=>{"Lorem"=>1, "ipsum"=>1, …}, "15:24:26"]
  178. Publish a Static Site Using Rails 42 Static::Application.configure do #

    ... # Show full error reports and disable caching config.consider_all_requests_local = true config.action_controller.perform_caching = !!ENV["GENERATING_SITE"] # ... # Don't fallback to assets pipeline if a precompiled asset is missed config.assets.compile = !ENV["GENERATING_SITE"] # Generate digests for assets URLs config.assets.digest = !!ENV["GENERATING_SITE"] # ... end
  179. Publish a Static Site Using Rails 42 Static::Application.configure do #

    ... # Show full error reports and disable caching config.consider_all_requests_local = true config.action_controller.perform_caching = !!ENV["GENERATING_SITE"] # ... # Don't fallback to assets pipeline if a precompiled asset is missed config.assets.compile = !ENV["GENERATING_SITE"] # Generate digests for assets URLs config.assets.digest = !!ENV["GENERATING_SITE"] # ... end
  180. Publish a Static Site Using Rails 42 class ApplicationController <

    ActionController::Base protect_from_forgery if ENV["GENERATING_SITE"] after_filter do |c| c.cache_page(nil, nil, Zlib::BEST_COMPRESSION) end end end
  181. Publish a Static Site Using Rails 42 class ApplicationController <

    ActionController::Base protect_from_forgery if ENV["GENERATING_SITE"] after_filter do |c| c.cache_page(nil, nil, Zlib::BEST_COMPRESSION) end end end
  182. Publish a Static Site Using Rails 42 require "open-uri" namespace

    :static do desc "Generate a static copy of the site" task :generate => %w[environment assets:precompile] do site = ENV.fetch("RSYNC_SITE_TO") { fail "Must set RSYNC_SITE_TO" } server = spawn( {"GENERATING_SITE" => "true"}, "bundle exec rails s thin -p 3001" ) sleep 10 # FIXME: start when the server is up # ... end
  183. Publish a Static Site Using Rails 42 require "open-uri" namespace

    :static do desc "Generate a static copy of the site" task :generate => %w[environment assets:precompile] do site = ENV.fetch("RSYNC_SITE_TO") { fail "Must set RSYNC_SITE_TO" } server = spawn( {"GENERATING_SITE" => "true"}, "bundle exec rails s thin -p 3001" ) sleep 10 # FIXME: start when the server is up # ... end
  184. Publish a Static Site Using Rails 42 require "open-uri" namespace

    :static do desc "Generate a static copy of the site" task :generate => %w[environment assets:precompile] do # ... # FIXME: improve the following super crude spider paths = %w[/] files = [ ] while path = paths.shift files << File.join("public", path.sub(%r{/\z}, "/index") + ".html") File.unlink(files.last) if File.exist? files.last files << files.last + ".gz" File.unlink(files.last) if File.exist? files.last page = open("http://localhost:3001#{path}") { |url| url.read } page.scan(/<a[^>]+href="([^"]+)"/) do |link| paths << link.first end end # ... end end
  185. Publish a Static Site Using Rails 42 require "open-uri" namespace

    :static do desc "Generate a static copy of the site" task :generate => %w[environment assets:precompile] do # ... # FIXME: improve the following super crude spider paths = %w[/] files = [ ] while path = paths.shift files << File.join("public", path.sub(%r{/\z}, "/index") + ".html") File.unlink(files.last) if File.exist? files.last files << files.last + ".gz" File.unlink(files.last) if File.exist? files.last page = open("http://localhost:3001#{path}") { |url| url.read } page.scan(/<a[^>]+href="([^"]+)"/) do |link| paths << link.first end end # ... end end
  186. Publish a Static Site Using Rails 42 require "open-uri" namespace

    :static do desc "Generate a static copy of the site" task :generate => %w[environment assets:precompile] do # ... system("rsync -a public #{site}") Process.kill("INT", server) Process.wait(server) system("bundle exec rake assets:clean") files.each do |file| File.unlink(file) end end end
  187. Publish a Static Site Using Rails 42 require "open-uri" namespace

    :static do desc "Generate a static copy of the site" task :generate => %w[environment assets:precompile] do # ... system("rsync -a public #{site}") Process.kill("INT", server) Process.wait(server) system("bundle exec rake assets:clean") files.each do |file| File.unlink(file) end end end