Рельсовые ассоциации - Callback Sequence / Magic - PullRequest
1 голос
/ 05 января 2010

Взяв в качестве примера следующую декларацию ассоциации:

class Post
 has_many :comments
end

Просто объявив has_many: comments, ActiveRecord добавляет несколько методов, которые меня особенно интересуют comments , который возвращает массив комментариев. Я просмотрел код, и следующая последовательность вызовов выглядит следующим образом:

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

В этой последовательности обратных вызовов, где именно выполняется SQL для получения комментариев, когда я выполняю @ post.comments?

Ответы [ 4 ]

0 голосов
/ 08 января 2010

Я не уверен на 100%, что понимаю, что вы ищете.

Генерация sql не находится в одном месте в AR. Некоторые из конкретных вещей базы данных находятся в базе данных "connection_adapters".

Если вы ищете способ поиска записей в базе данных, посмотрите методы "construct_finder_sql" и "add_joins" в модуле ActiveRecord :: Base.

    def construct_finder_sql(options)
      scope = scope(:find)
      sql  = "SELECT #{options[:select] || (scope && scope[:select]) || default_select(options[:joins] || (scope && scope[:joins]))} "
      sql << "FROM #{(scope && scope[:from]) || options[:from] || quoted_table_name} "

      add_joins!(sql, options[:joins], scope)
      ...

и

    def add_joins!(sql, joins, scope = :auto)
      scope = scope(:find) if :auto == scope
      merged_joins = scope && scope[:joins] && joins ? merge_joins(scope[:joins], joins) : (joins || scope && scope[:joins])
      case merged_joins
      when Symbol, Hash, Array
        if array_of_strings?(merged_joins)
          sql << merged_joins.join(' ') + " "
        else
          join_dependency = ActiveRecord::Associations::ClassMethods::InnerJoinDependency.new(self, merged_joins, nil)
          sql << " #{join_dependency.join_associations.collect { |assoc| assoc.association_join }.join} "
        end
      when String
        sql << " #{merged_joins} "
      end
    end

Надеюсь, это поможет!

0 голосов
/ 05 января 2010

В считывателе ассоциации строка

association = association_proxy_class.new(self, reflection)

в конце будет отвечать за выполнение поиска, когда переменная экземпляра «запрашивается» и «видит», что @loaded имеет значение false.

0 голосов
/ 05 января 2010

Вам нужно углубиться в определение HasManyAssociation.

colletion_reader_method определяет метод под названием comments для вашего класса Post. Когда вызывается метод comments, он гарантирует, что прокси-объект класса HasManyAssociation хранится в хранилище (вам нужно покопаться в методе association_instance_set, чтобы увидеть, где именно он его хранит), затем он возвращает этот прокси-объект.

Я предполагаю, что SQL приходит, когда вы вызываете метод на прокси, например, вызываете каждый, все или обращаетесь к индексу с помощью [].

0 голосов
/ 05 января 2010

Вот вам: стандартный запрос AR, получающий все идентификаторы связанных объектов

send(reflection.name).all(:select => "#{reflection.quoted_table_name}.#{reflection.klass.primary_key}").map(&:id)

но уверен, что Activerecord грязный ... повторная реализация (лучше без eval) has_many может быть полезна для вас:

def has_many(children)
  send(:define_method, children){ eval(children.to_s.singularize.capitalize).all( :conditions => { self.class.name.downcase => name }) }
end
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...