Come scrivere un mixin di Rails che si estenda su modello, controller e vista
-
09-06-2019 - |
Domanda
Nel tentativo di ridurre la duplicazione del codice nella mia piccola app Rails, ho lavorato per inserire il codice comune tra i miei modelli in un modulo separato, finora tutto bene.
Il materiale del modello è abbastanza semplice, devo solo includere il modulo all'inizio, ad esempio:
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
Funziona bene, ora però avrò un controller e un codice di visualizzazione che sarà comune anche tra questi modelli, finora ho questo per il mio materiale da inviare:
# 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
Fondamentalmente sto pensando di includere semplicemente Sendable::Controller e Sendable::View (o l'equivalente) per il controller e la vista, ma esiste un modo più semplice per farlo?Sto cercando un modo accurato per avere un sacco di codice comune tra il mio modello, controller e vista.
Modificare:Giusto per chiarire, questo deve essere condiviso tra 2 o 3 modelli.
Soluzione
Potresti pluginizzarlo (usa script/genera plugin).
Quindi nel tuo init.rb fai semplicemente qualcosa del tipo:
ActiveRecord::Base.send(:include, PluginName::Sendable)
ActionController::Base.send(:include, PluginName::SendableController)
E insieme a te stesso, incluso, dovrebbe funzionare perfettamente.
Dai un'occhiata ad alcuni plugin di act_*, è uno schema piuttosto comune (http://github.com/technoweenie/acts_as_paranoid/tree/master/init.rb, controlla la riga 30)
Altri suggerimenti
Se è necessario aggiungere quel codice a tutti i modelli e a tutti i controller, puoi sempre procedere come segue:
# maybe put this in environment.rb or in your module declaration
class ActiveRecord::Base
include Iso
end
# application.rb
class ApplicationController
include Iso
end
Se avessi bisogno che le funzioni di questo modulo siano disponibili per le visualizzazioni, potresti esporle individualmente con helper_method
dichiarazioni in application.rb.
Se segui il percorso del plugin, controlla Motori su rotaie, che hanno lo scopo di estendere la semantica dei plugin a Controller e View in modo chiaro.