2010-09-11 10 views
34

Estaba leyendo acerca del desarrollo de Gems/Plugin para Rails 3 y me encontré con this post que dice que alias_method_chain ya no se usa. Puedo ver que el método todavía está allí en activesupport-3.0.0/lib/active_support/core_ext/module/aliasing.rb.Rails 3: alias_method_chain aún se usa?

¿Debería seguir usando alias_method_chain en Rails 3?

¿Sigue siendo this un reflejo de las mejores prácticas para gemas/complementos en Rails 3 que desean modificar ActiveRecord?

Respuesta

57

No, se ha reemplazado por un uso inteligente de la anulación de método en los módulos y la palabra clave super.

Básicamente, usted define la función original en un módulo incluido y lo reemplaza en otro módulo incluido. Cuando llama al super en la función de anulación, llama a la función original. Pero hay una trampa. Debe incluir los módulos extendidos después de, incluido el módulo base, y en el orden en que desea que se realice el encadenamiento.

class Something 
    module Base 
    def my_method 
     # (A) original functionality 
    end 
    end 

    module PreExtension 
    def my_method 
     # (B) before the original 
     super # calls whatever was my_method before this definition was made 
    end 
    end 

    module PostExtension 
    def my_method 
     super # calls whatever was my_method before this definition was made 
     # (C) after the original 
    end 
    end 

    include Base # this is needed to place the base methods in the inheritance stack 
    include PreExtension # this will override the original my_method 
    include PostExtension # this will override my_method defined in PreExtension 
end 

s = Something.new 
s.my_method 
#=> this is a twice extended method call that will execute code in this order: 
#=> (B) before the original 
#=> (A) the original 
#=> (C) after the original 

Ryan Bates de Railscasts habla de how this is used in the Rails Routing code. Yo recomendaría verlo, y sus otros screencasts. Tienen el poder de transformar a una abuela que hace punto en un gurú de Rails.

PD: El crédito va a Peeja para corregir un error fundamental en mi respuesta original. Gracias.

+0

¡Dulce! Gracias. He visto la mayoría de los Railscasts, pero aún no los había visto. – mikewilliamson

+1

Hack muy inteligente, mucho mejor que usar el alias incorporado del langage también ... – Oct

+5

No creo que sea correcto. Según mi comprensión y este experimento, los métodos de los módulos nunca se llaman: https://gist.github.com/664352 – Peeja

0

Veo que alias_method_chain ya no está presente en Rails 3.0.0. http://api.rubyonrails.org/ no lo informa y rails console informa que es undefined local variable or method.

Ver también - https://rails.lighthouseapp.com/projects/8994/tickets/285-alias_method_chain-limits-extensibility#ticket-285-20

ACTUALIZACIÓN: Como señaló @ecoologic en los comentarios, alias_method_chain todavía está presente en Rails 3.1.1.

+2

[¿Qué es esto?] (Https://github.com/rails/rails/blob/v3.1.1.rc1/activesupport/lib/active_support/core_ext/module/aliasing.rb) – ecoologic

+1

Respuesta actualizada. Gracias @ecoologic. –

+1

de nada, mi anterior. el comentario suena un poco grosero, lo siento, esa no era mi intención – ecoologic

19

En general, un módulo nunca puede sustituir un método en la clase está incluido en. Esto se debe a la inclusión del módulo funciona como subclases. Una superclase tampoco puede anular los métodos de las subclases , ni lo esperaría.

Cuando se incluye un módulo en una clase, el módulo se inserta justo después de la clase en la cadena de antepasados ​​de la clase. Llamando super de la clase llamará a la implementación del módulo .

class Something 
    module PreExtension; end 
    module PostExtension; end 

    include PreExtension 
    include PostExtension 
end 

Something.ancestors # => [Something, Something::PostExtension, Something::PreExtension, Object, Kernel] 

Cada vez que un método es llamado en un Something, Ruby se ve a través esta lista en orden y llama a la primera aplicación que encuentra. Si la implementación llama al super, sigue buscando y encuentra el siguiente.

Esto significa que los módulos incluyen más adelante tener prioridad sobre módulos incluidos anteriormente, y pueden llamar super para obtener implementaciones anteriores los módulos.Esto se debe a que los módulos incluidos son insertados en la cadena de antecesores directamente después de la clase. Este es cómo funciona el código de enrutamiento edgerunner mencionado. Ese código pone todo en módulos, así:

class SomethingNew 
    module Base 
    def my_method 
     puts "(A)" 
    end 
    end 

    module Extension 
    def my_method 
     puts "(B)" 
     super 
    end 
    end 

    include Base 
    include Extension 
end 

SomethingNew.new.my_method 
# Output: 
# >> (B) 
# >> (A) 

SomethingNew.ancestors # => [SomethingNew, SomethingNew::Extension, SomethingNew::Base, Object, Kernel] 

Esta es la razón por alias_method_chain existió en el primer lugar. Si poner el código base en un módulo no es una opción, no estoy seguro de cómo lograr el equivalente de alias_method_chain.

+0

esta es una gran respuesta, solo desearía haber tenido una respuesta a su última pregunta –

+0

En realidad, creo que no hay otra manera de hacerlo. Es por eso que Rails 3 usa el patrón 'module Base'. Ese fue uno de los grandes empujones de los wycats en Rails 3. Y si lo piensas, no tiene sentido que un módulo incluido (o una superclase) pueda cambiar las implementaciones de una clase base contra su voluntad. – Peeja

+0

Bueno, a veces quieres extender código que no puedes tocar. Sin 'alias_method_chain' no veo una solución para eso. – crispy