Rails 3 overview

139
{ Rails 3

description

 

Transcript of Rails 3 overview

  • { Rails 3

  • Overview

  • A Lot Like Rails 2.3

  • Quick Refresher

  • What Hasnt 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/environment', __FILE__)

    run Tutorial::Application

  • 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.setupend

  • Gem!le

    source 'http://rubygems.org'

    gem 'rails', '3.0.0.beta3'gem 'sqlite3-ruby', :require => 'sqlite3'

    http://rubygems.orghttp://rubygems.org
  • con!g/environment.rb

    # Load the rails applicationrequire File.expand_path( '../application', __FILE__)

    # Initialize the rails applicationTutorial::Application.initialize!

  • con!g/application.rb (1)

    require File.expand_path( '../boot', __FILE__)

    require 'rails/all'

    if defined?(Bundler) Bundler.require(:default, Rails.env)end

  • con!g/application.rb (2)

    module Tutorial class Application < Rails::Application config.encoding = "utf-8" config.filter_parameters += [:password] endend

  • 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 = falseend

  • 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 installed from the # root of your application.

    ENV_PATH = File.expand_path( '../../config/environment', __FILE__)

  • script/rails (2)

    BOOT_PATH = File.expand_path( '../../config/boot', __FILE__)

    APP_PATH = File.expand_path( '../../config/application', __FILE__)

    require BOOT_PATHrequire 'rails/commands'

  • 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

  • app/layouts/application.html.erb

    Tutorial

  • public/javascripts/

    rails.js

  • github.com/rails/jquery-ujs

  • db/seeds.rb

  • rake db:setup

  • db:createdb:schema:load

    db:seed

  • lib/tasks/setup.rake

    task :bundle do system "bundle install"end

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

  • Rails Command

    generate | g

    console | c

    server | s

    dbconsole | db

    application

    destroy

    benchmarker

    profiler

    plugin

    runner

  • Block Helpers

  • Block Helpers (Before)

  • Block Helpers (Before)

    Hello World!

  • Block Helpers (Before)

    def box(&block) content = ""

  • Block Helpers (After)

    Hello World!

  • Block Helpers (After)

    def box(&block) "" \ "#{capture(&block)}" \ ""end

  • Block Helpers (After)

    def box(&block) "" \ "#{capture(&block)}" \ "".html_safeend

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

  • 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

    scope :path => "admin" do match "/posts" => "posts#index" match "/users" => "users#index"end

  • Path Scope

    scope "admin" do match "/posts" => "posts#index" match "/users" => "users#index"end

  • Module Scope

    match "/posts" => "admin/posts#index"match "/users" => "admin/users#index"

  • Module Scope

    scope :module => "admin" do match "/posts" => "posts#index" match "/users" => "users#index"end

  • Both

    match "admin/posts" => "admin/posts#index"match "admin/users" => "admin/users#index"

  • Both

    namespace "admin" do match "/posts" => "posts#index" match "/users" => "users#index"end

  • HTTP Methods

  • Get Request

    match "/posts" => "posts#index", :via => "get"

  • Get Request

    get "/posts" => "posts#index"

  • Scoping

    scope "/posts" do controller :posts do get "/" => :index endend

  • Scoping

    get "/posts" => "posts#index"

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

  • Default Resource Routecontroller :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 endend

  • 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 => {:mobile => true}

  • Request-Based Constraint

    get "/mobile" => "posts#index", :user_agent => /iPhone/, :mobile => true

  • Object Constraints

    class DubDubConstraint def self.matches?(request) request.host =~ /^(www\.)/ endend

    get "/" => "posts#index", :constraints => DubDubConstraint

  • Rack

  • Equivalent

    get "/posts" => "posts#index"

    get "/posts" => PostsController.action(:index)

  • Rack

    >> a = PostsController.action(:index)

  • Rack

    >> a = PostsController.action(:index)=> #

  • Rack

    >> a = PostsController.action(:index)=> # >> e = Rack::MockRequest.env_for("/")

  • Rack

    >> a = PostsController.action(:index)=> # >> e = Rack::MockRequest.env_for("/")=> {"SERVER_NAME"=>"example.org", "CONTENT_LENGTH"=>"0", ...}

  • Rack

    >> a = PostsController.action(:index)=> # >> e = Rack::MockRequest.env_for("/")=> {"SERVER_NAME"=>"example.org", "CONTENT_LENGTH"=>"0", ...}>> e.call(a)

  • Rack

    >> a = PostsController.action(:index)=> # >> e = Rack::MockRequest.env_for("/")=> {"SERVER_NAME"=>"example.org", "CONTENT_LENGTH"=>"0", ...}>> e.call(a)=> [200, {"ETag"=>"eca5953f36da05ff351d712d904ef28d",...}

  • Match to Rack

    class MyApp def call(env) [200, {"Content-Type" => "text/html"}, ["Hello World"]] endend

    get "/" => MyApp

  • 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 "/:id" => redirect { |params, req| ...}

  • Rack Appdef 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 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] ] endend

  • Rack Appdef 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 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] ] endend

    redirect(*args, &block)

  • Rack Applambda do |env| req = Request.new(env)

    params = [req.symbolized_path_parameters] params 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

  • Resources

  • Resources

    resources :magazines do resources :adsend

  • Member Resources

    resources :photos do member do get :preview get :print endend

  • One-Offs

    resources :photos do get :preview, :on => :memberend

  • Collectionsresources :photos do collection do get :search endend

  • Combination

    scope :module => "admin" do constraints IpBlacklist do resources :posts, :comments endend

  • ActiveRecord

  • New Chainable, Lazy API

  • Chainable Methods

    select from where joins having group

    order limit offset includes lock readonly

  • Controller

    def index @posts = Post. where(:published => true). order("publish_date desc")end

  • Model

    scope :published, where(:published => true). order("publish_date desc")

  • Model

    scope :desc, order("publish_date desc")

    scope :published, where(:published => true).desc

  • Controller

    def index @posts = Post. where("created_at < ?", Time.now). order("publish_date desc")end

  • Model

    scope :desc, order("publish_date desc")

    def self.past where("created_at < ?", Time.now).descend

  • Model

    scope :desc, order("publish_date desc")

    def self.past where("created_at < ?", Time.now).descend

    def self.recent(number) past.limit(5)end

  • Pagination

    class PostsController def index @posts = Posts.page(5, :per_page => 10) endend

    class Post < ActiveRecord::Base def self.page(number, options) per_page = options[:per_page]

    offset(per_page * (number - 1)). limit(per_page) endend

  • 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 man!")end

  • 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| format.html format.text { render "generic" } endend

  • Defaults

    default :from => "[email protected]"

    def welcome(user) @user = user mail(:to => user.email, :subject => "Welcome man!") do |format| format.html format.text { render "generic" } endend

    mailto:[email protected]:[email protected]
  • Attachments

    def welcome(user) @user = user

    file = Rails.public_path.join("hello.pdf") contents = File.readfile) attachments["welcome.pdf"] = contents

    mail(:to => user.email, :subject => "Welcome man!")end

  • Interceptors

    class MyInterceptor def self.delivering_email(mail) original = mail.to mail.to = "[email protected]"

    mail.subject = "#{original}: #{mail.subject}" endend

    mailto:[email protected]:[email protected]
  • Interceptors

    class MyInterceptor def self.delivering_email(mail) original = mail.to mail.to = "[email protected]"

    mail.subject = "#{original}: #{mail.subject}" endend

    config.action_mailer. register_interceptor(MyInterceptor)

    mailto:[email protected]:[email protected]
  • Interceptors

    Delivery

    Observers

  • delivering_mail

    deliver

    delivered_mail

  • Bundler

  • 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

    controller_example

    view_example

    request_example

    mailer_example

    rake tasks

  • 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

  • dm-rails

  • generators

    append_info_to_payload

    i18n_scope

    IdentityMap middleware

    rake tasks

  • 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

  • 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

  • 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

    DataMapper: data_mapper:migration data_mapper:model data_mapper:observer

    Rspec: rspec:controller rspec:helper rspec:install rspec:integration rspec:mailer rspec:model rspec:observer rspec:scaffold rspec:view