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.rbnella libdirectory.
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/initializersdirectory chiamata extensions.rbe aggiungere la seguente riga al file:
require "active_record_extension"
Fai riferimento alla risposta di Toby .
Crea un file nella config/initializersdirectory 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.
requireil file alla fine di environment.rb. Ho aggiunto questo ulteriore passaggio alla mia risposta.
ImprovedActiveRecorded 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.
Refinementsche 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 = trueal tuo AbstractModel. Rails ora riconoscerà il modello come modello astratto.
AbstractModelil 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::Concerned 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 foocome metodo di istanza e i metodi ClassMethodsinclusi come metodi di classe. Ad esempio su un FooBar < ActiveRecord::Baseavrai: FooBar.bareFooBar#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::Concernpercorso.)
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