Rails Associations - Callback Sequence/Magic

Posted by satynos on Stack Overflow See other posts from Stack Overflow or by satynos
Published on 2010-01-05T15:35:11Z Indexed on 2010/06/12 4:02 UTC
Read the original article Hit count: 635

Filed under:
|

Taking following association declaration as an example:

class Post
 has_many :comments
end

Just by declaring the has_many :comments, ActiveRecord adds several methods of which I am particularly interested in comments which returns array of comments. I browsed through the code and following seems to be the callback sequence:

def has_many(association_id, options = {}, &extension)
  reflection = create_has_many_reflection(association_id, options, &extension)
  configure_dependency_for_has_many(reflection)
  add_association_callbacks(reflection.name, reflection.options)

  if options[:through]
    collection_accessor_methods(reflection, HasManyThroughAssociation)
  else
    collection_accessor_methods(reflection, HasManyAssociation)
  end
end

def collection_accessor_methods(reflection, association_proxy_class, writer = true)
  collection_reader_method(reflection, association_proxy_class)

  if writer
    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 |new_value|
      ids = (new_value || []).reject { |nid| nid.blank? }
      send("#{reflection.name}=", reflection.class_name.constantize.find(ids))
    end
  end
end

def collection_reader_method(reflection, association_proxy_class)
  define_method(reflection.name) do |*params|
    force_reload = params.first unless params.empty?
    association = association_instance_get(reflection.name)

    unless association
      association = association_proxy_class.new(self, reflection)
      association_instance_set(reflection.name, association)
    end

    association.reload if force_reload

    association
  end

  define_method("#{reflection.name.to_s.singularize}_ids") do
    if send(reflection.name).loaded? || reflection.options[:finder_sql]
      send(reflection.name).map(&:id)
    else
      send(reflection.name).all(:select => "#{reflection.quoted_table_name}.#{reflection.klass.primary_key}").map(&:id)
    end
  end
end

In this sequence of callbacks, where exactly is the actual SQL being executed for retrieving the comments when I do @post.comments ?

© Stack Overflow or respective owner

Related posts about ruby-on-rails

Related posts about activerecord