Rails che estende ActiveRecord :: Base

Ho fatto alcune letture su come estendere ActiveRecord: class Base in modo che i miei modelli avessero alcuni metodi speciali. Qual è il modo semplice per estenderlo (tutorial passo passo)?

Ci sono diversi approcci:

Uso di ActiveSupport :: Concern (preferito)

Leggi la documentazione di ActiveSupport :: Concern per maggiori dettagli.

Crea un file chiamato active_record_extension.rb nella directory lib .

 module ActiveRecordExtension extend ActiveSupport::Concern # add your instance methods here def foo "foo" end # add your static(class) methods here class_methods do #Eg: Order.top_ten def top_ten limit(10) end end end # include the extension ActiveRecord::Base.send(:include, ActiveRecordExtension) 

Creare un file nella directory config/initializers chiamata extensions.rb e aggiungere la seguente riga al file:

 require "active_record_extension" 

Eredità (preferita)

Fare riferimento alla risposta di Toby.

Patch di scimmia (dovrebbe essere evitato)

Creare un file nella directory config/initializers chiamato active_record_monkey_patch.rb .

 class ActiveRecord::Base #instance method, Eg: Order.new.foo def foo "foo" end #class method, Eg: 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 alle patch delle scimmie.

Alcune persone, quando si trovano di fronte a un problema, pensano “Lo so, userò le patch per scimmia”. Ora hanno due problemi.

La patch delle scimmie è facile e veloce. Ma il tempo e lo sforzo risparmiati vengono sempre estratti in un secondo momento nel futuro; con interesse composto. In questi giorni ho limitato le patch della scimmia per prototipare rapidamente una soluzione nella console di rails.

Puoi semplicemente estendere la class e usare semplicemente l’ereditarietà.

 class AbstractModel < ActiveRecord::Base self.abstract_class = true end class Foo < AbstractModel end class Bar < AbstractModel end 

Puoi anche utilizzare ActiveSupport::Concern ed essere più idiomatico 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 modelli avranno il metodo foo incluso come metodo di istanza e i metodi in ClassMethods inclusi come metodi di class. Ad esempio su FooBar < ActiveRecord::Base avrai: FooBar.bar e FooBar#foo

http://api.rubyonrails.org/classs/ActiveSupport/Concern.html

Con Rails 4, il concetto di usare le preoccupazioni per modulare e asciugare i tuoi modelli è stato in evidenza.

Le preoccupazioni in sostanza consentono di raggruppare un codice simile di un modello o di più modelli in un singolo modulo e quindi utilizzare questo modulo nei modelli. Ecco un esempio:

Considerare un modello di articolo, un modello di evento e un modello di commento. Un articolo o un evento ha molti commenti. Un commento appartiene a un articolo o evento.

Tradizionalmente, i modelli possono assomigliare a questo:

Modello di commento:

 class Comment < ActiveRecord::Base belongs_to :commentable, polymorphic: true end 

Modello di 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, c'è una parte significativa del codice comune sia al modello di evento che al modello di articolo. Usando le preoccupazioni possiamo estrarre questo codice comune in un modulo separato.

Per questo crea un file commentable.rb 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 

E ora i tuoi modelli assomigliano a questo:

Modello di commento:

  class Comment < ActiveRecord::Base belongs_to :commentable, polymorphic: true end 

Modello di articolo:

 class Article < ActiveRecord::Base include Commentable end 

Modello di evento

 class Event < ActiveRecord::Base include Commentable end 

Un punto che vorrei sottolineare durante l'uso di Concerns è che le preoccupazioni dovrebbero essere utilizzate per il raggruppamento basato sul dominio piuttosto che sul raggruppamento "tecnico". Ad esempio, un raggruppamento di domini è simile a "Commentabile", "Taggabile" ecc. Un raggruppamento basato su tecniche sarà simile a "FinderMethods", "ValidationMethods".

Ecco un link a un post che ho trovato molto utile per comprendere le preoccupazioni nei modelli.

Spero che l'articolo 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 :) 

Solo per aggiungere a questo argomento, ho passato un po ‘di tempo a capire come testare tali estensioni (sono andato lungo il percorso ActiveSupport::Concern .)

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 

Rails 5 fornisce un meccanismo integrato per l’estensione di ActiveRecord::Base .

Ciò si ottiene fornendo un ulteriore livello:

 # 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 blogpost .

Con Rails 5, tutti i modelli sono ereditati da ApplicationRecord e offre un buon modo per 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, includendolo nel file application_record.rb. Se vogliamo applicare questo per un particolare set di modelli, includerlo nelle rispettive classi del modello.

 # 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 che i metodi definiti nel modulo siano come metodi di class, estendere il modulo a 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 sotto

 module Foo module Bar end end