首页 > 解决方案 > 跨 GEMS 的 Ruby 灵活配置

问题描述

如果 Ruby 应用程序包含附加但可选的子 GEMS,您将如何构建可以自动扩展的单个 GEM 配置。

我正在寻找可以为我正在构建的应用程序提供可扩展配置系统的 Code Pattern 或现有 GEM。

设想

假设您有一个可以发送/接收消息的通信应用程序,它使用一些通用配置。

gem 'communication-app'

您可以安装任意数量的可以通过不同平台发送/接收的提供程序。

gem 'communication-app-facebook'
gem 'communication-app-twitter'
gem 'communication-app-sms'
gem 'communication-app-email'

每个提供者都需要自己的自定义配置。这些应该挂起主配置,并且可以有一个自定义配置图,但只有在安装插件时才可用。

默认配置用法

CommsApp.configure do |config|
  config.some_setting2 = 'customised setting'
end

puts CommsApp.configuration.some_setting1
puts CommsApp.configuration.some_setting2
# --> some default value 1
# --> customised setting

示例代码

module CommsApp
  class << self
    attr_writer :configuration

    def configuration
      @configuration ||= Configuration.new
    end

    def configure
      yield(configuration)
    end
  end

  class Configuration < BaseConfiguration
    attr_accessor :some_setting1
    attr_accessor :some_setting2

    def initialize
      super
      @some_setting1 = 'some default value 1'
      @some_setting2 = 'some default value 2'
    end
  end
end

如果我尝试访问未安装的子 GEM 的配置,我预计会看到某种错误。例如。

puts CommsApp.configuration.twitter.access_token

# raises NoMethodError, "unknown method :twitter"

我已经在基本配置上试验了一个类方法,它允许我为新的配置类附加子配置属性。

module CommsApp
  class BaseConfiguration
    class << self
      # Attach a child configuration with it's own settings to a parent configuration
      #
      # @param [Class] klass_child what class would you like as the child
      # @param [Class] klass_parent what class would you like to extend with a new child configuration
      # @param [Symbol] accessor_name what is the name of the accessor that you are adding
      def attach_to(klass_child, klass_parent, accessor_name)
        # Create a memoized getter to an instance of the attaching class (:klass_child)
        #
        # def third_party
        #   @third_party ||= ThirdPartyGem::Configuration.new
        # end
        klass_parent.send(:define_method, accessor_name) do
          return instance_variable_get("@#{accessor_name}") if instance_variable_defined?("@#{accessor_name}")

          instance_variable_set("@#{accessor_name}", klass_child.new)
        end
      end
    end
  end
end

扩展配置使用

这就是可以使用扩展配置的方式。

class ApplicationConfig < CommsApp::Configuration
  attach_to(CommsApp::TwitterProvider::Configuration, self, :twitter)
  attach_to(CommsApp::FacebookProvider::Configuration, self, :fb)
end

CommsApp.configuration = ApplicationConfig.new
CommsApp.configure do |config|
  config.some_setting2 = 'customized setting'

  config.twitter.access_token = '123'

  config.fb.user_name = 'xmen'
  config.fb.account_id = 'xmen123'
end

这种技术工作正常,但它确实需要创建和配置一个自定义类,我希望这在(且仅当)提供者 GEM 存在时自动发生。

我知道我可以弄清楚如何做到这一点,但我想知道是否已经有一种模式或 GEM 可以解决这个特定问题。

标签: rubyrubygems

解决方案


推荐阅读