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. 10 Things You Didn’t Know
    Rails Could Do

    View full-size slide

  2. 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

    View full-size slide

  3. 10 Things You Didn’t Know
    Rails Could Do

    View full-size slide

  4. 10 Things You Didn’t Know
    Rails Could Do
    4.2⨉ more awesome!
    42

    View full-size slide

  5. Rails 3.2.3 Can…

    View full-size slide

  6. Get You a Hug, Every Friday
    1
    http://hugfriday.com/

    View full-size slide

  7. Get You a Hug, Every Friday
    1
    http://hugfriday.com/

    View full-size slide

  8. Get You a Hug, Every Friday
    1
    http://hugfriday.com/

    View full-size slide

  9. Get You a Hug, Every Friday
    1
    http://hugfriday.com/

    View full-size slide

  10. Get You a Hug, Every Friday
    1
    http://hugfriday.com/

    View full-size slide

  11. Get You a Hug, Every Friday
    1
    http://hugfriday.com/

    View full-size slide

  12. Get You a Hug, Every Friday
    1
    http://hugfriday.com/

    View full-size slide

  13. Interfaces
    Ways to give Rails a hug

    View full-size slide

  14. Run From a Single File
    From Avdi Grimm
    2

    View full-size slide

  15. Run From a Single File
    From Avdi Grimm
    2

    View full-size slide

  16. Run From a Single File
    From Avdi Grimm
    2

    View full-size slide

  17. Run From a Single File
    From Avdi Grimm
    2

    View full-size slide

  18. Run From a Single File
    From Avdi Grimm
    2

    View full-size slide

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

    View full-size slide

  20. 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

    View full-size slide

  21. <%# 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

    View full-size slide

  22. $ 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

    View full-size slide

  23. $ 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

    View full-size slide

  24. <%# 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?

    View full-size slide

  25. <%# 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?

    View full-size slide

  26. 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.

    View full-size slide

  27. 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.

    View full-size slide

  28. Remind You of Things
    3

    View full-size slide

  29. 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")
    => #
    >> article = Article.new(subject: "First Post").tap { |a|
    a.user = jeg2; a.save! }
    => #
    >> Comment.new(body: "I need to add this.").tap { |c|
    c.user, c.article = jeg2, article; c.save! }
    => #need to add this.", …>
    >> [Article, Comment, User].map(&:count)
    => [1, 1, 1]
    >> exit
    $ rails r 'p [Article, Comment, User].map(&:count)'
    [0, 0, 0]

    View full-size slide

  30. 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")
    => #
    >> article = Article.new(subject: "First Post").tap { |a|
    a.user = jeg2; a.save! }
    => #
    >> Comment.new(body: "I need to add this.").tap { |c|
    c.user, c.article = jeg2, article; c.save! }
    => #need to add this.", …>
    >> [Article, Comment, User].map(&:count)
    => [1, 1, 1]
    >> exit
    $ rails r 'p [Article, Comment, User].map(&:count)'
    [0, 0, 0]

    View full-size slide

  31. 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")
    => #
    >> article = Article.new(subject: "First Post").tap { |a|
    a.user = jeg2; a.save! }
    => #
    >> Comment.new(body: "I need to add this.").tap { |c|
    c.user, c.article = jeg2, article; c.save! }
    => #need to add this.", …>
    >> [Article, Comment, User].map(&:count)
    => [1, 1, 1]
    >> exit
    $ rails r 'p [Article, Comment, User].map(&:count)'
    [0, 0, 0]

    View full-size slide

  32. 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")
    => #
    >> article = Article.new(subject: "First Post").tap { |a|
    a.user = jeg2; a.save! }
    => #
    >> Comment.new(body: "I need to add this.").tap { |c|
    c.user, c.article = jeg2, article; c.save! }
    => #need to add this.", …>
    >> [Article, Comment, User].map(&:count)
    => [1, 1, 1]
    >> exit
    $ rails r 'p [Article, Comment, User].map(&:count)'
    [0, 0, 0]

    View full-size slide

  33. 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")
    => #
    >> article = Article.new(subject: "First Post").tap { |a|
    a.user = jeg2; a.save! }
    => #
    >> Comment.new(body: "I need to add this.").tap { |c|
    c.user, c.article = jeg2, article; c.save! }
    => #need to add this.", …>
    >> [Article, Comment, User].map(&:count)
    => [1, 1, 1]
    >> exit
    $ rails r 'p [Article, Comment, User].map(&:count)'
    [0, 0, 0]

    View full-size slide

  34. 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"

    View full-size slide

  35. 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"

    View full-size slide

  36. 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

    View full-size slide

  37. 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

    View full-size slide

  38. 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

    View full-size slide

  39. 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

    View full-size slide

  40. 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

    View full-size slide

  41. 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

    View full-size slide

  42. 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

    View full-size slide

  43. Keep You Entertained
    8

    View full-size slide

  44. Keep You Entertained
    8

    View full-size slide

  45. The Database
    A place to store all that drama

    View full-size slide

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

    View full-size slide

  47. 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

    View full-size slide

  48. 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

    View full-size slide

  49. 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

    View full-size slide

  50. 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

    View full-size slide

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

    View full-size slide

  52. 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

    View full-size slide

  53. 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

    View full-size slide

  54. 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

    View full-size slide

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

    View full-size slide

  56. 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

    View full-size slide

  57. 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

    View full-size slide

  58. 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

    View full-size slide

  59. 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

    View full-size slide

  60. 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

    View full-size slide

  61. 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

    View full-size slide

  62. 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

    View full-size slide

  63. 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

    View full-size slide

  64. 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

    View full-size slide

  65. 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

    View full-size slide

  66. 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

    View full-size slide

  67. 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

    View full-size slide

  68. 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

    View full-size slide

  69. 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.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

    View full-size slide

  70. 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.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

    View full-size slide

  71. 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.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

    View full-size slide

  72. “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]"]

    View full-size slide

  73. “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]"]

    View full-size slide

  74. “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]"]

    View full-size slide

  75. Count Records in Groups
    16
    $ rails g resource event article:belongs_to trigger

    View full-size slide

  76. 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
    => #
    >> {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}

    View full-size slide

  77. 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
    => #
    >> {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}

    View full-size slide

  78. 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
    => #
    >> {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}

    View full-size slide

  79. 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

    View full-size slide

  80. 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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  84. 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

    View full-size slide

  85. 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)
    => #"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    XX...", created_at: "2012-04-14 23:02:08",
    updated_at: "2012-04-14 23:02:08">
    >> User.last.bio.size
    => 10000

    View full-size slide

  86. 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)
    => #"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    XX...", created_at: "2012-04-14 23:02:08",
    updated_at: "2012-04-14 23:02:08">
    >> User.last.bio.size
    => 10000

    View full-size slide

  87. Use Full Text Search
    in PostgreSQL
    20
    $ rails g resource article subject body:text
    From PeepCode

    View full-size slide

  88. 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

    View full-size slide

  89. 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

    View full-size slide

  90. 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

    View full-size slide

  91. 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

    View full-size slide

  92. 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

    View full-size slide

  93. 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

    View full-size slide

  94. 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.create!(body: "A stemmed search.")
    => #
    >> Article.create!(body: "You won't find me!")
    => #
    >> 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."]

    View full-size slide

  95. 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.create!(body: "A stemmed search.")
    => #
    >> Article.create!(body: "You won't find me!")
    => #
    >> 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."]

    View full-size slide

  96. 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.create!(body: "A stemmed search.")
    => #
    >> Article.create!(body: "You won't find me!")
    => #
    >> 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."]

    View full-size slide

  97. 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

    View full-size slide

  98. 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

    View full-size slide

  99. 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

    View full-size slide

  100. 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"

    View full-size slide

  101. 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

    View full-size slide

  102. 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

    View full-size slide

  103. 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) =========================

    View full-size slide

  104. 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) =========================

    View full-size slide

  105. 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) =========================

    View full-size slide

  106. Use Different a Database
    for Each User
    21
    $ rails c
    >> connect_to_user_database("ruby_rogues")
    => #
    >> User.create!(name: "Chuck")
    => #
    >> User.create!(name: "Josh")
    => #
    >> User.create!(name: "Avdi")
    => #

    >> connect_to_user_database("grays")
    => #
    >> User.create!(name: "James")
    => #
    >> User.create!(name: "Dana")
    => #
    >> User.create!(name: "Summer")
    => #

    View full-size slide

  107. Use Different a Database
    for Each User
    21
    $ rails c
    >> connect_to_user_database("ruby_rogues")
    => #
    >> User.create!(name: "Chuck")
    => #
    >> User.create!(name: "Josh")
    => #
    >> User.create!(name: "Avdi")
    => #

    >> connect_to_user_database("grays")
    => #
    >> User.create!(name: "James")
    => #
    >> User.create!(name: "Dana")
    => #
    >> User.create!(name: "Summer")
    => #

    View full-size slide

  108. Use Different a Database
    for Each User
    21
    $ rails c
    >> connect_to_user_database("ruby_rogues")
    => #
    >> User.create!(name: "Chuck")
    => #
    >> User.create!(name: "Josh")
    => #
    >> User.create!(name: "Avdi")
    => #

    >> connect_to_user_database("grays")
    => #
    >> User.create!(name: "James")
    => #
    >> User.create!(name: "Dana")
    => #
    >> User.create!(name: "Summer")
    => #

    View full-size slide

  109. 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

    View full-size slide

  110. 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

    View full-size slide

  111. Use Different a Database
    for Each User
    21

    View full-size slide

  112. Use Different a Database
    for Each User
    21

    View full-size slide

  113. Intermission
    Am I going to make it???

    View full-size slide

  114. 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

    View full-size slide

  115. Less Talk, More Tricks!!!

    View full-size slide

  116. Refine Your Fashion Sense
    http://www.flickr.com/photos/oreillyconf/5735037920/
    22

    View full-size slide

  117. Refine Your Fashion Sense
    http://www.flickr.com/photos/oreillyconf/5735037920/ http://www.flickr.com/photos/oreillyconf/5735038376/
    22

    View full-size slide

  118. Refine Your Fashion Sense
    http://www.flickr.com/photos/oreillyconf/5735037920/ http://www.flickr.com/photos/oreillyconf/5735038376/ http://www.flickr.com/photos/aaronp/4609780419/
    22

    View full-size slide

  119. Ruby Extensions
    Updating Ruby’s fashion sense

    View full-size slide

  120. 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

    View full-size slide

  121. 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

    View full-size slide

  122. 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.

    View full-size slide

  123. 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.

    View full-size slide

  124. 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}}

    View full-size slide

  125. 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}}

    View full-size slide

  126. 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"}

    View full-size slide

  127. 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"}

    View full-size slide

  128. 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}

    View full-size slide

  129. 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}

    View full-size slide

  130. 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}

    View full-size slide

  131. 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}

    View full-size slide

  132. Answer Questions
    About Strings
    27
    $ rails g migration add_status_to_articles status

    View full-size slide

  133. 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

    View full-size slide

  134. 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

    View full-size slide

  135. 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.draft?
    => true
    >> article.published?
    => false

    View full-size slide

  136. 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.draft?
    => true
    >> article.published?
    => false

    View full-size slide

  137. 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.draft?
    => true
    >> article.published?
    => false

    View full-size slide

  138. 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.draft?
    => true
    >> article.published?
    => false

    View full-size slide

  139. 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

    View full-size slide

  140. 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

    View full-size slide

  141. 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

    View full-size slide

  142. 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

    View full-size slide

  143. Get you voted
    “Hottest Hacker”
    http://pageman.multiply.com/links/item/21/David_Heinemeier_Hanssons_Blog
    29

    View full-size slide

  144. Get you voted
    “Hottest Hacker”
    http://pageman.multiply.com/links/item/21/David_Heinemeier_Hanssons_Blog
    29

    View full-size slide

  145. Get you voted
    “Hottest Hacker”
    http://pageman.multiply.com/links/item/21/David_Heinemeier_Hanssons_Blog http://torrentfreak.com/richard-stallman-opts-to-disobey-anti-piracy-law-110610/
    29

    View full-size slide

  146. Views
    It’s all about how we look

    View full-size slide

  147. Hide Comments
    From Your Users
    30

    <%# ERb comments do not %>
    Home Page
    From The Rails View

    View full-size slide

  148. Hide Comments
    From Your Users
    30

    <%# ERb comments do not %>
    Home Page
    From The Rails View

    View full-size slide

  149. Hide Comments
    From Your Users
    30

    <%# ERb comments do not %>
    Home Page


    Home Page

    From The Rails View

    View full-size slide

  150. 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

    View full-size slide

  151. 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

    View full-size slide

  152. 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

    View full-size slide

  153. 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

    View full-size slide

  154. Use Blocks to Avoid
    Assignments in Views
    32

    <% @cart.products.each do |product| %>

    <%= product.name %>
    <%= number_to_currency product.price %>

    <% end %>

    Subtotal
    <%= number_to_currency @cart.total %>


    Tax
    <%= number_to_currency(tax = calculate_tax(@cart.total)) %>


    Total
    <%= number_to_currency(@cart.total + tax) %>


    From The Rails View

    View full-size slide

  155. Use Blocks to Avoid
    Assignments in Views
    32

    <% @cart.products.each do |product| %>

    <%= product.name %>
    <%= number_to_currency product.price %>

    <% end %>

    Subtotal
    <%= number_to_currency @cart.total %>


    Tax
    <%= number_to_currency(tax = calculate_tax(@cart.total)) %>


    Total
    <%= number_to_currency(@cart.total + tax) %>


    From The Rails View

    View full-size slide

  156. Use Blocks to Avoid
    Assignments in Views
    32

    <% @cart.products.each do |product| %>

    <%= product.name %>
    <%= number_to_currency product.price %>

    <% end %>

    Subtotal
    <%= number_to_currency @cart.total %>


    Tax
    <%= number_to_currency(tax = calculate_tax(@cart.total)) %>


    Total
    <%= number_to_currency(@cart.total + tax) %>


    From The Rails View

    View full-size slide


  157. <% @cart.products.each do |product| %>

    <%= product.name %>
    <%= number_to_currency product.price %>

    <% end %>

    Subtotal
    <%= number_to_currency @cart.total %>

    <% calculate_tax @cart.total do |tax| %>

    Tax
    <%= number_to_currency tax %>


    Total
    <%= number_to_currency(@cart.total + tax) %>

    <% end %>

    Use Blocks to Avoid
    Assignments in Views
    32
    From The Rails View

    View full-size slide


  158. <% @cart.products.each do |product| %>

    <%= product.name %>
    <%= number_to_currency product.price %>

    <% end %>

    Subtotal
    <%= number_to_currency @cart.total %>

    <% calculate_tax @cart.total do |tax| %>

    Tax
    <%= number_to_currency tax %>


    Total
    <%= number_to_currency(@cart.total + tax) %>

    <% end %>

    Use Blocks to Avoid
    Assignments in Views
    32
    From The Rails View

    View full-size slide

  159. 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

    View full-size slide

  160. 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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  166. 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 %>

    View full-size slide

  167. 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 %>

    View full-size slide

  168. 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 %>

    View full-size slide

  169. Group Menu Entries
    35
    From The Rails View

    View full-size slide

  170. 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]
    ) ) %>

    View full-size slide

  171. 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]
    ) ) %>

    View full-size slide

  172. 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_)?
    (?\w+?)
    (?_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

    View full-size slide

  173. 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_)?
    (?\w+?)
    (?_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

    View full-size slide

  174. 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_)?
    (?\w+?)
    (?_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

    View full-size slide

  175. 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_)?
    (?\w+?)
    (?_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

    View full-size slide

  176. 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

    View full-size slide

  177. 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

    View full-size slide

  178. 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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  181. Build Forms
    the Way You Like Them
    36
    From The Rails View


    Subject


    can't be blank
    Subject

    can't be blank

    View full-size slide

  182. Build Forms
    the Way You Like Them
    36
    From The Rails View


    Subject


    can't be blank
    Subject

    can't be blank

    View full-size slide

  183. Build Forms
    the Way You Like Them
    36
    From The Rails View


    Subject


    can't be blank
    Subject

    can't be blank

    View full-size slide

  184. Inspire Theme Songs
    About Your Work
    http://www.confreaks.com/videos/529-farmhouseconf-ruby-hero-tenderlove
    37

    View full-size slide

  185. The Router and Controllers
    They carry the theme of our applications

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  188. 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

    View full-size slide

  189. 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

    View full-size slide

  190. Route to Sinatra
    From Crafting Rails Applications
    39
    source 'https://rubygems.org'
    # ...
    gem "resque", require: "resque/server"

    View full-size slide

  191. Route to Sinatra
    From Crafting Rails Applications
    39
    source 'https://rubygems.org'
    # ...
    gem "resque", require: "resque/server"

    View full-size slide

  192. 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

    View full-size slide

  193. 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

    View full-size slide

  194. 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

    View full-size slide

  195. 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

    View full-size slide

  196. 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

    View full-size slide

  197. 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

    View full-size slide

  198. 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

    View full-size slide

  199. 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

    View full-size slide

  200. 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

    View full-size slide

  201. 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

    View full-size slide

  202. 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

    View full-size slide

  203. 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

    View full-size slide

  204. 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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  207. 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

    View full-size slide

  208. 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

    View full-size slide

  209. 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

    View full-size slide

  210. 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

    View full-size slide

  211. 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

    View full-size slide

  212. 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

    View full-size slide

  213. 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

    View full-size slide

  214. 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

    View full-size slide

  215. 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

    View full-size slide

  216. 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"]

    View full-size slide

  217. 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"]

    View full-size slide

  218. 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"]

    View full-size slide

  219. Have We All Learned
    Something New Yet?!

    View full-size slide

  220. 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

    View full-size slide

  221. 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

    View full-size slide

  222. 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

    View full-size slide

  223. 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

    View full-size slide

  224. 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

    View full-size slide

  225. 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

    View full-size slide

  226. 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(/]+href="([^"]+)"/) do |link|
    paths << link.first
    end
    end
    # ...
    end
    end

    View full-size slide

  227. 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(/]+href="([^"]+)"/) do |link|
    paths << link.first
    end
    end
    # ...
    end
    end

    View full-size slide

  228. 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

    View full-size slide

  229. 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

    View full-size slide

  230. Publish a Static Site
    Using Rails
    42
    $ rake static:generate RSYNC_SITE_TO=/Users/james/Desktop

    View full-size slide

  231. Publish a Static Site
    Using Rails
    42
    $ rake static:generate RSYNC_SITE_TO=/Users/james/Desktop

    View full-size slide

  232. Did you memorize that?!

    View full-size slide

  233. Did you memorize that?!
    Slides: http://speakerdeck.com/u/jeg2
    Twitter: @JEG2
    Email: [email protected]

    View full-size slide

  234. Thanks!
    Have fun using these Rails tricks

    View full-size slide