1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

Merge remote branch 'docrails/master'

This commit is contained in:
Xavier Noria 2010-08-12 17:36:09 +02:00
commit 4134d7db34
16 changed files with 112 additions and 104 deletions

View file

@ -65,8 +65,8 @@ simply call the method and optionally call +deliver+ on the return value.
Calling the method returns a Mail Message object: Calling the method returns a Mail Message object:
message = Notifier.welcome #=> Returns a Mail::Message object message = Notifier.welcome # => Returns a Mail::Message object
message.deliver #=> delivers the email message.deliver # => delivers the email
Or you can just chain the methods together like: Or you can just chain the methods together like:

View file

@ -107,8 +107,8 @@ modules:
extend ActiveModel::Naming extend ActiveModel::Naming
end end
NamedPerson.model_name #=> "NamedPerson" NamedPerson.model_name # => "NamedPerson"
NamedPerson.model_name.human #=> "Named person" NamedPerson.model_name.human # => "Named person"
{Learn more}[link:classes/ActiveModel/Naming.html] {Learn more}[link:classes/ActiveModel/Naming.html]
@ -139,7 +139,7 @@ modules:
end end
Person.human_attribute_name('my_attribute') Person.human_attribute_name('my_attribute')
#=> "My attribute" # => "My attribute"
{Learn more}[link:classes/ActiveModel/Translation.html] {Learn more}[link:classes/ActiveModel/Translation.html]
@ -157,7 +157,7 @@ modules:
person = Person.new person = Person.new
person.first_name = 'zoolander' person.first_name = 'zoolander'
person.valid? #=> false person.valid? # => false
{Learn more}[link:classes/ActiveModel/Validations.html] {Learn more}[link:classes/ActiveModel/Validations.html]
@ -176,9 +176,9 @@ modules:
end end
p = ValidatorPerson.new p = ValidatorPerson.new
p.valid? #=> false p.valid? # => false
p.errors.full_messages #=> ["Name must exist"] p.errors.full_messages # => ["Name must exist"]
p.name = "Bob" p.name = "Bob"
p.valid? #=> true p.valid? # => true
{Learn more}[link:classes/ActiveModel/Validator.html] {Learn more}[link:classes/ActiveModel/Validator.html]

View file

@ -18,9 +18,9 @@ module ActiveModel
# Returns a JSON string representing the model. Some configuration can be # Returns a JSON string representing the model. Some configuration can be
# passed through +options+. # passed through +options+.
# #
# The option <tt>ActiveModel::Base.include_root_in_json</tt> controls the # The option <tt>include_root_in_json</tt> controls the top-level behavior
# top-level behavior of +to_json+. If true (the default) +to_json+ will # of +to_json+. If true (the default) +to_json+ will emit a single root
# emit a single root node named after the object's type. For example: # node named after the object's type. For example:
# #
# konata = User.find(1) # konata = User.find(1)
# konata.to_json # konata.to_json

View file

@ -966,7 +966,7 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* Made increment_counter/decrement_counter play nicely with optimistic locking, and added a more general update_counters method [Jamis Buck] * Made increment_counter/decrement_counter play nicely with optimistic locking, and added a more general update_counters method [Jamis Buck]
* Reworked David's query cache to be available as Model.cache {...}. For the duration of the block no select query should be run more then once. Any inserts/deletes/executes will flush the whole cache however [Tobias Lütke] * Reworked David's query cache to be available as Model.cache {...}. For the duration of the block no select query should be run more then once. Any inserts/deletes/executes will flush the whole cache however [Tobias Lütke]
Task.cache { Task.find(1); Task.find(1) } #=> 1 query Task.cache { Task.find(1); Task.find(1) } # => 1 query
* When dealing with SQLite3, use the table_info pragma helper, so that the bindings can do some translation for when sqlite3 breaks incompatibly between point releases. [Jamis Buck] * When dealing with SQLite3, use the table_info pragma helper, so that the bindings can do some translation for when sqlite3 breaks incompatibly between point releases. [Jamis Buck]

View file

@ -311,7 +311,8 @@ module ActiveRecord
# You can set the :autosave option on a <tt>has_one</tt>, <tt>belongs_to</tt>, # You can set the :autosave option on a <tt>has_one</tt>, <tt>belongs_to</tt>,
# <tt>has_many</tt>, or <tt>has_and_belongs_to_many</tt> association. Setting it # <tt>has_many</tt>, or <tt>has_and_belongs_to_many</tt> association. Setting it
# to +true+ will _always_ save the members, whereas setting it to +false+ will # to +true+ will _always_ save the members, whereas setting it to +false+ will
# _never_ save the members. # _never_ save the members. More details about :autosave option is available at
# autosave_association.rb .
# #
# === One-to-one associations # === One-to-one associations
# #
@ -956,8 +957,7 @@ module ActiveRecord
# If false, don't validate the associated objects when saving the parent object. true by default. # If false, don't validate the associated objects when saving the parent object. true by default.
# [:autosave] # [:autosave]
# If true, always save the associated objects or destroy them if marked for destruction, # If true, always save the associated objects or destroy them if marked for destruction,
# when saving the parent object. # when saving the parent object. If false, never save or destroy the associated objects.
# If false, never save or destroy the associated objects.
# By default, only save associated objects that are new records. # By default, only save associated objects that are new records.
# [:inverse_of] # [:inverse_of]
# Specifies the name of the <tt>belongs_to</tt> association on the associated object # Specifies the name of the <tt>belongs_to</tt> association on the associated object

View file

@ -3,13 +3,13 @@ require 'active_support/core_ext/array/wrap'
module ActiveRecord module ActiveRecord
# = Active Record Autosave Association # = Active Record Autosave Association
# #
# AutosaveAssociation is a module that takes care of automatically saving # +AutosaveAssociation+ is a module that takes care of automatically saving
# associacted records when parent is saved. In addition to saving, it # associacted records when their parent is saved. In addition to saving, it
# also destroys any associated records that were marked for destruction. # also destroys any associated records that were marked for destruction.
# (See mark_for_destruction and marked_for_destruction?) # (See +mark_for_destruction+ and <tt>marked_for_destruction?</tt>).
# #
# Saving of the parent, its associations, and the destruction of marked # Saving of the parent, its associations, and the destruction of marked
# associations, all happen inside 1 transaction. This should never leave the # associations, all happen inside a transaction. This should never leave the
# database in an inconsistent state. # database in an inconsistent state.
# #
# If validations for any of the associations fail, their error messages will # If validations for any of the associations fail, their error messages will
@ -18,6 +18,9 @@ module ActiveRecord
# Note that it also means that associations marked for destruction won't # Note that it also means that associations marked for destruction won't
# be destroyed directly. They will however still be marked for destruction. # be destroyed directly. They will however still be marked for destruction.
# #
# Note that <tt>:autosave => false</tt> is not same as not declaring <tt>:autosave</tt>.
# When the <tt>:autosave</tt> option is not present new associations are saved.
#
# === One-to-one Example # === One-to-one Example
# #
# class Post # class Post
@ -46,6 +49,7 @@ module ActiveRecord
# post.author.marked_for_destruction? # => true # post.author.marked_for_destruction? # => true
# #
# Note that the model is _not_ yet removed from the database: # Note that the model is _not_ yet removed from the database:
#
# id = post.author.id # id = post.author.id
# Author.find_by_id(id).nil? # => false # Author.find_by_id(id).nil? # => false
# #
@ -53,40 +57,49 @@ module ActiveRecord
# post.reload.author # => nil # post.reload.author # => nil
# #
# Now it _is_ removed from the database: # Now it _is_ removed from the database:
#
# Author.find_by_id(id).nil? # => true # Author.find_by_id(id).nil? # => true
# #
# === One-to-many Example # === One-to-many Example
# #
# Consider a Post model with many Comments: # When <tt>:autosave</tt> is not declared new children are saved when their parent is saved:
#
# class Post
# has_many :comments # :autosave option is no declared
# end
#
# post = Post.new(:title => 'ruby rocks')
# post.comments.build(:body => 'hello world')
# post.save # => saves both post and comment
#
# post = Post.create(:title => 'ruby rocks')
# post.comments.build(:body => 'hello world')
# post.save # => saves both post and comment
#
# post = Post.create(:title => 'ruby rocks')
# post.comments.create(:body => 'hello world')
# post.save # => saves both post and comment
#
# When <tt>:autosave</tt> is true all children is saved, no matter whether they are new records:
# #
# class Post # class Post
# has_many :comments, :autosave => true # has_many :comments, :autosave => true
# end # end
# #
# Saving changes to the parent and its associated model can now be performed # post = Post.create(:title => 'ruby rocks')
# automatically _and_ atomically: # post.comments.create(:body => 'hello world')
# post.comments[0].body = 'hi everyone'
# post.save # => saves both post and comment, with 'hi everyone' as title
# #
# post = Post.find(1) # Destroying one of the associated models as part of the parent's save action
# post.title # => "The current global position of migrating ducks" # is as simple as marking it for destruction:
# post.comments.first.body # => "Wow, awesome info thanks!"
# post.comments.last.body # => "Actually, your article should be named differently."
#
# post.title = "On the migration of ducks"
# post.comments.last.body = "Actually, your article should be named differently. [UPDATED]: You are right, thanks."
#
# post.save
# post.reload
# post.title # => "On the migration of ducks"
# post.comments.last.body # => "Actually, your article should be named differently. [UPDATED]: You are right, thanks."
#
# Destroying one of the associated models members, as part of the parent's
# save action, is as simple as marking it for destruction:
# #
# post.comments.last.mark_for_destruction # post.comments.last.mark_for_destruction
# post.comments.last.marked_for_destruction? # => true # post.comments.last.marked_for_destruction? # => true
# post.comments.length # => 2 # post.comments.length # => 2
# #
# Note that the model is _not_ yet removed from the database: # Note that the model is _not_ yet removed from the database:
#
# id = post.comments.last.id # id = post.comments.last.id
# Comment.find_by_id(id).nil? # => false # Comment.find_by_id(id).nil? # => false
# #
@ -94,37 +107,13 @@ module ActiveRecord
# post.reload.comments.length # => 1 # post.reload.comments.length # => 1
# #
# Now it _is_ removed from the database: # Now it _is_ removed from the database:
#
# Comment.find_by_id(id).nil? # => true # Comment.find_by_id(id).nil? # => true
# #
# === Validation # === Validation
# #
# Validation is performed on the parent as usual, but also on all autosave # Validations on children records are run or not depending on the <tt>:validate</tt>
# enabled associations. If any of the associations fail validation, its # option of the association.
# error messages will be applied on the parents errors object and validation
# of the parent will fail.
#
# Consider a Post model with Author which validates the presence of its name
# attribute:
#
# class Post
# has_one :author, :autosave => true
# end
#
# class Author
# validates_presence_of :name
# end
#
# post = Post.find(1)
# post.author.name = ''
# post.save # => false
# post.errors # => #<ActiveRecord::Errors:0x174498c @errors={"author.name"=>["can't be blank"]}, @base=#<Post ...>>
#
# No validations will be performed on the associated models when validations
# are skipped for the parent:
#
# post = Post.find(1)
# post.author.name = ''
# post.save(:validate => false) # => true
module AutosaveAssociation module AutosaveAssociation
extend ActiveSupport::Concern extend ActiveSupport::Concern

View file

@ -1147,6 +1147,16 @@ MSG
# class Person < ActiveRecord::Base # class Person < ActiveRecord::Base
# default_scope order('last_name, first_name') # default_scope order('last_name, first_name')
# end # end
#
# <tt>default_scope</tt> is also applied while creating/building a record. It is not
# applied while updating a record.
#
# class Article < ActiveRecord::Base
# default_scope where(:published => true)
# end
#
# Article.new.published # => true
# Article.create.published # => true
def default_scope(options = {}) def default_scope(options = {})
self.default_scoping << construct_finder_arel(options, default_scoping.pop) self.default_scoping << construct_finder_arel(options, default_scoping.pop)
end end

View file

@ -88,6 +88,15 @@ module ActiveRecord
# end # end
# end # end
# end # end
#
# Scopes can also be used while creating/building a record.
#
# class Article < ActiveRecord::Base
# scope :published, where(:published => true)
# end
#
# Article.published.new.published # => true
# Article.published.create.published # => true
def scope(name, scope_options = {}, &block) def scope(name, scope_options = {}, &block)
name = name.to_sym name = name.to_sym
valid_scope_name?(name) valid_scope_name?(name)

View file

@ -41,7 +41,7 @@ module ActiveRecord
# Returns the AggregateReflection object for the named +aggregation+ (use the symbol). # Returns the AggregateReflection object for the named +aggregation+ (use the symbol).
# #
# Account.reflect_on_aggregation(:balance) #=> the balance AggregateReflection # Account.reflect_on_aggregation(:balance) # => the balance AggregateReflection
# #
def reflect_on_aggregation(aggregation) def reflect_on_aggregation(aggregation)
reflections[aggregation].is_a?(AggregateReflection) ? reflections[aggregation] : nil reflections[aggregation].is_a?(AggregateReflection) ? reflections[aggregation] : nil

View file

@ -34,7 +34,7 @@ lifecycle methods that operate against a persistent store.
# Find a person with id = 1 # Find a person with id = 1
ryan = Person.find(1) ryan = Person.find(1)
Person.exists?(1) #=> true Person.exists?(1) # => true
As you can see, the methods are quite similar to Active Record's methods for dealing with database As you can see, the methods are quite similar to Active Record's methods for dealing with database
records. But rather than dealing directly with a database record, you're dealing with HTTP resources (which may or may not be database records). records. But rather than dealing directly with a database record, you're dealing with HTTP resources (which may or may not be database records).
@ -69,8 +69,8 @@ for a request for a single element, the XML of that item is expected in response
The XML document that is received is used to build a new object of type Person, with each The XML document that is received is used to build a new object of type Person, with each
XML element becoming an attribute on the object. XML element becoming an attribute on the object.
ryan.is_a? Person #=> true ryan.is_a? Person # => true
ryan.attribute1 #=> 'value1' ryan.attribute1 # => 'value1'
Any complex element (one that contains other elements) becomes its own object: Any complex element (one that contains other elements) becomes its own object:
@ -81,8 +81,8 @@ Any complex element (one that contains other elements) becomes its own object:
# for GET http://api.people.com:3000/people/1.xml # for GET http://api.people.com:3000/people/1.xml
# #
ryan = Person.find(1) ryan = Person.find(1)
ryan.complex #=> <Person::Complex::xxxxx> ryan.complex # => <Person::Complex::xxxxx>
ryan.complex.attribute2 #=> 'value2' ryan.complex.attribute2 # => 'value2'
Collections can also be requested in a similar fashion Collections can also be requested in a similar fashion
@ -96,8 +96,8 @@ Collections can also be requested in a similar fashion
# for GET http://api.people.com:3000/people.xml # for GET http://api.people.com:3000/people.xml
# #
people = Person.find(:all) people = Person.find(:all)
people.first #=> <Person::xxx 'first' => 'Ryan' ...> people.first # => <Person::xxx 'first' => 'Ryan' ...>
people.last #=> <Person::xxx 'first' => 'Jim' ...> people.last # => <Person::xxx 'first' => 'Jim' ...>
==== Create ==== Create
@ -118,10 +118,10 @@ as the id of the ARes object.
# Response (201): Location: http://api.people.com:3000/people/2 # Response (201): Location: http://api.people.com:3000/people/2
# #
ryan = Person.new(:first => 'Ryan') ryan = Person.new(:first => 'Ryan')
ryan.new? #=> true ryan.new? # => true
ryan.save #=> true ryan.save # => true
ryan.new? #=> false ryan.new? # => false
ryan.id #=> 2 ryan.id # => 2
==== Update ==== Update
@ -139,9 +139,9 @@ server side was successful.
# is expected with code (204) # is expected with code (204)
# #
ryan = Person.find(1) ryan = Person.find(1)
ryan.first #=> 'Ryan' ryan.first # => 'Ryan'
ryan.first = 'Rizzle' ryan.first = 'Rizzle'
ryan.save #=> true ryan.save # => true
==== Delete ==== Delete
@ -155,10 +155,10 @@ Destruction of a resource can be invoked as a class and instance method of the r
# is expected with response code (200) # is expected with response code (200)
# #
ryan = Person.find(1) ryan = Person.find(1)
ryan.destroy #=> true ryan.destroy # => true
ryan.exists? #=> false ryan.exists? # => false
Person.delete(2) #=> true Person.delete(2) # => true
Person.exists?(2) #=> false Person.exists?(2) # => false
You can find more usage information in the ActiveResource::Base documentation. You can find more usage information in the ActiveResource::Base documentation.

View file

@ -245,8 +245,8 @@ ActiveSupport.escape_html_entities_in_json from true to false to match previousl
* Add Array#in_groups which splits or iterates over the array in specified number of groups. #579. [Adrian Mugnolo] Example: * Add Array#in_groups which splits or iterates over the array in specified number of groups. #579. [Adrian Mugnolo] Example:
a = (1..10).to_a a = (1..10).to_a
a.in_groups(3) #=> [[1, 2, 3, 4], [5, 6, 7, nil], [8, 9, 10, nil]] a.in_groups(3) # => [[1, 2, 3, 4], [5, 6, 7, nil], [8, 9, 10, nil]]
a.in_groups(3, false) #=> [[1, 2, 3, 4], [5, 6, 7], [8, 9, 10]] a.in_groups(3, false) # => [[1, 2, 3, 4], [5, 6, 7], [8, 9, 10]]
* Fix TimeWithZone unmarshaling: coerce unmarshaled Time instances to utc, because Ruby's marshaling of Time instances doesn't respect the zone [Geoff Buesing] * Fix TimeWithZone unmarshaling: coerce unmarshaled Time instances to utc, because Ruby's marshaling of Time instances doesn't respect the zone [Geoff Buesing]
@ -942,7 +942,7 @@ public for compatibility. [Jeremy Kemper]
* Enhance Symbol#to_proc so it works with list objects, such as multi-dimensional arrays. Closes #5295 [nov@yo.rim.or.jp]. Example: * Enhance Symbol#to_proc so it works with list objects, such as multi-dimensional arrays. Closes #5295 [nov@yo.rim.or.jp]. Example:
{1 => "one", 2 => "two", 3 => "three"}.sort_by(&:first).map(&:last) {1 => "one", 2 => "two", 3 => "three"}.sort_by(&:first).map(&:last)
#=> ["one", "two", "three"] # => ["one", "two", "three"]
* Added Hash.create_from_xml(string) which will create a hash from a XML string and even typecast if possible [David Heinemeier Hansson]. Example: * Added Hash.create_from_xml(string) which will create a hash from a XML string and even typecast if possible [David Heinemeier Hansson]. Example:

View file

@ -29,19 +29,19 @@ class Class
# In such cases, you don't want to do changes in places but use setters: # In such cases, you don't want to do changes in places but use setters:
# #
# Base.setting = [] # Base.setting = []
# Base.setting #=> [] # Base.setting # => []
# Subclass.setting #=> [] # Subclass.setting # => []
# #
# # Appending in child changes both parent and child because it is the same object: # # Appending in child changes both parent and child because it is the same object:
# Subclass.setting << :foo # Subclass.setting << :foo
# Base.setting #=> [:foo] # Base.setting # => [:foo]
# Subclass.setting #=> [:foo] # Subclass.setting # => [:foo]
# #
# # Use setters to not propagate changes: # # Use setters to not propagate changes:
# Base.setting = [] # Base.setting = []
# Subclass.setting += [:foo] # Subclass.setting += [:foo]
# Base.setting #=> [] # Base.setting # => []
# Subclass.setting #=> [:foo] # Subclass.setting # => [:foo]
# #
# For convenience, a query method is defined as well: # For convenience, a query method is defined as well:
# #

View file

@ -12,8 +12,8 @@ require 'active_support/core_ext/array/extract_options'
# end # end
# #
# Person.hair_colors = [:brown, :black, :blonde, :red] # Person.hair_colors = [:brown, :black, :blonde, :red]
# Person.hair_colors #=> [:brown, :black, :blonde, :red] # Person.hair_colors # => [:brown, :black, :blonde, :red]
# Person.new.hair_colors #=> [:brown, :black, :blonde, :red] # Person.new.hair_colors # => [:brown, :black, :blonde, :red]
# #
# To opt out of the instance writer method, pass :instance_writer => false. # To opt out of the instance writer method, pass :instance_writer => false.
# To opt out of the instance reader method, pass :instance_reader => false. # To opt out of the instance reader method, pass :instance_reader => false.

View file

@ -22,8 +22,8 @@ end
# end # end
# #
# Person.hair_colors = [:brown, :black, :blonde, :red] # Person.hair_colors = [:brown, :black, :blonde, :red]
# Person.hair_colors #=> [:brown, :black, :blonde, :red] # Person.hair_colors # => [:brown, :black, :blonde, :red]
# Person.new.hair_colors #=> [:brown, :black, :blonde, :red] # Person.new.hair_colors # => [:brown, :black, :blonde, :red]
# #
# To opt out of the instance writer method, pass :instance_writer => false. # To opt out of the instance writer method, pass :instance_writer => false.
# To opt out of the instance reader method, pass :instance_reader => false. # To opt out of the instance reader method, pass :instance_reader => false.

View file

@ -550,7 +550,7 @@ build_customer
create_customer create_customer
</ruby> </ruby>
h6. _association_(force_reload = false) h6. <em>association</em>(force_reload = false)
The <tt><em>association</em></tt> method returns the associated object, if any. If no associated object is found, it returns +nil+. The <tt><em>association</em></tt> method returns the associated object, if any. If no associated object is found, it returns +nil+.

View file

@ -371,7 +371,7 @@ The mass-assignment feature may become a problem, as it allows an attacker to se
<ruby> <ruby>
def signup def signup
params[:user] #=> {:name => “ow3ned”, :admin => true} params[:user] # => {:name => “ow3ned”, :admin => true}
@user = User.new(params[:user]) @user = User.new(params[:user])
end end
</ruby> </ruby>
@ -385,7 +385,7 @@ Mass-assignment saves you much work, because you don't have to set each value in
This will set the following parameters in the controller: This will set the following parameters in the controller:
<ruby> <ruby>
params[:user] #=> {:name => “ow3ned”, :admin => true} params[:user] # => {:name => “ow3ned”, :admin => true}
</ruby> </ruby>
So if you create a new user using mass-assignment, it may be too easy to become an administrator. So if you create a new user using mass-assignment, it may be too easy to become an administrator.
@ -423,11 +423,11 @@ attr_accessible :name
If you want to set a protected attribute, you will to have to assign it individually: If you want to set a protected attribute, you will to have to assign it individually:
<ruby> <ruby>
params[:user] #=> {:name => "ow3ned", :admin => true} params[:user] # => {:name => "ow3ned", :admin => true}
@user = User.new(params[:user]) @user = User.new(params[:user])
@user.admin #=> false # not mass-assigned @user.admin # => false # not mass-assigned
@user.admin = true @user.admin = true
@user.admin #=> true @user.admin # => true
</ruby> </ruby>
A more paranoid technique to protect your whole project would be to enforce that all models whitelist their accessible attributes. This can be easily achieved with a very simple initializer: A more paranoid technique to protect your whole project would be to enforce that all models whitelist their accessible attributes. This can be easily achieved with a very simple initializer: