Как делиться кодом между моделями? (Rails 2.3)
У меня есть несколько методов, которые я хотел бы разделить между моделями, а не копировать и вставлять их. В контроллере я могу это сделать, поместив методы в application_controller.rb, который автоматически включается в каждый контроллер.
Есть ли что-то подобное для моделей?
Спасибо!
Ответы
Ответ 1
Вы можете создать файл с именем functionality.rb
или что-то подобное в каталоге lib
вашего приложения Rails. Сделайте файл с именем после того, как модуль выполнит автозагрузку при запуске Rails. Например, если бы я хотел добавить флаг для нескольких моделей, я бы создал файл с именем lib/flagging.rb
, и он выглядел бы так:
module Flagging
# Flags an object for further review
def flag!
self.update_attribute(:flagged, true)
end
# Clears all flags on an object
def deflag!
self.update_attribute(:flagged, false)
end
end
В каждой модели я хотел добавить эту функциональность, я бы сделал следующее:
class Foo < ActiveRecord::Base
include Flagging
end
Тогда я смогу сделать что-то вроде:
foo = Foo.create
foo.flag!
Ответ 2
Верхнее решение выше работало для меня. В Rails 3 я обнаружил, что мне также пришлось обновить файл application.rb с помощью config.autoload_paths += Dir["#{config.root}/lib/**/"]
, как здесь было сказано: Лучший способ загрузить модуль/класс из папки lib в Rails 3?
Ответ 3
Вы можете либо a) сделать что-то похожее на application_controller, и создать класс модели, из которого другие могут подклассы или б) использовать модуль.
Ответ 4
Существует несколько способов совместного использования методов между двумя классами моделей.
- Используйте inheritance, если модели связаны друг с другом, например. общий родительский класс, который содержит методы, или один подкласс другого, который содержит методы.
- Используйте modules/mixins, которые можно использовать для нескольких моделей.
Ответ 5
Чтобы добавить ответ Джошу, иногда вам может понадобиться поделиться некоторым кодом, определяющим полиморфную ассоциацию. Вы не можете просто поместить вызов метода has_many
в свой модуль, потому что вы получите сообщение об ошибке, например, в модуле под названием Votable:
undefined method `has_many' for Voteable:Module (NoMethodError)
Поэтому вместо этого вам нужно использовать метод self.included(base)
и base.instance_eval
. Вот пример с моим модулем Voteable
:
module Voteable
def self.included(base)
base.instance_eval do
has_many :votes, as: :voteable
has_many :voters, through: :votes
end
end
def voted_up_by?(user)
user and votes.exists?(value: 1, voter_id: user)
end
def voted_down_by?(user)
user and votes.exists?(value: -1, voter_id: user)
end
end
Теперь вы можете include Voteable
в моделях, которые будут иметь такое поведение. Это выполнит метод self.included(base)
, и он определит ассоциацию для включенного класса.