Я читав про те, як розширити ActiveRecord: Base class, щоб мої моделі мали деякі спеціальні методи. Який простий спосіб розширити його (покроковий посібник)?
Я читав про те, як розширити ActiveRecord: Base class, щоб мої моделі мали деякі спеціальні методи. Який простий спосіб розширити його (покроковий посібник)?
Відповіді:
Існує кілька підходів:
Прочитайте документацію щодо 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
Знамениту цитату про регулярні вирази Джеймі Завінського можна переосмислити, щоб проілюструвати проблеми, пов’язані з виправленням мавп.
Деякі люди, стикаючись з проблемою, думають: «Я знаю, я буду використовувати патч-мавпи». Зараз у них дві проблеми.
Виправлення мавп легко і швидко. Але заощаджений час та зусилля завжди повертаються десь у майбутньому; зі складним відсотком. Ці дні я обмежую виправлення мавп для швидкого прототипу рішення в консолі рейок.
requireфайл у кінці environment.rb. Я додав цей додатковий крок до своєї відповіді.
ImprovedActiveRecordі успадкувати від цього, коли ви використовуєте module, ви оновлюєте визначення відповідного класу. Раніше я користувався спадщиною (причина років досвіду Java / C ++). У ці дні я в основному використовую модулі.
Refinementsяка вирішує більшість проблем з виправленням мавп ( yehudakatz.com/2010/11/30/ruby-2-0-refinances-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. Рейки тепер визнають модель абстрактною моделлю.
AbstractModelв базі даних. Хто знав, що простий сетер допоможе мені СУХІ речі! (Я починав плакати ... було погано). Дякую Тобі та Харішу!
Ви також можете використовувати ActiveSupport::Concernта бути більше ікономатичним Rails core на зразок:
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, то концепція використання занепокоєнь для модуляції та висушування ваших моделей була висвітлена.
Побоювання в основному дозволяють згрупувати подібний код моделі або по декількох моделях в одному модулі, а потім використовувати цей модуль у моделях. Ось приклад:
Розглянемо модель статті, модель події та модель коментаря. До статті чи події є багато коментарів. Коментар належить до будь-якої статті чи події.
Традиційно моделі можуть виглядати так:
Модель коментаря:
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
Один момент, який я хотів би підкреслити під час використання Concerns, полягає в тому, що стурбованість слід використовувати для групування на основі домена, а не для "технічного" групування. Наприклад, групування доменів схоже на "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