• Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
12,150
On Slideshare
0
From Embeds
0
Number of Embeds
4

Actions

Shares
Downloads
456
Comments
0
Likes
43

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 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?