lib/dry/behaviour/black_tie.rb in dry-behaviour-0.7.0 vs lib/dry/behaviour/black_tie.rb in dry-behaviour-0.8.0

- old
+ new

@@ -13,16 +13,18 @@ def implementations @implementations ||= Hash.new { |h, k| h[k] = h.dup.clear } end end - def defprotocol - raise if BlackTie.protocols.key?(self) # DUPLICATE DEF - raise unless block_given? + def defprotocol(implicit_inheritance: false, &λ) + raise ::Dry::Protocol::DuplicateDefinition.new(self) if BlackTie.protocols.key?(self) + raise ::Dry::Protocol::MalformedDefinition.new(self) unless block_given? + BlackTie.protocols[self][:__implicit_inheritance__] = !!implicit_inheritance + ims = instance_methods(false) - class_eval(&Proc.new) + class_eval(&λ) (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, inspect, method: method, self: self @@ -53,11 +55,11 @@ end end end singleton_class.send :define_method, :respond_to? do |method| - BlackTie.protocols[self].keys.include? method + NORMALIZE_KEYS.(self).include? method end end def defmethod(name, *params) BlackTie.protocols[self][name] = params @@ -76,24 +78,45 @@ tp.disable end end.enable end - def defimpl(protocol = nil, target: nil, delegate: [], map: {}) + NORMALIZE_KEYS = lambda do |protocol| + BlackTie.protocols[protocol].keys.reject { |k| k.to_s =~ /\A__.*__\z/ } + end + + IMPLICIT_DELEGATE_DEPRECATION = + "\n⚠️ DEPRECATED → Implicit delegation to the target class will be removed in 1.0\n" \ + "  ⮩ due to the lack of the explicit implementation of %s#%s for %s\n" \ + "  ⮩ it will be delegated to the target class itself.\n" \ + "  ⮩ Consider using explicit `delegate:' declaration in `defimpl' or\n" \ + "  ⮩ use `implicit_inheritance: true' parameter in protocol definition.".freeze + + def defimpl(protocol = nil, target: nil, delegate: [], map: {}, &λ) raise if target.nil? || !block_given? && delegate.empty? && map.empty? 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 + singleton_class.class_eval(&λ) if block_given? # block takes precedence end.tap do |mod| protocol ? mod.extend(protocol) : POSTPONE_EXTEND.(mod, protocol = self) mod.methods(false).tap do |meths| - (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) + (NORMALIZE_KEYS.(protocol) - meths).each_with_object(meths) do |m, acc| + if BlackTie.protocols[protocol][:__implicit_inheritance__] + mod.singleton_class.class_eval do + define_method m do |*args, &λ| + super(*args, &λ) + end + end + else + BlackTie.Logger.warn( + IMPLICIT_DELEGATE_DEPRECATION % [protocol.inspect, m, target] + ) + DELEGATE_METHOD.(mod.singleton_class, [m] * 2) + end acc << m end end.each do |m| target = [target] unless target.is_a?(Array) target.each do |tgt|