# frozen_string_literal: true class Sinclair # @api private # # Class responsible for configuring the configuration class # # @example General usage # factory = Sinclair::ConfigFactory.new # factory.add_configs(:name) # factory.configure { |c| c.name 'John' } # # config = factory.config # # config.class.superclass # returns Sinclair::Config # factory.config.equal?(config) # returns true # config.name # returns 'John' class ConfigFactory # @api private # Deprecation warning message # @see https://github.com/darthjee/sinclair/blob/master/WARNINGS.md#usage-of-custom-config-classes CONFIG_CLASS_WARNING = 'Config class is expected to be ConfigClass. ' \ "In future releases this will be enforced.\n" \ 'see more on https://github.com/darthjee/sinclair/blob/master/WARNINGS.md#usage-of-custom-config-classes' # @param config_class [Class] configuration class to be used # @param config_attributes [Array] list of possible configurations def initialize(config_class: Class.new(Config), config_attributes: []) @config_class = config_class @config_attributes = config_attributes.dup return if config_class.is_a?(ConfigClass) warn CONFIG_CLASS_WARNING end # @api public # # Returns current instance of config # # the method returns the same instance until +reset_config+ # is called # # @return [Config,Object] the instance of given # config_class. by default, this returns # +Class.new(Config).new+ # # @see #reset_config # # @example (see ConfigFactory) def config @config ||= config_class.new end # @api public # # Cleans the current config instance # # After cleaning it, {#config} will generate a new # instance # # @return [NilClass] # # @example # factory = Sinclair::ConfigFactory.new # # config = factory.config # # factory.reset_config # # factory.config == config # returns false def reset_config @config = nil end # Adds possible configurations # # It change the configuration class adding methods # and keeps track of those configurations so that # {ConfigBuilder} is able to set those values when invoked # # @return [Array] all known config attributes # @todo remove class check once only # ConfigClass are accepted # # @example Adding configuration name # factory = Sinclair::ConfigFactory.new # config = factory.config # # config.respond_to? :active # # returns false # # factory.add_configs(:active) # # config.respond_to? :active # # returns true def add_configs(*args) builder = if config_class.is_a?(Sinclair::ConfigClass) config_class.add_configs(*args) else Config::MethodsBuilder.build(config_class, *args) end config_attributes.concat(builder.config_names.map(&:to_sym)) end # @api public # # Set the values in the config # # The block given is evaluated by the {ConfigBuilder} # where each method missed will be used to set a variable # in the config # # @yield [ConfigBuilder] methods called in the block # that are not present in {ConfigBuilder} are # then set as instance variables of the config # # @return [Object] the result of the block # # @example Setting name on config # class MyConfig # extend Sinclair::ConfigClass # # attr_reader :name # end # # factory = Sinclair::ConfigFactory.new( # config_class: MyConfig, # config_attributes: [:name] # ) # # config = factory.config # # factory.configure { name 'John' } # # config.name # returns 'John' def configure(&block) config_builder.instance_eval(&block) end # Returns a new instance of ConfigFactory # # the new instance will have the same # config_attributes and for config_class a child # of the current config_class # # This method is called when initializing {Configurable} # config_factory and the superclass is also configurable # # This way, child classes from other {Configurable} classes # will have a config_class that is a child from the original # config_class # # @return [ConfigFactory] def child self.class.new( config_class: Class.new(config_class), config_attributes: config_attributes ) end private # @private attr_reader :config_class, :config_attributes # @private # # Returns a builder capable of injecting variables into config # # @return [ConfigBuilder] def config_builder ConfigBuilder.new(config, *config_attributes) end end end