0
{   Rails 3
Overview
Dashing to
the Finish
A Lot Like
Rails 2.3
Quick Refresher
What Hasn’t
 Changed?
MVC
REST
Resources
Controllers
Migrations
AR Ideas
Big User-Facing
   Changes
File Structure
con g.ru
# This file is used by Rack-based
# servers to start the application.
require ::File.expand_path(
  '../config/en...
con g/boot.rb
require 'rubygems'

# Set up gems listed in the Gemfile.
gemfile = File.expand_path(
  '../../Gemfile',
  __...
Gem le
source 'http://rubygems.org'

gem 'rails', '3.0.0.beta3'
gem 'sqlite3-ruby'
con g/environment.rb
# Load the rails application
require File.expand_path(
  '../application',
  __FILE__
)

# Initialize...
con g/application.rb (1)
require File.expand_path(
  '../boot',
  __FILE__
)

require 'rails/all'

if defined?(Bundler)
  ...
con g/application.rb (2)
module Tutorial
  class Application < Rails::Application
    config.encoding = "utf-8"
    config...
environments/production.rb
Tutorial::Application.configure do
  config.cache_classes = true
  config.consider_all_requests...
initializers/session_store.rb
Rails.application.
  config.session_store(
    :cookie_store,
    :key => '_tutorial_session...
script/rails (1)
#!/usr/bin/env ruby
# This command will automatically
# be run when you run "rails" with
# Rails 3 gems i...
script/rails (2)
BOOT_PATH = File.expand_path(
  '../../config/boot',
  __FILE__
)

APP_PATH = File.expand_path(
  '../../...
Recent
Even Easier to
  Remove
   Bundler
Removing Bundler
$ rm Gemfile
app/mailers
$ script/rails g mailer welcome
  create app/mailers/welcome.rb
  invoke erb
  create    app/views/welcome
  i...
app/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title>Tutorial</title>
    <%= stylesheet_link_tag :...
public/
javascripts/
   rails.js
github.com/
rails/jquery-ujs
db/seeds.rb
rake db:setup
db:create
db:schema:load
    db:seed
lib/tasks/setup.rake
task :bundle do
  system "bundle install"
end

task :setup => ["bundle", "db:setup"]
Rails Command

★ generate   | g   ★ destroy
★ console    | c   ★ benchmarker
★ server     | s   ★ profiler
★ dbconsole | d...
Block Helpers
Block Helpers (Before)
<% form_for @post do |f| %>
  <%= f.input_field :name %>
<% end %>
Block Helpers (Before)
<% box do %>
  <p>Hello World!</p>
<% end %>
Block Helpers (Before)
def box(&block)
  content = "<div class='box'>" <<
  capture(&block) << "</div>"

  if block_called...
Block Helpers (After)
<%= box do %>
  <p>Hello World!</p>
<% end %>
Block Helpers (After)
def box(&block)
  "<div class='box'>" 
  "#{capture(&block)}" 
  "</div>"
end
Block Helpers (After)
def box(&block)
  "<div class='box'>" 
  "#{capture(&block)}" 
  "</div>".html_safe
end
Recent
Tons of Fixes to
   XSS Safe
Lots of XSS-
  Related
Changes to
Your App...
You’re Doing it
    Wrong
Router
Note:
  Signi cant
Changes Ahead
Also Note:
 Old Mapper
Still Available
Matching
map.connect "posts",
  :controller => :posts,
  :action => :index
Matching
map.connect "posts",
  :controller => :posts,
  :action => :index

match "posts" => "posts#index"
Optional Segments
match "/posts(/page)" => "posts#index"
Optional Dynamic Segments
match "/posts(/:id)" => "posts#index"
Default Parameters
match "/posts(/:id)" => "posts#index",
      :defaults => {:id => 1}
Default Parameters
match "/posts(/:id)" => "posts#index",
      :id => 1
Named Routes
match "/posts(/:id)" => "posts#index",
      :id => 1,
      :as => "posts"
The Root
root :to => "posts#index"
Scopes
Path Scope
match "/admin/posts" => "posts#index"
match "/admin/users" => "users#index"
Path Scope
match "/admin/posts" => "posts#index"
match "/admin/users" => "users#index"

scope :path => "admin" do
  match ...
Path Scope
match "/admin/posts" => "posts#index"
match "/admin/users" => "users#index"

scope "admin" do
  match "/posts" ...
Module Scope
match "/posts" => "admin/posts#index"
match "/users" => "admin/users#index"
Module Scope
match "/posts" => "admin/posts#index"
match "/users" => "admin/users#index"

scope :module => "admin" do
  ma...
Both
match "admin/posts" => "admin/posts#index"
match "admin/users" => "admin/users#index"
Both
match "admin/posts" => "admin/posts#index"
match "admin/users" => "admin/users#index"

namespace "admin" do
  match "...
HTTP Methods
Get Request
match "/posts" => "posts#index",
      :via => "get"
Get Request
match "/posts" => "posts#index",
      :via => "get"

get "/posts" => "posts#index"
Scoping
scope "/posts" do
  controller :posts do
    get "/" => :index
  end
end
Scoping
scope "/posts" do
  controller :posts do
    get "/" => :index
  end
end

get "/posts" => "posts#index"
Default Resource Route
controller :posts do
  scope "/posts" do
    get    "/"           =>   :index
    post   "/"       ...
Default Resource Route
controller :posts do
  scope "/posts" do
    get    "/"           =>   :index,   :as => :posts
    ...
Constraints
Regex Constraint
get "/:id" => "posts#index",
  :constraints => {:id => /d+/}
Regex Constraint
get "/:id" => "posts#index",
  :id => /d+/
Request-Based Constraint
get "/mobile" => "posts#index",
  :constraints => {:user_agent => /iPhone/}
Request-Based Constraint
get "/mobile" => "posts#index",
  :constraints => {:user_agent => /iPhone/},
  :defaults    => {:...
Request-Based Constraint
get "/mobile" => "posts#index",
  :user_agent => /iPhone/,
  :mobile     => true
Object Constraints
class DubDubConstraint
  def self.matches?(request)
    request.host =~ /^(www.)/
  end
end

get "/" =>...
Rack
Equivalent
get "/posts" => "posts#index"
Equivalent
get "/posts" => "posts#index"

get "/posts" =>
    PostsController.action(:index)
Rack
>> a = PostsController.action(:index)
Rack
>> a = PostsController.action(:index)
=> #<Proc:0x0000000103d050d0@/Users/
wycats/Code/rails/actionpack/lib/
action_c...
Rack
>> a = PostsController.action(:index)
=> #<Proc:0x0000000103d050d0@/Users/
wycats/Code/rails/actionpack/lib/
action_c...
Rack
>> a = PostsController.action(:index)
=> #<Proc:0x0000000103d050d0@/Users/
wycats/Code/rails/actionpack/lib/
action_c...
Rack
>> a = PostsController.action(:index)
=> #<Proc:0x0000000103d050d0@/Users/
wycats/Code/rails/actionpack/lib/
action_c...
Rack
>> a = PostsController.action(:index)
=> #<Proc:0x0000000103d050d0@/Users/
wycats/Code/rails/actionpack/lib/
action_c...
Match to Rack
class MyApp
  def call(env)
    [200,
      {"Content-Type" => "text/html"},
      ["Hello World"]]
  end
en...
Redirection
get "/" => redirect("/foo")
Redirection
get "/" => redirect("/foo")

get "/:id" => redirect("/posts/%{id}")
get "/:id" => redirect("/posts/%s")
Redirection
get "/" => redirect("/foo")

get "/:id" => redirect("/posts/%{id}")
get "/:id" => redirect("/posts/%s")

get "...
Rack App
def redirect(*args, &block)
  options = args.last.is_a?(Hash) ? args.pop : {}

  path        =   args.shift || bl...
Rack App
def redirect(*args, &block)
  options = args.last.is_a?(Hash) ? args.pop : {}

  path        =   args.shift || bl...
Rack App
lambda do |env|
  req = Request.new(env)

  params = [req.symbolized_path_parameters]
  params << req if path_pro...
Rack App
lambda do |env|
  req = Request.new(env)

  params = [req.symbolized_path_parameters]
  params << req if path_pro...
Resources
Resources
resources :magazines do
  resources :ads
end
Member Resources
resources :photos do
  member do
    get :preview
    get :print
  end
end
One-Offs
resources :photos do
  get :preview, :on => :member
end
Collections
resources :photos do
  collection do
    get :search
  end
end
Combination
scope :module => "admin" do
  constraints IpBlacklist do
    resources :posts, :comments
  end
end
Recent
#mount
Rack Endpoint
class MountedEndpoint
  def call(env)
    head = {"Content-Type" => "text/html"}
    body = "script: #{env["...
Mounting
class MountedEndpoint
  def call(env)
    head = {"Content-Type" => "text/html"}
    body = "script: #{env["SCRIP...
Mounting
class MountedEndpoint
  def call(env)
    head = {"Content-Type" => "text/html"}
    body = "script: #{env["SCRIP...
Sinatra!
ActiveRecord
New Chainable,
  Lazy API
Chainable Methods

★ select        ★ order
★ from          ★ limit
★ where         ★ offset
★ joins         ★ includes
★ h...
Controller
def index
  @posts = Post.
    where(:published => true).
    order("publish_date desc")
end
Model
def index
  @posts = Post.published
end

class Post < ActiveRecord::Base
  scope :published,
    where(:published =>...
Model
class Post < ActiveRecord::Base
  scope :desc,
    order("publish_date desc")

  scope :published,
    where(:publis...
Controller
def index
  @posts = Post.
    where("created_at < ?", Time.now).
    order("publish_date desc")
end
Controller
def index
  @posts = Post.past
end

class Post < ActiveRecord::Base
  scope :desc,
    order("publish_date desc...
Model
class Post < ActiveRecord::Base
  scope :desc,
    order("publish_date desc")

 def self.past
   where("created_at <...
Pagination
class PostsController < ApplicationController
  def index
    @posts = Posts.page(5, :per_page => 10)
  end
end...
named_scope

with_scope

   nd(:all)
scope
ActionMailer
Massive API
 Overhaul
Sending Emails
def welcome(user)
  @user = user
  mail(:to => user.email,
       :subject => "Welcome man!")
end
welcome.text.erb

welcome.html.erb
Layouts
layout "rails_dispatch"

def welcome(user)
  @user = user
  mail(:to => user.email,
       :subject => "Welcome ma...
rails_dispatch.text.erb

rails_dispatch.html.erb
Be More Speci c
def welcome(user)
  @user = user
  mail(:to => user.email,
    :subject => "Welcome man!") do |format|
   ...
Defaults
default :from => "wycats@gmail.com"

def welcome(user)
  @user = user
  mail(:to => user.email,
    :subject => "...
Attachments
def welcome(user)
  @user = user
  file = Rails.public_path.join("hello.pdf")
  contents = File.read(file)
  a...
Interceptors
class MyInterceptor
  def self.delivering_email(mail)
    original = mail.to
    mail.to = "wycats@gmail.com"...
Interceptors
class MyInterceptor
  def self.delivering_email(mail)
    original = mail.to
    mail.to = "wycats@gmail.com"...
Interceptors

 Delivery

 Observers
delivering_mail

    deliver

delivered_mail
Bundler
bundle install
bundle lock
bundle lock
.gitignore

.dot les
Engine Yard
  Heroku
   chef
Engine Yard
  Heroku
   chef
capistrano?
gembundler.com
gembundler.com
  /rails3.html
railsdispatch.com
 /posts/bundler
yehudakatz.com/
2010/04/12/some-of-
   the-problems-
   bundler-solves/
yehudakatz.com/
2010/04/17/ruby-
 require-order-
   problems/
Choices
rspec-rails
generators

    rake tasks

controller_example

  view_example

request_example

 mailer_example
Mailer Generator
$ script/rails g mailer welcome
  create app/mailers/welcome.rb
  invoke erb
  create    app/views/welcom...
dm-rails
generators

      rake tasks

append_info_to_payload

      i18n_scope

IdentityMap middleware
generate a resource
$ rails g resource comment
  invoke data_mapper
  create    app/models/comment.rb
  invoke    test_uni...
generate a resource
$ rails g resource comment
  invoke data_mapper
  create    app/models/comment.rb
  invoke    rspec
  ...
rails g
$ rails g       Rails:
                  controller
                  generator
                  helper
         ...
What Else?
Ruby 1.9
Encoding
Mission
You Can
Assume UTF-8
Inside of Rails
(unless you
want to handle
  encodings
   yourself)
Testing
RSpec Driven
   Effort
Rails Testing
  Support
 Becomes
 Modular
ActionController Middleware
class PostsController
  use MyMiddleware, :only => :index
end
Metal
 becomes
AC::Metal
i18n
 Check Out Sven’s Talk

 Last slot in conference
Trimming
ActiveSupport
Dependencies
Thanks!
Questions?
Rails 3: Dashing to the Finish
Rails 3: Dashing to the Finish
Upcoming SlideShare
Loading in...5
×

Rails 3: Dashing to the Finish

12,397

Published on

Published in: Technology
0 Comments
43 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
12,397
On Slideshare
0
From Embeds
0
Number of Embeds
4
Actions
Shares
0
Downloads
456
Comments
0
Likes
43
Embeds 0
No embeds

No notes for slide

Transcript of "Rails 3: Dashing to the Finish"

  1. 1. { Rails 3
  2. 2. Overview
  3. 3. Dashing to the Finish
  4. 4. A Lot Like Rails 2.3
  5. 5. Quick Refresher
  6. 6. What Hasn’t Changed?
  7. 7. MVC
  8. 8. REST
  9. 9. Resources
  10. 10. Controllers
  11. 11. Migrations
  12. 12. AR Ideas
  13. 13. Big User-Facing Changes
  14. 14. File Structure
  15. 15. con g.ru # This file is used by Rack-based # servers to start the application. require ::File.expand_path( '../config/environment', __FILE__ ) run Tutorial::Application
  16. 16. con g/boot.rb require 'rubygems' # Set up gems listed in the Gemfile. gemfile = File.expand_path( '../../Gemfile', __FILE__ ) if File.exist?(gemfile) ENV['BUNDLE_GEMFILE'] = gemfile require 'bundler' Bundler.setup end
  17. 17. Gem le source 'http://rubygems.org' gem 'rails', '3.0.0.beta3' gem 'sqlite3-ruby'
  18. 18. con g/environment.rb # Load the rails application require File.expand_path( '../application', __FILE__ ) # Initialize the rails application Tutorial::Application.initialize!
  19. 19. con g/application.rb (1) require File.expand_path( '../boot', __FILE__ ) require 'rails/all' if defined?(Bundler) Bundler.require(:default, Rails.env) end
  20. 20. con g/application.rb (2) module Tutorial class Application < Rails::Application config.encoding = "utf-8" config.filter_parameters += [:password] end end
  21. 21. environments/production.rb Tutorial::Application.configure do config.cache_classes = true config.consider_all_requests_local = false config.action_controller.perform_caching = true config.action_dispatch.x_sendfile_header = "X-Sendfile" config.serve_static_assets = false end
  22. 22. initializers/session_store.rb Rails.application. config.session_store( :cookie_store, :key => '_tutorial_session' )
  23. 23. script/rails (1) #!/usr/bin/env ruby # This command will automatically # be run when you run "rails" with # Rails 3 gems installed from the # root of your application. ENV_PATH = File.expand_path( '../../config/environment', __FILE__ )
  24. 24. script/rails (2) BOOT_PATH = File.expand_path( '../../config/boot', __FILE__ ) APP_PATH = File.expand_path( '../../config/application', __FILE__ ) require BOOT_PATH require 'rails/commands'
  25. 25. Recent
  26. 26. Even Easier to Remove Bundler
  27. 27. Removing Bundler $ rm Gemfile
  28. 28. app/mailers $ script/rails g mailer welcome create app/mailers/welcome.rb invoke erb create app/views/welcome invoke test_unit create test/functional/welcome_test.rb
  29. 29. app/layouts/application.html.erb <!DOCTYPE html> <html> <head> <title>Tutorial</title> <%= stylesheet_link_tag :all %> <%= javascript_include_tag :defaults %> <%= csrf_meta_tag %> </head> <body> <%= yield %> </body> </html>
  30. 30. public/ javascripts/ rails.js
  31. 31. github.com/ rails/jquery-ujs
  32. 32. db/seeds.rb
  33. 33. rake db:setup
  34. 34. db:create db:schema:load db:seed
  35. 35. lib/tasks/setup.rake task :bundle do system "bundle install" end task :setup => ["bundle", "db:setup"]
  36. 36. Rails Command ★ generate | g ★ destroy ★ console | c ★ benchmarker ★ server | s ★ profiler ★ dbconsole | db ★ plugin ★ application ★ runner
  37. 37. Block Helpers
  38. 38. Block Helpers (Before) <% form_for @post do |f| %> <%= f.input_field :name %> <% end %>
  39. 39. Block Helpers (Before) <% box do %> <p>Hello World!</p> <% end %>
  40. 40. Block Helpers (Before) def box(&block) content = "<div class='box'>" << capture(&block) << "</div>" if block_called_from_erb? concat(content) else content end end
  41. 41. Block Helpers (After) <%= box do %> <p>Hello World!</p> <% end %>
  42. 42. Block Helpers (After) def box(&block) "<div class='box'>" "#{capture(&block)}" "</div>" end
  43. 43. Block Helpers (After) def box(&block) "<div class='box'>" "#{capture(&block)}" "</div>".html_safe end
  44. 44. Recent
  45. 45. Tons of Fixes to XSS Safe
  46. 46. Lots of XSS- Related Changes to Your App...
  47. 47. You’re Doing it Wrong
  48. 48. Router
  49. 49. Note: Signi cant Changes Ahead
  50. 50. Also Note: Old Mapper Still Available
  51. 51. Matching map.connect "posts", :controller => :posts, :action => :index
  52. 52. Matching map.connect "posts", :controller => :posts, :action => :index match "posts" => "posts#index"
  53. 53. Optional Segments match "/posts(/page)" => "posts#index"
  54. 54. Optional Dynamic Segments match "/posts(/:id)" => "posts#index"
  55. 55. Default Parameters match "/posts(/:id)" => "posts#index", :defaults => {:id => 1}
  56. 56. Default Parameters match "/posts(/:id)" => "posts#index", :id => 1
  57. 57. Named Routes match "/posts(/:id)" => "posts#index", :id => 1, :as => "posts"
  58. 58. The Root root :to => "posts#index"
  59. 59. Scopes
  60. 60. Path Scope match "/admin/posts" => "posts#index" match "/admin/users" => "users#index"
  61. 61. Path Scope match "/admin/posts" => "posts#index" match "/admin/users" => "users#index" scope :path => "admin" do match "/posts" => "posts#index" match "/users" => "users#index" end
  62. 62. Path Scope match "/admin/posts" => "posts#index" match "/admin/users" => "users#index" scope "admin" do match "/posts" => "posts#index" match "/users" => "users#index" end
  63. 63. Module Scope match "/posts" => "admin/posts#index" match "/users" => "admin/users#index"
  64. 64. Module Scope match "/posts" => "admin/posts#index" match "/users" => "admin/users#index" scope :module => "admin" do match "/posts" => "posts#index" match "/users" => "users#index" end
  65. 65. Both match "admin/posts" => "admin/posts#index" match "admin/users" => "admin/users#index"
  66. 66. Both match "admin/posts" => "admin/posts#index" match "admin/users" => "admin/users#index" namespace "admin" do match "/posts" => "posts#index" match "/users" => "users#index" end
  67. 67. HTTP Methods
  68. 68. Get Request match "/posts" => "posts#index", :via => "get"
  69. 69. Get Request match "/posts" => "posts#index", :via => "get" get "/posts" => "posts#index"
  70. 70. Scoping scope "/posts" do controller :posts do get "/" => :index end end
  71. 71. Scoping scope "/posts" do controller :posts do get "/" => :index end end get "/posts" => "posts#index"
  72. 72. Default Resource Route controller :posts do scope "/posts" do get "/" => :index post "/" => :create get "/:id" => :show put "/:id" => :update delete "/:id" => :delete get "/new" => :new get "/:id/edit" => :edit end end
  73. 73. Default Resource Route controller :posts do scope "/posts" do get "/" => :index, :as => :posts post "/" => :create get "/:id" => :show, :as => :post put "/:id" => :update delete "/:id" => :delete get "/new" => :new, :as => :new_post get "/:id/edit" => :edit, :as => :edit_post end end
  74. 74. Constraints
  75. 75. Regex Constraint get "/:id" => "posts#index", :constraints => {:id => /d+/}
  76. 76. Regex Constraint get "/:id" => "posts#index", :id => /d+/
  77. 77. Request-Based Constraint get "/mobile" => "posts#index", :constraints => {:user_agent => /iPhone/}
  78. 78. Request-Based Constraint get "/mobile" => "posts#index", :constraints => {:user_agent => /iPhone/}, :defaults => {:mobile => true}
  79. 79. Request-Based Constraint get "/mobile" => "posts#index", :user_agent => /iPhone/, :mobile => true
  80. 80. Object Constraints class DubDubConstraint def self.matches?(request) request.host =~ /^(www.)/ end end get "/" => "posts#index", :constraints => DubDubConstraint
  81. 81. Rack
  82. 82. Equivalent get "/posts" => "posts#index"
  83. 83. Equivalent get "/posts" => "posts#index" get "/posts" => PostsController.action(:index)
  84. 84. Rack >> a = PostsController.action(:index)
  85. 85. Rack >> a = PostsController.action(:index) => #<Proc:0x0000000103d050d0@/Users/ wycats/Code/rails/actionpack/lib/ action_controller/metal.rb:123>
  86. 86. Rack >> a = PostsController.action(:index) => #<Proc:0x0000000103d050d0@/Users/ wycats/Code/rails/actionpack/lib/ action_controller/metal.rb:123> >> e = Rack::MockRequest.env_for("/")
  87. 87. Rack >> a = PostsController.action(:index) => #<Proc:0x0000000103d050d0@/Users/ wycats/Code/rails/actionpack/lib/ action_controller/metal.rb:123> >> e = Rack::MockRequest.env_for("/") => {"SERVER_NAME"=>"example.org", "CONTENT_LENGTH"=>"0", ...}
  88. 88. Rack >> a = PostsController.action(:index) => #<Proc:0x0000000103d050d0@/Users/ wycats/Code/rails/actionpack/lib/ action_controller/metal.rb:123> >> e = Rack::MockRequest.env_for("/") => {"SERVER_NAME"=>"example.org", "CONTENT_LENGTH"=>"0", ...} >> e.call(a)
  89. 89. Rack >> a = PostsController.action(:index) => #<Proc:0x0000000103d050d0@/Users/ wycats/Code/rails/actionpack/lib/ action_controller/metal.rb:123> >> e = Rack::MockRequest.env_for("/") => {"SERVER_NAME"=>"example.org", "CONTENT_LENGTH"=>"0", ...} >> e.call(a) => [200, {"ETag"=> '"eca5953f36da05ff351d712d904e"', ...}, ["Hello World"]]
  90. 90. Match to Rack class MyApp def call(env) [200, {"Content-Type" => "text/html"}, ["Hello World"]] end end get "/" => MyApp.new
  91. 91. Redirection get "/" => redirect("/foo")
  92. 92. Redirection get "/" => redirect("/foo") get "/:id" => redirect("/posts/%{id}") get "/:id" => redirect("/posts/%s")
  93. 93. Redirection get "/" => redirect("/foo") get "/:id" => redirect("/posts/%{id}") get "/:id" => redirect("/posts/%s") get "/:id" => redirect { |params, req| ... }
  94. 94. Rack App def redirect(*args, &block) options = args.last.is_a?(Hash) ? args.pop : {} path = args.shift || block path_proc = path.is_a?(Proc) ? path : proc { |params| path % params } status = options[:status] || 301 body = 'Moved Permanently' lambda do |env| req = Request.new(env) params = [req.symbolized_path_parameters] params << req if path_proc.arity > 1 uri = URI.parse(path_proc.call(*params)) uri.scheme ||= req.scheme uri.host ||= req.host uri.port ||= req.port unless req.port == 80 headers = { 'Location' => uri.to_s, 'Content-Type' => 'text/html', 'Content-Length' => body.length.to_s } [ status, headers, [body] ] end end
  95. 95. Rack App def redirect(*args, &block) options = args.last.is_a?(Hash) ? args.pop : {} path = args.shift || block path_proc = path.is_a?(Proc) ? path : proc { |params| path % params } status = options[:status] || 301 body = 'Moved Permanently' lambda do |env| req = Request.new(env) redirect(*args, &block) params = [req.symbolized_path_parameters] params << req if path_proc.arity > 1 uri = URI.parse(path_proc.call(*params)) uri.scheme ||= req.scheme uri.host ||= req.host uri.port ||= req.port unless req.port == 80 headers = { 'Location' => uri.to_s, 'Content-Type' => 'text/html', 'Content-Length' => body.length.to_s } [ status, headers, [body] ] end end
  96. 96. Rack App lambda do |env| req = Request.new(env) params = [req.symbolized_path_parameters] params << req if path_proc.arity > 1 uri = URI.parse(path_proc.call(*params)) uri.scheme ||= req.scheme uri.host ||= req.host uri.port ||= req.port unless req.port == 80 headers = { 'Location' => uri.to_s, 'Content-Type' => 'text/html', 'Content-Length' => body.length.to_s } [ status, headers, [body] ] end
  97. 97. Rack App lambda do |env| req = Request.new(env) params = [req.symbolized_path_parameters] params << req if path_proc.arity > 1 uri = URI.parse(path_proc.call(*params)) [ status, headers, [body] ] uri.scheme ||= req.scheme uri.host ||= req.host uri.port ||= req.port unless req.port == 80 headers = { 'Location' => uri.to_s, 'Content-Type' => 'text/html', 'Content-Length' => body.length.to_s } [ status, headers, [body] ] end
  98. 98. Resources
  99. 99. Resources resources :magazines do resources :ads end
  100. 100. Member Resources resources :photos do member do get :preview get :print end end
  101. 101. One-Offs resources :photos do get :preview, :on => :member end
  102. 102. Collections resources :photos do collection do get :search end end
  103. 103. Combination scope :module => "admin" do constraints IpBlacklist do resources :posts, :comments end end
  104. 104. Recent
  105. 105. #mount
  106. 106. Rack Endpoint class MountedEndpoint def call(env) head = {"Content-Type" => "text/html"} body = "script: #{env["SCRIPT_NAME"]}" body += "path: #{env["PATH_INFO"]}" [200, head, [body]] end end
  107. 107. Mounting class MountedEndpoint def call(env) head = {"Content-Type" => "text/html"} body = "script: #{env["SCRIPT_NAME"]}" body += "path: #{env["PATH_INFO"]}" [200, head, [body]] end end mount "/end", :at => MountedEndpoint.new
  108. 108. Mounting class MountedEndpoint def call(env) head = {"Content-Type" => "text/html"} body = "script: #{env["SCRIPT_NAME"]}" body += "path: #{env["PATH_INFO"]}" [200, head, [body]] end end mount "/end", :at => MountedEndpoint.new # "/end/point" => # script: /end # path: /point
  109. 109. Sinatra!
  110. 110. ActiveRecord
  111. 111. New Chainable, Lazy API
  112. 112. Chainable Methods ★ select ★ order ★ from ★ limit ★ where ★ offset ★ joins ★ includes ★ having ★ lock ★ group ★ readonly
  113. 113. Controller def index @posts = Post. where(:published => true). order("publish_date desc") end
  114. 114. Model def index @posts = Post.published end class Post < ActiveRecord::Base scope :published, where(:published => true). order("publish_date desc") end
  115. 115. Model class Post < ActiveRecord::Base scope :desc, order("publish_date desc") scope :published, where(:published => true).desc end
  116. 116. Controller def index @posts = Post. where("created_at < ?", Time.now). order("publish_date desc") end
  117. 117. Controller def index @posts = Post.past end class Post < ActiveRecord::Base scope :desc, order("publish_date desc") def self.past where("created_at < ?", Time.now).desc end end
  118. 118. Model class Post < ActiveRecord::Base scope :desc, order("publish_date desc") def self.past where("created_at < ?", Time.now).desc end def self.recent(number) past.limit(5) end end
  119. 119. Pagination class PostsController < ApplicationController def index @posts = Posts.page(5, :per_page => 10) end end class Post < ActiveRecord::Base def self.page(number, options) per_page = options[:per_page] offset(per_page * (number - 1)). limit(per_page) end end
  120. 120. named_scope with_scope nd(:all)
  121. 121. scope
  122. 122. ActionMailer
  123. 123. Massive API Overhaul
  124. 124. Sending Emails def welcome(user) @user = user mail(:to => user.email, :subject => "Welcome man!") end
  125. 125. welcome.text.erb welcome.html.erb
  126. 126. Layouts layout "rails_dispatch" def welcome(user) @user = user mail(:to => user.email, :subject => "Welcome man!") end
  127. 127. rails_dispatch.text.erb rails_dispatch.html.erb
  128. 128. Be More Speci c def welcome(user) @user = user mail(:to => user.email, :subject => "Welcome man!") do |format| format.html format.text { render "generic" } end end
  129. 129. Defaults default :from => "wycats@gmail.com" def welcome(user) @user = user mail(:to => user.email, :subject => "Welcome man!") do |format| format.html format.text { render "generic" } end end
  130. 130. Attachments def welcome(user) @user = user file = Rails.public_path.join("hello.pdf") contents = File.read(file) attachments["welcome.pdf"] = contents mail(:to => user.email, :subject => "Welcome man!") end
  131. 131. Interceptors class MyInterceptor def self.delivering_email(mail) original = mail.to mail.to = "wycats@gmail.com" mail.subject = "#{original}: #{mail.subject}" end end
  132. 132. Interceptors class MyInterceptor def self.delivering_email(mail) original = mail.to mail.to = "wycats@gmail.com" mail.subject = "#{original}: #{mail.subject}" end end config.action_mailer. register_interceptor(MyInterceptor)
  133. 133. Interceptors Delivery Observers
  134. 134. delivering_mail deliver delivered_mail
  135. 135. Bundler
  136. 136. bundle install
  137. 137. bundle lock
  138. 138. bundle lock
  139. 139. .gitignore .dot les
  140. 140. Engine Yard Heroku chef
  141. 141. Engine Yard Heroku chef capistrano?
  142. 142. gembundler.com
  143. 143. gembundler.com /rails3.html
  144. 144. railsdispatch.com /posts/bundler
  145. 145. yehudakatz.com/ 2010/04/12/some-of- the-problems- bundler-solves/
  146. 146. yehudakatz.com/ 2010/04/17/ruby- require-order- problems/
  147. 147. Choices
  148. 148. rspec-rails
  149. 149. generators rake tasks controller_example view_example request_example mailer_example
  150. 150. Mailer Generator $ script/rails g mailer welcome create app/mailers/welcome.rb invoke erb create app/views/welcome invoke rspec create spec/mailers/welcome_spec.rb
  151. 151. dm-rails
  152. 152. generators rake tasks append_info_to_payload i18n_scope IdentityMap middleware
  153. 153. generate a resource $ rails g resource comment invoke data_mapper create app/models/comment.rb invoke test_unit create test/unit/comment_test.rb create test/fixtures/comments.yml invoke controller create app/controllers/comments_controller.rb invoke erb create app/views/commentses invoke test_unit create test/functional/comments_controller_test.rb invoke helper create app/helpers/commentses_helper.rb invoke test_unit create test/unit/helpers/comments_helper_test.rb route resources :commentses
  154. 154. generate a resource $ rails g resource comment invoke data_mapper create app/models/comment.rb invoke rspec create spec/models/comment_spec.rb invoke controller create app/controllers/comments_controller.rb invoke erb create app/views/comments invoke rspec create spec/controllers/comments_controller_spec.rb create spec/views/comments invoke helper create app/helpers/comments_helper.rb invoke rspec route resources :comments
  155. 155. rails g $ rails g Rails: controller generator helper integration_test mailer metal migration model observer performance_test plugin resource scaffold scaffold_controller session_migration stylesheets
  156. 156. What Else?
  157. 157. Ruby 1.9 Encoding
  158. 158. Mission
  159. 159. You Can Assume UTF-8 Inside of Rails
  160. 160. (unless you want to handle encodings yourself)
  161. 161. Testing
  162. 162. RSpec Driven Effort
  163. 163. Rails Testing Support Becomes Modular
  164. 164. ActionController Middleware class PostsController use MyMiddleware, :only => :index end
  165. 165. Metal becomes AC::Metal
  166. 166. i18n Check Out Sven’s Talk Last slot in conference
  167. 167. Trimming ActiveSupport Dependencies
  168. 168. Thanks!
  169. 169. Questions?
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×