Подтвердить что ты не робот

Rails: переопределение метода ассоциации ActiveRecord

Есть ли способ переопределить один из методов, предоставляемых ассоциацией ActiveRecord?

Скажем, например, у меня есть следующий типичный полиморфный has_many: через ассоциацию:

class Story < ActiveRecord::Base
    has_many :taggings, :as => :taggable
    has_many :tags, :through => :taggings, :order => :name
end


class Tag < ActiveRecord::Base
    has_many :taggings, :dependent => :destroy
    has_many :stories, :through => :taggings, :source => :taggable, :source_type => "Story"
end

Как вы, вероятно, знаете, это добавляет целое множество связанных методов в модель Story, например теги, теги <, теги =, теги.пути и т.д.

Как мне переопределить один из этих методов? В частности, теги < метод. Это довольно легко переопределить обычные методы класса, но я не могу найти какую-либо информацию о том, как переопределить методы ассоциации. Выполнение чего-то типа

def tags<< *new_tags
    #do stuff
end

создает синтаксическую ошибку, когда она называется так, что, очевидно, не так просто.

4b9b3361

Ответ 1

Вы можете использовать блок с has_many, чтобы расширить связь с методами. См. Комментарий "Используйте блок для расширения ассоциаций" здесь.
Переопределение существующих методов также работает, но не знаю, является ли это хорошей идеей.

  has_many :tags, :through => :taggings, :order => :name do
    def << (value)
      "overriden" #your code here
      super value
    end     
  end

Ответ 2

Если вы хотите получить доступ к самой модели в Rails 3.2, вы должны использовать proxy_association.owner

Пример:

class Author < ActiveRecord::Base
  has_many :books do
    def << (book)
      proxy_association.owner.add_book(book)
    end
  end

  def add_book (book)
    # do your thing here.
  end
end

Смотрите документацию

Ответ 3

Я думаю, вы хотели def tags.<<(*new_tags) для подписи, которая должна работать, или следующего, что эквивалентно и немного более чистым, если вам нужно переопределить несколько методов.

class << tags
  def <<(*new_tags)
    # rawr!
  end
end

Ответ 4

Вам нужно будет определить метод меток для возврата объекта, который имеет метод <<.

Вы могли бы сделать это так, но я действительно не рекомендовал бы этого. Вам будет намного лучше просто добавить метод к вашей модели, который сделает то, что вам нужно, чем пытаться заменить что-то, что использует ActiveRecord.

Это по существу работает по умолчанию, метод tags добавляет < метод к результирующему объекту и возвращает этот объект. Это может быть немного ресурсоемким, потому что при каждом запуске он создает новый метод.

def tags_with_append
  collection = tags_without_append
  def collection.<< (*arguments)
    ...
  end
  collection
end
# defines the method 'tags' by aliasing 'tags_with_append'
alias_method_chain :tags, :append  

Ответ 5

Метод, который я использую, заключается в расширении ассоциации. Вы можете увидеть, как я обрабатываю атрибуты количества: https://gist.github.com/1399762

В основном это позволяет просто делать

has_many : tags, :through => : taggings, extend => QuantityAssociation

Не зная точно, чего вы надеетесь достичь, переопределив методы, трудно понять, можете ли вы сделать то же самое.

Ответ 6

Это может быть не полезно в вашем случае, но может быть полезно для других, рассматривающих это.

Обратные вызовы Ассоциации: http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html

Пример из документов:

class Project
  has_and_belongs_to_many :developers, :after_add => :evaluate_velocity

  def evaluate_velocity(developer)
    ...
  end
end

Также см. Расширения ассоциации:

class Account < ActiveRecord::Base
  has_many :people do
    def find_or_create_by_name(name)
      first_name, last_name = name.split(" ", 2)
      find_or_create_by_first_name_and_last_name(first_name, last_name)
    end
  end
end

person = Account.first.people.find_or_create_by_name("David Heinemeier Hansson")
person.first_name # => "David"
person.last_name  # => "Heinemeier Hansson"

Ответ 7

Rails ведет документы об переопределении добавленных методов напрямую.

Ошибка OP с переопределением <<, вероятно, является единственным исключением из этого, за которым следует главный ответ. Но это не сработало бы для метода присваивания has_one = или методов getter.