title: Rails Associations - Callback Sequence/Magic
body: <p>Taking following association declaration as an example:</p>
  
  <pre><code>class Post
   has_many :comments
  end
  </code></pre>
  
  <p>Just by declaring the has_many :comments, ActiveRecord adds several methods of which I am particularly interested in <strong>comments</strong> which returns array of comments. I browsed through the code and following seems to be the callback sequence:</p>
  
  <pre><code>def has_many(association_id, options = {}, &amp;extension)
    reflection = create_has_many_reflection(association_id, options, &amp;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
  </code></pre>
  
  <hr>
  
  <pre><code>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
  </code></pre>
  
  <hr>
  
  <pre><code>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(&amp;:id)
      else
        send(reflection.name).all(:select =&gt; "#{reflection.quoted_table_name}.#{reflection.klass.primary_key}").map(&amp;:id)
      end
    end
  end
  </code></pre>
  
  <hr>
  
  <p>In this sequence of callbacks, where exactly is the actual SQL being executed for retrieving the comments when I do @post.comments ?</p>