Вопрос по ruby, rubygems – Настройка параметров конфигурации при написании драгоценного камня

14

Я пишу гем, с которым мне хотелось бы работать в среде Rails и без нее.

У меня естьConfiguration класс, чтобы разрешить настройку драгоценного камня:

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

Теперь это можно использовать так:

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

Однако, похоже, я не могу получить доступ к своим переменным конфигурации из других классов в моем геме. Например, когда я настраиваю драгоценный камень в моемspec_helper.rb вот так

# spec/spec_helper.rb
require "name_checker"

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

и ссылка на конфигурацию из моего кода:

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

Я получаю неопределенную ошибку метода:

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

Что не так с моим кодом?

Вот статья о настройке гемов для других, кому это может быть интересно: Robots.thoughtbot.com / mygem-Configure-блок Rimian

Ваш Ответ

2   ответа
18

Попробуйте рефакторинг на:

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

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

что вы применили слишком много косвенных указаний. Почему бы тебе просто не сделать

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

и покончим с этим? Вы также можете переопределить два сгенерированных читателя сразу после этого, чтобы они обеспечили наличие среды, которая вам нужна ...

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

Теперь реальная (техническая) проблема в том, что вы определяете класс с именемNetChecker и, определяя его, вы пытаетесь напечатать возвращаемое значениеapi_key позвони на предполагаемыйConfiguration объект (так что вы нарушаете закон Деметры здесь). Это не удается, потому что вы определяетеNetChecker прежде чем кто-либо действительно успел определить какую-либо конфигурацию. Итак, вы на самом деле запрашиваетеapi_key передconfigure метод был вызван наNameChecker, так что у него естьnil в этоconfiguration ивар.

Мой совет - снять переподготовку и попробовать еще раз; -)

ТочкаConfiguration class позволяет разрешить конфигурирование гема с помощью блока конфигурации. Я думаю, что это стандартный способ настройки драгоценных камней. Я также просто думаю, что это хорошая модель в целом. Но да, то, что вы говорите о «(технической) проблеме», имеет смысл, так что спасибо за это. Наконец, в мою защиту нарушение Деметры было изобретено ради краткости вопроса! David Tuite
Шаблон конфигурации блока является необязательным и приходит от Rails, это не шаблон, которого придерживаются все. Просто у Rails есть кое-что, что Есть происходит в блоке конфигурации, когда все загружается, в первую очередь, чтобы предотвратить загрузку Rails слишком большого количества кода (именно поэтому они делают это в специальном блоке). Julik
Проблема здесь не в технике (или в нарушении закона Деметры). Вопрос в том, какой код выполняется первым. Если вы запускаете весь OP-код в одном файле, он работает нормально. Ошибка возникает, когдаNameChecker.configuration.api_key выполняется перед заполнением объекта конфигурации. eremzeit

Похожие вопросы