orientado objetos ruby-on-rails ruby

ruby-on-rails - objetos - modulo ruby



Ruby mixins y llamar a los súper métodos (5)

Ok, he estado refacturando mi código en mi pequeña aplicación Rails en un esfuerzo por eliminar la duplicación y, en general, hacer mi vida más fácil (porque me gusta la vida fácil). Parte de esta refactorización ha sido mover el código que es común a dos de mis modelos a un módulo que puedo incluir donde lo necesito.

Hasta aquí todo bien. Parece que va a funcionar, pero acabo de encontrar un problema que no estoy seguro de cómo moverme. El módulo (que llamé sendable), simplemente va a ser el código que maneja el envío de faxes, el envío de correos electrónicos o la impresión de un PDF del documento. Entonces, por ejemplo, tengo una orden de compra, y tengo Órdenes de Ventas Internas (imaginativamente abreviadas a ISO).

El problema al que me he enfrentado es que quiero inicializar algunas variables (inicializadas para personas que no deletrean correctamente: P) después de cargar el objeto, así que he estado usando el gancho after_initialize . No hay problema ... hasta que empiece a agregar más mixins.

El problema que tengo es que puedo tener un after_initialize en cualquiera de mis mixins, así que tengo que incluir una súper llamada al comienzo para asegurarme de que se after_initialize las otras llamadas de mixin after_initialize . Lo cual es genial, hasta que termino llamando súper y me sale un error porque no hay super para llamar.

Aquí hay un pequeño ejemplo, en caso de que no haya sido lo suficientemente confuso:

class Iso < ActiveRecord::Base include Shared::TracksSerialNumberExtension include Shared::OrderLines extend Shared::Filtered include Sendable::Model validates_presence_of :customer validates_associated :lines owned_by :customer order_lines :despatched # Mixin tracks_serial_numbers :items # Mixin sendable :customer # Mixin attr_accessor :address def initialize( params = nil ) super self.created_at ||= Time.now.to_date end end

Entonces, si cada uno de los mixins tiene una llamada after_initialize, con una súper llamada, ¿cómo puedo evitar que la última súper llamada genere el error? ¿Cómo puedo probar que el súper método existe antes de que lo llame?


En lugar de verificar si existe el método súper, puedes simplemente definirlo

class ActiveRecord::Base def after_initialize end end

Esto funciona en mis pruebas, y no debería romper ninguno de sus códigos existentes, porque todas las otras clases que lo definen solo anularán silenciosamente este método de todos modos


¿Has probado alias_method_chain ? Básicamente puede encadenar todas sus llamadas after_initialize . Actúa como un decorador: cada método nuevo agrega una nueva capa de funcionalidad y pasa el control al método "anulado" para hacer el resto.


La clase incluida (lo que hereda de ActiveRecord::Base , que en este caso es Iso ) podría definir su propio after_initialize , por lo que cualquier solución que no sea alias_method_chain (u otro aliasing que alias_method_chain el original) corre el riesgo de sobreescribir el código. La solución de @Orion Edwards es lo mejor que se me ocurre. Hay otros, pero son mucho más hackish.

alias_method_chain también tiene la ventaja de crear versiones nombradas del método after_initialize, lo que significa que puede personalizar el orden de las llamadas en aquellos casos excepcionales en los que sea importante. De lo contrario, estás a merced del orden en que la clase incluye los mixins.

más tarde :

He publicado una pregunta en la lista de correo ruby-on-rails-core sobre la creación de implementaciones vacías predeterminadas de todas las devoluciones de llamadas. El proceso de guardado los verifica de todos modos, así que no veo por qué no deberían estar allí. El único inconveniente es crear cuadros de pila vacíos adicionales, pero eso es bastante barato en cada implementación conocida.


Puedes lanzar un condicional rápido allí:

super if respond_to?(''super'')

y deberías estar bien, sin agregar métodos inútiles; bonito y limpio.


Puedes usar esto:

super if defined?(super)

Aquí hay un ejemplo:

class A end class B < A def t super if defined?(super) puts "Hi from B" end end B.new.t