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.

Prem Sichanugrist

September 11, 2012
Tweet

More Decks by Prem Sichanugrist

Other Decks in Programming

Transcript

  1. What to expect in Rails 4.0

    View Slide

  2. 4.0

    View Slide

  3. Question?

    View Slide

  4. #rails4
    Tweet with this hashtag

    View Slide

  5. Stats

    View Slide

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

    View Slide

  7. $ git log 9d6e52b...master |\
    pipe> sort | uniq | wc -l
    683
    683 Contributors

    View Slide

  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

    View Slide

  9. Deprecations
    ActiveRecord::SessionStore
    Old Dynamic Finders
    ActiveResource
    .scoped()
    Old Hash Finders
    Eager Evaluated Scope
    Rails Plugins

    View Slide

  10. Release Note
    http://edgeguides.rubyonrails.org/4_0_release_notes.html

    View Slide

  11. Notable Changes

    View Slide

  12. Strong Parameters

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  29. Only works in controller
    @user.update_attributes(name: 'sikachu', admin: true)

    View Slide

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

    View Slide

  31. ActiveSupport::Queue

    View Slide

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

    View Slide

  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

    View Slide

  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)

    View Slide

  35. Defaults
    • ActiveSupport::Queuing::Queue
    • Inherits from ::Queue (STDLIB)
    • ThreadConsumer will run the job in the
    background thread automatically.

    View Slide

  36. Configurations
    # Default Container and Consumer
    config.queue = :asynchronous
    config.queue = :synchronous
    # Resque support in Resque 2.0.0.beta
    config.queue = :resque

    View Slide

  37. Cache Digests

    View Slide

  38. Cache Digests
    “Russian Doll Caching”

    View Slide

  39. Russian Doll Caching
    @topic

    View Slide

  40. Russian Doll Caching
    @post
    @topic

    View Slide

  41. Russian Doll Caching
    @post
    @user
    @topic

    View Slide

  42. <%# app/views/topics/show.html.erb %>
    <% cache [ "v1", @topic ] do %>
    <%= Topic.title %>
    <%= 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

    View Slide

  43. @post
    @user
    @topic
    Update Template

    View Slide

  44. @topic
    @user
    @post
    Update Template

    View Slide

  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

    View Slide

  46. @topic
    @user
    @post
    Update Template

    View Slide

  47. @topic
    @user
    @post
    Update Template

    View Slide

  48. @topic
    @user
    @post
    Update Template

    View Slide

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

    View Slide

  50. @topic
    @user
    @post
    Update Template

    View Slide

  51. @user
    @topic
    @post
    Update Template

    View Slide

  52. @user
    @topic
    @post
    Update Template

    View Slide

  53. @user
    @topic
    @post
    Update Template

    View Slide

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

    View Slide

  55. Crazy!

    View Slide

  56. Cache Digests

    View Slide

  57. <%# app/views/topics/show.html.erb %>
    <% cache @topic do %>
    <%= Topic.title %>
    <%= 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

    View Slide

  58. Generate MD5 of template and
    its dependencies as cache key
    views/topics/605816632-20120810191209/d9fb66b120b61f46707c67ab41d93cb2

    View Slide

  59. Cache Expiration
    It just works!

    View Slide

  60. $ rake cache_digests:dependencies TEMPLATE=topics/show
    [
    "posts/post"
    ]
    $ rake cache_digests:nested_dependencies TEMPLATE=topics/show
    [
    {
    "posts/post": [
    "users/user"
    ]
    }
    ]
    Inspect Dependencies

    View Slide

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

    View Slide

  62. Cached Schema Dump

    View Slide

  63. Cached Schema Dump
    Speed up Rails startup time

    View Slide

  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

    View Slide

  65. # config/environments/production.rb
    # Enabled by default
    config.active_record.use_schema_cache_dump = true

    View Slide

  66. PATCH verb support

    View Slide

  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.

    View Slide

  68. <%= form_for @user do |f| %>





    ...

    View Slide

  69. PUT /users/1
    PATCH /users/1
    => class UsersController < Appli
    def update
    end
    end

    View Slide

  70. Routing Concern

    View Slide

  71. Routing Concern
    Clean up duplicate routes

    View Slide

  72. Before
    resources :messages do
    resources :comments
    end
    resources :posts do
    resources :comments
    resources :images, only: :index
    end

    View Slide

  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]

    View Slide

  74. ActiveRecord::Relation

    View Slide

  75. Relation.first
    Relation.last

    View Slide

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

    View Slide

  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

    View Slide

  78. Relation.all

    View Slide

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

    View Slide

  80. Relation.all
    User.all
    # => #, #, ...]>
    User.to_a
    # => [#, #, ...]

    View Slide

  81. Relation.none

    View Slide

  82. Relation.none
    Returns ActiveRecord::NullRelation

    View Slide

  83. Relation.none
    Still chainable!

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  88. Relation._____!

    View Slide

  89. Relation._____!
    Mutates current relation

    View Slide

  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

    View Slide

  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

    View Slide

  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!

    View Slide

  93. Deprecations

    View Slide

  94. AR::Base.scoped

    View Slide

  95. AR::Base.scoped
    Removed

    View Slide

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

    View Slide

  97. Dynamic Finder
    Methods

    View Slide

  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

    View Slide

  99. Bring Back
    gem 'activerecord-deprecated_finders',
    github: 'rails/activerecord-deprecated_finders'
    External dependency in Rails 4.0
    (Remove in Rails 4.1)

    View Slide

  100. Hash-based Finders

    View Slide

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

    View Slide

  102. Bring Back
    gem 'activerecord-deprecated_finders',
    github: 'rails/activerecord-deprecated_finders'
    External dependency in Rails 4.0
    (Remove in Rails 4.1)

    View Slide

  103. Eager Evaluated Scope

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  107. Scope now requires a lambda

    View Slide

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

    View Slide

  109. ActiveRecord::SessionStore

    View Slide

  110. ActiveRecord::SessionStore
    Extracted as a gem

    View Slide

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

    View Slide

  112. ActiveResource

    View Slide

  113. ActiveResource
    Extracted as a gem

    View Slide

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

    View Slide

  115. Rails::Plugin

    View Slide

  116. Rails::Plugin
    Removed

    View Slide

  117. • Move it to your lib/ directory
    • Make it as a gem

    View Slide

  118. New Deprecation Policy

    View Slide

  119. SemVer
    Semantic Versioning

    View Slide

  120. SemVer
    Semantic Versioning

    View Slide

  121. Ver

    View Slide

  122. Rails 3.1.x
    End of Life

    View Slide

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

    View Slide

  124. Rails 4.0.0
    Deprecation Warnings

    View Slide

  125. Rails 4.1.0
    Remove Deprecated Features

    View Slide

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

    View Slide

  127. Release Candidate?

    View Slide

  128. Release Candidate?
    Soon!

    View Slide

  129. Try Now!
    # Gemfile
    # gem 'rails', '3.2.8'
    gem 'rails', github: 'rails/rails'
    $ bundle update

    View Slide

  130. Report Bugs
    https://github.com/rails/rails/issues

    View Slide

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

    View Slide

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

    View Slide