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

What to expect in Rails 4.0

What to expect in Rails 4.0

The 4.0 release of Ruby on Rails is right around the corner. I'm going to highlight some of the new features and changes in the newest version of Rails, as well as features that will be removed or deprecated in Rails core.

(Note: The content of this slide may not reflect the final version of the release.)

Presented at BostonRB on September 11, 2012. Video is available at http://bostonrb.org/presentations/what-to-expect-in-rails-40.

F1c4a3bb1606cc4a61711e61e2fe6146?s=128

Prem Sichanugrist

September 11, 2012
Tweet

More Decks by Prem Sichanugrist

Other Decks in Programming

Transcript

  1. What to expect in Rails 4.0

  2. 4.0

  3. Question?

  4. #rails4 Tweet with this hashtag

  5. Stats

  6. 5,863 Commits 62,684 + 64,585 –

  7. $ git log 9d6e52b...master |\ pipe> sort | uniq |

    wc -l 683 683 Contributors
  8. New Features Strong Parameters Null Relation Rails Queue Russian Doll

    Caching Relation#inspect Schema Cache Dump Routing Concern HTML5 tag helpers HTTP PATCH {asset}_url helpers .first and .last now have order() defined Scope mutation config.eager_load replaces threadsafe! Relation#pluck takes multiple arguments
  9. Deprecations ActiveRecord::SessionStore Old Dynamic Finders ActiveResource .scoped() Old Hash Finders

    Eager Evaluated Scope Rails Plugins
  10. Release Note http://edgeguides.rubyonrails.org/4_0_release_notes.html

  11. Notable Changes

  12. Strong Parameters

  13. Strong Parameters • Replaces attr_accessor and attr_protected • Will be

    extracted as a gem (Dependency of Rails 4.0) • Moves parameters filtering concern to controller
  14. # app/models/user.rb class User < ActiveRecord::Base attr_accessible :username, :password, :password_confirmation

    end # app/controllers/users_controller.rb class UsersController < ApplicationController def create @user = User.create!(params[:user]) redirect_to @user end end
  15. # app/models/user.rb class User < ActiveRecord::Base; end # app/controllers/users_controller.rb class

    UsersController < ApplicationController def create @user = User.create!(user_params) redirect_to @user end private def user_params params.require(:user).permit(:username, :password, :password_confirmation) end end
  16. # app/controllers/users_controller.rb class UsersController < ApplicationController def create @user =

    User.create!(params[:user]) redirect_to @user end end
  17. # app/controllers/users_controller.rb class UsersController < ApplicationController def create @user =

    User.create!(params[:user]) redirect_to @user end end #=> Raises ActiveModel::ForbiddenAttributes
  18. params.require(:user).permit(:username, :password, :password_confirmation)

  19. params.require(:user).permit(:username, :password, :password_confirmation)

  20. params.require(:user).permit(:username, :password, :password_confirmation) => POST /users Data: username=sikachu

  21. params.require(:user).permit(:username, :password, :password_confirmation) => POST /users Data: username=sikachu <= 400

    Bad Request
  22. params.require(:user).permit(:username, :password, :password_confirmation) => POST /users Data: user[username]=sikachu

  23. params.require(:user).permit(:username, :password, :password_confirmation) => POST /users Data: user[username]=sikachu <= 201

    Created
  24. params.require(:user).permit(:username, :password, :password_confirmation)

  25. params.require(:user).permit(:username, :password, :password_confirmation) => POST /users Data: user[username]=sikachu&user[admin]=true

  26. params.require(:user).permit(:username, :password, :password_confirmation) => POST /users Data: user[username]=sikachu&user[admin]=true <= 201

    Created
  27. params.require(:user).permit(:username, :password, :password_confirmation) => POST /users Data: user[username]=sikachu&user[admin]=true <= 201

    Created irb> User.find_by_username('sikachu').admin? => false
  28. Easier multi-roles filtering # app/controllers/users_controller.rb class UsersController < ApplicationController def

    user_params params.require(:user).permit(:username, :password, :password_confirmation) end end # app/controllers/admin/users_controller.rb class Admin::UsersController < ApplicationController def user_params params.require(:user).permit(User.attribute_names) end end
  29. Only works in controller @user.update_attributes(name: 'sikachu', admin: true)

  30. Try Out Today! https://github.com/rails/strong_parameters Fully compatible with Rails 3.2.8

  31. ActiveSupport::Queue

  32. *Not Finalized Yet* At the time of this presentation ...

  33. Job # Requires an object that respond to #run class

    UserRegistrationMailerJob < Struct.new(:user_id) def run user = User.find(user_id) UserMailer.welcome_email(user).deliver end end
  34. Queue it! # Pass in an object that respond to

    #run class UserRegistrationMailerJob < Struct.new(:user_id) def run user = User.find(user_id) UserMailer.welcome_email(user).deliver end end # Usage Rails.queue.push UserRegistrationMailerJob(@user.id)
  35. Defaults • ActiveSupport::Queuing::Queue • Inherits from ::Queue (STDLIB) • ThreadConsumer

    will run the job in the background thread automatically.
  36. Configurations # Default Container and Consumer config.queue = :asynchronous config.queue

    = :synchronous # Resque support in Resque 2.0.0.beta config.queue = :resque
  37. Cache Digests

  38. Cache Digests “Russian Doll Caching”

  39. Russian Doll Caching @topic

  40. Russian Doll Caching @post @topic

  41. Russian Doll Caching @post @user @topic

  42. <%# app/views/topics/show.html.erb %> <% cache [ "v1", @topic ] do

    %> <h1><%= Topic.title %></h1> <%= render @topic.posts %> <% end %> <%# app/views/posts/_post.html.erb %> <% cache [ "v1", post ] do %> <%= render post.author %> <%= post.content %> <% end %> <%# app/views/users/_user.html.erb %> <% cache [ "v1", user ] do %> Posted by <%= user.name %> <% end %> Russian Doll Caching
  43. @post @user @topic Update Template

  44. @topic @user @post Update Template

  45. <%# app/views/topics/show.html.erb %> <% cache [ "v2", @topic ] do

    %> ... <% end %> <%# app/views/posts/_post.html.erb %> <% cache [ "v1", post ] do %> ... <% end %> <%# app/views/users/_user.html.erb %> <% cache [ "v1", user ] do %> ... <% end %> Expire Cache
  46. @topic @user @post Update Template

  47. @topic @user @post Update Template

  48. @topic @user @post Update Template

  49. Expire Cache <%# app/views/topics/show.html.erb %> <% cache [ "v3", @topic

    ] do %> ... <% end %> <%# app/views/posts/_post.html.erb %> <% cache [ "v2", post ] do %> ... <% end %> <%# app/views/users/_user.html.erb %> <% cache [ "v1", user ] do %> ... <% end %>
  50. @topic @user @post Update Template

  51. @user @topic @post Update Template

  52. @user @topic @post Update Template

  53. @user @topic @post Update Template

  54. Expire Cache <%# app/views/topics/show.html.erb %> <% cache [ "v4", @topic

    ] do %> ... <% end %> <%# app/views/posts/_post.html.erb %> <% cache [ "v3", post ] do %> ... <% end %> <%# app/views/users/_user.html.erb %> <% cache [ "v2", user ] do %> ... <% end %>
  55. Crazy!

  56. Cache Digests

  57. <%# app/views/topics/show.html.erb %> <% cache @topic do %> <h1><%= Topic.title

    %></h1> <%= render @topic.posts %> <% end %> <%# app/views/posts/_post.html.erb %> <% cache post do %> <%= render post.author %> <%= post.content %> <% end %> <%# app/views/users/_user.html.erb %> <% cache user do %> Posted by <%= user.name %> <% end %> Your Template
  58. Generate MD5 of template and its dependencies as cache key

    views/topics/605816632-20120810191209/d9fb66b120b61f46707c67ab41d93cb2
  59. Cache Expiration It just works!

  60. $ rake cache_digests:dependencies TEMPLATE=topics/show [ "posts/post" ] $ rake cache_digests:nested_dependencies

    TEMPLATE=topics/show [ { "posts/post": [ "users/user" ] } ] Inspect Dependencies
  61. Try Out Today! https://github.com/rails/cache_digests Fully compatible with Rails 3.2.8

  62. Cached Schema Dump

  63. Cached Schema Dump Speed up Rails startup time

  64. $ bundle exec rake db:schema:cache:dump => generate db/schema_cache.dump # You

    might want to add this to your repository $ RAILS_ENV=production bundle exec rails server => use db/schema_cache.dump
  65. # config/environments/production.rb # Enabled by default config.active_record.use_schema_cache_dump = true

  66. PATCH verb support

  67. RFC 5789 • The PUT method is already defined to

    overwrite a resource with a complete new body, and cannot be reused to do partial changes. Otherwise, proxies and caches, and even clients and servers, may get confused as to the result of the operation. • This specification defines the new HTTP/1.1 method, PATCH, which is used to apply partial modifications to a resource.
  68. <%= form_for @user do |f| %> <form action="/users/1" method="post" ...

    > <div style="margin:0;padding:0;display:inline"> <input name="utf8" type="hidden" value="&#x2713;" /> <input name="_method" type="hidden" value="patch" /> <input name="authenticity_token" ... /> ...
  69. PUT /users/1 PATCH /users/1 => class UsersController < Appli def

    update end end
  70. Routing Concern

  71. Routing Concern Clean up duplicate routes

  72. Before resources :messages do resources :comments end resources :posts do

    resources :comments resources :images, only: :index end
  73. After concern :commentable do resources :comments end concern :image_attachables do

    resources :images, only: :index end resources :messages, concerns: :commentable resources :posts, concerns: [:commentable, : image_attachables]
  74. ActiveRecord::Relation

  75. Relation.first Relation.last

  76. Relation.first Relation.last Always return record ordered by id

  77. Relation.first Relation.last # User.first SELECT “users”.* FROM “users” ORDER BY

    id LIMIT 1 # User.last SELECT “users”.* FROM “users” ORDER BY id DESC LIMIT 1
  78. Relation.all

  79. Relation.all Always return ActiveRecord::Relation object

  80. Relation.all User.all # => #<ActiveRecord::Relation [#<User ...>, #<User ...>, ...]>

    User.to_a # => [#<User ...>, #<User ...>, ...]
  81. Relation.none

  82. Relation.none Returns ActiveRecord::NullRelation

  83. Relation.none Still chainable!

  84. # app/controllers/users_controller.rb class UsersController < ApplicationController def index @users =

    Users.name_starts_with(params[:prefix]). order(:name).paginate(params[:page], :per_page => 15) end end # app/models/user.rb class User < ActiveRecord::Base def self.starts_with(prefix) if prefix.present? where('name LIKE ?', "#{prefix}%") else # ... ? ... end end end
  85. # app/controllers/users_controller.rb class UsersController < ApplicationController def index @users =

    Users.name_starts_with(params[:prefix]). order(:name).paginate(params[:page], :per_page => 15) end end # app/models/user.rb class User < ActiveRecord::Base def self.starts_with(prefix) if prefix.present? where('name LIKE ?', "#{prefix}%") else [] end end end
  86. # app/controllers/users_controller.rb class UsersController < ApplicationController def index @users =

    Users.name_starts_with(params[:prefix]). order(:name).paginate(params[:page], :per_page => 15) end end # app/models/user.rb class User < ActiveRecord::Base def self.starts_with(prefix) if prefix.present? where('name LIKE ?', "#{prefix}%") else [] #=> NoMethodError when calling #order end end end
  87. # app/controllers/users_controller.rb class UsersController < ApplicationController def index @users =

    Users.name_starts_with(params[:prefix]). order(:name).paginate(params[:page], :per_page => 15) end end # app/models/user.rb class User < ActiveRecord::Base def self.starts_with(prefix) if prefix.present? where('name LIKE ?', "#{prefix}%") else none end end end
  88. Relation._____!

  89. Relation._____! Mutates current relation

  90. # app/controller/users_controller.rb class UsersController < ApplicationController def index @users =

    User @users = @users.where(:id => params[:id]) if params[:id] @users = @users.includes(:comments) if params[:comments] @users = @users.order(:name) end end
  91. # app/controller/users_controller.rb class UsersController < ApplicationController def index @users =

    User @users.where!(:id => params[:id]) if params[:id] @users.includes!(:comments) if params[:comments] @users.order!(:name) end end
  92. bind! create_with! eager_load! extending! from! group! having! includes! joins! limit!

    lock! offset! order! preload! readonly! references! reorder! reverse_order! select! uniq! where!
  93. Deprecations

  94. AR::Base.scoped

  95. AR::Base.scoped Removed

  96. AR::Base.scoped Use Model.all instead

  97. Dynamic Finder Methods

  98. find_all_by_... => where(...) find_last_by_... => where(...).last scoped_by_... => where(...) find_or_create_by_...

    => where(...).first_or_create find_or_create_by_...! => where(...).first_or_create! find_or_initialize_by_... => where(...).first_or_initialize
  99. Bring Back gem 'activerecord-deprecated_finders', github: 'rails/activerecord-deprecated_finders' External dependency in Rails

    4.0 (Remove in Rails 4.1)
  100. Hash-based Finders

  101. User.find(:first) User.find(:last, :order => 'id') User.find(:all, :conditions => { :active

    => true })
  102. Bring Back gem 'activerecord-deprecated_finders', github: 'rails/activerecord-deprecated_finders' External dependency in Rails

    4.0 (Remove in Rails 4.1)
  103. Eager Evaluated Scope

  104. class User < ActiveRecord::Base scope :recent, where('created_at >= ?', 1.week.ago)

    end
  105. class User < ActiveRecord::Base scope :recent, where('created_at >= ?', 1.week.ago)

    end
  106. class User < ActiveRecord::Base scope :recent, where('created_at >= ?', 1.week.ago)

    end
  107. Scope now requires a lambda

  108. class User < ActiveRecord::Base scope :recent, -> { where('created_at >=

    ?', 1.week.ago) } end
  109. ActiveRecord::SessionStore

  110. ActiveRecord::SessionStore Extracted as a gem

  111. gem 'activerecord-session_store', github: 'rails/activerecord_session_store' https://github.com/rails/activerecord-session_store

  112. ActiveResource

  113. ActiveResource Extracted as a gem

  114. gem 'activeresource' https://github.com/rails/activeresource

  115. Rails::Plugin

  116. Rails::Plugin Removed

  117. • Move it to your lib/ directory • Make it

    as a gem
  118. New Deprecation Policy

  119. SemVer Semantic Versioning

  120. SemVer Semantic Versioning

  121. Ver

  122. Rails 3.1.x End of Life

  123. Rails 3.2.x Some Deprecation Warnings (Support Until Rails 4.1.0 Comes

    Out)
  124. Rails 4.0.0 Deprecation Warnings

  125. Rails 4.1.0 Remove Deprecated Features

  126. Smooth Upgrade From Rails 3.2.x to Rails 4.0.0

  127. Release Candidate?

  128. Release Candidate? Soon!

  129. Try Now! # Gemfile # gem 'rails', '3.2.8' gem 'rails',

    github: 'rails/rails' $ bundle update
  130. Report Bugs https://github.com/rails/rails/issues

  131. Learn https://learn.thoughtbot.com/rails

  132. Thank you! Prem Sichanugrist https://github.com/sikachu @sikachu Questions: #rails4