5
6
7
8
9
10
11
12
13
14
15
16
17
18
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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
|
# File 'lib/delivered/signature.rb', line 5
def sig(*sig_args, **sig_kwargs, &return_blk)
returns = return_blk&.call
if sig_kwargs.keys[0].is_a?(Array)
unless returns.nil?
raise Delivered::ArgumentError,
'Cannot mix block and hash for return type. Use one or the other.', caller
end
returns = sig_kwargs.values[0]
sig_args = sig_kwargs.keys[0]
sig_kwargs = sig_args.pop if sig_args.last.is_a?(Hash)
end
meta = class << self; self; end
sig_check = lambda do |klass, class_method, name, *args, **kwargs, &block|
cname = class_method ? "#{klass.name}.#{name}" : "#{klass.class.name}##{name}"
sig_args.each.with_index do |arg, i|
args[i] => ^arg
rescue NoMatchingPatternError => e
raise Delivered::ArgumentError,
"`#{cname}` expected #{arg.inspect} as argument #{i}, but received " \
"`#{args[i].inspect}`",
caller, cause: e
end
unless sig_kwargs.empty?
kwargs.each do |key, value|
value => ^(sig_kwargs[key])
rescue NoMatchingPatternError => e
raise Delivered::ArgumentError,
"`#{cname}` expected #{sig_kwargs[key].inspect} as keyword argument :#{key}, " \
"but received `#{value.inspect}`",
caller, cause: e
end
end
result = if block
klass.send(:"__#{name}", *args, **kwargs, &block)
else
klass.send(:"__#{name}", *args, **kwargs)
end
begin
result => ^returns unless returns.nil?
rescue NoMatchingPatternError => e
raise Delivered::ArgumentError,
"`#{cname}` expected to return #{returns.inspect}, " \
"but returned `#{result.inspect}`",
caller, cause: e
end
result
end
meta.send :define_method, :method_added do |name|
meta.send :remove_method, :method_added
meta.send :remove_method, :singleton_method_added
alias_method :"__#{name}", name
define_method name do |*args, **kwargs, &block|
sig_check.call(self, false, name, *args, **kwargs, &block)
end
end
meta.send :define_method, :singleton_method_added do |name|
next if name == :singleton_method_added
meta.send :remove_method, :singleton_method_added
meta.send :remove_method, :method_added
meta.alias_method :"__#{name}", name
define_singleton_method name do |*args, **kwargs, &block|
sig_check.call(self, true, name, *args, **kwargs, &block)
end
end
end
|