Я читав про те, як розширити 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