Я немного читал о том, как расширить ActiveRecord: базовый класс, чтобы в моих моделях были специальные методы. Каков простой способ его расширения (пошаговое руководство)?
Я немного читал о том, как расширить ActiveRecord: базовый класс, чтобы в моих моделях были специальные методы. Каков простой способ его расширения (пошаговое руководство)?
Ответы:
Есть несколько подходов:
Прочитайте документацию ActiveSupport :: Concern для более подробной информации.
Создайте файл с именем active_record_extension.rbв libкаталоге.
require 'active_support/concern'
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"
Обратитесь к ответу Тоби .
Создайте файл в 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
Знаменитая цитата о регулярных выражениях Джейми Завински может быть переосмыслена, чтобы проиллюстрировать проблемы, связанные с исправлением обезьян.
Некоторые люди, сталкиваясь с проблемой, думают: «Я знаю, я использую исправления обезьян». Теперь у них две проблемы.
Исправление обезьян легко и быстро. Но сэкономленные время и усилия всегда извлекаются в будущем; с сложным интересом. В эти дни я ограничиваю исправление обезьян, чтобы быстро создать прототип решения в консоли rails.
requireфайл в конце environment.rb. Я добавил этот дополнительный шаг в мой ответ.
ImprovedActiveRecordи наследовать от него, когда вы используете module, вы обновляете определение рассматриваемого класса. Раньше я использовал наследование (причина многолетнего опыта работы с Java / C ++). В эти дни я в основном использую модули.
Refinementsкоторая решает большинство проблем с исправлениями обезьян ( yehudakatz.com/2010/11/30/ruby-2-0-refinements-in-practice ). Иногда есть особенность, чтобы заставить вас испытать судьбу. И иногда ты делаешь.
Вы можете просто расширить класс и просто использовать наследование.
class AbstractModel < ActiveRecord::Base
self.abstract_class = true
end
class Foo < AbstractModel
end
class Bar < AbstractModel
end
abstract_models. Где я должен положить это?
self.abstract_class = trueк своему AbstractModel. Rails теперь распознает модель как абстрактную модель.
AbstractModelв базе данных. Кто знал, что простой сеттер поможет мне высушить вещи! (Я начинал съеживаться ... это было плохо). Спасибо Тоби и Хариш!
Вы также можете использовать 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
InstanceMethodsне рекомендуется с Rails 3.2, просто поместите ваши методы в тело модуля.
ActiveRecord::Base.send(:include, MyExtension)в инициализатор, и тогда это сработало для меня. Рельсы 4.1.9
В Rails 4 концепция использования проблем для модульности и сушки ваших моделей была в центре внимания.
Проблемы, в основном, позволяют группировать сходный код модели или нескольких моделей в одном модуле, а затем использовать этот модуль в моделях. Вот пример:
Рассмотрим модель Article, модель Event и модель Comment. Статья или событие имеет много комментариев. Комментарий относится либо к статье, либо к событию.
Традиционно модели могут выглядеть так:
Модель комментария:
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 в приложении / модель / концерны.
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».
Вот ссылка на пост, который я нашел очень полезным для понимания проблем в моделях.
Надеюсь, рецензия поможет :)
Шаг 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 :)
Направляющие 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
Смотрите, например, этот блог .
Просто чтобы добавить к этой теме, я потратил некоторое время на разработку, как протестировать такие расширения (я пошел по 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
В 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
Надеюсь, это поможет другим!
у меня есть
ActiveRecord::Base.extend Foo::Bar
в инициализаторе
Для модуля, как показано ниже
module Foo
module Bar
end
end