Я прочитал о том, как расширить класс ActiveRecord: Base, чтобы в моих моделях были специальные методы. Как легко его расширить (пошаговое руководство)?
Rails расширяет ActiveRecord :: Base
Ответы (9)
Есть несколько подходов:
Использование ActiveSupport :: Concern (предпочтительно)
Дополнительные сведения см. В документации 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
Знаменитая цитата Джейми Завински о регулярных выражениях может быть использована для иллюстрации проблемы, связанные с исправлением обезьян.
Некоторые люди, сталкиваясь с проблемой, думают: «Я знаю, я буду использовать обезьяний патч». Теперь у них две проблемы.
Исправление Monkey выполняется легко и быстро. Но сэкономленное время и усилия всегда возвращаются когда-нибудь в будущем; со сложными процентами. В наши дни я ограничиваю установку патчей обезьяны, чтобы быстро создать прототип решения в консоли rails.
class MyActiveRecordExtensions
вместо module MyActiveRecordExtensions
.
- person Jimmy; 25.02.2010
require
файл в конце environment.rb
. Я добавил этот дополнительный шаг к своему ответу.
- person Harish Shetty; 25.02.2010
ImprovedActiveRecord
и наследовать от него, когда вы используете module
, вы обновляете определение рассматриваемого класса. Раньше я использовал наследование (из-за многолетнего опыта работы с Java / C ++). Сейчас я в основном использую модули.
- person Harish Shetty; 06.11.2013
Refinements
, которая решает большинство проблем с исправлением обезьян (yehudakatz.com/2010/11/30/ruby-2-0-refinements-in-practice). Иногда особенность нужна только для того, чтобы заставить вас искушать судьбу. И иногда да.
- person Harish Shetty; 25.11.2013
ActiveSupport::Concern
, а затем смешивая с классом. Может, здесь можно это использовать?
- person Dennis; 10.04.2014
list
, который получает 10 строк из базы данных в модуле, тогда мне не нужно копировать его в каждый подкласс.
- person worldask; 12.09.2014
top_ten
. Вы можете связать этот метод с другими методами AR, например: Order.where('qty > ?', 10).top_ten
- person Harish Shetty; 12.09.2014
serializable_hash
, вы должны использовать параметр exclude
функции. Если вы пытаетесь настроить таргетинг только на один класс, возможно, лучше переопределить метод в этом классе. Вот пример: robots.oughttbot.com/better-serialization-less-as -json
- person Harish Shetty; 05.03.2015
class_methods do
вместо module ClassMethods
, верно?
- person Trantor Liu; 11.07.2016
require 'active_support/concern'
к lib/active_record_extension.rb
файлу. См. документацию по ActiveSupport Concern.
- person a.barbieri; 29.08.2017
Вы можете просто расширить класс и просто использовать наследование.
class AbstractModel < ActiveRecord::Base
self.abstract_class = true
end
class Foo < AbstractModel
end
class Bar < AbstractModel
end
abstract_models
. Куда мне его поставить?
- person xpepermint; 25.02.2010
self.abstract_class = true
в свой AbstractModel
. Rails теперь распознает модель как абстрактную.
- person Harish Shetty; 25.02.2010
AbstractModel
в базе данных. Кто знал, что простой сеттер поможет мне СУХОСТЬ! (Я начала передергиваться ... было плохо). Спасибо Тоби и Харишу!
- person dooleyo; 28.06.2013
Вы также можете использовать ActiveSupport::Concern
и использовать идиоматику ядра Rails, например:
module MyExtension
extend ActiveSupport::Concern
def foo
end
module ClassMethods
def bar
end
end
end
ActiveRecord::Base.send(:include, MyExtension)
[Edit] после комментария @daniel
Тогда все ваши модели будут иметь метод foo
, включенный как метод экземпляра, и методы в ClassMethods
, включенные как методы класса. Например. на FooBar < ActiveRecord::Base
у вас будет: FooBar.bar
и FooBar#foo
http://api.rubyonrails.org/classes/ActiveSupport/Concern.html
InstanceMethods
устарел, начиная с Rails 3.2, просто поместите свои методы в тело модуля.
- person Daniel Rikowski; 12.12.2012
ActiveRecord::Base.send(:include, MyExtension)
в инициализатор, и это сработало для меня. Рельсы 4.1.9
- person 6ft Dan; 20.01.2015
В Rails 4 концепция использования функций для модуляции и СУХОЙ обработки ваших моделей была в центре внимания.
Проблемы в основном позволяют вам сгруппировать похожий код модели или нескольких моделей в один модуль, а затем использовать этот модуль в моделях. Вот пример:
Рассмотрим модель статьи, модель события и модель комментария. В статье или мероприятии много комментариев. Комментарий относится либо к статье, либо к событию.
Традиционно модели могут выглядеть так:
Комментарий Модель:
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
Как мы можем заметить, существует значительный фрагмент кода, общего как для модели события, так и для модели статьи. Используя проблемы, мы можем выделить этот общий код в отдельный модуль 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
Один момент, который я хотел бы выделить при использовании проблем, заключается в том, что проблемы следует использовать для группировки на основе домена, а не для «технической» группировки. Например, группировка доменов похожа на «Комментарии», «С тегами» 'и т. д. Группировка на основе технической информации будет похожа на' 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 :)
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
См., Например, этот блог.
Чтобы добавить к этой теме, я потратил некоторое время на разработку того, как тестировать такие расширения (я пошел по пути 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