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
создает синтаксическую ошибку, когда она называется так, что, очевидно, не так просто.
Ответы
Ответ 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.