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

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

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

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 так же, как если бы я определил его в каждом?

4b9b3361

Ответ 1

Создайте модуль, который вы можете поместить в каталог 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 in foo.rb и FooBar в файле foo_bar.rb), Rails автоматически загрузит файл для вас. В противном случае вам нужно будет использовать require_dependency 'file_name' для загрузки вашего файла lib.

Ответ 2

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

  • Используйте модуль для общей логики и включите его в и 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

Ответ 3

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

Ответ 4

Вот как я это сделал... Сначала создайте mixin:

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

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

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

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

Ответ 5

Если вам нужен 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 как лучший подход.

Ответ 6

Как упоминалось выше, 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