2010-02-24 15 views
150

He leído algo sobre cómo extender ActiveRecord: clase base para que mis modelos tengan algunos métodos especiales. ¿Cuál es la manera más fácil de ampliarlo (tutorial paso a paso)?Raíles que extienden ActiveRecord :: Base

+0

¿Qué tipo de extensiones? Realmente necesitamos más para continuar. – jonnii

Respuesta

320

Existen varios enfoques:

Usando ActiveSupport :: La preocupación (Preferido)

leer la documentación ActiveSupport::Concern para más detalles.

Cree un archivo llamado active_record_extension.rb en el directorio lib.

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) 

crear un archivo en el directorio llamado config/initializersextensions.rb y añadir la siguiente línea al archivo:

require "active_record_extension" 

Herencia (Preferido)

Consulte Toby answer.

parches mono (se debe evitar)

crear un archivo en el directorio llamado config/initializersactive_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 expresiones regulares por Jamie Zawinski puede ser re-propuesto para ilustrar los problemas asociados con el mono-aplicación de parches.

Some people, when confronted with a problem, think “I know, I'll use monkey patching.” Now they have two problems.

Monkey patching es fácil y rápido. Pero, el tiempo y el esfuerzo guardado se extraen siempre en el futuro; con interés compuesto. En estos días, limito el parche de mono para prototipar rápidamente una solución en la consola de rieles.

+0

Hum ... para el segundo ejemplo cuando ejecuto ./scripts/console Recibo un error "' include ': TypeError: tipo de argumento incorrecto Class (Módulo esperado) ". – xpepermint

+0

@xpepermint Parece que lo comenzó con' clase MyActiveRecordExtensions' en lugar de 'módulo MyActiveRecordExtensions'. –

+0

No ... Me enojé con copiar y pegar. Extraño ... – xpepermint

7

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 :) 
+1

Supongo que el paso 2 debe colocarse en config/environment.rb. No funciona para mí :(. ¿Puedes escribir algo más de ayuda? Thx. – xpepermint

66

Puede ampliar la clase y simplemente usar la herencia.

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

class Foo < AbstractModel 
end 

class Bar < AbstractModel 
end 
+0

Me gusta esta idea porque es una forma estándar de hacerlo, pero ... Aparece un error Tabla 'moboolo_development. abstract_models 'does not exists: MOSTRAR CAMPOS DE 'abstract_models'. ¿Dónde debería ponerlo? – xpepermint

+23

Agregue 'self.abstract_class = true' a su' AbstractModel'. Rails ahora reconocerá el modelo como un modelo abstracto. –

+0

¡Guau! No pensé que esto fuera posible. Lo intenté antes y me rendí cuando ActiveRecord se atragantó buscando 'AbstractModel' en la base de datos. ¡Quién sabía que un simple setter me ayudaría a SECAR las cosas! (Estaba empezando a encogerme ... era malo). Gracias Toby y Harish! – dooleyo

19

También puede utilizar ActiveSupport::Concern y ser más central rieles idiomática 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

Entonces todos sus modelos tendrán el método foo incluido como un método de instancia y los métodos en ClassMethods incluidos como métodos de clase. P.ej.en un FooBar < ActiveRecord::Base tendrá: FooBar.bar y FooBar#foo

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

+5

Tenga en cuenta que 'InstanceMethods' está en desuso desde Rails 3.2, simplemente ponga sus métodos en el cuerpo del módulo. –

+0

Coloqué 'ActiveRecord :: Base.send (: include, MyExtension)' en un inicializador y luego funcionó para mí. Rails 4.1.9 –

4

Sólo para añadir a este tema, pasé un tiempo trabajando cómo probar tales extensiones (descendí la ruta ActiveSupport::Concern.)

Así es como configuré 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 
0

que tienen

ActiveRecord::Base.extend Foo::Bar 

en un inicializador

Para un módulo, como a continuación

module Foo 
    module Bar 
    end 
end 
14

con los carriles 4, el concepto de la utilización de las preocupaciones de modularizar y seco hasta su modelos ha estado en destacados.

Las preocupaciones básicamente le permiten agrupar 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 cualquier artículo o evento.

Tradicionalmente, los modelos pueden tener este aspecto:

Comentario Modelo:

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

Artículo 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 

Evento Modelo

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 , ahí es una pieza importante de código común para el modelo de evento y artículo. Usando las preocupaciones, podemos extraer este código común en un módulo separado Commentable.

Para esto crea un archivo commentable.rb en la aplicación/modelo/inquietudes.

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 sus modelos se parecen a esto:

Comentario Modelo:

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

Artículo Modelo:

class Article < ActiveRecord::Base 
    include Commentable 
end 

Modelo de eventos

class Event < ActiveRecord::Base  
    include Commentable 
end 

Un punto que me gustaría resaltar durante el uso de las preocupaciones es que Las preocupaciones se deben utilizar para la agrupación 'basada en dominio' en lugar de la agrupación 'técnica'. Por ejemplo, una agrupación de dominios es como 'Commentable', 'Etiquetable', etc. Una agrupación basada en tecnología será como 'FinderMethods', 'ValidationMethods'.

Aquí hay un link to a post que encontré muy útil para entender las preocupaciones en los modelos.

Esperamos que la valoración crítica ayude :)

+0

Esa es mi respuesta favorita aquí, gracias :) –

3

rieles 5 proporciona un mecanismo incorporado para extender ActiveRecord::Base.

Esto se consigue proporcionando 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 que uno:

class Post < ApplicationRecord 
end 

Véase, por ejemplo this blogpost.

0

Con Rails 5, todos los modelos se heredan de ApplicationRecord & y ofrecen una forma agradable 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 

Supongamos que el módulo de métodos especiales debe estar disponible en todos los modelos, se incluyen en el archivo application_record.rb. Si queremos aplicar esto para un conjunto particular de modelos, luego incluirlo en las respectivas clases 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 

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 los demás!

Cuestiones relacionadas