Eu li algumas coisas sobre como estender a classe ActiveRecord: Base para que meus modelos tenham alguns métodos especiais. Qual é a maneira mais fácil de estendê-lo (tutorial passo a passo)?
Eu li algumas coisas sobre como estender a classe ActiveRecord: Base para que meus modelos tenham alguns métodos especiais. Qual é a maneira mais fácil de estendê-lo (tutorial passo a passo)?
Respostas:
Existem várias abordagens:
Leia a documentação do ActiveSupport :: Concern para obter mais detalhes.
Crie um arquivo chamado active_record_extension.rb
no lib
diretório
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)
Crie um arquivo no config/initializers
diretório chamado extensions.rb
e adicione a seguinte linha ao arquivo:
require "active_record_extension"
Consulte a resposta de Toby .
Crie um arquivo no config/initializers
diretório chamado 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
A famosa citação sobre expressões regulares de Jamie Zawinski pode ser redefinida para ilustrar os problemas associados à correção de macacos.
Algumas pessoas, quando confrontadas com um problema, pensam: "Eu sei, usarei remendo de macacos". Agora eles tem dois problemas.
O patch dos macacos é fácil e rápido. Mas, o tempo e o esforço economizados são sempre extraídos de volta em algum momento no futuro; com juros compostos. Hoje em dia, limitei a aplicação de patches de macaco para prototipar rapidamente uma solução no console do rails.
require
o arquivo no final de environment.rb
. Eu adicionei esta etapa extra à minha resposta.
ImprovedActiveRecord
e herdá-lo; ao usar module
, você está atualizando a definição da classe em questão. Eu costumava usar herança (causa de anos de experiência em Java / C ++). Hoje em dia eu uso principalmente módulos.
Refinements
que aborda a maioria dos problemas relacionados à aplicação de patches de macaco ( yehudakatz.com/2010/11/30/ruby-2-0-refinements-in-practice ). Às vezes, um recurso existe apenas para obrigá-lo a tentar o destino. E às vezes você faz.
Você pode apenas estender a classe e simplesmente usar herança.
class AbstractModel < ActiveRecord::Base
self.abstract_class = true
end
class Foo < AbstractModel
end
class Bar < AbstractModel
end
abstract_models
. Onde devo colocá-lo?
self.abstract_class = true
ao seu AbstractModel
. O Rails agora reconhecerá o modelo como um modelo abstrato.
AbstractModel
no banco de dados. Quem sabia que um simples levantador me ajudaria a secar as coisas! (Eu estava começando a me encolher ... era ruim). Obrigado Toby e Harish!
Você também pode usar 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] após o comentário de @daniel
Todos os seus modelos terão o método foo
incluído como método de instância e os métodos ClassMethods
incluídos como métodos de classe. Por exemplo, em um FooBar < ActiveRecord::Base
você terá: FooBar.bar
eFooBar#foo
http://api.rubyonrails.org/classes/ActiveSupport/Concern.html
InstanceMethods
está obsoleto desde o Rails 3.2, basta colocar seus métodos no corpo do módulo.
ActiveRecord::Base.send(:include, MyExtension)
um inicializador e, em seguida, isso funcionou para mim. Rails 4.1.9
Com o Rails 4, o conceito de usar preocupações para modularizar e secar seus modelos tem sido destaque.
As preocupações basicamente permitem agrupar código semelhante de um modelo ou entre vários modelos em um único módulo e, em seguida, usar esse 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 evento tem muitos comentários. Um comentário pertence ao artigo ou evento.
Tradicionalmente, os modelos podem ser assim:
Modelo de comentário:
class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true
end
Artigo Modelo:
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 observar, há um pedaço significativo de código comum ao modelo de eventos e artigos. Usando preocupações, podemos extrair esse código comum em um módulo separado.
Para isso, crie um arquivo commentable.rb em app / model / preocupações.
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 ficam assim:
Modelo de comentário:
class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true
end
Artigo Modelo:
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 agrupamentos 'baseados em domínio' em vez de agrupamentos 'técnicos'. Por exemplo, um agrupamento de domínio é como 'Comentável', 'Taggable' etc. Um agrupamento técnico será como 'FinderMethods', 'ValidationMethods'.
Aqui está um link para um post que achei muito útil para entender as preocupações nos modelos.
Espero que a redação 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 :)
O Rails 5 fornece um mecanismo interno para extensão ActiveRecord::Base
.
Isso é conseguido fornecendo uma 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 post do blog .
Apenas para adicionar a este tópico, passei um tempo tentando descobrir como testar essas extensões (segui o ActiveSupport::Concern
caminho).
Veja como eu configuro 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
Com o Rails 5, todos os modelos são herdados do ApplicationRecord e oferecem uma boa maneira de incluir 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 métodos especiais precise estar disponível em todos os modelos, inclua-o no arquivo application_record.rb. Se quisermos aplicar isso a um conjunto específico de modelos, inclua-o nas respectivas classes 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ê quiser ter os métodos definidos no módulo como métodos de classe, 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