has_many :children, :class_name => name, :foreign_key => 'parent_id', :order => 'position', :dependent => :destroy | has_many_test.rb:8
+ calling ActiveRecord::Associations::ClassMethods::has_many
/ def has_many(association_id, options = {}, &extension) | ...y/gems/1.8/gems/activerecord-1.15.3/lib/active_record/associations.rb:555
| (association_id = :children; options = {:class_name=>"Person", :foreign_key=>"parent_id", :order=>"position", :dependent=>:destroy}; extension = nil; reflection = nil)
| reflection = create_has_many_reflection(association_id, options, &extension) | ...ems/activerecord-1.15.3/lib/active_record/associations.rb:556
| + calling ActiveRecord::Associations::ClassMethods::create_has_many_reflection
| / def create_has_many_reflection(association_id, options, &extension) | ...ms/1.8/gems/activerecord-1.15.3/lib/active_record/associations.rb:1093
| | (association_id = :children; options = {:class_name=>"Person", :foreign_key=>"parent_id", :order=>"position", :dependent=>:destroy}; extension = nil)
| | options.assert_valid_keys( | .../gems/1.8/gems/activerecord-1.15.3/lib/active_record/associations.rb:1094
| | options[:extend] = create_extension_module(association_id, extension) if block_given? | ...ord-1.15.3/lib/active_record/associations.rb:1105
| | create_reflection(:has_many, association_id, options, self) | .../gems/1.8/gems/activerecord-1.15.3/lib/active_record/associations.rb:1107
| | + calling ActiveRecord::Reflection::ClassMethods::create_reflection
| | / def create_reflection(macro, name, options, active_record) | ...ruby/gems/1.8/gems/activerecord-1.15.3/lib/active_record/reflection.rb:13
| | | (macro = :has_many; name = :children; options = {:class_name=>"Person", :foreign_key=>"parent_id", :order=>"position", :dependent=>:destroy}; active_record = Person; reflection = nil)
| | | case macro | ...ruby/gems/1.8/gems/activerecord-1.15.3/lib/active_record/reflection.rb:14
| | | when :has_many, :belongs_to, :has_one, :has_and_belongs_to_many | ...gems/1.8/gems/activerecord-1.15.3/lib/active_record/reflection.rb:15
| | | reflection = AssociationReflection.new(macro, name, options, active_record) | ...s/activerecord-1.15.3/lib/active_record/reflection.rb:16
| | | + calling ActiveRecord::Reflection::MacroReflection::initialize
| | | / def initialize(macro, name, options, active_record) | ...ruby/gems/1.8/gems/activerecord-1.15.3/lib/active_record/reflection.rb:74
| | | | (macro = :has_many; name = :children; options = {:class_name=>"Person", :foreign_key=>"parent_id", :order=>"position", :dependent=>:destroy}; active_record = Person)
| | | | @macro, @name, @options, @active_record = macro, name, options, active_record | ...iverecord-1.15.3/lib/active_record/reflection.rb:75
| | | \ end (returning from ActiveRecord::Reflection::MacroReflection::initialize) | ...ms/activerecord-1.15.3/lib/active_record/reflection.rb:76
| | | write_inheritable_hash :reflections, name => reflection | ...ruby/gems/1.8/gems/activerecord-1.15.3/lib/active_record/reflection.rb:20
| | | reflection | ...ruby/gems/1.8/gems/activerecord-1.15.3/lib/active_record/reflection.rb:21
| | \ (returning from ActiveRecord::Reflection::ClassMethods::create_reflection) | ...erecord-1.15.3/lib/active_record/reflection.rb:14
| \ def create_has_many_reflection(association_id, options, &extension) (returning from ActiveRecord::Associations::ClassMe... | ...iations.rb:1093
| configure_dependency_for_has_many(reflection) | ...y/gems/1.8/gems/activerecord-1.15.3/lib/active_record/associations.rb:558
| + calling ActiveRecord::Associations::ClassMethods::configure_dependency_for_has_many
| / def configure_dependency_for_has_many(reflection) | .../gems/1.8/gems/activerecord-1.15.3/lib/active_record/associations.rb:1029
| | (reflection = #<ActiveRecord::Reflection::AssociationReflection:0xb78d405c @macro=:has_many, @name=:children, @options={:class_name=>"Person", :foreign_key=>"parent_id", :order=>"position", :dependent=>:destroy}, @active_record=Person>; dependent_conditions = nil)
| | if reflection.options[:dependent] == true | .../gems/1.8/gems/activerecord-1.15.3/lib/active_record/associations.rb:1030
| \ (returning from ActiveRecord::Associations::ClassMethods::configure_dependency_for_has_many) | ...1030
| + calling ActiveRecord::Associations::ClassMethods::add_multiple_associated_save_callbacks
| / def add_multiple_associated_save_callbacks(association_name) | ...y/gems/1.8/gems/activerecord-1.15.3/lib/active_record/associations.rb:964
| | (association_name = :children; method_name = nil; after_callback = nil)
| | method_name = "validate_associated_records_for_#{association_name}".to_sym | ...ms/activerecord-1.15.3/lib/active_record/associations.rb:965
| | define_method(method_name) do | ...y/gems/1.8/gems/activerecord-1.15.3/lib/active_record/associations.rb:966
...
| \ (returning from ActiveRecord::Associations::... | ...ciations.rb:965
| add_association_callbacks(reflection.name, reflection.options) | ...y/gems/1.8/gems/activerecord-1.15.3/lib/active_record/associations.rb:564
| + calling ActiveRecord::Associations::ClassMethods::add_association_callbacks
| / def add_association_callbacks(association_name, options) | .../gems/1.8/gems/activerecord-1.15.3/lib/active_record/associations.rb:1267
| | (association_name = :children; options = {:class_name=>"Person", :foreign_key=>"parent_id", :order=>"position", :dependent=>:destroy}; callbacks = nil)
| | callbacks = %w(before_add after_add before_remove after_remove) | ...ems/1.8/gems/activerecord-1.15.3/lib/active_record/associations.rb:1268
| | callbacks.each do |callback_name| | .../gems/1.8/gems/activerecord-1.15.3/lib/active_record/associations.rb:1269
| | full_callback_name = "#{callback_name}_for_#{association_name}" | ...ems/1.8/gems/activerecord-1.15.3/lib/active_record/associations.rb:1270
| | defined_callbacks = options[callback_name.to_sym] | .../gems/1.8/gems/activerecord-1.15.3/lib/active_record/associations.rb:1271
| | if options.has_key?(callback_name.to_sym) | .../gems/1.8/gems/activerecord-1.15.3/lib/active_record/associations.rb:1272
| | full_callback_name = "#{callback_name}_for_#{association_name}" | ...ems/1.8/gems/activerecord-1.15.3/lib/active_record/associations.rb:1270
| | defined_callbacks = options[callback_name.to_sym] | .../gems/1.8/gems/activerecord-1.15.3/lib/active_record/associations.rb:1271
| | if options.has_key?(callback_name.to_sym) | .../gems/1.8/gems/activerecord-1.15.3/lib/active_record/associations.rb:1272
| | full_callback_name = "#{callback_name}_for_#{association_name}" | ...ems/1.8/gems/activerecord-1.15.3/lib/active_record/associations.rb:1270
| | defined_callbacks = options[callback_name.to_sym] | .../gems/1.8/gems/activerecord-1.15.3/lib/active_record/associations.rb:1271
| | if options.has_key?(callback_name.to_sym) | .../gems/1.8/gems/activerecord-1.15.3/lib/active_record/associations.rb:1272
| | full_callback_name = "#{callback_name}_for_#{association_name}" | ...ems/1.8/gems/activerecord-1.15.3/lib/active_record/associations.rb:1270
| | defined_callbacks = options[callback_name.to_sym] | .../gems/1.8/gems/activerecord-1.15.3/lib/active_record/associations.rb:1271
| | if options.has_key?(callback_name.to_sym) | .../gems/1.8/gems/activerecord-1.15.3/lib/active_record/associations.rb:1272
...
| collection_accessor_methods(reflection, HasManyAssociation) | ...y/gems/1.8/gems/activerecord-1.15.3/lib/active_record/associations.rb:565
| + calling ActiveRecord::Associations::ClassMethods::collection_accessor_methods
| / def collection_accessor_methods(reflection, association_proxy_class) | ...ms/1.8/gems/activerecord-1.15.3/lib/active_record/associations.rb:944
| | (reflection = #<ActiveRecord::Reflection::AssociationReflection:0xb78d405c @macro=:has_many, @name=:children, @options={:class_name=>"Person", :foreign_key=>"parent_id", :order=>"position", :dependent=>:destroy}, @active_record=Person, @primary_key_name="parent_id">; association_proxy_class = ActiveRecord::Associations::HasManyAssociation)
| | collection_reader_method(reflection, association_proxy_class) | ...y/gems/1.8/gems/activerecord-1.15.3/lib/active_record/associations.rb:945
...
\ def has_many(association_id, options = {}, &extension) (returning from ActiveRecord::Associations::ClassMethods::has_many) | ...ssociations.rb:555
def has_many(association_id, options = {}, &extension)
reflection = create_has_many_reflection(association_id, options, &extension)
configure_dependency_for_has_many(reflection)
if options[:through]
...
else
add_multiple_associated_save_callbacks(reflection.name)
add_association_callbacks(reflection.name, reflection.options)
collection_accessor_methods(reflection, HasManyAssociation)
end
add_deprecated_api_for_has_many(reflection.name)
end
def create_has_many_reflection(association_id, options, &extension)
...
create_reflection(:has_many, association_id, options, self)
end
def create_reflection(macro, name, options, active_record)
case macro
when :has_many, :belongs_to, :has_one, :has_and_belongs_to_many
reflection = AssociationReflection.new(macro, name, options, active_record)
when :composed_of
...
end
write_inheritable_hash :reflections, name => reflection
reflection
end
class AssociationReflection < MacroReflection
...
end
class MacroReflection
attr_reader :active_record
def initialize(macro, name, options, active_record)
@macro, @name, @options, @active_record = macro, name, options, active_record
end
...
end
def configure_dependency_for_has_many(reflection)
...
# See HasManyAssociation#delete_records. Dependent associations
# delete children, otherwise foreign key is set to NULL.
...
case reflection.options[:dependent]
when :destroy, true
module_eval "before_destroy '#{reflection.name}.each { |o| o.destroy }'"
...
end
end
def add_multiple_associated_save_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.respond_to?(:loaded?)
if new_record?
association
else
association.select { |record| record.new_record? }
end.each do |record|
errors.add "#{association_name}" unless record.valid?
end
end
end
validate method_name
before_save("@new_record_before_save = new_record?; true")
after_callback = <<-end_eval
association = instance_variable_get("@#{association_name}")
if association.respond_to?(:loaded?)
if @new_record_before_save
records_to_save = association
else
records_to_save = association.select { |record| record.new_record? }
end
records_to_save.each { |record| association.send(:insert_record, record) }
association.send(:construct_sql) # reconstruct the SQL queries now that we know the owner's id
end
end_eval
# Doesn't use after_save as that would save associations added in after_create/after_update twice
after_create(after_callback)
after_update(after_callback)
end
def add_association_callbacks(association_name, options)
callbacks = %w(before_add after_add before_remove after_remove)
callbacks.each do |callback_name|
full_callback_name = "#{callback_name}_for_#{association_name}"
defined_callbacks = options[callback_name.to_sym]
if options.has_key?(callback_name.to_sym)
class_inheritable_reader full_callback_name.to_sym
write_inheritable_array(full_callback_name.to_sym, [defined_callbacks].flatten)
end
end
end
def collection_accessor_methods(reflection, association_proxy_class)
collection_reader_method(reflection, association_proxy_class)
define_method("#{reflection.name}=") do |new_value|
# Loads proxy class instance (defined in collection_reader_method) if not already loaded
association = send(reflection.name)
association.replace(new_value)
association
end
define_method("#{reflection.name.to_s.singularize}_ids") do
send(reflection.name).map(&:id)
end
define_method("#{reflection.name.to_s.singularize}_ids=") do |new_value|
ids = (new_value || []).reject { |nid| nid.blank? }
send("#{reflection.name}=", reflection.class_name.constantize.find(ids))
end
end