Я немного читал о том, как расширить 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