Module: Dry::BlackTie

Defined in:
lib/dry/behaviour/black_tie.rb

Overview

rubocop:disable Style/AsciiIdentifiers rubocop:disable Style/MultilineBlockChain rubocop:disable Style/EmptyCaseCondition rubocop:disable Metrics/PerceivedComplexity rubocop:disable Metrics/CyclomaticComplexity rubocop:disable Metrics/AbcSize

Constant Summary collapse

DELEGATE_METHOD =
lambda do |klazz, (source, target)|
  klazz.class_eval do
    define_method source do |this, *args, **params, &

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.defimpl(protocol = nil, target: nil, delegate: [], map: {}) ⇒ Object



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/dry/behaviour/black_tie.rb', line 64

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
  end.tap do |mod|
    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}")
        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
      end
    end
  end
end

.implementationsObject



14
15
16
# File 'lib/dry/behaviour/black_tie.rb', line 14

def implementations
  @implementations ||= Hash.new { |h, k| h[k] = h.dup.clear }
end

.normalize_map_delegates(delegate, map) ⇒ Object



98
99
100
101
102
103
104
105
# File 'lib/dry/behaviour/black_tie.rb', line 98

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

.protocolsObject



10
11
12
# File 'lib/dry/behaviour/black_tie.rb', line 10

def protocols
  @protocols ||= Hash.new { |h, k| h[k] = h.dup.clear }
end

Instance Method Details

#defmethod(name, *params) ⇒ Object



47
48
49
# File 'lib/dry/behaviour/black_tie.rb', line 47

def defmethod(name, *params)
  BlackTie.protocols[self][name] = params
end

#defprotocolObject



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/dry/behaviour/black_tie.rb', line 19

def defprotocol
  raise if BlackTie.protocols.key?(self) # DUPLICATE DEF
  raise unless block_given?

  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)
  end

  singleton_class.send :define_method, :implementation_for do |receiver|
    receiver.class.ancestors.lazy.map do |c|
      BlackTie.implementations[self].fetch(c, nil)
    end.reject(&:nil?).first
  end

  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))
    end
  end
end