require 'yaml' require 'devinstall/deep_symbolize' require 'singleton' require 'pp' class Hash include DeepSymbolizable end module Devinstall class KeyNotDefinedError < RuntimeError end class UnknownKeyError < RuntimeError end class Settings include Singleton FILES = [] SETTINGS = {} MDEFS = { local: [:folder, :temp], build: [:folder, :command, :provider, :type, :arch, :target], install: [:folder, :command, :provider, :type, :arch], tests: [:folder, :command, :provider], repos: [:folder, :provider, :type, :arch], defaults: [:type, :env] } PROVIDERS = {} class Action include Enumerable def initialize(m, pkg, type, env) @method, @pkg, @type, @env = (m.to_sym rescue m), (pkg.to_sym rescue pkg), (type.to_sym rescue type), (env.to_sym rescue env) end def has_key?(key) Settings.instance.send(@method, key, pkg: @pkg, type: @type, env: @env) rescue false end def [](key) Settings.instance.send(@method, key, pkg: @pkg, type: @type, env: @env) end def each config=Settings.instance Settings::MDEFS[@method].each do |key| yield(key, config.send(@method, key, pkg: @pkg, type: @type, env: @env)) if block_given? end end end ## Class Action def load! (filename) if FILES.include? filename true else FILES << filename data = YAML::load_file(filename).deep_symbolize merger = proc do |_, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : v2 end SETTINGS.merge! data, &merger end if File.exist?(File.expand_path(filename)) end def method_missing (method, *args) raise UnknownKeyError, "Undefined section '#{method}'" unless method_defined? method key = (args.shift or {}) rest = (Hash === key) ? key : (args.shift or {}) pkg = rest[:pkg] if pkg.nil? raise UnknownKeyError, "Unknown key #{key}" unless key_defined? method, key return SETTINGS[method][key] rescue raise "#{method}: Package must be defined" end type = rest[:type] || defaults(:type) env = rest[:env] || defaults(:env) return Action.new(method, pkg, type, env) if Hash === key raise UnknownKeyError, "Unknown key #{key}" unless key_defined? method, key global_or_local(method, key, pkg, type, env) or raise KeyNotDefinedError, "Undefined key '#{method}:#{key}' or alternate for ['#{pkg}' '#{type}' '#{env}']" end def respond_to_missing?(method, _) method_defined? method end def register_provider(provider, methods) PROVIDERS[provider]=methods end def unregister_provider(provider) PROVIDERS.delete(provider) end private def key_chain(*keys) res=SETTINGS keys.each do |key| next if key.nil? return nil unless res.has_key? key.to_sym res=res[key.to_sym] end res end def global_or_local(section, key, pkg, type, env) key_chain(:packages, pkg, type, section, env, key) || key_chain(:packages, pkg, type, section, key) || key_chain(section, env, key) || key_chain(section, key) end def key_defined?(method, key) method, key = (method.to_sym rescue method), (key.to_sym rescue key) method_defined? method and (MDEFS[method].include? key rescue false) or PROVIDERS.inject(false) { |res, (_, v)| res or (v[method].include? key rescue false) } end def method_defined?(method) method = (method.to_sym rescue method) (MDEFS.has_key?(method) or PROVIDERS.inject(false) { |res, (k, _)| res or PROVIDERS[k].has_key? method }) and SETTINGS.has_key? method end end end