Ich habe etwas darüber gelesen, wie die ActiveRecord: Base-Klasse erweitert werden kann, damit meine Modelle einige spezielle Methoden haben. Was ist der einfache Weg, um es zu erweitern (Schritt-für-Schritt-Tutorial)?
Ich habe etwas darüber gelesen, wie die ActiveRecord: Base-Klasse erweitert werden kann, damit meine Modelle einige spezielle Methoden haben. Was ist der einfache Weg, um es zu erweitern (Schritt-für-Schritt-Tutorial)?
Antworten:
Es gibt verschiedene Ansätze:
Weitere Informationen finden Sie in der Dokumentation zu ActiveSupport :: Concern .
Erstellen Sie eine Datei, die active_record_extension.rb
im lib
Verzeichnis aufgerufen wird .
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)
Erstellen Sie eine Datei in dem config/initializers
Verzeichnis mit dem Namen extensions.rb
und fügen Sie der Datei die folgende Zeile hinzu:
require "active_record_extension"
Siehe Tobys Antwort .
Erstellen Sie eine Datei im config/initializers
Verzeichnis mit dem Namen 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
Das berühmte Zitat über reguläre Ausdrücke von Jamie Zawinski kann neu definiert werden, um die Probleme zu veranschaulichen, die mit dem Patchen von Affen verbunden sind.
Einige Leute denken, wenn sie mit einem Problem konfrontiert werden: "Ich weiß, ich werde Affen-Patches verwenden." Jetzt haben sie zwei Probleme.
Das Patchen von Affen ist einfach und schnell. Die eingesparte Zeit und Mühe wird jedoch immer irgendwann in der Zukunft zurückgewonnen. mit Zinseszins. Heutzutage beschränke ich das Patchen von Affen, um schnell eine Lösung in der Rails-Konsole zu erstellen.
require
die Datei am Ende von environment.rb
. Ich habe diesen zusätzlichen Schritt zu meiner Antwort hinzugefügt.
ImprovedActiveRecord
und von dieser erben. Wenn Sie die Vererbung verwenden module
, aktualisieren Sie die Definition der betreffenden Klasse. Ich habe früher die Vererbung verwendet (aufgrund jahrelanger Java / C ++ - Erfahrung). Heutzutage benutze ich meistens Module.
Refinements
die die meisten Probleme beim Patchen von Affen behebt ( yehudakatz.com/2010/11/30/ruby-2-0-refinements-in-practice ). Manchmal gibt es eine Funktion, die Sie nur dazu zwingt, das Schicksal in Versuchung zu führen. Und manchmal tust du es.
Sie können die Klasse einfach erweitern und einfach die Vererbung verwenden.
class AbstractModel < ActiveRecord::Base
self.abstract_class = true
end
class Foo < AbstractModel
end
class Bar < AbstractModel
end
abstract_models
. Wo soll ich es hinstellen?
self.abstract_class = true
Ihrem hinzu AbstractModel
. Rails erkennt das Modell nun als abstraktes Modell.
AbstractModel
in der Datenbank suchte . Wer wusste, dass ein einfacher Setter mir helfen würde, die Dinge zu trocknen! (Ich fing an zu schaudern ... es war schlimm). Danke Toby und Harish!
Sie können auch verwenden ActiveSupport::Concern
und mehr Rails Kern idiomatisch sein wie:
module MyExtension
extend ActiveSupport::Concern
def foo
end
module ClassMethods
def bar
end
end
end
ActiveRecord::Base.send(:include, MyExtension)
[Bearbeiten] nach dem Kommentar von @daniel
Dann haben alle Ihre Modelle die Methode foo
als Instanzmethode und die Methoden ClassMethods
als Klassenmethoden. ZB auf einem haben FooBar < ActiveRecord::Base
Sie: FooBar.bar
undFooBar#foo
http://api.rubyonrails.org/classes/ActiveSupport/Concern.html
InstanceMethods
seit Rails 3.2 veraltet ist. Fügen Sie einfach Ihre Methoden in den Modulkörper ein.
ActiveRecord::Base.send(:include, MyExtension)
einen Initialisierer eingesetzt und dann hat das bei mir funktioniert. Schienen 4.1.9
Mit Rails 4 wurde das Konzept der Verwendung von Bedenken zur Modularisierung und Trocknung Ihrer Modelle hervorgehoben.
Bedenken ermöglichen es Ihnen grundsätzlich, ähnlichen Code eines Modells oder über mehrere Modelle hinweg in einem einzigen Modul zu gruppieren und dieses Modul dann in den Modellen zu verwenden. Hier ist ein Beispiel:
Betrachten Sie ein Artikelmodell, ein Ereignismodell und ein Kommentarmodell. Ein Artikel oder eine Veranstaltung hat viele Kommentare. Ein Kommentar gehört entweder zum Artikel oder zum Ereignis.
Traditionell sehen die Modelle folgendermaßen aus:
Kommentar Modell:
class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true
end
Artikelmodell:
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
Ereignismodell
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
Wie wir feststellen können, gibt es einen wichtigen Code, der sowohl dem Ereignis- als auch dem Artikelmodell gemeinsam ist. Mit Bedenken können wir diesen gemeinsamen Code in einem separaten Modul Commentable extrahieren.
Erstellen Sie dazu eine commentable.rb-Datei in App / Model / Concerns.
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
Und jetzt sehen Ihre Modelle so aus:
Kommentar Modell:
class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true
end
Artikelmodell:
class Article < ActiveRecord::Base
include Commentable
end
Ereignismodell
class Event < ActiveRecord::Base
include Commentable
end
Ein Punkt, den ich bei der Verwendung von Bedenken hervorheben möchte, ist, dass Bedenken eher für die domänenbasierte Gruppierung als für die technische Gruppierung verwendet werden sollten. Beispielsweise ist eine Domänengruppierung wie "Kommentierbar", "Taggable" usw. Eine technisch basierte Gruppierung ist wie "FinderMethods", "ValidationMethods".
Hier ist ein Link zu einem Beitrag , den ich sehr nützlich fand, um Bedenken in Modellen zu verstehen.
Hoffe das Aufschreiben hilft :)
Schritt 1
module FooExtension
def foo
puts "bar :)"
end
end
ActiveRecord::Base.send :include, FooExtension
Schritt 2
# Require the above file in an initializer (in config/initializers)
require 'lib/foo_extension.rb'
Schritt 3
There is no step 3 :)
Die Schienen 5 bieten einen eingebauten Mechanismus zum Ausfahren ActiveRecord::Base
.
Dies wird durch die Bereitstellung einer zusätzlichen Schicht erreicht:
# app/models/application_record.rb
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
# put your extensions here
end
und alle Modelle erben von diesem:
class Post < ApplicationRecord
end
Siehe zB diesen Blogpost .
Um dieses Thema zu ergänzen, habe ich eine Weile daran gearbeitet, solche Erweiterungen zu testen (ich bin den ActiveSupport::Concern
Weg gegangen .)
So richte ich ein Modell zum Testen meiner Erweiterungen ein.
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
Mit Rails 5 werden alle Modelle von ApplicationRecord geerbt und bieten eine gute Möglichkeit, andere Erweiterungsbibliotheken einzuschließen oder zu erweitern.
# 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
Angenommen, das Modul für spezielle Methoden muss für alle Modelle verfügbar sein, und fügen Sie es in die Datei application_record.rb ein. Wenn wir dies für einen bestimmten Satz von Modellen anwenden möchten, fügen Sie es in die jeweiligen Modellklassen ein.
# 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
Wenn Sie die Methoden im Modul als Klassenmethoden definieren möchten, erweitern Sie das Modul auf ApplicationRecord.
# app/models/application_record.rb
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
extend SpecialMethods
end
Hoffe es hilft anderen!
ich habe
ActiveRecord::Base.extend Foo::Bar
in einem Initialisierer
Für ein Modul wie unten
module Foo
module Bar
end
end