abstractivator 0.4.0 → 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/abstractivator/proc_ext.rb +26 -5
- data/lib/abstractivator/version.rb +1 -1
- data/spec/lib/abstractivator/proc_ext_spec.rb +86 -18
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2b0ee668927a5106db357ec157e8be9899625987
|
4
|
+
data.tar.gz: 9c21aa5f625b98a54947173793153cd28001583b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2f236c615a444fc301302a193c5c084e8710d9dd9522a92a6d69b27a31f7de32e1f1599153482c5ebda9cd41be6d77e805b41672f3b13f8bf5d8ff14a11150bb
|
7
|
+
data.tar.gz: 6cf0c72dd6be94141af891b6a2ced2b3ef9a13be9d973a64095afc2157b1a7bca73ba82b7dcee80a7675cadc8d2688d089b7d6a4aa944426362318038f2d7dd5
|
@@ -1,10 +1,11 @@
|
|
1
1
|
require 'abstractivator/enumerable_ext'
|
2
|
+
require 'abstractivator/array_ext'
|
2
3
|
|
3
4
|
module MethodAndProcExtensions
|
4
5
|
# returns a version of the procedure that accepts any number of arguments
|
5
6
|
def loosen_args
|
6
|
-
proc do |*args, &block|
|
7
|
-
Proc.loose_call(self, args, &block)
|
7
|
+
proc do |*args, **kws, &block|
|
8
|
+
Proc.loose_call(self, args, kws, &block)
|
8
9
|
end
|
9
10
|
end
|
10
11
|
end
|
@@ -54,11 +55,31 @@ class Proc
|
|
54
55
|
# tries to coerce x into a procedure, then calls it with
|
55
56
|
# the given argument list.
|
56
57
|
# If x cannot be coerced into a procedure, returns x.
|
57
|
-
def self.loose_call(x, args, &block)
|
58
|
+
def self.loose_call(x, args, kws={}, &block)
|
58
59
|
x = x.to_proc if x.respond_to?(:to_proc)
|
59
60
|
x.callable? or return x
|
60
|
-
|
61
|
-
|
61
|
+
arg_types = x.parameters.map(&:first)
|
62
|
+
# customize args
|
63
|
+
req_arity = arg_types.select{|x| x == :req}.size
|
64
|
+
total_arity = req_arity + arg_types.select{|x| x == :opt}.size
|
65
|
+
accepts_arg_splat = arg_types.include?(:rest)
|
66
|
+
unless accepts_arg_splat
|
67
|
+
args = args.take(total_arity).pad_right(req_arity)
|
68
|
+
end
|
69
|
+
# customize keywords
|
70
|
+
accepts_kw_splat = arg_types.include?(:keyrest)
|
71
|
+
unless accepts_kw_splat
|
72
|
+
opt_key_names = x.parameters.select{|(type, name)| type == :key && !name.nil?}.map(&:value)
|
73
|
+
req_key_names = x.parameters.select{|(type, name)| type == :keyreq && !name.nil?}.map(&:value)
|
74
|
+
all_key_names = opt_key_names + req_key_names
|
75
|
+
padding = req_key_names.hash_map{nil}
|
76
|
+
kws = padding.merge(kws.select{|k| all_key_names.include?(k)})
|
77
|
+
end
|
78
|
+
if kws.any?
|
79
|
+
x.call(*args, **kws, &block)
|
80
|
+
else
|
81
|
+
x.call(*args, &block)
|
82
|
+
end
|
62
83
|
end
|
63
84
|
end
|
64
85
|
|
@@ -51,35 +51,103 @@ context 'in the world of functional programming' do
|
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
54
|
-
|
55
|
-
it 'returns the first argument if it is not a proc' do
|
56
|
-
expect(Proc.loose_call('a', ['b', 'c'])).to eql 'a'
|
57
|
-
end
|
58
|
-
it 'attempts to convert the first argument to proc' do
|
59
|
-
expect(Proc.loose_call(:to_s, ['5'])).to eql '5'
|
60
|
-
end
|
54
|
+
shared_examples 'an arity loosener' do
|
61
55
|
it 'calls the proc with an appropriate number of arguments' do
|
62
56
|
events = []
|
63
57
|
args = [:here, :are, :some, :arguments]
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
expect(events).to eql [0, 1, 2, 3]
|
58
|
+
do_call(->{events << 0}, args)
|
59
|
+
do_call(->(a){events << 1}, args)
|
60
|
+
do_call(->(a, b){events << 2}, args)
|
61
|
+
do_call(->(a, b, c){events << 3}, args)
|
62
|
+
expect(events).to eql [0, 1, 2, 3] # lambdas are strict in arity,
|
63
|
+
# so if their bodies executed, then we know
|
64
|
+
# they were called with the correct number of arguments
|
65
|
+
|
69
66
|
end
|
70
67
|
it 'pads with nils' do
|
71
|
-
|
68
|
+
verify_called_with ->(a, b) {[a, b]},
|
69
|
+
[1],
|
70
|
+
[1, nil]
|
71
|
+
end
|
72
|
+
it 'passes optional arguments, while preserving defaults' do
|
73
|
+
verify_called_with ->(a, b=2, c=3) {[a, b, c]},
|
74
|
+
[11, 22],
|
75
|
+
[11, 22, 3]
|
72
76
|
end
|
73
77
|
it 'works with variable-arity procs' do
|
74
|
-
|
78
|
+
verify_called_with ->(*args) {args},
|
79
|
+
[1, 2],
|
80
|
+
[1, 2]
|
81
|
+
verify_called_with ->(first, *rest) {[first, rest]},
|
82
|
+
[1, 2, 3],
|
83
|
+
[1, [2, 3]]
|
84
|
+
end
|
85
|
+
it 'passes the block' do
|
86
|
+
expect(do_call(proc{|&b| b.call}, []) {42}).to eql 42
|
87
|
+
end
|
88
|
+
it 'calls the proc with the declared keyword arguments, while preserving defaults' do
|
89
|
+
verify_called_with_keywords ->(a: 1, b: 2, c: 3) {[a, b, c]},
|
90
|
+
{a: 11, b: 22, d: 44},
|
91
|
+
[11, 22, 3]
|
92
|
+
end
|
93
|
+
it 'calls the proc with the declared required keyword arguments' do
|
94
|
+
verify_called_with_keywords ->(a:, b:) {[a, b]},
|
95
|
+
{a: 1, b: 2, c: 3},
|
96
|
+
[1, 2]
|
97
|
+
end
|
98
|
+
it 'replaces missing required keyword arguments with nil' do
|
99
|
+
verify_called_with_keywords ->(a:, b:) {[a, b]},
|
100
|
+
{a: 1},
|
101
|
+
[1, nil]
|
102
|
+
end
|
103
|
+
it 'works with variable-keyword procs' do
|
104
|
+
verify_called_with_keywords ->(**kws) {kws},
|
105
|
+
{a: 1, b: 2, c: 3},
|
106
|
+
{a: 1, b: 2, c: 3}
|
107
|
+
verify_called_with_keywords ->(a:, **kws) {[a, kws]},
|
108
|
+
{a: 1, b: 2, c: 3},
|
109
|
+
[1, {b: 2, c: 3}]
|
110
|
+
end
|
111
|
+
it 'works with a mixture of argument types' do
|
112
|
+
p = ->(a, b=2, c=3, d: 4, e: 5, f:, &block){[a, b, c, d, e, f, block.call]}
|
113
|
+
result = do_call(p, [11, 22], {d: 44, z: 5}) { 42 }
|
114
|
+
expect(result).to eql [11, 22, 3, 44, 5, nil, 42]
|
115
|
+
end
|
116
|
+
it 'works with a mixture of splat types' do
|
117
|
+
p = ->(*args, **kws, &block){[args, kws, block.call]}
|
118
|
+
result = do_call(p, [1, 2], {c: 3, d: 4}) { 42 }
|
119
|
+
expect(result).to eql [[1, 2], {c: 3, d: 4}, 42]
|
120
|
+
end
|
121
|
+
|
122
|
+
def verify_called_with(p, input_args, output)
|
123
|
+
expect(do_call(p, input_args, {})).to eql output
|
124
|
+
end
|
125
|
+
|
126
|
+
def verify_called_with_keywords(p, input_kws, output)
|
127
|
+
expect(do_call(p, [], input_kws)).to eql output
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
describe 'Proc::loose_call' do
|
132
|
+
it_behaves_like 'an arity loosener'
|
133
|
+
|
134
|
+
it 'returns the first argument if it is not a proc' do
|
135
|
+
expect(Proc.loose_call('a', ['b', 'c'])).to eql 'a'
|
136
|
+
end
|
137
|
+
it 'attempts to convert the first argument to proc' do
|
138
|
+
expect(Proc.loose_call(:to_s, ['5'])).to eql '5'
|
139
|
+
end
|
140
|
+
|
141
|
+
def do_call(p, args, kws={}, &block)
|
142
|
+
Proc.loose_call(p, args, kws, &block)
|
75
143
|
end
|
76
144
|
end
|
77
145
|
|
78
146
|
describe 'Proc#loosen_args' do
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
147
|
+
it_behaves_like 'an arity loosener'
|
148
|
+
|
149
|
+
def do_call(p, args, kws={}, &block)
|
150
|
+
p.loosen_args.call(*args, **kws, &block)
|
83
151
|
end
|
84
152
|
end
|
85
153
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: abstractivator
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Peter Winton
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-11-
|
11
|
+
date: 2015-11-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|