2013-01-13 13 views
9

Sono in procinto di scrivere una preoccupazione Importabile per il mio progetto di rotaie. Questa preoccupazione fornirà un modo generico per importare un file CSV in qualsiasi modello che includa Importabile.Come si aggiunge un'opzione di configurazione specifica del modello a un problema di rotaie?

Ho bisogno di un modo per ciascun modello per specificare quale campo deve essere utilizzato dal codice di importazione per trovare i record esistenti. Esistono modi consigliati per aggiungere questo tipo di configurazione a un problema?

risposta

9

Piuttosto che includere la preoccupazione in ogni modello, io suggerirei di creare un ActiveRecord modulo e estendo ActiveRecord::Base con esso, e quindi aggiungere un metodo che modulo (diciamo include_importable) che fa il compreso. È quindi possibile passare il nome del campo come argomento a tale metodo e nel metodo definire una variabile di istanza e un accessorio (ad esempio, ad esempio importable_field) per salvare il nome del campo come riferimento nei metodi di classe e istanza Importable.

Quindi qualcosa di simile:

module Importable 
    extend ActiveSupport::Concern 

    module ActiveRecord 
    def include_importable(field_name) 

     # create a reader on the class to access the field name 
     class << self; attr_reader :importable_field; end 
     @importable_field = field_name.to_s 

     include Importable 

     # do any other setup 
    end 
    end 

    module ClassMethods 
    # reference field name as self.importable_field 
    end 

    module InstanceMethods 
    # reference field name as self.class.importable_field 
    end 

end 

Sarai quindi necessario estendere ActiveRecord con questo modulo, dicono mettendo questa linea in un inizializzatore (config/initializers/active_record.rb):

ActiveRecord::Base.extend(Importable::ActiveRecord) 

(Se il preoccupazione è nel tuo config.autoload_paths quindi non dovrebbe essere necessario richiedere qui, vedere i commenti qui sotto.)

Quindi nei tuoi modelli, yo u dovrebbe includere Importable come questo:

class MyModel 
    include_importable 'some_field' 
end 

E il lettore imported_field restituirà il nome del campo:

MyModel.imported_field 
#=> 'some_field' 

Nella tua InstanceMethods, è possibile impostare il valore del campo importato nel vostro caso metodi passando il nome del campo per write_attribute, e di ottenere il valore utilizzando read_attribute:

m = MyModel.new 
m.write_attribute(m.class.imported_field, "some value") 
m.some_field 
#=> "some value" 
m.read_attribute(m.class.importable_field) 
#=> "some value" 

Spero che questo aiuti. Questa è solo la mia opinione personale su questo, però, ci sono altri modi per farlo (e sarei interessato a sentir parlare anche di loro).

+0

Grande risposta, grazie. Tutto ha un senso. Proverò ciò che hai suggerito domani e assegnerò la soluzione dopo. – Col

+0

Funziona tutto ma ho un piccolo problema. Ottengo il seguente errore a meno che non richieda espressamente "preoccupazioni/importazione" nella parte superiore di ciascun file di modello. NameError: undefined variabile locale o metodo 'include_importable' per # Ho aggiunto la directory preoccupazioni al autoload_path in questo modo: config.autoload_paths + = Dir [" # {} config.root/app/models/**/"] Non è un grosso problema, ma preferirei non doverlo richiedere ovunque. Eventuali suggerimenti? – Col

+2

Il problema è la riga 'ActiveRecord :: Base.extend (Importabile :: ActiveRecord)', che deve essere chiamata * prima * i tuoi modelli sono caricati in modo che ActiveRecord abbia il metodo 'includes_importable'. L'autocaricamento non funziona perché il nome reale del modulo "Importabile" non è menzionato nel codice del modello. Ci sono alcuni modi per risolvere il problema, immagino che il modo più semplice sia creare un inizializzatore in 'config/initializers/active_record.rb' e spostare la riga' extend' in là, con un 'require 'preoccupazioni/importable'' a la cima. Quindi non dovrai richiederlo nei tuoi modelli. –

9

Una soluzione leggermente più "vanilla-look", lo facciamo (per coincidenza, per l'esatto problema di importazione di csv) per evitare la necessità di passare argomenti alla Concern. Sono sicuro che ci sono pro e contro nel metodo astratto che genera errori, ma mantiene tutto il codice nella cartella dell'app e nei modelli in cui prevedi di trovarlo.

Nel modulo "preoccupazione", solo le basi:

module CsvImportable 
    extend ActiveSupport::Concern 

    # concern methods, perhaps one that calls 
    # some_method_that_differs_by_target_class() ... 

    def some_method_that_differs_by_target_class() 
    raise 'you must implement this in the target class' 
    end 

end 

E nel modello avere la preoccupazione:

class Exemption < ActiveRecord::Base 
    include CsvImportable 

    # ... 

private 
    def some_method_that_differs_by_target_class 
    # real implementation here 
    end 
end 
+0

Mi piace di più questa variante, soprattutto se la preoccupazione è specifica per le app (non in una gemma) e non è inclusa in molti altri modelli. – srecnig

Problemi correlati