He leído un poco sobre cómo extender ActiveRecord: Clase base para que mis modelos tengan algunos métodos especiales. ¿Cuál es la manera fácil de extenderlo (tutorial paso a paso)?
He leído un poco sobre cómo extender ActiveRecord: Clase base para que mis modelos tengan algunos métodos especiales. ¿Cuál es la manera fácil de extenderlo (tutorial paso a paso)?
Respuestas:
Hay varios enfoques:
Lea la documentación de ActiveSupport :: Concern para obtener más detalles.
Cree un archivo llamado active_record_extension.rb
en el lib
directorio.
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)
Cree un archivo en el config/initializers
directorio llamado extensions.rb
y agregue la siguiente línea al archivo:
require "active_record_extension"
Consulte la respuesta de Toby .
Cree un archivo en el config/initializers
directorio llamado 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 cita sobre las expresiones regulares de Jamie Zawinski se puede cambiar de propósito para ilustrar los problemas asociados con el parcheado de monos.
Algunas personas, cuando se enfrentan a un problema, piensan "Lo sé, usaré parches de mono". Ahora ellos tienen dos problemas.
El parcheado de monos es fácil y rápido. Pero, el tiempo y el esfuerzo ahorrados siempre se extraen en algún momento en el futuro; con interés compuesto En estos días limito los parches de mono para crear rápidamente una solución prototipo en la consola de rails.
require
archivo al final de environment.rb
. He agregado este paso adicional a mi respuesta.
ImprovedActiveRecord
y heredar de ella, cuando está usando module
, está actualizando la definición de la clase en cuestión. Solía usar la herencia (causa de años de experiencia en Java / C ++). En estos días uso principalmente módulos.
Refinements
que aborda la mayoría de los problemas con parches de mono ( yehudakatz.com/2010/11/30/ruby-2-0-refinements-in-practice ). A veces hay una función que solo te obliga a tentar al destino. Y a veces lo haces.
Simplemente puede extender la clase y simplemente usar la herencia.
class AbstractModel < ActiveRecord::Base
self.abstract_class = true
end
class Foo < AbstractModel
end
class Bar < AbstractModel
end
abstract_models
. ¿Dónde debería ponerlo?
self.abstract_class = true
a tu AbstractModel
. Rails ahora reconocerá el modelo como un modelo abstracto.
AbstractModel
en la base de datos. ¡Quién sabía que un setter simple me ayudaría a SECAR las cosas! (Estaba empezando a encogerme ... era malo). Gracias Toby y Harish!
También puede usar ActiveSupport::Concern
y ser más Rails core idiomático como:
module MyExtension
extend ActiveSupport::Concern
def foo
end
module ClassMethods
def bar
end
end
end
ActiveRecord::Base.send(:include, MyExtension)
[Editar] siguiendo el comentario de @daniel
Luego, todos sus modelos tendrán el método foo
incluido como método de instancia y los métodos ClassMethods
incluidos como métodos de clase. Por ejemplo, en un FooBar < ActiveRecord::Base
tendrá: FooBar.bar
yFooBar#foo
http://api.rubyonrails.org/classes/ActiveSupport/Concern.html
InstanceMethods
está en desuso desde Rails 3.2, solo ponga sus métodos en el cuerpo del módulo.
ActiveRecord::Base.send(:include, MyExtension)
en un inicializador y luego esto funcionó para mí. Rails 4.1.9
Con Rails 4, el concepto de utilizar las preocupaciones para modularizar y SECAR sus modelos ha sido destacado.
Las preocupaciones básicamente le permiten agrupar un código similar de un modelo o en varios modelos en un solo módulo y luego usar este módulo en los modelos. Aquí hay un ejemplo:
Considere un modelo de artículo, un modelo de evento y un modelo de comentario. Un artículo o un evento tiene muchos comentarios. Un comentario pertenece a un artículo o evento.
Tradicionalmente, los modelos pueden verse así:
Modelo de comentario:
class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true
end
Modelo de artículo:
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, hay un código significativo común tanto para el Modelo de Evento como para el Artículo. Usando preocupaciones, podemos extraer este código común en un módulo separado Comentario.
Para esto, cree un archivo commentable.rb en app / model / concern
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
Y ahora tus modelos se ven así:
Modelo de comentario:
class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true
end
Modelo de artículo:
class Article < ActiveRecord::Base
include Commentable
end
Modelo de evento
class Event < ActiveRecord::Base
include Commentable
end
Un punto que me gustaría destacar al usar Preocupaciones es que las Preocupaciones deben usarse para la agrupación 'basada en el dominio' en lugar de la agrupación 'técnica'. Por ejemplo, una agrupación de dominio es como 'Commentable', 'Etiquetable', etc. Una agrupación de base técnica será como 'FinderMethods', 'ValidationMethods'.
Aquí hay un enlace a una publicación que encontré muy útil para comprender las preocupaciones en Modelos.
Espero que la redacción ayude :)
Paso 1
module FooExtension
def foo
puts "bar :)"
end
end
ActiveRecord::Base.send :include, FooExtension
Paso 2
# Require the above file in an initializer (in config/initializers)
require 'lib/foo_extension.rb'
Paso 3
There is no step 3 :)
Los rieles 5 proporcionan un mecanismo incorporado para extender ActiveRecord::Base
.
Esto se logra al proporcionar una capa adicional:
# app/models/application_record.rb
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
# put your extensions here
end
y todos los modelos heredan de ese:
class Post < ApplicationRecord
end
Ver, por ejemplo, este blog .
Solo para agregar a este tema, pasé un tiempo resolviendo cómo probar tales extensiones (seguí la ActiveSupport::Concern
ruta).
Así es como configuro un modelo para probar mis extensiones.
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, todos los modelos se heredan de ApplicationRecord y es una buena manera de incluir o ampliar otras bibliotecas de extensiones.
# 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
Suponga que el módulo de métodos especiales necesita estar disponible en todos los modelos, inclúyalo en el archivo application_record.rb. Si queremos aplicar esto para un conjunto particular de modelos, entonces inclúyalo en las clases de modelo respectivas.
# 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
Si desea tener los métodos definidos en el módulo como métodos de clase, extienda el módulo a ApplicationRecord.
# app/models/application_record.rb
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
extend SpecialMethods
end
¡Espero que ayude a otros!
yo tengo
ActiveRecord::Base.extend Foo::Bar
en un inicializador
Para un módulo como el siguiente
module Foo
module Bar
end
end