2012-05-14 8 views
14

Sto scrivendo una gemma con cui vorrei lavorare e senza l'ambiente Rails.Impostazione delle impostazioni di configurazione durante la scrittura di un gioiello

Ho una classe Configuration per consentire la configurazione della gemma:

module NameChecker 
    class Configuration 
    attr_accessor :api_key, :log_level 

    def initialize 
     self.api_key = nil 
     self.log_level = 'info' 
    end 
    end 

    class << self 
    attr_accessor :configuration 
    end 

    def self.configure 
    self.configuration ||= Configuration.new 
    yield(configuration) if block_given? 
    end 
end 

Questo può ora essere utilizzato in questo modo:

NameChecker.configure do |config| 
    config.api_key = 'dfskljkf' 
end 

Tuttavia, non sembrano essere in grado di accedere le mie variabili di configurazione da withing le altre classi nella mia gemma. Per esempio, quando configuro la gemma nel mio spec_helper.rb in questo modo:

# spec/spec_helper.rb 
require "name_checker" 

NameChecker.configure do |config| 
    config.api_key = 'dfskljkf' 
end 

e di riferimento la configurazione dal mio codice:

# lib/name_checker/net_checker.rb 
module NameChecker 
    class NetChecker 
    p NameChecker.configuration.api_key 
    end 
end 

ottengo un errore metodo non definito:

`<class:NetChecker>': undefined method `api_key' for nil:NilClass (NoMethodError) 

cosa c'è che non va nel mio codice?

+1

Ecco un articolo su come configurare le gemme per gli altri che potrebbero essere interessati: http: // robot .thoughtbot.com/mygem-configure-block – Rimian

risposta

17

Prova refactoring a:

def self.configuration 
    @configuration ||= Configuration.new 
end 

def self.configure 
    yield(configuration) if block_given? 
end 
-2

Il problema principale è che hai applicato troppa indiretta. Perché non lo fai semplicemente

module NameChecker 
    class << self 
    attr_accessor :api_key, :log_level 
    end 
end 

e si deve fare? Si potrebbe anche ignorare i due lettori generati subito dopo in modo tale da garantire la presenza dell'ambiente che è necessario ...

module NameChecker 
    class << self 
    attr_accessor :api_key, :log_level 

    def api_key 
     raise "NameChecker really needs is't api_key set to work" unless @api_key 
     @api_key 
    end 

    DEFAULT_LOG_LEVEL = 'info' 

    def log_level 
     @log_level || DEFAULT_LOG_LEVEL 
    end 

    end 
end 

Ora, l'attuale (tecnico) problema è che si sta definendo una classe denominata NetChecker e durante la definizione si sta tentando di stampare il valore di ritorno della chiamata api_key su un presunto oggetto Configuration (quindi si sta violando la legge di Demeter qui). Questo fallisce, perché stai definendo NetChecker prima che qualcuno abbia davvero avuto il tempo di definire qualsiasi configurazione. Quindi stai richiedendo il numero api_key prima che il metodo configure sia stato chiamato su NameChecker, quindi ha nil nel suo numero configuration ivar.

Il mio consiglio sarebbe quello di rimuovere l'overengineering e riprova ;-)

+1

Il punto della classe 'Configuration' è di consentire alla gem di essere configurata con un blocco di configurazione. Penso che questo sia il modo standard per configurare le gemme. Penso anche che sia un bel modello in generale. Ma sì, quello che dici riguardo al "problema (tecnico)" ha senso, quindi grazie. Infine, a mia difesa, la violazione di Demetra è stata inventata per brevità! –

+0

Il modello di blocco di configurazione è completamente opzionale e proviene da Rails, non è un modello a cui tutti aderiscono. È solo che Rails ha alcune cose che _has_ accadono all'interno di un blocco configure quando tutto viene avviato, principalmente per impedire a Rails di caricare troppo codice (è per questo che lo fanno in un blocco speciale). – Julik

+0

Il problema qui non è l'ingegneria (o la violazione della legge di demeter). È un problema su quale codice viene eseguito per primo. Se si esegue tutto il codice OP in un singolo file, funziona correttamente. L'errore si verifica quando 'NameChecker.configuration.api_key' viene eseguito prima che l'oggetto di configurazione venga popolato. – eremzeit

Problemi correlati