2012-05-14 10 views
14

Piszę klejnot, który chciałbym pracować z i bez środowiska Rails.Konfigurowanie ustawień konfiguracyjnych podczas pisania gem

Mam klasy Configuration umożliwiający konfigurację gem:

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 

To może teraz być używane tak:

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

Ja jednak nie wydają się być w stanie uzyskać dostęp moje zmienne konfiguracyjne z pozostałych klas w moim klejnocie. Na przykład, kiedy skonfigurować gem w moim spec_helper.rb tak:

# spec/spec_helper.rb 
require "name_checker" 

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

i odniesienie do konfiguracji z mojego kodu:

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

pojawia się niezdefiniowany błąd metoda:

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

Co jest nie tak z moim kodem?

+1

Oto artykuł na temat konfiguracji perełki dla innych, którzy mogą być zainteresowani: http: // robotów .thoughtbot.com/mygem-configure-block – Rimian

Odpowiedz

17

Spróbuj refactoring do:

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

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

Głównym problemem jest to, że zastosowałeś zbyt dużo pośrednictwa. Dlaczego po prostu nie wykonasz tego po prostu? Można również zastąpić dwa wygenerowane czytelników tuż potem, tak aby zapewnić obecność w środowisku, które trzeba ...

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 

Teraz rzeczywista (techniczne) Problem polega na tym, że są zdefiniowaniu klasy o nazwie NetChecker i definiując je, próbujesz wydrukować wartość zwracaną wywołania api_key na założonym obiekcie Configuration (tak, że naruszasz prawo Demeter tutaj). Nie powiedzie się, ponieważ definiujesz NetChecker, zanim ktokolwiek naprawdę zdąży zdefiniować dowolną konfigurację. Tak więc, w rzeczywistości wymagasz api_key, zanim metoda configure została wywołana na NameChecker, więc ma ona nil w swoim configuration ivar.

Moja rada byłoby usunąć overengineering i spróbuj ponownie ;-)

+1

Punkt klasy "Konfiguracja" polega na umożliwieniu konfiguracji klejnotu z blokiem konfiguracji. Myślę, że jest to standardowy sposób konfigurowania klejnotów. Po prostu myślę, że to ogólnie dobry wzór. Ale tak, to, co mówisz o "problemie technicznym" ma sens, dzięki za to. Wreszcie, w mojej obronie, naruszenie Demeter było wymyślone ze względu na zwięzłość pytań! –

+0

Konfiguracja wzorca blokowego jest całkowicie opcjonalna i pochodzi z Railsów, nie jest to wzorzec, który każdy przylega. Chodzi o to, że Rails ma pewne rzeczy, które muszą się wydarzyć w bloku konfiguracyjnym, gdy wszystko jest uruchamiane, głównie po to, aby Rails nie ładował zbyt wiele kodu (dlatego robią to w specjalnym bloku). – Julik

+0

Problemem tutaj nie jest nadmierna inżynieria (lub naruszenie prawa demeter). Jest to kwestia, który kod jest wykonywany jako pierwszy. Jeśli uruchomisz cały kod OP w jednym pliku, działa dobrze. Błąd pojawia się, gdy 'NameChecker.configuration.api_key' zostaje wykonane zanim obiekt konfiguracji zostanie wypełniony. – eremzeit

Powiązane problemy