Permalink
Browse files

Merge remote branch 'docrails/master' into 3-0-stable

  • Loading branch information...
2 parents 88b5f93 + db0e3e5 commit 4dc2521028140d005b0871ab5d8b02694dbf58c3 @fxn fxn committed Aug 3, 2010
Showing with 707 additions and 497 deletions.
  1. +64 −51 activerecord/lib/active_record/aggregations.rb
  2. +9 −7 activerecord/lib/active_record/association_preload.rb
  3. +269 −182 activerecord/lib/active_record/associations.rb
  4. +4 −3 activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb
  5. +4 −3 activerecord/lib/active_record/associations/has_many_through_association.rb
  6. +2 −1 activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
  7. +2 −2 activerecord/lib/active_record/attribute_methods/write.rb
  8. +14 −20 activerecord/lib/active_record/autosave_association.rb
  9. +136 −91 activerecord/lib/active_record/base.rb
  10. +35 −31 activerecord/lib/active_record/callbacks.rb
  11. +6 −4 activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
  12. +2 −1 activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
  13. +8 −4 activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
  14. +2 −2 activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
  15. +2 −2 activerecord/lib/active_record/dynamic_finder_match.rb
  16. +9 −5 activerecord/lib/active_record/errors.rb
  17. +30 −20 activerecord/lib/active_record/fixtures.rb
  18. +12 −9 activerecord/lib/active_record/named_scope.rb
  19. +2 −2 activerecord/lib/active_record/observer.rb
  20. +2 −2 activerecord/lib/active_record/persistence.rb
  21. +8 −6 activerecord/lib/active_record/relation.rb
  22. +52 −27 activerecord/lib/active_record/relation/calculations.rb
  23. +14 −9 activerecord/lib/active_record/relation/finder_methods.rb
  24. +2 −1 activerecord/lib/active_record/timestamp.rb
  25. +4 −3 activerecord/lib/active_record/validations/associated.rb
  26. +13 −9 activerecord/lib/active_record/validations/uniqueness.rb
@@ -9,11 +9,13 @@ def clear_aggregation_cache #:nodoc:
end unless self.new_record?
end
- # Active Record implements aggregation through a macro-like class method called +composed_of+ for representing attributes
- # as value objects. It expresses relationships like "Account [is] composed of Money [among other things]" or "Person [is]
- # composed of [an] address". Each call to the macro adds a description of how the value objects are created from the
- # attributes of the entity object (when the entity is initialized either as a new object or from finding an existing object)
- # and how it can be turned back into attributes (when the entity is saved to the database). Example:
+ # Active Record implements aggregation through a macro-like class method called +composed_of+
+ # for representing attributes as value objects. It expresses relationships like "Account [is]
+ # composed of Money [among other things]" or "Person [is] composed of [an] address". Each call
+ # to the macro adds a description of how the value objects are created from the attributes of
+ # the entity object (when the entity is initialized either as a new object or from finding an
+ # existing object) and how it can be turned back into attributes (when the entity is saved to
+ # the database).
#
# class Customer < ActiveRecord::Base
# composed_of :balance, :class_name => "Money", :mapping => %w(balance amount)
@@ -68,9 +70,10 @@ def clear_aggregation_cache #:nodoc:
# end
# end
#
- # Now it's possible to access attributes from the database through the value objects instead. If you choose to name the
- # composition the same as the attribute's name, it will be the only way to access that attribute. That's the case with our
- # +balance+ attribute. You interact with the value objects just like you would any other attribute, though:
+ # Now it's possible to access attributes from the database through the value objects instead. If
+ # you choose to name the composition the same as the attribute's name, it will be the only way to
+ # access that attribute. That's the case with our +balance+ attribute. You interact with the value
+ # objects just like you would any other attribute, though:
#
# customer.balance = Money.new(20) # sets the Money value object and the attribute
# customer.balance # => Money value object
@@ -79,8 +82,8 @@ def clear_aggregation_cache #:nodoc:
# customer.balance == Money.new(20) # => true
# customer.balance < Money.new(5) # => false
#
- # Value objects can also be composed of multiple attributes, such as the case of Address. The order of the mappings will
- # determine the order of the parameters. Example:
+ # Value objects can also be composed of multiple attributes, such as the case of Address. The order
+ # of the mappings will determine the order of the parameters.
#
# customer.address_street = "Hyancintvej"
# customer.address_city = "Copenhagen"
@@ -91,38 +94,43 @@ def clear_aggregation_cache #:nodoc:
#
# == Writing value objects
#
- # Value objects are immutable and interchangeable objects that represent a given value, such as a Money object representing
- # $5. Two Money objects both representing $5 should be equal (through methods such as <tt>==</tt> and <tt><=></tt> from Comparable if ranking
- # makes sense). This is unlike entity objects where equality is determined by identity. An entity class such as Customer can
- # easily have two different objects that both have an address on Hyancintvej. Entity identity is determined by object or
- # relational unique identifiers (such as primary keys). Normal ActiveRecord::Base classes are entity objects.
+ # Value objects are immutable and interchangeable objects that represent a given value, such as
+ # a Money object representing $5. Two Money objects both representing $5 should be equal (through
+ # methods such as <tt>==</tt> and <tt><=></tt> from Comparable if ranking makes sense). This is
+ # unlike entity objects where equality is determined by identity. An entity class such as Customer can
+ # easily have two different objects that both have an address on Hyancintvej. Entity identity is
+ # determined by object or relational unique identifiers (such as primary keys). Normal
+ # ActiveRecord::Base classes are entity objects.
#
- # It's also important to treat the value objects as immutable. Don't allow the Money object to have its amount changed after
- # creation. Create a new Money object with the new value instead. This is exemplified by the Money#exchange_to method that
- # returns a new value object instead of changing its own values. Active Record won't persist value objects that have been
- # changed through means other than the writer method.
+ # It's also important to treat the value objects as immutable. Don't allow the Money object to have
+ # its amount changed after creation. Create a new Money object with the new value instead. This
+ # is exemplified by the Money#exchange_to method that returns a new value object instead of changing
+ # its own values. Active Record won't persist value objects that have been changed through means
+ # other than the writer method.
#
- # The immutable requirement is enforced by Active Record by freezing any object assigned as a value object. Attempting to
- # change it afterwards will result in a ActiveSupport::FrozenObjectError.
+ # The immutable requirement is enforced by Active Record by freezing any object assigned as a value
+ # object. Attempting to change it afterwards will result in a ActiveSupport::FrozenObjectError.
#
- # Read more about value objects on http://c2.com/cgi/wiki?ValueObject and on the dangers of not keeping value objects
- # immutable on http://c2.com/cgi/wiki?ValueObjectsShouldBeImmutable
+ # Read more about value objects on http://c2.com/cgi/wiki?ValueObject and on the dangers of not
+ # keeping value objects immutable on http://c2.com/cgi/wiki?ValueObjectsShouldBeImmutable
#
# == Custom constructors and converters
#
- # By default value objects are initialized by calling the <tt>new</tt> constructor of the value class passing each of the
- # mapped attributes, in the order specified by the <tt>:mapping</tt> option, as arguments. If the value class doesn't support
- # this convention then +composed_of+ allows a custom constructor to be specified.
+ # By default value objects are initialized by calling the <tt>new</tt> constructor of the value
+ # class passing each of the mapped attributes, in the order specified by the <tt>:mapping</tt>
+ # option, as arguments. If the value class doesn't support this convention then +composed_of+ allows
+ # a custom constructor to be specified.
#
- # When a new value is assigned to the value object the default assumption is that the new value is an instance of the value
- # class. Specifying a custom converter allows the new value to be automatically converted to an instance of value class if
- # necessary.
+ # When a new value is assigned to the value object the default assumption is that the new value
+ # is an instance of the value class. Specifying a custom converter allows the new value to be automatically
+ # converted to an instance of value class if necessary.
#
- # For example, the NetworkResource model has +network_address+ and +cidr_range+ attributes that should be aggregated using the
- # NetAddr::CIDR value class (http://netaddr.rubyforge.org). The constructor for the value class is called +create+ and it
- # expects a CIDR address string as a parameter. New values can be assigned to the value object using either another
- # NetAddr::CIDR object, a string or an array. The <tt>:constructor</tt> and <tt>:converter</tt> options can be used to
- # meet these requirements:
+ # For example, the NetworkResource model has +network_address+ and +cidr_range+ attributes that
+ # should be aggregated using the NetAddr::CIDR value class (http://netaddr.rubyforge.org). The constructor
+ # for the value class is called +create+ and it expects a CIDR address string as a parameter. New
+ # values can be assigned to the value object using either another NetAddr::CIDR object, a string
+ # or an array. The <tt>:constructor</tt> and <tt>:converter</tt> options can be used to meet
+ # these requirements:
#
# class NetworkResource < ActiveRecord::Base
# composed_of :cidr,
@@ -149,9 +157,9 @@ def clear_aggregation_cache #:nodoc:
#
# == Finding records by a value object
#
- # Once a +composed_of+ relationship is specified for a model, records can be loaded from the database by specifying an instance
- # of the value object in the conditions hash. The following example finds all customers with +balance_amount+ equal to 20 and
- # +balance_currency+ equal to "USD":
+ # Once a +composed_of+ relationship is specified for a model, records can be loaded from the database
+ # by specifying an instance of the value object in the conditions hash. The following example
+ # finds all customers with +balance_amount+ equal to 20 and +balance_currency+ equal to "USD":
#
# Customer.find(:all, :conditions => {:balance => Money.new(20, "USD")})
#
@@ -160,23 +168,28 @@ module ClassMethods
# <tt>composed_of :address</tt> adds <tt>address</tt> and <tt>address=(new_address)</tt> methods.
#
# Options are:
- # * <tt>:class_name</tt> - Specifies the class name of the association. Use it only if that name can't be inferred
- # from the part id. So <tt>composed_of :address</tt> will by default be linked to the Address class, but
- # if the real class name is CompanyAddress, you'll have to specify it with this option.
- # * <tt>:mapping</tt> - Specifies the mapping of entity attributes to attributes of the value object. Each mapping
- # is represented as an array where the first item is the name of the entity attribute and the second item is the
- # name the attribute in the value object. The order in which mappings are defined determine the order in which
- # attributes are sent to the value class constructor.
+ # * <tt>:class_name</tt> - Specifies the class name of the association. Use it only if that name
+ # can't be inferred from the part id. So <tt>composed_of :address</tt> will by default be linked
+ # to the Address class, but if the real class name is CompanyAddress, you'll have to specify it
+ # with this option.
+ # * <tt>:mapping</tt> - Specifies the mapping of entity attributes to attributes of the value
+ # object. Each mapping is represented as an array where the first item is the name of the
+ # entity attribute and the second item is the name the attribute in the value object. The
+ # order in which mappings are defined determine the order in which attributes are sent to the
+ # value class constructor.
# * <tt>:allow_nil</tt> - Specifies that the value object will not be instantiated when all mapped
- # attributes are +nil+. Setting the value object to +nil+ has the effect of writing +nil+ to all mapped attributes.
+ # attributes are +nil+. Setting the value object to +nil+ has the effect of writing +nil+ to all
+ # mapped attributes.
# This defaults to +false+.
- # * <tt>:constructor</tt> - A symbol specifying the name of the constructor method or a Proc that is called to
- # initialize the value object. The constructor is passed all of the mapped attributes, in the order that they
- # are defined in the <tt>:mapping option</tt>, as arguments and uses them to instantiate a <tt>:class_name</tt> object.
+ # * <tt>:constructor</tt> - A symbol specifying the name of the constructor method or a Proc that
+ # is called to initialize the value object. The constructor is passed all of the mapped attributes,
+ # in the order that they are defined in the <tt>:mapping option</tt>, as arguments and uses them
+ # to instantiate a <tt>:class_name</tt> object.
# The default is <tt>:new</tt>.
- # * <tt>:converter</tt> - A symbol specifying the name of a class method of <tt>:class_name</tt> or a Proc that is
- # called when a new value is assigned to the value object. The converter is passed the single value that is used
- # in the assignment and is only called if the new value is not an instance of <tt>:class_name</tt>.
+ # * <tt>:converter</tt> - A symbol specifying the name of a class method of <tt>:class_name</tt>
+ # or a Proc that is called when a new value is assigned to the value object. The converter is
+ # passed the single value that is used in the assignment and is only called if the new value is
+ # not an instance of <tt>:class_name</tt>.
#
# Option examples:
# composed_of :temperature, :mapping => %w(reading celsius)
@@ -9,8 +9,8 @@ module AssociationPreload #:nodoc:
# Implements the details of eager loading of Active Record associations.
# Application developers should not use this module directly.
#
- # ActiveRecord::Base is extended with this module. The source code in
- # ActiveRecord::Base references methods defined in this module.
+ # <tt>ActiveRecord::Base</tt> is extended with this module. The source code in
+ # <tt>ActiveRecord::Base</tt> references methods defined in this module.
#
# Note that 'eager loading' and 'preloading' are actually the same thing.
# However, there are two different eager loading strategies.
@@ -55,7 +55,7 @@ module ClassMethods
# == Parameters
# +records+ is an array of ActiveRecord::Base. This array needs not be flat,
# i.e. +records+ itself may also contain arrays of records. In any case,
- # +preload_associations+ will preload the associations all records by
+ # +preload_associations+ will preload the all associations records by
# flattening +records+.
#
# +associations+ specifies one or more associations that you want to
@@ -110,8 +110,8 @@ def preload_associations(records, associations, preload_options={})
def preload_one_association(records, association, preload_options={})
class_to_reflection = {}
# Not all records have the same class, so group then preload
- # group on the reflection itself so that if various subclass share the same association then we do not split them
- # unnecessarily
+ # group on the reflection itself so that if various subclass share the same association then
+ # we do not split them unnecessarily
records.group_by { |record| class_to_reflection[record.class] ||= record.class.reflections[association]}.each do |reflection, _records|
raise ConfigurationError, "Association named '#{ association }' was not found; perhaps you misspelled it?" unless reflection
@@ -149,7 +149,8 @@ def set_association_single_records(id_to_record_map, reflection_name, associated
seen_keys = {}
associated_records.each do |associated_record|
#this is a has_one or belongs_to: there should only be one record.
- #Unfortunately we can't (in portable way) ask the database for 'all records where foo_id in (x,y,z), but please
+ #Unfortunately we can't (in portable way) ask the database for
+ #'all records where foo_id in (x,y,z), but please
# only one row per distinct foo_id' so this where we enforce that
next if seen_keys[associated_record[key].to_s]
seen_keys[associated_record[key].to_s] = true
@@ -304,7 +305,8 @@ def preload_belongs_to_association(records, reflection, preload_options={})
polymorph_type = options[:foreign_type]
klasses_and_ids = {}
- # Construct a mapping from klass to a list of ids to load and a mapping of those ids back to their parent_records
+ # Construct a mapping from klass to a list of ids to load and a mapping of those ids back
+ # to their parent_records
records.each do |record|
if klass = record.send(polymorph_type)
klass_id = record.send(primary_key_name)
Oops, something went wrong.

0 comments on commit 4dc2521

Please sign in to comment.