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

  2. 2

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

  5. 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
  6. # 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
  7. 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
  8. # 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
  9. 3 Try it now! #gemfile gem 'strong_parameters', github: 'rails/strong_parameters' #

    application.rb config.active_record.whitelist_attributes = false 12
  10. 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
  11. # 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
  12. # 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
  13. # 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
  14. 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
  15. 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
  16. 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
  17. 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
  18. 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
  19. 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
  20. 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
  21. 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
  22. 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
  23. • 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
  24. • 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
  25. 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
  26. 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
  27. $(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
  28. • 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
  29. • not idempotent • allows full and partial updates •

    allows side-effects on other resources PATCH 48
  30. PATCH • PATCH is going to be the primary method

    for updates in Rails 4.0 • both PUT and PATCH are routed to update 49
  31. • match respond to all HTTP verbs (POST, PUT, DELETE

    and GET) • potential cross site request forgery (CSRF) #match 3 51
  32. 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
  33. # 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
  34. # 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
  35. # 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
  36. 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
  37. 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
  38. 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
  39. 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
  40. 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
  41. 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
  42. new form helpers collection_check_boxes(:toy, :category_ids, Category.all, :id, :name) # Returns:

    # <input id="toy_category_ids_1" name="toy[category_ids][]" type="checkbox" # value="1" checked="checked" /> # <label for="toy_category_ids_1">Ruby</label> # <input id="toy_category_ids_2" name="toy[category_ids][]" type="checkbox" # value="2" /> # <label for="toy_category_ids_2">Javascript</label> # ... # <input name="toy[category_ids][]" type="hidden" value="" /> 70
  43. new form helpers collection_radio_buttons(:toy, :category_ids, Category.all, :id, :name) # Returns:

    # <input id="toy_category_ids_1" name="toy[category_ids][]" type="radio" value="1" checked="checked" /> # <label for="toy_category_ids_1">Ruby</label> # <input id="toy_category_ids_2" name="toy[category_ids][]" type="radio" value="2" / > # <label for="toy_category_ids_2">Javascript</label> # ... 71
  44. 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
  45. 73

  46. Install it today! • git clone git://github.com/rails/rails.git path_to/ rails •

    path_to/rails/railties/bin/rails new AppName --edge 74
  47. 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
  48. 80