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)

Avatar for Yannis Jaquet

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