2012-05-14 4 views
14

J'écris une gemme avec laquelle j'aimerais travailler et sans l'environnement Rails.Configuration des paramètres de configuration lors de l'écriture d'une gemme

J'ai une classe Configuration pour permettre la configuration de la gemme:

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 

Cela peut maintenant être utilisé comme ceci:

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

Cependant, je ne semble pas être en mesure d'accéder mes variables de configuration venant avec les autres classes dans ma gemme. Par exemple, quand je configure la pierre précieuse dans mon spec_helper.rb comme ceci:

# spec/spec_helper.rb 
require "name_checker" 

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

et faire référence à la configuration de mon code:

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

Je reçois une erreur de méthode non définie:

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

Quel est le problème avec mon code?

+1

Voici un article sur la configuration des pierres précieuses pour d'autres qui pourraient être intéressés: http: // robots .thoughtbot.com/mygem-configure-block – Rimian

Répondre

17

Essayez refactorisation à:

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

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

Le problème principal est que vous avez appliqué trop d'indirection. Pourquoi ne faites-vous pas simplement

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

et d'en finir? Vous pouvez également remplacer les deux lecteurs générés juste après afin qu'ils assurent la présence de l'environnement que vous avez besoin ...

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 

Maintenant, le problème réel (technique) est que vous définissez une classe appelée NetChecker et tout en le définissant, vous essayez d'imprimer la valeur de retour de l'appel api_key sur un objet supposé Configuration (donc vous enfreignez la loi de Demeter ici). Cela échoue, car vous définissez NetChecker avant que quiconque ait vraiment eu le temps de définir une configuration. Donc vous demandez en fait api_key avant que la méthode configure ait été appelée sur NameChecker, elle a donc nil dans son configuration ivar.

Mon conseil serait de supprimer le overengineering et essayez à nouveau ;-)

+1

Le point de la classe 'Configuration' est de permettre à la gemme d'être configurée avec un bloc de configuration. Je pense que c'est la façon standard de configurer les gemmes. Je pense aussi que c'est un joli modèle en général. Mais oui, ce que vous dites à propos du "problème (technique)" est logique, donc merci pour cela. Enfin, pour ma défense, la violation de Demeter a été inventée pour des raisons de brièveté de question! –

+0

Le motif de bloc configure est entièrement facultatif et provient de Rails, ce n'est pas un motif auquel tout le monde adhère. C'est juste que Rails a quelque chose qui doit arriver dans un bloc configure quand tout est démarré, principalement pour empêcher Rails de charger trop de code (c'est pourquoi ils le font dans un bloc spécial). – Julik

+0

Le problème ici n'est pas sur l'ingénierie (ou violer la loi de demeter). C'est un problème de code qui est exécuté en premier. Si vous exécutez tout le code OP dans un seul fichier, cela fonctionne très bien. Le bug arrive quand 'NameChecker.configuration.api_key' est exécuté avant que l'objet de configuration ne soit rempli. – eremzeit

Questions connexes