abstractivator 0.4.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|