lib/dry/behaviour/black_tie.rb in dry-behaviour-0.3.1 vs lib/dry/behaviour/black_tie.rb in dry-behaviour-0.4.0

- old
+ new

@@ -3,10 +3,11 @@ # rubocop:disable Style/MultilineBlockChain # rubocop:disable Style/EmptyCaseCondition # rubocop:disable Metrics/PerceivedComplexity # rubocop:disable Metrics/CyclomaticComplexity # rubocop:disable Metrics/AbcSize + # rubocop:disable Style/MethodName module BlackTie class << self def protocols @protocols ||= Hash.new { |h, k| h[k] = h.dup.clear } end @@ -22,12 +23,12 @@ ims = instance_methods(false) class_eval(&Proc.new) (instance_methods(false) - ims).each { |m| class_eval { module_function m } } - singleton_class.send :define_method, :method_missing do |method, *args| - raise Dry::Protocol::NotImplemented.new(:method, self.inspect, method) + singleton_class.send :define_method, :method_missing do |method, *_args| + raise Dry::Protocol::NotImplemented.new(:method, inspect, method) end singleton_class.send :define_method, :implementation_for do |receiver| receiver.class.ancestors.lazy.map do |c| BlackTie.implementations[self].fetch(c, nil) @@ -36,12 +37,18 @@ BlackTie.protocols[self].each do |method, *_| # FIXME: CHECK PARAMS CORRESPONDENCE HERE singleton_class.send :define_method, method do |receiver = nil, *args| impl = implementation_for(receiver) - raise Dry::Protocol::NotImplemented.new(:protocol, self.inspect, receiver.class) unless impl - impl[method].(*args.unshift(receiver)) + raise Dry::Protocol::NotImplemented.new(:protocol, inspect, receiver.class) unless impl + begin + impl[method].(*args.unshift(receiver)) + rescue NoMethodError => e + raise Dry::Protocol::NotImplemented.new(:method, inspect, e.message) + rescue ArgumentError => e + raise Dry::Protocol::NotImplemented.new(:method, inspect, "#{method} (#{e.message})") + end end end end def defmethod(name, *params) @@ -66,46 +73,54 @@ mds = normalize_map_delegates(delegate, map) Module.new do mds.each(&DELEGATE_METHOD.curry[singleton_class]) singleton_class.class_eval(&Proc.new) if block_given? # block takes precedence + if protocol + extend protocol + else # FIXME: + BlackTie.Logger.warn('Cross-calling protocol methods is not yet implemented for inplace declarated implementations.') + end end.tap do |mod| + protocol ||= self + mod.methods(false).tap do |meths| - (BlackTie.protocols[protocol || self].keys - meths).each_with_object(meths) do |m, acc| - safe_logger.warn("Implicit delegate #{(protocol || self).inspect}##{m} to #{target}") + (BlackTie.protocols[protocol].keys - meths).each_with_object(meths) do |m, acc| + BlackTie.Logger.warn("Implicit delegate #{protocol.inspect}##{m} to #{target}") DELEGATE_METHOD.(mod.singleton_class, [m] * 2) acc << m end end.each do |m| [*target].each do |tgt| - BlackTie.implementations[protocol || self][tgt][m] = mod.method(m).to_proc + BlackTie.implementations[protocol][tgt][m] = mod.method(m).to_proc end end end end module_function :defimpl - private - - def safe_logger - if Kernel.const_defined?('::Rails') - Rails.logger - else - require 'logger' - Logger.new($stdout) - end + def self.Logger + @logger ||= if Kernel.const_defined?('::Rails') + Rails.logger + else + require 'logger' + Logger.new($stdout) + end end + private + def normalize_map_delegates(delegate, map) [*delegate, *map].map do |e| case e when Symbol, String then [e.to_sym] * 2 when Array then e.map(&:to_sym) if e.size == 2 end end.compact end module_function :normalize_map_delegates end + # rubocop:enable Style/MethodName # rubocop:enable Metrics/AbcSize # rubocop:enable Metrics/CyclomaticComplexity # rubocop:enable Metrics/PerceivedComplexity # rubocop:enable Style/EmptyCaseCondition # rubocop:enable Style/MultilineBlockChain