Ruby 2.0 / Rails 4.0, A selection of new features.

Post on 06-May-2015

513 views 5 download

description

A selection of new features features from Ruby 2.0 and Rails 4.0.

Transcript of Ruby 2.0 / Rails 4.0, A selection of new features.

Ruby 2.0 / Rails 4.0A selection of new features

Evan DornFounder, Logical Reality Design

http://lrdesign.comevan@lrdesign.com

@idahoev

Friday, August 9, 13

RUBY 2.0:A FEW COOL BITS

Friday, August 9, 13

#to_h

hash representation, like to_a or to_s or

to_sym.

Friday, August 9, 13

#to_h

Implemented on:•ENV•NilClass•Struct•Your classes!•ActiveRecord::Base (hopefully soon...)

Friday, August 9, 13

#bsearch

Fast binary search!

•Array•Range

Friday, August 9, 13

2.0.0p195 :003 > arr = [1, 3, 5, 7, 10, 11, 12, 14, 16, 18, 19] => [1, 3, 5, 7, 10, 11, 12, 14, 16, 18, 19]

#bsearch

Friday, August 9, 13

2.0.0p195 :003 > arr = [1, 3, 5, 7, 10, 11, 12, 14, 16, 18, 19] => [1, 3, 5, 7, 10, 11, 12, 14, 16, 18, 19] 2.0.0p195 :004 > n = 0 => 0

#bsearch

Friday, August 9, 13

2.0.0p195 :003 > arr = [1, 3, 5, 7, 10, 11, 12, 14, 16, 18, 19] => [1, 3, 5, 7, 10, 11, 12, 14, 16, 18, 19] 2.0.0p195 :004 > n = 0 => 0 2.0.0p195 :005 > arr.find{ |elem| p (n = n+1); elem == 11 }

#bsearch

Friday, August 9, 13

2.0.0p195 :003 > arr = [1, 3, 5, 7, 10, 11, 12, 14, 16, 18, 19] => [1, 3, 5, 7, 10, 11, 12, 14, 16, 18, 19] 2.0.0p195 :004 > n = 0 => 0 2.0.0p195 :005 > arr.find{ |elem| p (n = n+1); elem == 11 }1234567891011 => 11

#bsearch

Friday, August 9, 13

2.0.0p195 :003 > arr.bsearch{ |elem| p (n = n+1); elem == 11 }

#bsearch

Friday, August 9, 13

2.0.0p195 :003 > arr.bsearch{ |elem| p (n = n+1); elem == 11 }123 => 11

#bsearch

Friday, August 9, 13

WARNING!

Friday, August 9, 13

Undefined behavior if your Array is not

sorted!

Friday, August 9, 13

REFINEMENTS

•Hopefully improves the monkeypatch madness

•How many Core or StdLib classes does Rails extend?

•String

•Integer•Hash• Range• Array

•Regexp

•Proc•Date

•File

•Marshal

•Logger•NameError

•Numeric

•BigDecimal

•LoadError •Time

Friday, August 9, 13

REFINEMENTS

•Hopefully improves the monkeypatch madness

•How many Core or StdLib classes does Rails extend?

•String

•Integer•Hash• Range• Array

•Regexp

•Proc•Date

•File

•Marshal

•Logger•NameError

•Numeric

•BigDecimal

•LoadError •Time

•Module

•Class•Object

Friday, August 9, 13

REFINEMENTS

•What’s wrong with all this?

•“core” becomes a bit meaningless

•All loaded code sees these mutant core classes

•Harder to write gems that both with and without Rails

Friday, August 9, 13

NON-REFINEMENTSmodule MyExtensions def do_stuff endend

class String include MyExtensionsend

# all loaded code sees String#do_stuff

# String.new.instance_methods changes...

# String.new.respond_to changes...

Friday, August 9, 13

NON-REFINEMENTSmodule MyExtensions def do_stuff endend

class String include MyExtensionsend

# all loaded code sees String#do_stuff

# String.new.instance_methods changes...

# String.new.respond_to changes...

Friday, August 9, 13

NON-REFINEMENTSmodule MyExtensions def do_stuff endend

class String include MyExtensionsend

# all loaded code sees String#do_stuff

# String.new.instance_methods changes...

# String.new.respond_to changes...

Friday, August 9, 13

REFINEMENTSmodule MyExtensions refine String do def do_stuff p “awesome!” end endend

String.new.do_stuff # undefined method “do_stuff”

using MyExtensions

String.new.do_stuff

=> “awesome!”

Friday, August 9, 13

REFINEMENTSmodule MyExtensions refine String do def do_stuff p “awesome!” end endend

String.new.do_stuff # undefined method “do_stuff”

using MyExtensions

String.new.do_stuff

=> “awesome!”

Friday, August 9, 13

REFINEMENTSmodule MyExtensions refine String do def do_stuff p “awesome!” end endend

String.new.do_stuff # undefined method “do_stuff”

using MyExtensions

String.new.do_stuff

=> “awesome!”

Friday, August 9, 13

REFINEMENTSmodule MyExtensions refine String do def do_stuff p “awesome!” end endend

String.new.do_stuff # undefined method “do_stuff”

using MyExtensions

String.new.do_stuff

=> “awesome!”

Friday, August 9, 13

REFINEMENTSmodule MyExtensions refine String do def do_stuff p “awesome!” end endend

String.new.do_stuff # undefined method “do_stuff”

using MyExtensions

String.new.do_stuff

=> “awesome!”

Friday, August 9, 13

REFINEMENTSmodule MyExtensions refine String do def do_stuff p “awesome!” end endend

String.new.do_stuff # undefined method “do_stuff”

using MyExtensions

String.new.do_stuff

=> “awesome!”

Friday, August 9, 13

REFINEMENTSmodule MyExtensions refine String do def do_stuff p “awesome!” end endend

String.new.do_stuff # undefined method “do_stuff”

using MyExtensions

String.new.do_stuff

=> “awesome!”

Friday, August 9, 13

KEYWORD ARGUMENTS

•my_method(options = {}) has problems

•assigning defaults with merge({...}) is fugly

•options[:mispeled_arg] silently returns nil!

•options = {} allocates a new hash on every call

Friday, August 9, 13

KEYWORD ARGUMENTSdef my_method(arg1: ‘foo’, arg2: 123) p arg1 p arg2end

my_method“foo” 123=> 123

Friday, August 9, 13

KEYWORD ARGUMENTSdef my_method(arg1: ‘foo’, arg2: 123) p arg1 p arg2end

my_method“foo” 123=> 123

Friday, August 9, 13

WARNING!

Friday, August 9, 13

THOSE ARGS ARE NOT A HASH!

Friday, August 9, 13

KEYWORD ARGUMENTSdef my_method(:arg1 => 'foo', :arg2 => 123) # doesn’t workend

SyntaxError: (irb):27: syntax error, unexpected tSYMBEG, expecting ')'def my_method(:arg1 => 'foo', :arg2 => 123) ^

Friday, August 9, 13

KEYWORD ARGUMENTSdef my_method(:arg1 => 'foo', :arg2 => 123) # doesn’t workend

SyntaxError: (irb):27: syntax error, unexpected tSYMBEG, expecting ')'def my_method(:arg1 => 'foo', :arg2 => 123) ^

Friday, August 9, 13

MODULE#PREPEND •I didn’t have time to write this slide!

•But the feature is awesome, I promise!

Friday, August 9, 13

Friday, August 9, 13

SAD PUPPY

Friday, August 9, 13

RAILS 4.0!

Friday, August 9, 13

MASS-ASSIGNMENTPROTECTION

•No more “attr_accessible” in the Model

•It’s a controller concern

•... because it’s often different in different controllers

•(admin controllers, etc.)

Friday, August 9, 13

STRONG PARAMSclass UsersController < ApplicationController def update user = User.find(params[:id]) if user.update_attributes(user_params) # see below redirect_to home_path else render :edit end end private # Require that :user be a key in the params Hash, # and only accept :first, :last, and :email attributes def user_params params.require(:user).permit(:first, :last, :email) endend

Friday, August 9, 13

STRONG PARAMSclass UsersController < ApplicationController def update user = User.find(params[:id]) if user.update_attributes(user_params) # see below redirect_to home_path else render :edit end end private # Require that :user be a key in the params Hash, # and only accept :first, :last, and :email attributes def user_params params.require(:user).permit(:first, :last, :email) endend

Friday, August 9, 13

STRONG PARAMSclass UsersController < ApplicationController def update user = User.find(params[:id]) if user.update_attributes(user_params) # see below redirect_to home_path else render :edit end end private # Require that :user be a key in the params Hash, # and only accept :first, :last, and :email attributes def user_params params.require(:user).permit(:first, :last, :email) endend

Friday, August 9, 13

STRONG PARAMSclass UsersController < ApplicationController def update user = User.find(params[:id]) if user.update_attributes(user_params) # see below redirect_to home_path else render :edit end end private # Require that :user be a key in the params Hash, # and only accept :first, :last, and :email attributes def user_params params.require(:user).permit(:first, :last, :email) endend

Friday, August 9, 13

STRONG PARAMS

•Params hashes get some new methods

•e.g. params.permitted?

•params hash will contain only the permitted keys

•No deep permits: if the Hash contains a Hash, you must permit nested keys specifically

Friday, August 9, 13

Friday, August 9, 13

FRAGMENT CACHING

•Caching HTML fragments can be a huge speedup

•Expiring fragments correctly is a bitch

•... especially if they’re nested

•... and slow if you have a lot of cache keys

Friday, August 9, 13

OLD BUSTED CACHING%h2 To-do List- cache “todo_for_#{current_user.id}” do %table - @todo_items.each do |item| - cache “todo_item_#{item.id}” do %tr %td= item.name %td= item.importance

Friday, August 9, 13

OLD BUSTED CACHING%h2 To-do List- cache “todo_for_#{current_user.id}” do %table - @todo_items.each do |item| - cache “todo_item_#{item.id}” do %tr %td= item.name %td= item.importance

Friday, August 9, 13

RUSSIAN DOLL CACHING

•Important changes:

•cache(key) do ...end takes an Array for the key

•key generation concatenates the array elements

•after calling #cache_key on any members that respond to that method

Friday, August 9, 13

NEW HOTNESS CACHING%h2 To-do List- cache [ :todo_list, current_user, @todo_list ] do %table - @todo_list.items.each do |item| - cache [ :todo_item, item ] do %tr %td= item.name %td= item.importance

Friday, August 9, 13

NEW HOTNESS CACHING%h2 To-do List- cache [ :todo_list, current_user, todo_list ] do %table - @todo_list.items.each do |item| - cache [ :todo_item, item ] do %tr %td= item.name %td= item.importance

Friday, August 9, 13

class TodoItem < ActiveRecord::Base def cache_key “#{self.id}-#{self.updated_at.to_s(:number)}” endend

class TodoList # A presenter attr_accessor :items # array of items attr_accessor :user # User object def cache_key “#{user.id}-#{items.newest.updated_at.to_s(:number)“ end end

Friday, August 9, 13

class TodoItem < ActiveRecord::Base def cache_key “#{self.id}-#{self.updated_at.to_s(:number)}” endend

class TodoList # A presenter attr_accessor :items # array of items attr_accessor :user # User object def cache_key “#{user.id}-#{items.newest.updated_at.to_s(:number)“ end end

Friday, August 9, 13

class TodoItem < ActiveRecord:: Base def cache_key “#{self.id}-#{self.updated_at.to_s(:number)}” endend

class TodoList # A Presenter attr_accessor :items # array of items attr_accessor :user # User object def cache_key “#{user.id}-#{items.newest.updated_at.to_s(:number)“ end end

Friday, August 9, 13

class TodoItem def cache_key “#{self.id}-#{self.updated_at.to_s(:number)}” endend

class TodoList attr_accessor :items # array of items attr_accessor :user # User object def cache_key “#{items.newest.updated_at.to_s(:number)“ end end

Friday, August 9, 13

class TodoItem def cache_key “#{self.id}-#{self.updated_at.to_s(:number)}” endend

class TodoList attr_accessor :items # array of items attr_accessor :user # User object def cache_key “#{items.newest.updated_at.to_s(:number)“ end end

class User < ActiveRecord::Base def cache_key “#{self.id}-#{self.updated_at.to_s(:number)}” end end

Friday, August 9, 13

NEW HOTNESS CACHING%h2 To-do List- cache [ :todo_list, current_user, todo_list ] do -# “todo_list/1234-201308081234/201308081146” %table - @todo_list.items.each do |item| - cache [ :todo_item, item ] do %tr %td= item.name %td= item.importance

Friday, August 9, 13

NEW HOTNESS CACHING%h2 To-do List- cache [ :todo_list, current_user, todo_list ] do -# “todo_list/1234-201308081234/201308081146” %table - @todo_list.items.each do |item| - cache [ :todo_item, item ] do -# “todo_item/5316-201306071156” %tr %td= item.name %td= item.importance

Friday, August 9, 13

RUSSIAN DOLL CACHING

•With clever key construction:

•Every model update causes a cache miss

•... Never have to invalidate fragments!

•Any decent memory store will expire unused keys for you

Friday, August 9, 13

PSST... A SECRET:You can do this in Rails 3, too.

Just write your own smart cache key helpers.

Friday, August 9, 13

PATCH•PUT is no longer the default HTTP method for #update

actions.

•(Because PUT is defined to be whole-object, never partial, and should avoid side-effects.)

•Use PATCH instead

•Affects: config/routes.rb, tests, etc.

Friday, August 9, 13

ACTIVEMODEL::MODEL

•Turns any object into a proper model

•For use with form_for(), etc.

•attr_accessors become AM-style attributes

•Adds name introspection, conversion, I18n support, validations, etc...

Friday, August 9, 13

ACTIVEMODEL::MODEL

Helps to break the tendency to equate “resource” with “ActiveRecord Model.”

Friday, August 9, 13

ACTIVEMODEL::MODELclass UserFormModel include ActiveModel::Model

def new(user) @user = user @settings = user.settings end

attr_accessor :name, :admin

def persisted? @user.persisted? end

def persist! @user.name = name @user.save! @settings.admin = admin @settings.save! endend

Friday, August 9, 13

ACTIVEMODEL::MODELclass UserFormModel include ActiveModel::Model

def new(user) @user = user @settings = user.settings end

attr_accessor :name, :admin

def persisted? @user.persisted? end

def persist! @user.name = name @user.save! @settings.admin = admin @settings.save! end

Friday, August 9, 13

ACTIVEMODEL::MODELclass UserFormModel include ActiveModel::Model

def new(user) @user = user @settings = user.settings end

attr_accessor :name, :admin

def persisted? @user.persisted? end

def persist! @user.name = name @user.save! @settings.admin = admin @settings.save! end

Friday, August 9, 13

ACTIVEMODEL::MODELclass UserFormModel include ActiveModel::Model

def new(user) @user = user @settings = user.settings end

attr_accessor :name, :admin

def persisted? # default false if not overridden! @user.persisted? end

def persist! @user.name = name @user.save! @settings.admin = admin @settings.save! endend

Friday, August 9, 13

ACTIVEMODEL::MODELclass UserFormModel include ActiveModel::Model

def new(user) @user = user @settings = user.settings end

attr_accessor :name, :admin

def persisted? @user.persisted? end

def persist! @user.name = name @user.save! @settings.admin = admin @settings.save! endend

Friday, August 9, 13

ACTIVEMODEL::MODEL# /app/views/users/_form

= form_for(@user_form_model) = f.input(:name) = f.check_box(:admin) = f.submit!

Friday, August 9, 13

ACTIVEMODEL::MODEL# /app/views/users/_form

= form_for(@user_form_model) = f.input(:name) = f.check_box(:admin) = f.submit!

Friday, August 9, 13

AVOIDING FULL PAGE LOADS

Friday, August 9, 13

CHECK OUT “TURBOLINKS”

Friday, August 9, 13

OR JUST USE:•AngularJS•EmberJS•Backbone•etc...

Friday, August 9, 13

‘cause that’s probably the

right tool,really.

Friday, August 9, 13

STREAMING SUPPORT!

Friday, August 9, 13

ACTIONCONTROLLER::LIVEclass 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 } ensure response.stream.close end

end

Friday, August 9, 13

ACTIONCONTROLLER::LIVEclass 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 } ensure response.stream.close end

end

Friday, August 9, 13

ACTIONCONTROLLER::LIVEclass 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 } ensure response.stream.close end

end

Friday, August 9, 13

ACTIONCONTROLLER::LIVEclass 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 } ensure response.stream.close end

end

Friday, August 9, 13

ACTIONCONTROLLER::LIVE

•Don’t try to change headers after calling response.stream.write...

•Don’t forget to close...

•Stream actions automatically happen in a separate thread.

•(So make sure it’s thread-safe!)

Friday, August 9, 13

ANY TIME LEFT? (IF SO, I’LL DO

MODULE#PREPEND ON A WHITEBOARD OR SOMETHING.)

Friday, August 9, 13

Thanks!

Evan DornFounder, Logical Reality Design

http://lrdesign.comevan@lrdesign.com

@idahoev

Friday, August 9, 13