Slide 1

Slide 1 text

WHAT’S NEW IN RAILS 4 Ben Hughes @rubiety http://benhugh.es Thursday, February 7, 13

Slide 2

Slide 2 text

RAILS EVOLUTION Thursday, February 7, 13

Slide 3

Slide 3 text

RAILS 2: REST ARCHITECTURE Thursday, February 7, 13

Slide 4

Slide 4 text

RAILS 3: MODULARIZATION ASSET PIPELINE Thursday, February 7, 13

Slide 5

Slide 5 text

RAILS 4: CLOSET-CLEANING FORWARD REFINEMENT Thursday, February 7, 13

Slide 6

Slide 6 text

Thursday, February 7, 13

Slide 7

Slide 7 text

RUBY 1.9.3 REQUIRED Thursday, February 7, 13

Slide 8

Slide 8 text

STRONG PARAMETERS class PeopleController < ApplicationController def create @person = Person.create(params[:person]) end end class Person < ActiveRecord::Base attr_accessible :name, :age end In Rails 3: Thursday, February 7, 13

Slide 9

Slide 9 text

STRONG PARAMETERS class PeopleController < ApplicationController def create @person = Person.create(person_params) end private def person_params params.require(:person).permit(:name, :age) end end Use in Rails 3 Today: https://github.com/rails/strong_parameters In Rails 4: Thursday, February 7, 13

Slide 10

Slide 10 text

THREAD SAFE BY DEFAULT Make sure your code & all gems is thread safe! Or disable thread safety (middleware Rack::Lock). Thursday, February 7, 13

Slide 11

Slide 11 text

LIVE STREAMING class MyController < ActionController::Base include ActionController::Live def stream response.headers['Content-Type'] = 'text/event-stream' 100.times { response.stream.write "hello world\n" sleep 1 } response.stream.close end end Thursday, February 7, 13

Slide 12

Slide 12 text

LIVE STREAMING CAVEATS • Actions run in separate thread; controller action code must be thread save! • Concurrent Ruby Server Required (puma, rainbows!, thin) • Headers must be written before anything else • Streams must be closed http://tenderlovemaking.com/2012/07/30/is-it-live.html Thursday, February 7, 13

Slide 13

Slide 13 text

TURBOLINKS https://github.com/rails/turbolinks http://railscasts.com/episodes/390-turbolinks Similar to PJAX Use in Rails 3 Today: gem “turbolinks” // app/assets/javascripts/application.js //= require jquery //= require jquery_ujs //= require turbolinks //= require_tree . Thursday, February 7, 13

Slide 14

Slide 14 text

BEFORE_FILTER => BEFORE_ACTION Thursday, February 7, 13

Slide 15

Slide 15 text

POSTGRES ARRAY SUPPORT class AddTagsToArticles < ActiveRecord::Migration def change change_table :articles do |t| t.string :tags, array: true end end end article.tags = ['rails'] article.save # UPDATE "articles" SET "tags" = '{"rails"}' # WHERE "articles"."id" = 1 HStore Support New Data Types Support Thursday, February 7, 13

Slide 16

Slide 16 text

AREL .WHERE.NOT(:A => 1) Article.where.not(title: 'Rails 3') Article.where.not(title: ['Rails 3', 'Rails 5']) Article.where("title != ?", "Rails 3") Article.where("title NOT IN (?)", ["Rails 3", "Rails 5"]) In Rails 3: In Rails 4: Thursday, February 7, 13

Slide 17

Slide 17 text

AREL .FIRST / .LAST In Rails 3: Calling .first or .last was indeterminate (DB-specific). In Rails 4: Calling .first or .last defaults to using ORDER BY id. Thursday, February 7, 13

Slide 18

Slide 18 text

AREL .NONE @posts = current_user.visible_posts.where(:name => params[:name]) def visible_posts case role if 'Country Manager' Post.where(:country => country) if 'Reviewer' Post.published if 'Bad User' # returning [] instead breaks the previous code; not chainable Post.where("1=0") end end In Rails 3: Thursday, February 7, 13

Slide 19

Slide 19 text

AREL .NONE @posts = current_user.visible_posts.where(:name => params[:name]) def visible_posts case role if 'Country Manager' Post.where(:country => country) if 'Reviewer' Post.published if 'Bad User' # returning [] instead breaks the previous code; not chainable Post.none end end In Rails 4: Thursday, February 7, 13

Slide 20

Slide 20 text

AREL BANG! METHODS class PeopleController < ApplicationController def index @people = People.active @people = @people.where(:first_name => params[:first_name]) if params[:last_name].present? @people = @people.where(:last_name => params[:last_name]) if params[:last_name].present? end end In Rails 3: Thursday, February 7, 13

Slide 21

Slide 21 text

AREL BANG! METHODS class PeopleController < ApplicationController def index @people = People.active @people.where!(:first_name => params[:first_name]) if params[:last_name].present? @people.where!(:last_name => params[:last_name]) if params[:last_name].present? end end In Rails 4: Thursday, February 7, 13

Slide 22

Slide 22 text

ROUTING CONCERNS Penguin::Application.routes.draw do resources :projects do resources :comments end resources :tasks do resources :comments end resources :articles do resources :comments end end In Rails 3: Thursday, February 7, 13

Slide 23

Slide 23 text

ROUTING CONCERNS In Rails 4: Penguin::Application.routes.draw do concern :commentable do resources :comments end resources :projects, concerns: :commentable resources :tasks, concerns: :commentable resources :articles, concerns: :commentable end Use in Rails 3 Today: https://github.com/rails/routing_concerns Thursday, February 7, 13

Slide 24

Slide 24 text

CACHING GEM EXTRACTIONS: gem “actionpack-page_caching” gem “actionpack-action_caching” Thursday, February 7, 13

Slide 25

Slide 25 text

OBSERVERS/SWEEPERS EXTRACTED gem “rails-observers” Thursday, February 7, 13

Slide 26

Slide 26 text

FRAGMENT CACHE DIGESTS # app/views/todolists/_todolist.html.erb <% cache todolist do %> My todolist: <%= todolist.name %> <%= render document.comments %> <% end %> # app/views/comments/_comment.html.erb <% cache comment do %> My comment: <%= comment.body %> <% end %> In Rails 3: Thursday, February 7, 13

Slide 27

Slide 27 text

FRAGMENT CACHE DIGESTS # app/views/todolists/_todolist.html.erb <% cache [ "v1", todolist ] do %> My todolist: <%= todolist.name %> <%= render document.comments %> <% end %> # app/views/comments/_comment.html.erb <% cache [ "v1", comment ] do %> My comment: <%= comment.body %> <% end %> In Rails 3: Thursday, February 7, 13

Slide 28

Slide 28 text

FRAGMENT CACHE DIGESTS # app/views/todolists/_todolist.html.erb <% cache todolist do %> My todolist: <%= todolist.name %> <%= render document.comments %> <% end %> # app/views/comments/_comment.html.erb <% cache comment do %> My comment: <%= comment.body %> <% end %> In Rails 4: Thursday, February 7, 13

Slide 29

Slide 29 text

NO VENDORED PLUGINS rm -rf vendor/plugins Thursday, February 7, 13

Slide 30

Slide 30 text

AR SESSION STORE GEM EXTRACTION gem “activerecord-session_store” Thursday, February 7, 13

Slide 31

Slide 31 text

ACTIVERESOURCE EXTRACTED gem “activeresource” Thursday, February 7, 13

Slide 32

Slide 32 text

SPROCKET-POWERD ASSET PIPELINE EXTRACTED gem “sprocket-rails” Differential Pre-compilation (Finally!) Silent Logging (replaces need for quiet_assets gem) Thursday, February 7, 13

Slide 33

Slide 33 text

SCOPES REQUIRE CALLABLES In Rails 4: class Team < ActiveRecord::Base scope :active, where(active: true) scope :recent, where(published_at: Time.now - 2.weeks) end class Team < ActiveRecord::Base scope :active, -> { where(active: true) } scope :recent, -> { where(published_at: Time.now - 2.weeks) } end Thursday, February 7, 13

Slide 34

Slide 34 text

DEPRECATED FINDERS # find_all_by_ Rails 3: Team.find_all_by_name('Justice League') Rails 4: Team.where(name: 'Justice League') # find_last_by_ Rails 3: Team.find_last_by_name('Justice League') Rails 4: Team.where(name: 'Justice League').last # find_or_create_by_ Rails 3: Team.find_or_create_by_name('Justice League') Rails 4: Team.where(name: 'Justice League').first_or_create # find_or_initialize_by_ Rails 3: Team.find_or_initialize_by_name('Justice League') Rails 4: Team.where(name: 'Justice League').first_or_initialize # scoped_by_ Rails 3: Team.scoped_by_name('Justice League') Rails 4: Team.where(name: 'Justice League') Thursday, February 7, 13

Slide 35

Slide 35 text

DEPRECATED HASH FINDERS Rails 3: Team.all(conditions: { name: 'Test' }) Rails 4: Team.where(name: 'Test') Really like this syntax anyways? Okay then... gem “activerecord-deprecated_finders” Thursday, February 7, 13

Slide 36

Slide 36 text

PATCH, NOT PUT RFC 5789 technically describes PATCH as a partial update, what everyone is using PUT for today. Default for update will now be PATCH. https://github.com/rails/rails/pull/505 Thursday, February 7, 13

Slide 37

Slide 37 text

PLAY WITH RAILS 4 NOW $ git clone http://github.com/rails/rails.git $ cd rails && bundle install $ ruby railties/bin/rails new ../try_four --dev $ cd ../try_four Thursday, February 7, 13

Slide 38

Slide 38 text

RESOURCES • http://blog.remarkablelabs.com/2012/11/rails-4-countdown- to-2013 • https://speakerdeck.com/kvzb/wtf-rails-4 • http://blog.wyeworks.com/2012/10/29/rails-4-in-30-minutes/ • http://edgeguides.rubyonrails.org/4_0_release_notes.html • http://afreshcup.com/ Thursday, February 7, 13

Slide 39

Slide 39 text

THANKS! QUESTIONS? Ben Hughes @rubiety http://benhugh.es Thursday, February 7, 13