Каков код по умолчанию для массового has_many: сквозное назначение соединения в рельсах?

У меня есть базовое отношение has_many :through, которое является двунаправленным:

calendars have many calendar_calendar_events
calendars have many events through calendar_calendar_events


events have many calendar_calendar_events
events have many calendars through calendar_calendar_events

Я хочу назначить календари событию с помощью базовой функции calendar_ids=, которая устанавливает has_many :through, однако я хочу переопределить эту функцию, чтобы добавить дополнительную магию. Я просмотрел исходный код рельсов и не могу найти код для этой функции. Мне интересно, может ли кто-нибудь указать мне на это. Затем я переопределю его для этого класса, чтобы добавить то, что я хочу :)


person Brendon Muir    schedule 15.07.2009    source источник


Ответы (3)


Вы можете найти исходный код в файле lib/active_record/associations.rb в строке 1295.

    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

Вам определенно следует избегать перезаписи такого метода, чтобы добавить волшебные вещи. Rails уже иногда «слишком много волшебства». Я бы предложил создать виртуальный атрибут со всей вашей пользовательской логикой по нескольким причинам:

  1. некоторые другие методы rails могут полагаться на реализацию по умолчанию.
  2. вы полагаетесь на определенный API, который может измениться в будущих версиях ActiveRecord
person Simone Carletti    schedule 15.07.2009
comment
Это очень хороший момент. В моем примере я просто перезаписал метод calendars= в своей модели событий. Это все еще плохо? Можно ли связать эти методы? Я никогда не делал этого раньше, но я видел это вокруг немного. Все, что я хочу сделать, это добавить дополнительное событие в массив событий new_value в параметрах calendars=, если это необходимо. - person Brendon Muir; 16.07.2009

После недолгих поисков нашел:

http://apidock.com/rails/ActiveRecord/Associations/ClassMethods/collection_accessor_methods

Это выглядело не так, как я думал, поэтому я, вероятно, пропустил это. В итоге я переопределил метод calendars= вместо метода calendar_ids=, и все работает хорошо.

person Brendon Muir    schedule 15.07.2009

В ответ на ответ выше я использовал alias_method_chain, чтобы переопределить установщик по умолчанию и добавить свою функцию. Работает довольно хорошо, хотя я не уверен, почему я должен отправлять установщик метода вместо того, чтобы просто использовать его в обычном режиме. Это, похоже, не сработало, так что это сработает :)

  def calendars_with_primary_calendar=(new_calendars)
    new_calendars << calendar unless new_record?
    send('calendars_without_primary_calendar=', new_calendars) # Not sure why we have to call it this way
  end

  alias_method_chain :calendars=, :primary_calendar
person Brendon Muir    schedule 15.07.2009