Rails 3: Dashing to the Finish

26,056 views
20,093 views

Published on

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

No Downloads
Views
Total views
26,056
On SlideShare
0
From Embeds
0
Number of Embeds
826
Actions
Shares
0
Downloads
458
Comments
0
Likes
43
Embeds 0
No embeds

No notes for slide

Rails 3: Dashing to the Finish

  1. { Rails 3
  2. Overview
  3. Dashing to the Finish
  4. A Lot Like Rails 2.3
  5. Quick Refresher
  6. What Hasn’t Changed?
  7. MVC
  8. REST
  9. Resources
  10. Controllers
  11. Migrations
  12. AR Ideas
  13. Big User-Facing Changes
  14. File Structure
  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. 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. Gem le source 'http://rubygems.org' gem 'rails', '3.0.0.beta3' gem 'sqlite3-ruby'
  18. con g/environment.rb # Load the rails application require File.expand_path( '../application', __FILE__ ) # Initialize the rails application Tutorial::Application.initialize!
  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. con g/application.rb (2) module Tutorial class Application < Rails::Application config.encoding = "utf-8" config.filter_parameters += [:password] end end
  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. initializers/session_store.rb Rails.application. config.session_store( :cookie_store, :key => '_tutorial_session' )
  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. 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. Recent
  26. Even Easier to Remove Bundler
  27. Removing Bundler $ rm Gemfile
  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. 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. public/ javascripts/ rails.js
  31. github.com/ rails/jquery-ujs
  32. db/seeds.rb
  33. rake db:setup
  34. db:create db:schema:load db:seed
  35. lib/tasks/setup.rake task :bundle do system "bundle install" end task :setup => ["bundle", "db:setup"]
  36. Rails Command ★ generate | g ★ destroy ★ console | c ★ benchmarker ★ server | s ★ profiler ★ dbconsole | db ★ plugin ★ application ★ runner
  37. Block Helpers
  38. Block Helpers (Before) <% form_for @post do |f| %> <%= f.input_field :name %> <% end %>
  39. Block Helpers (Before) <% box do %> <p>Hello World!</p> <% end %>
  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. Block Helpers (After) <%= box do %> <p>Hello World!</p> <% end %>
  42. Block Helpers (After) def box(&block) "<div class='box'>" "#{capture(&block)}" "</div>" end
  43. Block Helpers (After) def box(&block) "<div class='box'>" "#{capture(&block)}" "</div>".html_safe end
  44. Recent
  45. Tons of Fixes to XSS Safe
  46. Lots of XSS- Related Changes to Your App...
  47. You’re Doing it Wrong
  48. Router
  49. Note: Signi cant Changes Ahead
  50. Also Note: Old Mapper Still Available
  51. Matching map.connect "posts", :controller => :posts, :action => :index
  52. Matching map.connect "posts", :controller => :posts, :action => :index match "posts" => "posts#index"
  53. Optional Segments match "/posts(/page)" => "posts#index"
  54. Optional Dynamic Segments match "/posts(/:id)" => "posts#index"
  55. Default Parameters match "/posts(/:id)" => "posts#index", :defaults => {:id => 1}
  56. Default Parameters match "/posts(/:id)" => "posts#index", :id => 1
  57. Named Routes match "/posts(/:id)" => "posts#index", :id => 1, :as => "posts"
  58. The Root root :to => "posts#index"
  59. Scopes
  60. Path Scope match "/admin/posts" => "posts#index" match "/admin/users" => "users#index"
  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. 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. Module Scope match "/posts" => "admin/posts#index" match "/users" => "admin/users#index"
  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. Both match "admin/posts" => "admin/posts#index" match "admin/users" => "admin/users#index"
  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. HTTP Methods
  68. Get Request match "/posts" => "posts#index", :via => "get"
  69. Get Request match "/posts" => "posts#index", :via => "get" get "/posts" => "posts#index"
  70. Scoping scope "/posts" do controller :posts do get "/" => :index end end
  71. Scoping scope "/posts" do controller :posts do get "/" => :index end end get "/posts" => "posts#index"
  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. 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. Constraints
  75. Regex Constraint get "/:id" => "posts#index", :constraints => {:id => /d+/}
  76. Regex Constraint get "/:id" => "posts#index", :id => /d+/
  77. Request-Based Constraint get "/mobile" => "posts#index", :constraints => {:user_agent => /iPhone/}
  78. Request-Based Constraint get "/mobile" => "posts#index", :constraints => {:user_agent => /iPhone/}, :defaults => {:mobile => true}
  79. Request-Based Constraint get "/mobile" => "posts#index", :user_agent => /iPhone/, :mobile => true
  80. Object Constraints class DubDubConstraint def self.matches?(request) request.host =~ /^(www.)/ end end get "/" => "posts#index", :constraints => DubDubConstraint
  81. Rack
  82. Equivalent get "/posts" => "posts#index"
  83. Equivalent get "/posts" => "posts#index" get "/posts" => PostsController.action(:index)
  84. Rack >> a = PostsController.action(:index)
  85. Rack >> a = PostsController.action(:index) => #<Proc:0x0000000103d050d0@/Users/ wycats/Code/rails/actionpack/lib/ action_controller/metal.rb:123>
  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. 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. 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. 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. Match to Rack class MyApp def call(env) [200, {"Content-Type" => "text/html"}, ["Hello World"]] end end get "/" => MyApp.new
  91. Redirection get "/" => redirect("/foo")
  92. Redirection get "/" => redirect("/foo") get "/:id" => redirect("/posts/%{id}") get "/:id" => redirect("/posts/%s")
  93. Redirection get "/" => redirect("/foo") get "/:id" => redirect("/posts/%{id}") get "/:id" => redirect("/posts/%s") get "/:id" => redirect { |params, req| ... }
  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. 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. 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. 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. Resources
  99. Resources resources :magazines do resources :ads end
  100. Member Resources resources :photos do member do get :preview get :print end end
  101. One-Offs resources :photos do get :preview, :on => :member end
  102. Collections resources :photos do collection do get :search end end
  103. Combination scope :module => "admin" do constraints IpBlacklist do resources :posts, :comments end end
  104. Recent
  105. #mount
  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. 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. 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. Sinatra!
  110. ActiveRecord
  111. New Chainable, Lazy API
  112. Chainable Methods ★ select ★ order ★ from ★ limit ★ where ★ offset ★ joins ★ includes ★ having ★ lock ★ group ★ readonly
  113. Controller def index @posts = Post. where(:published => true). order("publish_date desc") end
  114. Model def index @posts = Post.published end class Post < ActiveRecord::Base scope :published, where(:published => true). order("publish_date desc") end
  115. Model class Post < ActiveRecord::Base scope :desc, order("publish_date desc") scope :published, where(:published => true).desc end
  116. Controller def index @posts = Post. where("created_at < ?", Time.now). order("publish_date desc") end
  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. 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. 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. named_scope with_scope nd(:all)
  121. scope
  122. ActionMailer
  123. Massive API Overhaul
  124. Sending Emails def welcome(user) @user = user mail(:to => user.email, :subject => "Welcome man!") end
  125. welcome.text.erb welcome.html.erb
  126. Layouts layout "rails_dispatch" def welcome(user) @user = user mail(:to => user.email, :subject => "Welcome man!") end
  127. rails_dispatch.text.erb rails_dispatch.html.erb
  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. 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. 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. Interceptors class MyInterceptor def self.delivering_email(mail) original = mail.to mail.to = "wycats@gmail.com" mail.subject = "#{original}: #{mail.subject}" end end
  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. Interceptors Delivery Observers
  134. delivering_mail deliver delivered_mail
  135. Bundler
  136. bundle install
  137. bundle lock
  138. bundle lock
  139. .gitignore .dot les
  140. Engine Yard Heroku chef
  141. Engine Yard Heroku chef capistrano?
  142. gembundler.com
  143. gembundler.com /rails3.html
  144. railsdispatch.com /posts/bundler
  145. yehudakatz.com/ 2010/04/12/some-of- the-problems- bundler-solves/
  146. yehudakatz.com/ 2010/04/17/ruby- require-order- problems/
  147. Choices
  148. rspec-rails
  149. generators rake tasks controller_example view_example request_example mailer_example
  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. dm-rails
  152. generators rake tasks append_info_to_payload i18n_scope IdentityMap middleware
  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. 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. 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. What Else?
  157. Ruby 1.9 Encoding
  158. Mission
  159. You Can Assume UTF-8 Inside of Rails
  160. (unless you want to handle encodings yourself)
  161. Testing
  162. RSpec Driven Effort
  163. Rails Testing Support Becomes Modular
  164. ActionController Middleware class PostsController use MyMiddleware, :only => :index end
  165. Metal becomes AC::Metal
  166. i18n Check Out Sven’s Talk Last slot in conference
  167. Trimming ActiveSupport Dependencies
  168. Thanks!
  169. Questions?

×