Где разместить общий код, найденный в нескольких моделях?

У меня есть две модели, которые содержат один и тот же метод:

def foo
  # do something
end

Где я должен положить это?

Я знаю, что общий код идет вlib каталог в приложении Rails.

Но если я положу его в новый класс вlib называется ''Fooи мне нужно добавить его функциональность в оба моихActiveRecord models, я делаю это так:

class A < ActiveRecord::Base
includes Foo

class B < ActiveRecord::Base
includes Foo

а потом обаA а такжеB будет содержатьfoo метод, как если бы я определил его в каждом?

Ответы на вопрос(6)

У вас действительно есть два варианта:

Используйте модуль для общей логики и включите его в A & BИспользуйте общий класс C, который расширяет ActiveRecord и имеет A & B продлить C.

Используйте # 1, если общая функциональность не является основной для каждого класса, но применима к каждому классу. Например:

(app/lib/serializable.rb)
module Serializable
  def serialize
    # do something to serialize this object
  end
end

Используйте # 2, если общая функциональность является общей для каждого класса и A & B разделяют естественные отношения:

(app/models/letter.rb)
class Letter < ActiveRecord::Base
  def cyrilic_equivilent
    # return somethign similar
  end
end

class A < Letter
end

class B < Letter
end
 EK001 июл. 2015 г., 15:50
Опция №2 заставляет Rails предполагать, что A и B хранятся в таблице с именем "буквы", Если вы хотите использовать только общую логику, а A и B хранятся в отдельных таблицах, то, как указал @Ron, лучше всего использовать абстрактный родительский класс.ниже.
 Ron23 дек. 2013 г., 19:57
в этом случае необходимобуквы' отношение, верно? Как насчет того, чтобы сделать то же самое и фактически расширить activerecord на A и B?

Как уже упоминалось, Foo - это способ делать вещи ... Однако это не так.Похоже, вы получите необходимую вам функциональность с помощью базового модуля. Ниже приведена форма, которую принимают многие плагины Rails для добавления методов класса и новых обратных вызовов в дополнение к новым методам экземпляра.

module Foo #:nodoc:

  def self.included(base) # :nodoc:
    base.extend ClassMethods
  end

  module ClassMethods
    include Foo::InstanceMethods

    before_create :before_method
  end

  module InstanceMethods
    def foo
      ...
    end

    def before_method
      ...
    end
  end 

end
 Jonah04 сент. 2013 г., 02:16
Я попробовал этот метод, но он все еще говорит мне, что метод не определен (в моем случае,scope).
Решение Вопроса

Создайте модуль, который вы можете поместить вlib каталог:

module Foo
  def foo
    # do something
  end
end

Вы можете тогдаinclude модуль в каждом из ваших классов моделей:

class A < ActiveRecord::Base
  include Foo
end

class B < ActiveRecord::Base
  include Foo
end

A а такжеB модели теперь будут иметьfoo метод определен.

Если вы будете следовать соглашениям об именах Rails с именем модуля и именем файла (например, Foo в foo.rb и FooBar в foo_bar.rb), Rails автоматически загрузит файл для вас. В противном случае вам нужно будет использоватьrequire_dependency 'file_name' загрузить ваш файл lib.

Если тебе надоActiveRecord :: Base код как часть вашей общей функциональности, использование абстрактного класса также может быть полезным. Что-то вроде:

class Foo < ActiveRecord::Base
  self.abstract_class = true
  #Here ActiveRecord specific code, for example establish_connection to a different DB.
end

class A < Foo; end
class B < Foo; end

Так просто. Кроме того, если код не связан с ActiveRecord, найдитеActiveSupport::Concerns как лучший подход.

Один из вариантов - поместить их в новый каталог, напримерapp/models/modules/, Затем вы можете добавить это к:config/environment.rb

Dir["#{RAILS_ROOT}/app/models/modules/*.rb"].each do |filename|
  require filename
end

Это будетrequire каждый файл в этом каталоге, так что если вы поместите файл, подобный следующему, в ваш каталог модулей:

module SharedMethods
  def foo
    #...
  end
end

Тогда вы можете просто использовать его в своих моделях, потому что он будет автоматически загружен:

class User < ActiveRecord::Base
  include SharedMethods
end

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

 Bryan Locke09 нояб. 2009 г., 01:12
Кроме того, имеет ли значение, где ваштребуют» код появляется в environment.rb?
 Bryan Locke09 нояб. 2009 г., 01:10
Если обе модели звонятbefore_save: before_method " и я также поместил это в SharedMethods, это тоже будет работать? Или это работает только для определений метода?
 nicholaides09 нояб. 2009 г., 16:36
И да, методbefore_method» можно в миксине, но кодеbefore_save: before_method " не могу.
 nicholaides09 нояб. 2009 г., 16:34
Вы'наверно захочу вRails :: Initializer.run do | config | ... конец" раздел
 EmFi09 нояб. 2009 г., 19:20
Можно добавить вызов before_save в mixin. Я'Мы добавили ответ, который показывает, как.
 zx198605 июл. 2017 г., 05:30
это не работа

Вот'Как я это сделал ... Сначала создайте миксин:

module Slugged
  extend ActiveSupport::Concern

  included do
    has_many :slugs, :as => :target
    has_one :slug, :as => :target, :order => :created_at
  end
end

Затем смешайте это с каждой моделью, которая нуждается в этом:

class Sector < ActiveRecord::Base
  include Slugged

  validates_uniqueness_of :name
  etc
end

Это'это почти красиво!

Чтобы завершить пример, хотя этоне имеет отношения к вопросу, здесьМоя модель пули:

class Slug < ActiveRecord::Base
  belongs_to :target, :polymorphic => true
end

Ваш ответ на вопрос