Рельсы, расширяющие ActiveRecord:: Base

Я читал о том, как расширить класс ActiveRecord: Base, поэтому мои модели будут иметь специальные методы. Каков простой способ его расширения (пошаговое руководство)?

Ответ 1

Существует несколько подходов:

Использование ActiveSupport:: Концерн (предпочтительный)

Подробнее см. документацию ActiveSupport:: Concern.

Создайте файл с именем active_record_extension.rb в каталоге lib.

module ActiveRecordExtension

  extend ActiveSupport::Concern

  # add your instance methods here
  def foo
     "foo"
  end

  # add your static(class) methods here
  class_methods do
    #E.g: Order.top_ten        
    def top_ten
      limit(10)
    end
  end
end

# include the extension 
ActiveRecord::Base.send(:include, ActiveRecordExtension)

Создайте файл в каталоге config/initializers с именем extensions.rb и добавьте следующую строку в файл:

require "active_record_extension"

Наследование (предпочтительный)

Обратитесь к Тоби answer.

Патч обезьяны (следует избегать)

Создайте файл в каталоге config/initializers с именем active_record_monkey_patch.rb.

class ActiveRecord::Base     
  #instance method, E.g: Order.new.foo       
  def foo
   "foo"
  end

  #class method, E.g: Order.top_ten        
  def self.top_ten
    limit(10)
  end
end

Знаменитая цитата о регулярных выражениях Jamie Zawinski может быть переназначена, чтобы проиллюстрировать проблемы, связанные с переделкой обезьян.

Некоторые люди, столкнувшись с проблемой, думают: "Я знаю, я буду использовать патч обезьяны". Теперь у них есть две проблемы.

Патч обезьяны прост и быстр. Но время и усилия, сохраненные, всегда извлекаются обратно когда-нибудь в будущем; с комплексным интересом. В эти дни я ограничиваю патч обезьяны, чтобы быстро прототипировать решение в консоли rails.

Ответ 2

Вы можете просто расширить класс и просто использовать наследование.

class AbstractModel < ActiveRecord::Base  
  self.abstract_class = true
end

class Foo < AbstractModel
end

class Bar < AbstractModel
end

Ответ 3

Вы также можете использовать ActiveSupport::Concern и быть более ядром Rails, таким как:

module MyExtension
  extend ActiveSupport::Concern

  def foo
  end

  module ClassMethods
    def bar
    end
  end
end

ActiveRecord::Base.send(:include, MyExtension)

[Изменить] после комментария @daniel

Затем все ваши модели будут иметь метод foo, включенный в качестве метода экземпляра, и методы из ClassMethods, включенные в качестве методов класса. Например. на FooBar < ActiveRecord::Base у вас будут: FooBar.bar и FooBar#foo

http://api.rubyonrails.org/classes/ActiveSupport/Concern.html

Ответ 4

В Rails 4 концепция использования проблем для модуляции и сушки ваших моделей была в центре внимания.

Концерты в основном позволяют группировать аналогичный код модели или через несколько моделей в одном модуле, а затем использовать этот модуль в моделях. Вот пример:

Рассмотрим модель статьи, модель события и модель комментария. В статье или событии A есть много комментариев. Комментарий относится к любой статье или событию.

Традиционно модели могут выглядеть так:

Модель комментария:

class Comment < ActiveRecord::Base
  belongs_to :commentable, polymorphic: true
end

Модель статьи:

class Article < ActiveRecord::Base
  has_many :comments, as: :commentable 

  def find_first_comment
    comments.first(created_at DESC)
  end

  def self.least_commented
   #return the article with least number of comments
  end
end

Модель события

class Event < ActiveRecord::Base
  has_many :comments, as: :commentable 

  def find_first_comment
    comments.first(created_at DESC)
  end

  def self.least_commented
   #returns the event with least number of comments
  end
end

Как мы можем заметить, существует значительная часть кода, общая для модели Event и Article Model. Используя проблемы, мы можем извлечь этот общий код в отдельный модуль Commentable.

Для этого создайте файл commentable.rb в app/model/problems.

module Commentable
    extend ActiveSupport::Concern

    included do 
        has_many :comments, as: :commentable 
    end

    # for the given article/event returns the first comment
    def find_first_comment
        comments.first(created_at DESC)
    end

    module ClassMethods     
        def least_commented
           #returns the article/event which has the least number of comments
        end
    end 
end

И теперь ваши модели выглядят так:

Модель комментария:

    class Comment < ActiveRecord::Base
      belongs_to :commentable, polymorphic: true
    end

Модель статьи:

class Article < ActiveRecord::Base
    include Commentable
end

Модель события

class Event < ActiveRecord::Base    
    include Commentable
end

Одна точка, которую я хотел бы выделить при использовании Концернов, заключается в том, что Концерты должны использоваться для группировки "на основе домена", а не "технической" группировки. Например, группировка домена похожа на "Commentable", "Taggable" и т.д. Техническая группировка будет похожа на "FinderMethods", "ValidationMethods".

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

Надеемся, что справка поможет:)

Ответ 5

Шаг 1

module FooExtension
  def foo
    puts "bar :)"
  end
end
ActiveRecord::Base.send :include, FooExtension

Шаг 2

# Require the above file in an initializer (in config/initializers)
require 'lib/foo_extension.rb'

Шаг 3

There is no step 3 :)

Ответ 6

Просто чтобы добавить к этой теме, я потратил некоторое время на то, как тестировать такие расширения (я пошел по маршруту ActiveSupport::Concern.)

Вот как я создал модель для тестирования моих расширений.

describe ModelExtensions do
  describe :some_method do
    it 'should return the value of foo' do
      ActiveRecord::Migration.create_table :test_models do |t|
        t.string :foo
      end

      test_model_class = Class.new(ActiveRecord::Base) do
        def self.name
          'TestModel'
        end

        attr_accessible :foo
      end

      model = test_model_class.new(:foo => 'bar')

      model.some_method.should == 'bar'
    end
  end
end

Ответ 7

Rails 5 обеспечивает встроенный механизм расширения ActiveRecord::Base.

Это достигается путем предоставления дополнительного слоя:

# app/models/application_record.rb
class ApplicationRecord < ActiveRecord::Base
  self.abstract_class = true
  # put your extensions here
end

и все модели наследуют от этого:

class Post < ApplicationRecord
end

См. этот блогпост.

Ответ 8

С Rails 5 все модели наследуются от ApplicationRecord, и это дает хороший способ включить или расширить другие библиотеки расширений.

# app/models/concerns/special_methods.rb
module SpecialMethods
  extend ActiveSupport::Concern

  scope :this_month, -> { 
    where("date_trunc('month',created_at) = date_trunc('month',now())")
  }

  def foo
    # Code
  end
end

Предположим, модуль специальных методов должен быть доступен во всех моделях, включите его в файл application_record.rb. Если мы хотим применить это для определенного набора моделей, включите его в соответствующие классы моделей.

# app/models/application_record.rb
class ApplicationRecord < ActiveRecord::Base
  self.abstract_class = true
  include SpecialMethods
end

# app/models/user.rb
class User < ApplicationRecord
  include SpecialMethods

  # Code
end

Если вы хотите, чтобы методы, определенные в модуле как методы класса, расширили модуль до ApplicationRecord.

# app/models/application_record.rb
class ApplicationRecord < ActiveRecord::Base
  self.abstract_class = true
  extend SpecialMethods
end

Надеюсь, это поможет другим!

Ответ 9

У меня

ActiveRecord::Base.extend Foo::Bar

в инициализаторе

Для модуля, подобного ниже

module Foo
  module Bar
  end
end