Rails 3 overview
-
Upload
yehuda-katz -
Category
Technology
-
view
8.628 -
download
0
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