Rails estendendo o ActiveRecord :: Base

Fiz algumas leituras sobre como estender a class ActiveRecord: Base para que meus modelos tivessem alguns methods especiais. Qual é a maneira fácil de estendê-lo (tutorial passo a passo)?

Existem várias abordagens:

Usando o ActiveSupport :: Concern (Preferred)

Leia a documentação do ActiveSupport :: Concern para mais detalhes.

Crie um arquivo chamado active_record_extension.rb no diretório 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) 

Crie um arquivo no diretório config/initializers chamado extensions.rb e inclua a seguinte linha no arquivo:

 require "active_record_extension" 

Herança (preferida)

Consulte a resposta de Toby.

Patch de Macaco (deve ser evitado)

Crie um arquivo no diretório config/initializers chamado 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 

A famosa citação sobre Expressões regulares de Jamie Zawinski pode ser redirecionada para ilustrar os problemas associados ao uso de macacos.

Algumas pessoas, quando confrontadas com um problema, pensam “eu sei, vou usar o remendo do macaco”. Agora elas têm dois problemas.

Macaco de patch é fácil e rápido. Mas o tempo e o esforço salvos são sempre extraídos em algum momento no futuro; com juros compostos. Atualmente limito o patch de macacos para prototipar rapidamente uma solução no console de rails.

Você pode apenas estender a class e simplesmente usar inheritance.

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

Você também pode usar o ActiveSupport::Concern e ser mais idiomático do núcleo do Rails, como:

 module MyExtension extend ActiveSupport::Concern def foo end module ClassMethods def bar end end end ActiveRecord::Base.send(:include, MyExtension) 

[Editar] seguindo o comentário de @daniel

Em seguida, todos os seus modelos terão o método foo incluído como um método de instância e os methods em ClassMethods incluídos como methods de class. Por exemplo, em um FooBar < ActiveRecord::Base você terá: FooBar.bar e FooBar#foo

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

Com o Rails 4, o conceito de usar preocupações para modularizar e secar seus modelos tem sido destaque.

Preocupações basicamente permitem que você agrupe código semelhante de um modelo ou entre vários modelos em um único módulo e, em seguida, use este módulo nos modelos. Aqui está um exemplo:

Considere um modelo de artigo, um modelo de evento e um modelo de comentário. Um artigo ou um evento tem muitos comentários. Um comentário pertence a um artigo ou evento.

Tradicionalmente, os modelos podem ser assim:

Modelo de comentário:

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

Modelo do artigo:

 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 

Modelo de 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 

Como podemos notar, existe um código significativo comum ao Modelo de Evento e Artigo. Usando preocupações podemos extrair este código comum em um módulo separado Comentável.

Para isso, crie um arquivo commentable.rb em 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 agora seus modelos são assim:

Modelo de comentário:

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

Modelo do artigo:

 class Article < ActiveRecord::Base include Commentable end 

Modelo de evento

 class Event < ActiveRecord::Base include Commentable end 

Um ponto que gostaria de destacar ao usar o Concerns é que o Concerns deve ser usado para agrupamento baseado em domínio em vez de agrupamento técnico. Por exemplo, um agrupamento de domínio é como 'Comentável', 'Marcável' etc. Um agrupamento técnico será como 'Métodos do Finder', 'Métodos de Validação'.

Aqui está um link para uma postagem que achei muito útil para entender as preocupações nos Modelos.

Espero que o writeup ajude 🙂

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' 

etapa 3

 There is no step 3 :) 

Apenas para adicionar a este tópico, eu passei um tempo trabalhando como testar essas extensões (eu ActiveSupport::Concern rota ActiveSupport::Concern .)

Veja como eu configurei um modelo para testar minhas extensões.

 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 

O Rails 5 fornece um mecanismo ActiveRecord::Base para estender o ActiveRecord::Base .

Isto é conseguido através do fornecimento de camada adicional:

 # app/models/application_record.rb class ApplicationRecord < ActiveRecord::Base self.abstract_class = true # put your extensions here end 

e todos os modelos herdam desse:

 class Post < ApplicationRecord end 

Veja por exemplo este blogpost .

Com o Rails 5, todos os modelos são herdados do ApplicationRecord e oferecem uma boa maneira de include ou estender outras bibliotecas de extensão.

 # 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 

Suponha que o módulo de methods especiais precise estar disponível em todos os modelos, inclua-o no arquivo application_record.rb. Se quisermos aplicar isso a um conjunto particular de modelos, inclua-o nas respectivas classs de modelo.

 # 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 você deseja ter os methods definidos no módulo como methods de class, estenda o módulo para ApplicationRecord.

 # app/models/application_record.rb class ApplicationRecord < ActiveRecord::Base self.abstract_class = true extend SpecialMethods end 

Espero que ajude os outros!

eu tenho

 ActiveRecord::Base.extend Foo::Bar 

em um inicializador

Para um módulo como abaixo

 module Foo module Bar end end