lib/data_provider/base.rb in data-provider-0.1.0 vs lib/data_provider/base.rb in data-provider-0.2.0

- old
+ new

@@ -1,227 +1,230 @@ require 'logger' module DataProvider - class ProviderMissingException < Exception - end - module Base def self.included(base) base.class_eval do include InstanceMethods + include ProxyMethods extend ClassMethods + extend ProxyMethods end end - module ClassMethods - # provides, when called with a hash param, will define 'simple providers' (providers - # with a simple, static value). When called without a param (or nil) it returns - # the current cumulative 'simple providers' hash - def provides simple_provides = nil - if simple_provides - @data_provider ||= {} - @data_provider[:provides] ||= {} - @data_provider[:provides].merge!(simple_provides) - return self - end - # no data given? just return existing hash - (@data_provider || {})[:provides] || {} + # both instance- class-level + module ProxyMethods + def provides *args + return dpc.provides if args.length == 0 + dpc.provides *args + return self end - # returns the requested provider as a Provider object - def get_provider(id) - args = data_provider_definitions.find{|args| args.first == id} - return args.nil? ? nil : Provider.new(*args) + def provider_identifiers *args + dpc.provider_identifiers *args end - def provider_identifiers - (provides.keys + data_provider_definitions.map(&:first)).compact.uniq + def provider *args, &block + dpc.provider *args, &block end - # adds a new provider to the class - def provider identifier, opts = {}, &block - add_provider(identifier, opts, block_given? ? block : nil) + def has_provider? *args + dpc.has_provider? *args end - # reader method for the raw data of the currently defined providers - def data_provider_definitions - ((@data_provider || {})[:provider_args] || []) + def has_providers_with_scope?(*args) + dpc.has_providers_with_scope?(*args) end - # returns wether a provider with the given identifier is available - def has_provider?(identifier) - (provides[identifier] || get_provider(identifier)) != nil + def fallback_provider? + dpc.fallback_provider? end - def has_providers_with_scope?(args) - scope = args.is_a?(Array) ? args : [args] - provider_identifiers.find{|id| id.is_a?(Array) && id.length > scope.length && id[0..(scope.length-1)] == scope} != nil + def provider_missing *args, &block + dpc.provider_missing *args, &block end - def fallback_provider? - !fallback_provider.nil? + def take(id, opts = {}) + dpc.take(id, opts.merge(:scope => self)) end - # adds all the providers defined in the given module to this class - def add(providers_module) - data = providers_module.instance_variable_get('@data_provider') || {} + def try_take(id, opts = {}) + dpc.try_take(id, opts.merge(:scope => self)) + end - (data[:provider_args] || []).each do |definition| - add_provider(*definition) - end - - self.provides(data[:provides] || {}) + def got?(*args) + dpc.got?(*args) end - # adds all the providers defined in the given module to this class, - # but turns their identifiers into array and prefixes the array with the :scope option - def add_scoped(providers_module, _options = {}) - data = providers_module.instance_variable_get('@data_provider') || {} + alias :has_data? :got? - (data[:provider_args] || []).each do |definition| - definition[0] = [definition[0]].flatten - definition[0] = [_options[:scope]].flatten.compact + definition[0] if _options[:scope] - add_provider(*definition) - end - - (data[:provides] || {}).each_pair do |key, value| - provides(([_options[:scope]].flatten.compact + [key].flatten.compact) => value) - end + def given *args + dpc.given *args end - def provider_missing &block - raise "DataProvider::Base#provider_missing expects a block as an argument" if !block_given? - @data_provider ||= {} - @data_provider[:provider_missing] = block - end + alias :get_data :given - def fallback_provider - block = (@data_provider || {})[:provider_missing] - block.nil? ? nil : Provider.new(nil, nil, block) + def give! *args + dpc.give! *args + return self end + alias :add_scope! :give! + alias :add_data! :give! + private - def add_provider(identifier, opts = {}, block = nil) - @data_provider ||= {} - @data_provider[:provider_args] ||= [] - @data_provider[:provider_args].unshift [identifier, opts, block] + def missing_provider *args + dpc.missing_provider *args end - end # module ClassMethods + def scoped_take *args + dpc.scoped_take *args + end - module InstanceMethods + def scope *args + dpc.scope *args + end - attr_reader :data - attr_reader :options - - def initialize(opts = {}) - @options = opts.is_a?(Hash) ? opts : {} - @data = options[:data].is_a?(Hash) ? options[:data] : {} + def scopes *args + dpc.scopes *args end - def logger - @logger ||= options[:logger] || Logger.new(STDOUT) + def provider_stack *args + dpc.provider_stack *args end - def has_provider?(id) - self.class.has_provider?(id) + def provider_id *args + dpc.provider_id *args end + end - def has_providers_with_scope?(scope) - self.class.has_providers_with_scope?(scope) + module ClassMethods + def data_provider_container + @data_provider_container ||= DataProvider::Container.new end - def fallback_provider? - self.class.fallback_provider? + alias :dpc :data_provider_container + + # can't copy self on a class-level + def give *args + dpc.give! *args + return self end - def take(id) - # first try the simple providers - if self.class.provides.has_key?(id) - provider = self.class.provides[id] - return provider.is_a?(Proc) ? provider.call : provider + # alias :give :give! + alias :add_scope :give + alias :add_data :give + + def add! _module + if _module.is_a?(DataProvider::Container) + dpc.add!(_module) + else + dpc.add!(_module.dpc) end - # try to get a provider object - provider = self.class.get_provider(id) - if provider - @scope ||= [] - @scope << (id.is_a?(Array) ? id[0..-2] : []) - result = instance_eval(&provider.block) - @scope.pop - # execute provider object's block within the scope of self - return result - end - # couldn't find requested provider, let's see if there's a fallback - if provider = self.class.fallback_provider - # temporarily set the @missing_provider instance variable, so the - # fallback provider can use it through the missing_provider private method - @missing_provider = id - @scope ||= [] - @scope << (id.is_a?(Array) ? id[0..-2] : []) - result = instance_eval(&provider.block) # provider.block.call # with the block.call method the provider can't access private methods like missing_provider - @scope = nil - return result + include _module + return self + end + + def add_scoped! _module, options = {} + if _module.is_a?(DataProvider::Container) + dpc.add_scoped!(_module, options) + else + dpc.add_scoped! _module.dpc, options end - # no fallback either? Time for an error - raise ProviderMissingException.new(:message=>"Tried to take data from missing provider.", :provider_id => id) + + include _module + return self end - def try_take(id, opts = {}) - return take(id) if self.has_provider?(id) || self.fallback_provider? - if opts[:fallback] == true + # classes/modules can't be cloned, so add behave just like add! + alias :add :add! + alias :add_scoped :add_scoped! + end # module ClassMethods - logger.debug "Try for missing provider: #{id.inspect}" - return nil + + module InstanceMethods + + attr_reader :options + + def initialize(opts = {}) + @options = opts.is_a?(Hash) ? opts : {} + dpc.give!(options[:data]) if options[:data].is_a?(Hash) + end + + def logger + @logger ||= options[:logger] || Logger.new(STDOUT).tap do |lger| + lger.level = Logger::WARN end end - private - - def scoped_take(id) - take(((@scope || []).last || []) + [id].flatten) + def data_provider_container + @data_provider_container ||= options[:dpc] || DataProvider::Container.new.tap do |c| + # automatically adopt all class-level providers/provides/data + c.add! self.class.dpc + end end - public + alias :dpc :data_provider_container - def given(param_name) - return data[param_name] if data.has_key?(param_name) - logger.error "Data provider expected missing data with identifier: #{param_name.inspect}" - # TODO: raise? - return nil + def add _module + if _module.is_a?(DataProvider::Container) + _dpc = _module + else + _dpc = _module.dpc + self.class.include _module # todo: make optional? + end + + self.class.new(options.merge({ + :data => nil, + :dpc => dpc.add(_dpc) + })) end - alias :get_data :given + def add_scoped _module, options = {} + if _module.is_a?(DataProvider::Container) + _dpc = _module + else + _dpc = _module.dpc + self.class.include _module # todo: make optional? + end - def give(_data = {}) - return self.class.new(options.merge(:data => data.merge(_data))) + self.class.new(options.merge({ + :data => nil, + :dpc => dpc.add_scoped(_dpc, :scope => options[:scope]) + })) end + def give *args + self.class.new(options.merge(:data => nil, :dpc => self.dpc.give(*args))) + end + alias :add_scope :give alias :add_data :give - def give!(_data = {}) - @data = data.merge(_data) + def add! _module + if _module.is_a?(DataProvider::Container) + dpc.add!(_module) + else + dpc.add!(_module.dpc) + self.class.include _module + end + return self end - alias :add_scope! :give! - alias :add_data! :give! - - private - - def missing_provider - # byebug - @missing_provider + def add_scoped! _module, options = {} + if _module.is_a?(DataProvider::Container) + dpc.add_scoped!(_module, options) + else + dpc.add_scoped! _module.dpc, options + self.class.include _module + end + + return self end - - def scope - @scope || [] - end end # module InstanceMethods - end # module Base - end # module DataProvider \ No newline at end of file