Ho fatto alcune letture su come estendere ActiveRecord: classe Base in modo che i miei modelli avessero alcuni metodi speciali. Qual è il modo semplice per estenderlo (tutorial passo per passo)?
Ho fatto alcune letture su come estendere ActiveRecord: classe Base in modo che i miei modelli avessero alcuni metodi speciali. Qual è il modo semplice per estenderlo (tutorial passo per passo)?
Risposte:
Esistono diversi approcci:
Leggi la documentazione di ActiveSupport :: Preoccupazione per maggiori dettagli.
Crea un file chiamato active_record_extension.rb
nella lib
directory.
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)
Creare un file nella config/initializers
directory chiamata extensions.rb
e aggiungere la seguente riga al file:
require "active_record_extension"
Fai riferimento alla risposta di Toby .
Crea un file nella config/initializers
directory chiamata 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
La famosa citazione sulle espressioni regolari di Jamie Zawinski può essere riproposta per illustrare i problemi associati al patching delle scimmie.
Alcune persone, di fronte a un problema, pensano "Lo so, userò il patching delle scimmie". Ora hanno due problemi.
Il patching delle scimmie è semplice e veloce. Ma il tempo e lo sforzo risparmiati vengono sempre estratti in futuro; con interesse composto. Oggi limito il patching delle scimmie per prototipare rapidamente una soluzione nella console delle rotaie.
require
il file alla fine di environment.rb
. Ho aggiunto questo ulteriore passaggio alla mia risposta.
ImprovedActiveRecord
ed ereditare da quello, quando stai usando module
, stai aggiornando la definizione della classe in questione. Usavo l'ereditarietà (causa di anni di esperienza Java / C ++). In questi giorni uso principalmente moduli.
Refinements
che affronta la maggior parte dei problemi con il patching delle scimmie ( yehudakatz.com/2010/11/30/ruby-2-0-refinements-in-practice ). A volte una funzione è lì solo per costringerti a tentare il destino. E a volte lo fai.
Puoi semplicemente estendere la classe e usare semplicemente l'ereditarietà.
class AbstractModel < ActiveRecord::Base
self.abstract_class = true
end
class Foo < AbstractModel
end
class Bar < AbstractModel
end
abstract_models
. Dove dovrei metterlo?
self.abstract_class = true
al tuo AbstractModel
. Rails ora riconoscerà il modello come modello astratto.
AbstractModel
il database. Chi sapeva che un semplice setter mi avrebbe aiutato a ASCIUGARE le cose! (Stavo iniziando a rabbrividire ... era male). Grazie Toby e Harish!
Puoi anche usare ActiveSupport::Concern
ed essere più idiomatico del core di Rails come:
module MyExtension
extend ActiveSupport::Concern
def foo
end
module ClassMethods
def bar
end
end
end
ActiveRecord::Base.send(:include, MyExtension)
[Modifica] seguendo il commento di @daniel
Quindi tutti i tuoi modelli includeranno il metodo foo
come metodo di istanza e i metodi ClassMethods
inclusi come metodi di classe. Ad esempio su un FooBar < ActiveRecord::Base
avrai: FooBar.bar
eFooBar#foo
http://api.rubyonrails.org/classes/ActiveSupport/Concern.html
InstanceMethods
è deprecato da Rails 3.2, inserisci i tuoi metodi nel corpo del modulo.
ActiveRecord::Base.send(:include, MyExtension)
un inizializzatore e poi ha funzionato per me. Rotaie 4.1.9
Con Rails 4, il concetto di utilizzo delle preoccupazioni per modulare e ASCIUGARE i tuoi modelli è stato messo in evidenza.
Le preoccupazioni fondamentalmente consentono di raggruppare un codice simile di un modello o tra più modelli in un singolo modulo e quindi utilizzare questo modulo nei modelli. Ecco un esempio:
Considera un modello di articolo, un modello di evento e un modello di commento. Un articolo o un evento ha molti commenti. Un commento appartiene all'articolo o all'evento.
Tradizionalmente, i modelli possono apparire così:
Modello di commento:
class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true
end
Modello dell'articolo:
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
Modello di evento
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
Come possiamo notare, esiste una parte significativa del codice comune sia al modello di evento che a quello di articolo. Usando le preoccupazioni possiamo estrarre questo codice comune in un modulo separato Commentable.
Per questo creare un file commentable.rb in app / modello / preoccupazioni.
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
E ora i tuoi modelli si presentano così:
Modello di commento:
class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true
end
Modello dell'articolo:
class Article < ActiveRecord::Base
include Commentable
end
Modello di evento
class Event < ActiveRecord::Base
include Commentable
end
Un punto che vorrei evidenziare mentre uso le preoccupazioni è che le preoccupazioni dovrebbero essere usate per il raggruppamento "basato sul dominio" piuttosto che per il raggruppamento "tecnico". Ad esempio, un raggruppamento di domini è come "Commentable", "Taggable" ecc. Un raggruppamento basato su tecniche sarà come "FinderMethods", "ValidationMethods".
Ecco un link a un post che ho trovato molto utile per comprendere le preoccupazioni in Modelli.
Spero che il writeup aiuti :)
Passo 1
module FooExtension
def foo
puts "bar :)"
end
end
ActiveRecord::Base.send :include, FooExtension
Passo 2
# Require the above file in an initializer (in config/initializers)
require 'lib/foo_extension.rb'
Passaggio 3
There is no step 3 :)
Rails 5 fornisce un meccanismo integrato per l'estensione ActiveRecord::Base
.
Ciò si ottiene fornendo un livello aggiuntivo:
# app/models/application_record.rb
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
# put your extensions here
end
e tutti i modelli ereditano da quello:
class Post < ApplicationRecord
end
Vedi ad esempio questo post sul blog .
Solo per aggiungere a questo argomento, ho trascorso un po 'di tempo a capire come testare tali estensioni (sono andato giù per il ActiveSupport::Concern
percorso.)
Ecco come ho impostato un modello per testare le mie estensioni.
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
Con Rails 5, tutti i modelli sono ereditati da ApplicationRecord e consentono di includere o estendere altre librerie di estensioni.
# 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
Supponiamo che il modulo dei metodi speciali debba essere disponibile su tutti i modelli, includerlo nel file application_record.rb. Se vogliamo applicarlo per un particolare set di modelli, includerlo nelle rispettive classi di modelli.
# 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
Se si desidera avere i metodi definiti nel modulo come metodi di classe, estendere il modulo ad ApplicationRecord.
# app/models/application_record.rb
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
extend SpecialMethods
end
Spero che aiuti gli altri!
io ho
ActiveRecord::Base.extend Foo::Bar
in un inizializzatore
Per un modulo come di seguito
module Foo
module Bar
end
end