lib/dry/behaviour/black_tie.rb in dry-behaviour-0.1.0 vs lib/dry/behaviour/black_tie.rb in dry-behaviour-0.1.1

- old
+ new

@@ -1,6 +1,13 @@ module Dry + # rubocop:disable Style/VariableName + # rubocop:disable Style/AsciiIdentifiers + # rubocop:disable Style/MultilineBlockChain + # rubocop:disable Style/EmptyCaseCondition + # rubocop:disable Metrics/PerceivedComplexity + # rubocop:disable Metrics/CyclomaticComplexity + # rubocop:disable Metrics/AbcSize module BlackTie class << self def protocols @protocols ||= Hash.new { |h, k| h[k] = h.dup.clear } end @@ -8,41 +15,73 @@ def implementations @implementations ||= Hash.new { |h, k| h[k] = h.dup.clear } end end - def defprotocol(delegate: []) + def defprotocol raise if BlackTie.protocols.key?(self) # DUPLICATE DEF - raise unless block_given? || !delegate.empty? - # FIXME IMPLEMENT DELEGATES! raise unless block_given? ims = instance_methods(false) class_eval(&Proc.new) (instance_methods(false) - ims).each { |m| class_eval { module_function m } } - BlackTie.protocols[self].each do |method, *_| # FIXME CHECK PARAMS CORRESPONDENCE HERE + BlackTie.protocols[self].each do |method, *_| # FIXME: CHECK PARAMS CORRESPONDENCE HERE # receiver, *args = *args singleton_class.send :define_method, method do |receiver, *args| receiver.class.ancestors.lazy.map do |c| BlackTie.implementations[self].fetch(c, nil) end.reject(&:nil?).first[method].(receiver, *args) end end end - def defmethod name, *params + def defmethod(name, *params) BlackTie.protocols[self][name] = params end - def defimpl protocol = nil, **params - raise unless block_given? - raise if params[:for].nil? + def defimpl(protocol = nil, target: nil, delegate: [], map: {}) + raise if target.nil? || !block_given? && delegate.empty? && map.empty? - Module.new { singleton_class.class_eval(&Proc.new) }.tap do |mod| + mds = normalize_map_delegates(delegate, map) + Module.new do + mds.each do |k, v| + singleton_class.class_eval do + define_method k do |this, *args, **params, &λ| + case + when !args.empty? && !params.empty? then this.send(v, *args, **params, &λ) + when !args.empty? then this.send(v, *args, &λ) + when !params.empty? then this.send(v, **params, &λ) + else this.send(v, &λ) + end + end + end + end + singleton_class.class_eval(&Proc.new) if block_given? # block takes precedence + end.tap do |mod| mod.methods(false).each do |m| - BlackTie.implementations[protocol || self][params[:for]][m] = mod.method(m).to_proc + BlackTie.implementations[protocol || self][target][m] = mod.method(m).to_proc end end end + module_function :defimpl + + 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 Metrics/AbcSize + # rubocop:enable Metrics/CyclomaticComplexity + # rubocop:enable Metrics/PerceivedComplexity + # rubocop:enable Style/EmptyCaseCondition + # rubocop:enable Style/MultilineBlockChain + # rubocop:enable Style/AsciiIdentifiers + # rubocop:enable Style/VariableName end