Cómo escribir un mixin de Rails que abarque el modelo, el controlador y la vista
-
09-06-2019 - |
Pregunta
En un esfuerzo por reducir la duplicación de código en mi pequeña aplicación Rails, he estado trabajando para obtener código común entre mis modelos en su propio módulo separado, hasta ahora todo bien.
El modelo es bastante fácil, sólo tengo que incluir el módulo al principio, por ejemplo:
class Iso < Sale
include Shared::TracksSerialNumberExtension
include Shared::OrderLines
extend Shared::Filtered
include Sendable::Model
validates_presence_of :customer
validates_associated :lines
owned_by :customer
def initialize( params = nil )
super
self.created_at ||= Time.now.to_date
end
def after_initialize
end
order_lines :despatched
# tracks_serial_numbers :items
sendable :customer
def created_at=( date )
write_attribute( :created_at, Chronic.parse( date ) )
end
end
Esto está funcionando bien, sin embargo, ahora voy a tener un código de controlador y de vista que también será común entre estos modelos, hasta ahora tengo esto para mis cosas que se pueden enviar:
# This is a module that is used for pages/forms that are can be "sent"
# either via fax, email, or printed.
module Sendable
module Model
def self.included( klass )
klass.extend ClassMethods
end
module ClassMethods
def sendable( class_to_send_to )
attr_accessor :fax_number,
:email_address,
:to_be_faxed,
:to_be_emailed,
:to_be_printed
@_class_sending_to ||= class_to_send_to
include InstanceMethods
end
def class_sending_to
@_class_sending_to
end
end # ClassMethods
module InstanceMethods
def after_initialize( )
super
self.to_be_faxed = false
self.to_be_emailed = false
self.to_be_printed = false
target_class = self.send( self.class.class_sending_to )
if !target_class.nil?
self.fax_number = target_class.send( :fax_number )
self.email_address = target_class.send( :email_address )
end
end
end
end # Module Model
end # Module Sendable
Básicamente, estoy planeando simplemente incluir Sendable::Controller y Sendable::View (o el equivalente) para el controlador y la vista, pero, ¿hay una manera más limpia de hacer esto?Busco una manera clara de tener un montón de código común entre mi modelo, controlador y vista.
Editar:Solo para aclarar, esto solo debe compartirse entre 2 o 3 modelos.
Solución
Puede complementarlo (usar script/generar complemento).
Luego en tu init.rb simplemente haz algo como:
ActiveRecord::Base.send(:include, PluginName::Sendable)
ActionController::Base.send(:include, PluginName::SendableController)
Y junto con usted mismo, incluido, eso debería funcionar bien.
Echa un vistazo a algunos de los complementos acts_*, es un patrón bastante común (http://github.com/technoweenie/acts_as_paranoid/tree/master/init.rb, marque la línea 30)
Otros consejos
Si es necesario agregar ese código a todos los modelos y todos los controladores, siempre puede hacer lo siguiente:
# maybe put this in environment.rb or in your module declaration
class ActiveRecord::Base
include Iso
end
# application.rb
class ApplicationController
include Iso
end
Si necesitara funciones de este módulo disponibles para las vistas, podría exponerlas individualmente con helper_method
declaraciones en application.rb.
Si sigue la ruta del complemento, consulte Motores-rieles, que están destinados a extender la semántica de los complementos a Controladores y Vistas de una manera clara.