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

Rails4: a Christmas tale

Rails4: a Christmas tale

Presentation given during the genevarb meetup, in Geneva (Switzerland)

Yannis Jaquet

December 12, 2012
Tweet

More Decks by Yannis Jaquet

Other Decks in Programming

Transcript

  1. 4
    1

    View Slide

  2. 2

    View Slide

  3. Mass extraction
    • Hash-based & Dynamic finder methods
    rails / activerecord-deprecated_finders
    • Mass assignment protection in Active Record models
    rails/protected_attributes
    • ActiveRecord::SessionStore
    rails/activerecord-session_store
    • Active Record Observers
    rails/rails-observers
    • Active Resource
    rails/activeresource
    • Action Caching
    rails/actionpack-action_caching
    • Page Caching
    rails/actionpack-page_caching
    • Sprockets
    rails/sprockets-rails
    3

    View Slide

  4. 4

    View Slide

  5. Ruby 1.9.3
    5

    View Slide

  6. Deprecation policy
    • Rails 3.2.x
    • new deprecation warnings
    • Rails 4.0.0
    • new features
    • new deprecation warnings
    • nothing is removed
    • easy upgrade
    • Rails 4.0.x
    • no new features
    • no new deprecation warnings
    • Rails 4.1:
    • new features
    • removes deprecated stuff
    6

    View Slide

  7. strong parameters queue API russian dolls caching
    7

    View Slide

  8. strong parameters
    queue API russian dolls caching
    7

    View Slide

  9. # app/models/kid.rb
    class Kid < ActiveRecord::Base
    attr_accessible :name, :age, :address, :girl #, :good
    end
    # app/controllers/kids_controller.rb
    class KidsController < ApplicationController
    def create
    @kid = Kid.new(params[:kid])
    if @kid.save
    redirect_to root_url
    else
    render action: "new"
    end
    end
    end
    3
    Mass-asignment protection
    8

    View Slide

  10. ActionController::StrongParameters
    4
    9

    View Slide

  11. ActionController::StrongParameters
    # app/models/kid.rb
    class Kid < ActiveRecord::Base
    end
    # app/controllers/kids_controller.rb
    class KidsController < ActionController::Base
    def create
    Kid.create(params.require(:kid).permit(:name, :age))
    end
    # Kid.create(params[:kid]) =>
    # ActiveModel::ForbiddenAttributes
    end
    4
    10

    View Slide

  12. # app/controllers/kidss_controller.rb
    class KidsController < ActionController::Base
    def create
    Kid.create(kid_params)
    end
    def update
    redirect_to kids.find(params[:id]).tap { |kid|
    kid.update_attributes!(kid_params)
    }
    end
    private
    def kid_params
    params.require(:kid).permit(:name, :age)
    end
    # params.require(:kid).permit(:name, :age, pets_attributes:
    # [ :name, :category ])
    end
    4
    ActionController::StrongParameters
    11

    View Slide

  13. 3
    Try it now!
    #gemfile
    gem 'strong_parameters', github: 'rails/strong_parameters'
    # application.rb
    config.active_record.whitelist_attributes = false
    12

    View Slide

  14. #gemfile
    gem 'protected_attributes', github: 'rails/protected_attributes'
    # application.rb
    config.active_record.whitelist_attributes = true
    Easy transition…
    4
    13

    View Slide

  15. Rails.queue
    14

    View Slide

  16. Many choices…
    • gem 'delayed_job_active_record'
    • gem 'resque'
    • gem 'sidekiq'
    • …
    3
    15

    View Slide

  17. Rails.queue (ActiveSupport::Queue)
    4
    16

    View Slide

  18. class ToySender
    attr_reader :kid_id, :toy_id
    def initialize(attr)
    @kid_id = attr[:kid_id]
    @toy_id = attr[:toy_id]
    end
    def run
    kid = Kid.find(@kid_id)
    toy = Toy.find(@toy_id)
    # toy.send_to(kid)
    end
    end
    # Then, add an instance of WishSender to Rails.queue.
    # A likely place for this code is in a controller:
    class KidsController < ApplicationController
    def send_toy
    Rails.queue << ToySender.new(params)
    flash[:notice] = "Toy queued to be sent!"
    redirect_to kids_url
    end
    end
    4
    4
    17

    View Slide

  19. # config/application.rb
    # Default Synchronous
    config.queue = ActiveSupport::SynchronousQueue.new
    # Default Threaded
    config.queue = ActiveSupport::Queue.new
    # Resque Queue
    config.queue = Resque::Rails::Queue.new
    # Sidekiq Queue
    config.queue = Sidekiq::Client::Queue.new
    # Test Queue
    config.queue = ActiveSupport::TestQueue.new
    4
    18

    View Slide

  20. asynchronous ActionMailer
    19

    View Slide

  21. # application.rb
    config.action_mailer.async = true
    # welcome_mailer.rb
    class WelcomeMailer < ActionMailer::Base
    self.async = true
    end
    4
    20

    View Slide

  22. # application.rb
    config.action_mailer.queue = :sidekiq
    # welcome_mailer.rb
    class WelcomeMailer < ActionMailer::Base
    def queue
    MyQueue.new
    # Sidekiq::Client::Queue.new
    end
    end
    4
    21

    View Slide

  23. # kids_controller.rb
    # don’t do this! can't be marshalled (serialized) in the
    # queue system
    WelcomeMailer.welcome(@kid).deliver
    # You should do
    WelcomeMailer.welcome(@kid.id).deliver
    # welcome_mailer.rb
    class WelcomeMailer < ActionMailer::Base
    def welcome(id)
    @kid = User.find(id)
    # prepare your mail
    end
    end
    4
    22

    View Slide

  24. russian dolls caching
    23

    View Slide

  25. 3
    - cache [ "v1", kid ] do
    %h1 All toys
    = render kid.toys
    ! - cache [ "v1", toy ] do
    %h2= toy.name
    ! = render toy.elves
    ! ! - cache [ "v1", elf ] do
    ! ! %p
    ! ! ! = elf.first_name
    kids/show.html.erb
    toys/_toy.html.erb
    elves/_elf.html.erb
    24

    View Slide

  26. 3
    - cache [ "v2", kid ] do
    %h1= "All toys for #{kid.name}"
    = render kid.toys
    ! - cache [ "v1", toy ] do
    %h2= toy.name
    ! = render toy.elves
    ! ! - cache [ "v1", elf ] do
    ! ! %p
    ! ! ! = elf.first_name
    kids/show.html.erb
    toys/_toy.html.erb
    elves/_elf.html.erb
    25

    View Slide

  27. 3
    - cache [ "v2", kid ] do
    %h1= "All toys for #{kid.name}"
    = render kid.toys
    ! - cache [ "v2", toy ] do
    %em= toy.build_on
    %h2= toy.name
    ! = render toy.elves
    ! ! - cache [ "v1", elf ] do
    ! ! %p
    ! ! ! = elf.first_name
    kids/show.html.erb
    toys/_toy.html.erb
    elves/_elf.html.erb
    26

    View Slide

  28. 3
    - cache [ "v3", kid ] do
    %h1= "All toys for #{kid.name}"
    = render kid.toys
    ! - cache [ "v2", toy ] do
    %em= toy.build_on
    %h2= toy.name
    ! = render toy.elves
    ! ! - cache [ "v1", elf ] do
    ! ! %p
    ! ! ! = elf.first_name
    kids/show.html.erb
    toys/_toy.html.erb
    elves/_elf.html.erb
    27

    View Slide

  29. 3
    - cache [ "v3", kid ] do
    %h1= "All toys for #{kid.name}"
    = render kid.toys
    ! - cache [ "v2", toy ] do
    %em= toy.build_on
    %h2= toy.name
    ! = render toy.elves
    ! ! - cache [ "v2", elf ] do
    ! ! %p
    ! ! ! = elf.first_name
    ! ! ! = elf.last_name
    kids/show.html.erb
    toys/_toy.html.erb
    elves/_elf.html.erb
    28

    View Slide

  30. 3
    - cache [ "v3", kid ] do
    %h1= "All toys for #{kid.name}"
    = render kid.toys
    ! - cache [ "v3", toy ] do
    %em= toy.build_on
    %h2= toy.name
    ! = render toy.elves
    ! ! - cache [ "v2", elf ] do
    ! ! %p
    ! ! ! = elf.first_name
    ! ! ! = elf.last_name
    kids/show.html.erb
    toys/_toy.html.erb
    elves/_elf.html.erb
    29

    View Slide

  31. 3
    - cache [ "v4", kid ] do
    %h1= "All toys for #{kid.name}"
    = render kid.toys
    ! - cache [ "v3", toy ] do
    %em= toy.build_on
    %h2= toy.name
    ! = render toy.elves
    ! ! - cache [ "v2", elf ] do
    ! ! %p
    ! ! ! = elf.first_name
    ! ! ! = elf.last_name
    kids/show.html.erb
    toys/_toy.html.erb
    elves/_elf.html.erb
    30

    View Slide

  32. 4
    https://github.com/rails/cache_digests
    31

    View Slide

  33. views/kids/45-20121211152602/d9fb66b120b61f46707c67ab41d93cb2
    MD5 hash of the template content
    and of all its dependencies
    4
    32

    View Slide

  34. 4
    - cache [ kid ] do
    %h1 All toys
    = render kid.toys
    ! - cache [ toy ] do
    %h2= toy.name
    ! = render toy.elves
    ! ! - cache [ elf ] do
    ! ! %p
    ! ! ! = elf.first_name
    kids/show.html.erb
    toys/_toy.html.erb
    elves/_elf.html.erb
    33

    View Slide

  35. 4
    - cache [ kid ] do
    %h1= "All toys for #{kid.name}"
    = render kid.toys
    ! - cache [ toy ] do
    %em= toy.build_on
    %h2= toy.name
    ! = render toy.elves
    ! ! - cache [ elf ] do
    ! ! %p
    ! ! ! = elf.first_name
    ! ! ! = elf.last_name
    kids/show.html.erb
    toys/_toy.html.erb
    elves/_elf.html.erb
    34

    View Slide

  36. Use it now!
    # gemfile
    gem 'cache_digests'
    3
    35

    View Slide

  37. and…
    4
    ActionController::Caching::Pages
    ActionController::Caching::Actions
    36

    View Slide

  38. and…
    4
    ActionController::Caching::Pages
    ActionController::Caching::Actions
    36

    View Slide

  39. and…
    4
    ActionController::Caching::Pages
    ActionController::Caching::Actions
    # gemfile
    gem 'actionpack-page_caching'
    # gemfile
    gem 'actionpack-action_caching'
    36

    View Slide

  40. turbolinks
    37

    View Slide

  41. • doesn’t re-load JS and CSS
    • doesn’t re-compile
    • page instance is kept alive
    • only the body and title of the page are
    replaced
    4
    38

    View Slide

  42. • doesn’t re-load JS and CSS
    • doesn’t re-compile
    • page instance is kept alive
    • only the body and title of the page are
    replaced
    4
    • response contains the full rendered
    page, so no bandwidth is saved
    38

    View Slide

  43. Gotchas
    $(document).ready( function() {
    ! …
    });
    39

    View Slide

  44. Gotchas
    3
    $(document).ready(function() {
    ! $("a.special_link").on("click", function(event) {
    ! ! alert('Christmas!!!');
    ! });
    });
    40

    View Slide

  45. Gotchas
    3
    $(document).ready(function() {
    ! $("a.special_link").on("click", function(event) {
    ! ! alert('Christmas!!!');
    ! });
    });
    4
    $(document).ready(function() {
    ! $(document).on("click", "a.special_link", function(event) {
    ! ! alert('Christmas!!!');
    ! });
    });
    40

    View Slide

  46. Turbolinks events
    page:fetch
    starting to fetch the target page (only called if loading fresh,
    not from cache).
    page:load fetched page is being retrieved fresh from the server
    page:restore
    fetched page is being retrieved from the 10-slot client-side
    cache
    page:change page has changed to the newly fetched version
    41

    View Slide

  47. $(document).ready(function() {
    ! $(document).on("click", "a.special_link", function(event) {
    ! ! alert('Christmas!!!');
    ! });
    });
    $(document).on("page:load" function() {
    ! $("a.special_link").on("click", function(event) {
    ! ! alert('Christmas!!!');
    ! });
    });
    4
    42

    View Slide

  48. Track asset changes 4
    type="text/css" data-turbolinks-track>
    43

    View Slide

  49. Don’t want turbolinks?
    4
    Articles
    44

    View Slide

  50. Use it now!
    https://github.com/rails/turbolinks
    45

    View Slide

  51. PATCH verb
    46

    View Slide

  52. • resource creation or replacement at some given URL
    • must send a complete representation of the resource
    • idempotent method (same request has always same result,
    think :updated_at)
    PUT
    47

    View Slide

  53. • not idempotent
    • allows full and partial updates
    • allows side-effects on other resources
    PATCH
    48

    View Slide

  54. PATCH
    • PATCH is going to be the primary method for updates in Rails 4.0
    • both PUT and PATCH are routed to update
    49

    View Slide

  55. match
    50

    View Slide

  56. • match respond to all HTTP verbs (POST,
    PUT, DELETE and GET)
    • potential cross site request forgery (CSRF)
    #match
    3
    51

    View Slide

  57. match '/toys/:id/purchase' => 'toys#purchase'
    CSRF protection already exists in Rails but
    not for the GET method
    Problem
    3
    52

    View Slide

  58. get '/toys/:id/purchase' => 'Toy#purchase'
    match '/toys/:id/purchase' => 'Toy#purchase', :via => :get
    match '/toys/:id/purchase' => 'Toy#purchase', :via => :any
    Rails4 forces a verb to be present on match
    routes
    Solution
    53

    View Slide

  59. concerns
    54

    View Slide

  60. # config/routes.rb
    Christmas::Application.routes.draw do
    ! resources :toys do
    ! ! resources :pictures
    ! end
    ! resources :kids do
    ! ! resources :pictures
    ! end
    ! resources :elves do
    ! ! resources :pictures
    ! end
    end
    Your routes are not DRY
    3
    55

    View Slide

  61. # config/routes.rb
    Christmas::Application.routes.draw do
    ! concern :picturable do |options|
    ! ! resources :pictures, options
    ! end
    resources :toys, :concerns :picturable
    resources :kids, :concerns :picturable
    resources :elves, :concerns :picturable
    end
    Your routes are DRY
    4
    56

    View Slide

  62. # config/routes.rb
    Christmas::Application.routes.draw do
    ! concern :picturable do
    ! ! resources :pictures
    ! end
    resources [:toys, :kids, :elves], :concerns :picturable
    end
    Your routes are DRY
    4
    57

    View Slide

  63. various
    58

    View Slide

  64. ActiveRecord::Relation
    59

    View Slide

  65. 3
    # toys_controller.rb
    def index
    ! if current_kid.good_kid?
    ! ! if current_kid.girl?
    ! ! ! @toys = Toy.for_girl
    ! ! else
    ! ! ! @toys = Toy.for_boy
    ! ! end
    ! else
    ! ! @toys = []
    ! end
    !

    ! @toys = @toys.in_stock unless @toys.is_a?(Array)
    end
    60

    View Slide

  66. ActiveRecord::Relation#none
    # toys_controller.rb
    def index
    ! if current_kid.good_kid?
    ! ! if current_kid.girl?
    ! ! ! @toys = Toy.for_girl
    ! ! else
    ! ! ! @toys = Toy.for_boy
    ! ! end
    ! else
    ! ! @toys = Toy.none
    ! end
    !

    ! @toys = @toys.in_stock
    end
    4
    61

    View Slide

  67. ActiveRecord::Relation#all
    ActiveRecord::Relation#where!
    ActiveRecord::Relation#none!
    # toys_controller.rb
    def index
    ! @toys = Toy.all
    ! if current_kid.good_kid?
    ! ! if current_kid.girl?
    ! ! ! @toys.where!(girl: true)
    ! ! else
    ! ! ! @toys.where!(boy: true)
    ! ! end
    ! else
    ! ! @toys.none!
    ! end
    ! …
    ! @toys = @toys.in_stock
    end
    4
    62

    View Slide

  68. ActiveRecord::Relation#not
    Item.where "kid_id != ?", current_kid.kid_id
    Item.where.not kid_id: current_kid.id
    4
    3
    63

    View Slide

  69. ActiveRecord::Relation#order
    Toy.order("orders_count DESC").order(:created_at)
    # SELECT * FROM toys ORDER BY orders_count DESC, created_at
    3
    64

    View Slide

  70. Toy.order("orders_count DESC").order(:created_at)
    # SELECT * FROM toys ORDER BY created_at, orders_count DESC
    4
    ActiveRecord::Relation#order
    65

    View Slide

  71. Toy.order("orders_count DESC").order(:created_at)
    # SELECT * FROM toys ORDER BY created_at, orders_count DESC
    4
    Comment.order("replies_count DESC", :created_at)
    ActiveRecord::Relation#order
    65

    View Slide

  72. deprecated finders
    3
    Item.find(:first, :conditions => ...) Item.where(...).first
    find_all_by_...
    scoped_by_...
    where(...)
    find_last_by_... where(...).last
    first_or_initialize_by_...
    first_or_initialize_by(...)
    find_or_create_by_... find_or_create_by(...)
    first_or_create_by_...! find_or_create_by!(...)
    4
    # Gemfile
    gem "activerecord-deprecated_finders"
    66

    View Slide

  73. class Order < ActiveRecord::Base
    ! scope :past, where("orders.sent_on < ?", Time.current)
    end
    scope
    3
    67

    View Slide

  74. class Order < ActiveRecord::Base
    ! scope :past, where("orders.sent_on < ?", Time.current)
    end
    scope
    # DEPRECATION WARNING: Using #scope without passing a
    callable object is deprecated.
    4
    68

    View Slide

  75. class Order < ActiveRecord::Base
    ! scope :past, ->{ where("orders.sent_on < ?", Time.current) }
    end
    scope
    4
    69

    View Slide

  76. new form helpers
    collection_check_boxes(:toy, :category_ids, Category.all, :id, :name)
    # Returns:
    # # value="1" checked="checked" />
    # Ruby
    # # value="2" />
    # Javascript
    # ...
    #
    70

    View Slide

  77. new form helpers
    collection_radio_buttons(:toy, :category_ids, Category.all, :id, :name)
    # Returns:
    # checked="checked" />
    # Ruby
    # >
    # Javascript
    # ...
    71

    View Slide

  78. new form helpers
    collection_check_boxes(:toy, :category_ids, Category.all, :id, :name) do |b|
    b.label(:"data-value" => b.value) { b.check_box + b.text }
    end
    Customizable by a block
    72

    View Slide

  79. 73

    View Slide

  80. Install it today!
    • git clone git://github.com/rails/rails.git path_to/
    rails
    • path_to/rails/railties/bin/rails new AppName --edge
    74

    View Slide

  81. When?
    75

    View Slide

  82. When?
    75

    View Slide

  83. A few resources…
    76

    View Slide

  84. …for Christmas time!
    77

    View Slide

  85. Prepare to upgrade
    https://github.com/alindeman/rails4_upgrade
    78

    View Slide

  86. presentations
    • rails 4 in 30 minutes (Santiago Pastorino)
    • slides: http://blog.wyeworks.com/2012/10/29/rails-4-in-30-minutes/
    • Rails 4.0 Whirlwind Tour (Andy Lindeman)
    • slides: https://speakerdeck.com/alindeman/rails-4-dot-0-whirlwind-tour
    • video: http://vimeo.com/51181496
    • What to Expect in Rails 4.0 (Prem Sichanugrist)
    • slides: https://speakerdeck.com/bostonrb/what-to-expect-in-rails-4-dot-0
    • video: http://www.youtube.com/watch?v=z6YgD6tVPQs
    • Why should I care about Rails 4? (Steve Klabnik)
    • slides: https://speakerdeck.com/steveklabnik/why-should-i-care-about-rails-4
    • video: http://vimeo.com/51898266
    • Rails 4 and the Future of Web (Aaron Patterson)
    • slides: https://speakerdeck.com/tenderlove/aloha-ruby-conference-2012
    • video: http://www.youtube.com/watch?v=kufXhNkm5WU
    79

    View Slide

  87. 80

    View Slide