Как делиться кодом между моделями? (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!

Ответ 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), и он определит ассоциацию для включенного класса.