SlideShare una empresa de Scribd logo
1 de 81
Descargar para leer sin conexión
Ruby 2.0 / Rails 4.0
A selection of new features
Evan Dorn
Founder, Logical Reality Design
http://lrdesign.com
evan@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 }
1
2
3
4
5
6
7
8
9
10
11
=> 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 }
1
2
3
=> 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-REFINEMENTS
module MyExtensions
def do_stuff
end
end
class String
include MyExtensions
end
# all loaded code sees String#do_stuff
# String.new.instance_methods changes...
# String.new.respond_to changes...
Friday, August 9, 13
NON-REFINEMENTS
module MyExtensions
def do_stuff
end
end
class String
include MyExtensions
end
# all loaded code sees String#do_stuff
# String.new.instance_methods changes...
# String.new.respond_to changes...
Friday, August 9, 13
NON-REFINEMENTS
module MyExtensions
def do_stuff
end
end
class String
include MyExtensions
end
# all loaded code sees String#do_stuff
# String.new.instance_methods changes...
# String.new.respond_to changes...
Friday, August 9, 13
REFINEMENTS
module MyExtensions
refine String do
def do_stuff
p “awesome!”
end
end
end
String.new.do_stuff # undefined method “do_stuff”
using MyExtensions
String.new.do_stuff
=> “awesome!”
Friday, August 9, 13
REFINEMENTS
module MyExtensions
refine String do
def do_stuff
p “awesome!”
end
end
end
String.new.do_stuff # undefined method “do_stuff”
using MyExtensions
String.new.do_stuff
=> “awesome!”
Friday, August 9, 13
REFINEMENTS
module MyExtensions
refine String do
def do_stuff
p “awesome!”
end
end
end
String.new.do_stuff # undefined method “do_stuff”
using MyExtensions
String.new.do_stuff
=> “awesome!”
Friday, August 9, 13
REFINEMENTS
module MyExtensions
refine String do
def do_stuff
p “awesome!”
end
end
end
String.new.do_stuff # undefined method “do_stuff”
using MyExtensions
String.new.do_stuff
=> “awesome!”
Friday, August 9, 13
REFINEMENTS
module MyExtensions
refine String do
def do_stuff
p “awesome!”
end
end
end
String.new.do_stuff # undefined method “do_stuff”
using MyExtensions
String.new.do_stuff
=> “awesome!”
Friday, August 9, 13
REFINEMENTS
module MyExtensions
refine String do
def do_stuff
p “awesome!”
end
end
end
String.new.do_stuff # undefined method “do_stuff”
using MyExtensions
String.new.do_stuff
=> “awesome!”
Friday, August 9, 13
REFINEMENTS
module MyExtensions
refine String do
def do_stuff
p “awesome!”
end
end
end
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 ARGUMENTS
def my_method(arg1: ‘foo’, arg2: 123)
p arg1
p arg2
end
my_method
“foo”
123
=> 123
Friday, August 9, 13
KEYWORD ARGUMENTS
def my_method(arg1: ‘foo’, arg2: 123)
p arg1
p arg2
end
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 ARGUMENTS
def my_method(:arg1 => 'foo', :arg2 => 123)
# doesn’t work
end
SyntaxError: (irb):27: syntax error, unexpected tSYMBEG, expecting ')'
def my_method(:arg1 => 'foo', :arg2 => 123)
^
Friday, August 9, 13
KEYWORD ARGUMENTS
def my_method(:arg1 => 'foo', :arg2 => 123)
# doesn’t work
end
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-ASSIGNMENT
PROTECTION
•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 PARAMS
class 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)
end
end
Friday, August 9, 13
STRONG PARAMS
class 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)
end
end
Friday, August 9, 13
STRONG PARAMS
class 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)
end
end
Friday, August 9, 13
STRONG PARAMS
class 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)
end
end
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)}”
end
end
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)}”
end
end
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)}”
end
end
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)}”
end
end
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)}”
end
end
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::MODEL
class 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
end
Friday, August 9, 13
ACTIVEMODEL::MODEL
class 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::MODEL
class 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::MODEL
class 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!
end
end
Friday, August 9, 13
ACTIVEMODEL::MODEL
class 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
end
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::LIVE
class MyController < ActionController::Base
include ActionController::Live
def stream
response.headers['Content-Type'] = 'text/event-stream'
100.times {
response.stream.write "hello worldn"
sleep 1
}
ensure
response.stream.close
end
end
Friday, August 9, 13
ACTIONCONTROLLER::LIVE
class MyController < ActionController::Base
include ActionController::Live
def stream
response.headers['Content-Type'] = 'text/event-stream'
100.times {
response.stream.write "hello worldn"
sleep 1
}
ensure
response.stream.close
end
end
Friday, August 9, 13
ACTIONCONTROLLER::LIVE
class MyController < ActionController::Base
include ActionController::Live
def stream
response.headers['Content-Type'] = 'text/event-stream'
100.times {
response.stream.write "hello worldn"
sleep 1
}
ensure
response.stream.close
end
end
Friday, August 9, 13
ACTIONCONTROLLER::LIVE
class MyController < ActionController::Base
include ActionController::Live
def stream
response.headers['Content-Type'] = 'text/event-stream'
100.times {
response.stream.write "hello worldn"
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 Dorn
Founder, Logical Reality Design
http://lrdesign.com
evan@lrdesign.com
@idahoev
Friday, August 9, 13

Más contenido relacionado

Similar a Ruby 2.0 and Rails 4.0 New Features Guide

Mastering ElasticSearch with Ruby and Tire
Mastering ElasticSearch with Ruby and TireMastering ElasticSearch with Ruby and Tire
Mastering ElasticSearch with Ruby and TireLuca Bonmassar
 
Expressjs basic to advance, power by Node.js
Expressjs basic to advance, power by Node.jsExpressjs basic to advance, power by Node.js
Expressjs basic to advance, power by Node.jsCaesar Chi
 
elasticsearch basics workshop
elasticsearch basics workshopelasticsearch basics workshop
elasticsearch basics workshopMathieu Elie
 
How We Look At Software
How We Look At SoftwareHow We Look At Software
How We Look At SoftwareDevrim Yasar
 
The Future of JRuby - Baruco 2013
The Future of JRuby - Baruco 2013The Future of JRuby - Baruco 2013
The Future of JRuby - Baruco 2013Charles Nutter
 
Advanced Perl Techniques
Advanced Perl TechniquesAdvanced Perl Techniques
Advanced Perl TechniquesDave Cross
 
SassConf: It takes a village to raise a stylesheet
SassConf: It takes a village to raise a stylesheetSassConf: It takes a village to raise a stylesheet
SassConf: It takes a village to raise a stylesheetchriseppstein
 
Front-end development automation with Grunt
Front-end development automation with GruntFront-end development automation with Grunt
Front-end development automation with Gruntbenko
 
Scaling PHP to 40 Million Uniques
Scaling PHP to 40 Million UniquesScaling PHP to 40 Million Uniques
Scaling PHP to 40 Million UniquesJonathan Klein
 
Intro to pattern matching in scala
Intro to pattern matching in scalaIntro to pattern matching in scala
Intro to pattern matching in scalaJan Krag
 
Oxente on Rails 2009
Oxente on Rails 2009Oxente on Rails 2009
Oxente on Rails 2009Fabio Akita
 
The Shape of Functional Programming
The Shape of Functional ProgrammingThe Shape of Functional Programming
The Shape of Functional ProgrammingMike Fogus
 
Cooking an Omelette with Chef
Cooking an Omelette with ChefCooking an Omelette with Chef
Cooking an Omelette with Chefctaintor
 
Basics of Metaprogramming in Ruby
Basics of Metaprogramming in RubyBasics of Metaprogramming in Ruby
Basics of Metaprogramming in RubyDigital Natives
 
Ruby 入門 第一次就上手
Ruby 入門 第一次就上手Ruby 入門 第一次就上手
Ruby 入門 第一次就上手Wen-Tien Chang
 
Introducing Modern Perl
Introducing Modern PerlIntroducing Modern Perl
Introducing Modern PerlDave Cross
 
Pgbr 2013 postgres on aws
Pgbr 2013   postgres on awsPgbr 2013   postgres on aws
Pgbr 2013 postgres on awsEmanuel Calvo
 
Regex Considered Harmful: Use Rosie Pattern Language Instead
Regex Considered Harmful: Use Rosie Pattern Language InsteadRegex Considered Harmful: Use Rosie Pattern Language Instead
Regex Considered Harmful: Use Rosie Pattern Language InsteadAll Things Open
 
APMG juni 2014 - Regular Expression
APMG juni 2014 - Regular ExpressionAPMG juni 2014 - Regular Expression
APMG juni 2014 - Regular ExpressionByte
 

Similar a Ruby 2.0 and Rails 4.0 New Features Guide (20)

Mastering ElasticSearch with Ruby and Tire
Mastering ElasticSearch with Ruby and TireMastering ElasticSearch with Ruby and Tire
Mastering ElasticSearch with Ruby and Tire
 
Expressjs basic to advance, power by Node.js
Expressjs basic to advance, power by Node.jsExpressjs basic to advance, power by Node.js
Expressjs basic to advance, power by Node.js
 
Ruby101
Ruby101Ruby101
Ruby101
 
elasticsearch basics workshop
elasticsearch basics workshopelasticsearch basics workshop
elasticsearch basics workshop
 
How We Look At Software
How We Look At SoftwareHow We Look At Software
How We Look At Software
 
The Future of JRuby - Baruco 2013
The Future of JRuby - Baruco 2013The Future of JRuby - Baruco 2013
The Future of JRuby - Baruco 2013
 
Advanced Perl Techniques
Advanced Perl TechniquesAdvanced Perl Techniques
Advanced Perl Techniques
 
SassConf: It takes a village to raise a stylesheet
SassConf: It takes a village to raise a stylesheetSassConf: It takes a village to raise a stylesheet
SassConf: It takes a village to raise a stylesheet
 
Front-end development automation with Grunt
Front-end development automation with GruntFront-end development automation with Grunt
Front-end development automation with Grunt
 
Scaling PHP to 40 Million Uniques
Scaling PHP to 40 Million UniquesScaling PHP to 40 Million Uniques
Scaling PHP to 40 Million Uniques
 
Intro to pattern matching in scala
Intro to pattern matching in scalaIntro to pattern matching in scala
Intro to pattern matching in scala
 
Oxente on Rails 2009
Oxente on Rails 2009Oxente on Rails 2009
Oxente on Rails 2009
 
The Shape of Functional Programming
The Shape of Functional ProgrammingThe Shape of Functional Programming
The Shape of Functional Programming
 
Cooking an Omelette with Chef
Cooking an Omelette with ChefCooking an Omelette with Chef
Cooking an Omelette with Chef
 
Basics of Metaprogramming in Ruby
Basics of Metaprogramming in RubyBasics of Metaprogramming in Ruby
Basics of Metaprogramming in Ruby
 
Ruby 入門 第一次就上手
Ruby 入門 第一次就上手Ruby 入門 第一次就上手
Ruby 入門 第一次就上手
 
Introducing Modern Perl
Introducing Modern PerlIntroducing Modern Perl
Introducing Modern Perl
 
Pgbr 2013 postgres on aws
Pgbr 2013   postgres on awsPgbr 2013   postgres on aws
Pgbr 2013 postgres on aws
 
Regex Considered Harmful: Use Rosie Pattern Language Instead
Regex Considered Harmful: Use Rosie Pattern Language InsteadRegex Considered Harmful: Use Rosie Pattern Language Instead
Regex Considered Harmful: Use Rosie Pattern Language Instead
 
APMG juni 2014 - Regular Expression
APMG juni 2014 - Regular ExpressionAPMG juni 2014 - Regular Expression
APMG juni 2014 - Regular Expression
 

Último

Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101Paola De la Torre
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking MenDelhi Call girls
 
Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024The Digital Insurer
 
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptxEIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptxEarley Information Science
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)Gabriella Davis
 
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure serviceWhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure servicePooja Nehwal
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationSafe Software
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonAnna Loughnan Colquhoun
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking MenDelhi Call girls
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsMaria Levchenko
 
Unblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesUnblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesSinan KOZAK
 
Developing An App To Navigate The Roads of Brazil
Developing An App To Navigate The Roads of BrazilDeveloping An App To Navigate The Roads of Brazil
Developing An App To Navigate The Roads of BrazilV3cube
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationMichael W. Hawkins
 
Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Allon Mureinik
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerThousandEyes
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slidevu2urc
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonetsnaman860154
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slidespraypatel2
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024The Digital Insurer
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreternaman860154
 

Último (20)

Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men
 
Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024
 
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptxEIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure serviceWhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed texts
 
Unblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesUnblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen Frames
 
Developing An App To Navigate The Roads of Brazil
Developing An App To Navigate The Roads of BrazilDeveloping An App To Navigate The Roads of Brazil
Developing An App To Navigate The Roads of Brazil
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day Presentation
 
Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slide
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonets
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slides
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreter
 

Ruby 2.0 and Rails 4.0 New Features Guide

  • 1. Ruby 2.0 / Rails 4.0 A selection of new features Evan Dorn Founder, Logical Reality Design http://lrdesign.com evan@lrdesign.com @idahoev Friday, August 9, 13
  • 2. RUBY 2.0: A FEW COOL BITS Friday, August 9, 13
  • 3. #to_h hash representation, like to_a or to_s or to_sym. Friday, August 9, 13
  • 6. 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
  • 7. 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
  • 8. 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
  • 9. 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 } 1 2 3 4 5 6 7 8 9 10 11 => 11 #bsearch Friday, August 9, 13
  • 10. 2.0.0p195 :003 > arr.bsearch{ |elem| p (n = n+1); elem == 11 } #bsearch Friday, August 9, 13
  • 11. 2.0.0p195 :003 > arr.bsearch{ |elem| p (n = n+1); elem == 11 } 1 2 3 => 11 #bsearch Friday, August 9, 13
  • 13. Undefined behavior if your Array is not sorted! Friday, August 9, 13
  • 14. 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
  • 15. 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
  • 16. 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
  • 17. NON-REFINEMENTS module MyExtensions def do_stuff end end class String include MyExtensions end # all loaded code sees String#do_stuff # String.new.instance_methods changes... # String.new.respond_to changes... Friday, August 9, 13
  • 18. NON-REFINEMENTS module MyExtensions def do_stuff end end class String include MyExtensions end # all loaded code sees String#do_stuff # String.new.instance_methods changes... # String.new.respond_to changes... Friday, August 9, 13
  • 19. NON-REFINEMENTS module MyExtensions def do_stuff end end class String include MyExtensions end # all loaded code sees String#do_stuff # String.new.instance_methods changes... # String.new.respond_to changes... Friday, August 9, 13
  • 20. REFINEMENTS module MyExtensions refine String do def do_stuff p “awesome!” end end end String.new.do_stuff # undefined method “do_stuff” using MyExtensions String.new.do_stuff => “awesome!” Friday, August 9, 13
  • 21. REFINEMENTS module MyExtensions refine String do def do_stuff p “awesome!” end end end String.new.do_stuff # undefined method “do_stuff” using MyExtensions String.new.do_stuff => “awesome!” Friday, August 9, 13
  • 22. REFINEMENTS module MyExtensions refine String do def do_stuff p “awesome!” end end end String.new.do_stuff # undefined method “do_stuff” using MyExtensions String.new.do_stuff => “awesome!” Friday, August 9, 13
  • 23. REFINEMENTS module MyExtensions refine String do def do_stuff p “awesome!” end end end String.new.do_stuff # undefined method “do_stuff” using MyExtensions String.new.do_stuff => “awesome!” Friday, August 9, 13
  • 24. REFINEMENTS module MyExtensions refine String do def do_stuff p “awesome!” end end end String.new.do_stuff # undefined method “do_stuff” using MyExtensions String.new.do_stuff => “awesome!” Friday, August 9, 13
  • 25. REFINEMENTS module MyExtensions refine String do def do_stuff p “awesome!” end end end String.new.do_stuff # undefined method “do_stuff” using MyExtensions String.new.do_stuff => “awesome!” Friday, August 9, 13
  • 26. REFINEMENTS module MyExtensions refine String do def do_stuff p “awesome!” end end end String.new.do_stuff # undefined method “do_stuff” using MyExtensions String.new.do_stuff => “awesome!” Friday, August 9, 13
  • 27. 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
  • 28. KEYWORD ARGUMENTS def my_method(arg1: ‘foo’, arg2: 123) p arg1 p arg2 end my_method “foo” 123 => 123 Friday, August 9, 13
  • 29. KEYWORD ARGUMENTS def my_method(arg1: ‘foo’, arg2: 123) p arg1 p arg2 end my_method “foo” 123 => 123 Friday, August 9, 13
  • 31. THOSE ARGS ARE NOT A HASH! Friday, August 9, 13
  • 32. KEYWORD ARGUMENTS def my_method(:arg1 => 'foo', :arg2 => 123) # doesn’t work end SyntaxError: (irb):27: syntax error, unexpected tSYMBEG, expecting ')' def my_method(:arg1 => 'foo', :arg2 => 123) ^ Friday, August 9, 13
  • 33. KEYWORD ARGUMENTS def my_method(:arg1 => 'foo', :arg2 => 123) # doesn’t work end SyntaxError: (irb):27: syntax error, unexpected tSYMBEG, expecting ')' def my_method(:arg1 => 'foo', :arg2 => 123) ^ Friday, August 9, 13
  • 34. MODULE#PREPEND •I didn’t have time to write this slide! •But the feature is awesome, I promise! Friday, August 9, 13
  • 38. MASS-ASSIGNMENT PROTECTION •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
  • 39. STRONG PARAMS class 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) end end Friday, August 9, 13
  • 40. STRONG PARAMS class 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) end end Friday, August 9, 13
  • 41. STRONG PARAMS class 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) end end Friday, August 9, 13
  • 42. STRONG PARAMS class 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) end end Friday, August 9, 13
  • 43. 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
  • 45. 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
  • 46. 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
  • 47. 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
  • 48. 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
  • 49. 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
  • 50. 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
  • 51. class TodoItem < ActiveRecord::Base def cache_key “#{self.id}-#{self.updated_at.to_s(:number)}” end end 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
  • 52. class TodoItem < ActiveRecord::Base def cache_key “#{self.id}-#{self.updated_at.to_s(:number)}” end end 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
  • 53. class TodoItem < ActiveRecord:: Base def cache_key “#{self.id}-#{self.updated_at.to_s(:number)}” end end 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
  • 54. class TodoItem def cache_key “#{self.id}-#{self.updated_at.to_s(:number)}” end end 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
  • 55. class TodoItem def cache_key “#{self.id}-#{self.updated_at.to_s(:number)}” end end 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
  • 56. 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
  • 57. 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
  • 58. 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
  • 59. PSST... A SECRET: You can do this in Rails 3, too. Just write your own smart cache key helpers. Friday, August 9, 13
  • 60. 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
  • 61. 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
  • 62. ACTIVEMODEL::MODEL Helps to break the tendency to equate “resource” with “ActiveRecord Model.” Friday, August 9, 13
  • 63. ACTIVEMODEL::MODEL class 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 end Friday, August 9, 13
  • 64. ACTIVEMODEL::MODEL class 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
  • 65. ACTIVEMODEL::MODEL class 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
  • 66. ACTIVEMODEL::MODEL class 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! end end Friday, August 9, 13
  • 67. ACTIVEMODEL::MODEL class 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 end Friday, August 9, 13
  • 68. ACTIVEMODEL::MODEL # /app/views/users/_form = form_for(@user_form_model) = f.input(:name) = f.check_box(:admin) = f.submit! Friday, August 9, 13
  • 69. ACTIVEMODEL::MODEL # /app/views/users/_form = form_for(@user_form_model) = f.input(:name) = f.check_box(:admin) = f.submit! Friday, August 9, 13
  • 73. ‘cause that’s probably the right tool, really. Friday, August 9, 13
  • 75. ACTIONCONTROLLER::LIVE class MyController < ActionController::Base include ActionController::Live def stream response.headers['Content-Type'] = 'text/event-stream' 100.times { response.stream.write "hello worldn" sleep 1 } ensure response.stream.close end end Friday, August 9, 13
  • 76. ACTIONCONTROLLER::LIVE class MyController < ActionController::Base include ActionController::Live def stream response.headers['Content-Type'] = 'text/event-stream' 100.times { response.stream.write "hello worldn" sleep 1 } ensure response.stream.close end end Friday, August 9, 13
  • 77. ACTIONCONTROLLER::LIVE class MyController < ActionController::Base include ActionController::Live def stream response.headers['Content-Type'] = 'text/event-stream' 100.times { response.stream.write "hello worldn" sleep 1 } ensure response.stream.close end end Friday, August 9, 13
  • 78. ACTIONCONTROLLER::LIVE class MyController < ActionController::Base include ActionController::Live def stream response.headers['Content-Type'] = 'text/event-stream' 100.times { response.stream.write "hello worldn" sleep 1 } ensure response.stream.close end end Friday, August 9, 13
  • 79. 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
  • 80. ANY TIME LEFT? (IF SO, I’LL DO MODULE#PREPEND ON A WHITEBOARD OR SOMETHING.) Friday, August 9, 13
  • 81. Thanks! Evan Dorn Founder, Logical Reality Design http://lrdesign.com evan@lrdesign.com @idahoev Friday, August 9, 13