aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/associations.rb
diff options
context:
space:
mode:
authorEloy Duran <eloy.de.enige@gmail.com>2009-02-01 14:44:30 +1300
committerMichael Koziarski <michael@koziarski.com>2009-02-01 14:44:30 +1300
commitec8f04584479aff895b0b511a7ba1e9d33f84067 (patch)
tree86d6580345865cd1ce63b2055c0eca091ba8ee52 /activerecord/lib/active_record/associations.rb
parenta02d752ae408b34e048b7b040e28c36074a9119f (diff)
downloadrails-ec8f04584479aff895b0b511a7ba1e9d33f84067.tar.gz
rails-ec8f04584479aff895b0b511a7ba1e9d33f84067.tar.bz2
rails-ec8f04584479aff895b0b511a7ba1e9d33f84067.zip
Add support for nested object forms to ActiveRecord and the helpers in ActionPack
Signed-Off-By: Michael Koziarski <michael@koziarski.com> [#1202 state:committed]
Diffstat (limited to 'activerecord/lib/active_record/associations.rb')
-rwxr-xr-xactiverecord/lib/active_record/associations.rb91
1 files changed, 48 insertions, 43 deletions
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index 8b51a38f48..7a88465f8c 100755
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -88,6 +88,18 @@ module ActiveRecord
end unless self.new_record?
end
+ private
+ # Gets the specified association instance if it responds to :loaded?, nil otherwise.
+ def association_instance_get(name)
+ association = instance_variable_get("@#{name}")
+ association if association.respond_to?(:loaded?)
+ end
+
+ # Set the specified association instance.
+ def association_instance_set(name, association)
+ instance_variable_set("@#{name}", association)
+ end
+
# Associations are a set of macro-like class methods for tying objects together through foreign keys. They express relationships like
# "Project has one Project Manager" or "Project belongs to a Portfolio". Each macro adds a number of methods to the class which are
# specialized according to the collection or association symbol and the options hash. It works much the same way as Ruby's own <tt>attr*</tt>
@@ -256,6 +268,10 @@ module ActiveRecord
# You can manipulate objects and associations before they are saved to the database, but there is some special behavior you should be
# aware of, mostly involving the saving of associated objects.
#
+ # Unless you enable 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,
+ # in which case the members are always saved.
+ #
# === One-to-one associations
#
# * Assigning an object to a +has_one+ association automatically saves that object and the object being replaced (if there is one), in
@@ -752,6 +768,9 @@ module ActiveRecord
# If true, all the associated objects are readonly through the association.
# [:validate]
# If false, don't validate the associated objects when saving the parent object. true by default.
+ # [:autosave]
+ # If true, always save any loaded members and destroy members marked for destruction, when saving the parent object. Off by default.
+ #
# Option examples:
# has_many :comments, :order => "posted_on"
# has_many :comments, :include => :author
@@ -865,6 +884,8 @@ module ActiveRecord
# If true, the associated object is readonly through the association.
# [:validate]
# If false, don't validate the associated object when saving the parent object. +false+ by default.
+ # [:autosave]
+ # If true, always save the associated object or destroy it if marked for destruction, when saving the parent object. Off by default.
#
# Option examples:
# has_one :credit_card, :dependent => :destroy # destroys the associated credit card
@@ -882,13 +903,10 @@ module ActiveRecord
else
reflection = create_has_one_reflection(association_id, options)
- ivar = "@#{reflection.name}"
-
method_name = "has_one_after_save_for_#{reflection.name}".to_sym
define_method(method_name) do
- association = instance_variable_get(ivar) if instance_variable_defined?(ivar)
-
- if !association.nil? && (new_record? || association.new_record? || association[reflection.primary_key_name] != id)
+ association = association_instance_get(reflection.name)
+ if association && (new_record? || association.new_record? || association[reflection.primary_key_name] != id)
association[reflection.primary_key_name] = id
association.save(true)
end
@@ -979,6 +997,8 @@ module ActiveRecord
# If true, the associated object is readonly through the association.
# [:validate]
# If false, don't validate the associated objects when saving the parent object. +false+ by default.
+ # [:autosave]
+ # If true, always save the associated object or destroy it if marked for destruction, when saving the parent object. Off by default.
#
# Option examples:
# belongs_to :firm, :foreign_key => "client_of"
@@ -991,15 +1011,12 @@ module ActiveRecord
def belongs_to(association_id, options = {})
reflection = create_belongs_to_reflection(association_id, options)
- ivar = "@#{reflection.name}"
-
if reflection.options[:polymorphic]
association_accessor_methods(reflection, BelongsToPolymorphicAssociation)
method_name = "polymorphic_belongs_to_before_save_for_#{reflection.name}".to_sym
define_method(method_name) do
- association = instance_variable_get(ivar) if instance_variable_defined?(ivar)
-
+ association = association_instance_get(reflection.name)
if association && association.target
if association.new_record?
association.save(true)
@@ -1019,9 +1036,7 @@ module ActiveRecord
method_name = "belongs_to_before_save_for_#{reflection.name}".to_sym
define_method(method_name) do
- association = instance_variable_get(ivar) if instance_variable_defined?(ivar)
-
- if !association.nil?
+ if association = association_instance_get(reflection.name)
if association.new_record?
association.save(true)
end
@@ -1196,6 +1211,8 @@ module ActiveRecord
# If true, all the associated objects are readonly through the association.
# [:validate]
# If false, don't validate the associated objects when saving the parent object. +true+ by default.
+ # [:autosave]
+ # If true, always save any loaded members and destroy members marked for destruction, when saving the parent object. Off by default.
#
# Option examples:
# has_and_belongs_to_many :projects
@@ -1243,33 +1260,30 @@ module ActiveRecord
end
def association_accessor_methods(reflection, association_proxy_class)
- ivar = "@#{reflection.name}"
-
define_method(reflection.name) do |*params|
force_reload = params.first unless params.empty?
-
- association = instance_variable_get(ivar) if instance_variable_defined?(ivar)
+ association = association_instance_get(reflection.name)
if association.nil? || force_reload
association = association_proxy_class.new(self, reflection)
retval = association.reload
if retval.nil? and association_proxy_class == BelongsToAssociation
- instance_variable_set(ivar, nil)
+ association_instance_set(reflection.name, nil)
return nil
end
- instance_variable_set(ivar, association)
+ association_instance_set(reflection.name, association)
end
association.target.nil? ? nil : association
end
define_method("loaded_#{reflection.name}?") do
- association = instance_variable_get(ivar) if instance_variable_defined?(ivar)
+ association = association_instance_get(reflection.name)
association && association.loaded?
end
define_method("#{reflection.name}=") do |new_value|
- association = instance_variable_get(ivar) if instance_variable_defined?(ivar)
+ association = association_instance_get(reflection.name)
if association.nil? || association.target != new_value
association = association_proxy_class.new(self, reflection)
@@ -1280,7 +1294,7 @@ module ActiveRecord
self.send(reflection.name, new_value)
else
association.replace(new_value)
- instance_variable_set(ivar, new_value.nil? ? nil : association)
+ association_instance_set(reflection.name, new_value.nil? ? nil : association)
end
end
@@ -1288,20 +1302,18 @@ module ActiveRecord
return if target.nil? and association_proxy_class == BelongsToAssociation
association = association_proxy_class.new(self, reflection)
association.target = target
- instance_variable_set(ivar, association)
+ association_instance_set(reflection.name, association)
end
end
def collection_reader_method(reflection, association_proxy_class)
define_method(reflection.name) do |*params|
- ivar = "@#{reflection.name}"
-
force_reload = params.first unless params.empty?
- association = instance_variable_get(ivar) if instance_variable_defined?(ivar)
+ association = association_instance_get(reflection.name)
- unless association.respond_to?(:loaded?)
+ unless association
association = association_proxy_class.new(self, reflection)
- instance_variable_set(ivar, association)
+ association_instance_set(reflection.name, association)
end
association.reload if force_reload
@@ -1339,8 +1351,7 @@ module ActiveRecord
def add_single_associated_validation_callbacks(association_name)
method_name = "validate_associated_records_for_#{association_name}".to_sym
define_method(method_name) do
- association = instance_variable_get("@#{association_name}")
- if !association.nil?
+ if association = association_instance_get(association_name)
errors.add association_name unless association.target.nil? || association.valid?
end
end
@@ -1350,12 +1361,10 @@ module ActiveRecord
def add_multiple_associated_validation_callbacks(association_name)
method_name = "validate_associated_records_for_#{association_name}".to_sym
- ivar = "@#{association_name}"
-
define_method(method_name) do
- association = instance_variable_get(ivar) if instance_variable_defined?(ivar)
+ association = association_instance_get(association_name)
- if association.respond_to?(:loaded?)
+ if association
if new_record?
association
elsif association.loaded?
@@ -1372,8 +1381,6 @@ module ActiveRecord
end
def add_multiple_associated_save_callbacks(association_name)
- ivar = "@#{association_name}"
-
method_name = "before_save_associated_records_for_#{association_name}".to_sym
define_method(method_name) do
@new_record_before_save = new_record?
@@ -1383,13 +1390,13 @@ module ActiveRecord
method_name = "after_create_or_update_associated_records_for_#{association_name}".to_sym
define_method(method_name) do
- association = instance_variable_get(ivar) if instance_variable_defined?(ivar)
+ association = association_instance_get(association_name)
records_to_save = if @new_record_before_save
association
- elsif association.respond_to?(:loaded?) && association.loaded?
+ elsif association && association.loaded?
association.select { |record| record.new_record? }
- elsif association.respond_to?(:loaded?) && !association.loaded?
+ elsif association && !association.loaded?
association.target.select { |record| record.new_record? }
else
[]
@@ -1407,15 +1414,13 @@ module ActiveRecord
def association_constructor_method(constructor, reflection, association_proxy_class)
define_method("#{constructor}_#{reflection.name}") do |*params|
- ivar = "@#{reflection.name}"
-
attributees = params.first unless params.empty?
replace_existing = params[1].nil? ? true : params[1]
- association = instance_variable_get(ivar) if instance_variable_defined?(ivar)
+ association = association_instance_get(reflection.name)
- if association.nil?
+ unless association
association = association_proxy_class.new(self, reflection)
- instance_variable_set(ivar, association)
+ association_instance_set(reflection.name, association)
end
if association_proxy_class == HasOneAssociation