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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fde467c865fb41d53a7c256fd150d5c2f3bbb947
4
- data.tar.gz: 97e60569e7a56d45c24bafaf345f22856eb3b8c7
3
+ metadata.gz: 2b0ee668927a5106db357ec157e8be9899625987
4
+ data.tar.gz: 9c21aa5f625b98a54947173793153cd28001583b
5
5
  SHA512:
6
- metadata.gz: 1671b95d9e955f6ec00fc93972b6cea6b18311a0c0be80880010394c26fd3563c42d32228ceb8c500d463c4d95b567e4f48b03a88fdfea76d476ddd3b76f2535
7
- data.tar.gz: 5655785a88eb97cd688bfbecf4923ce4f3db7074425e470c33d52143207d72681a49980cba7a44db07bfd592b37fa8a2abacab4463681403567776f83eba7450
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
- args = args.take(x.arity).pad_right(x.arity) if x.arity >= 0
61
- x.call(*args, &block)
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
 
@@ -1,3 +1,3 @@
1
1
  module Abstractivator
2
- VERSION = '0.4.0'
2
+ VERSION = '0.4.1'
3
3
  end
@@ -51,35 +51,103 @@ context 'in the world of functional programming' do
51
51
  end
52
52
  end
53
53
 
54
- describe 'Proc::loose_call' do
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
- Proc.loose_call(->{events << 0}, args)
65
- Proc.loose_call(->(a){events << 1}, args)
66
- Proc.loose_call(->(a, b){events << 2}, args)
67
- Proc.loose_call(->(a, b, c){events << 3}, args)
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
- expect(Proc.loose_call(->(a, b) {[a, b]}, [1])).to eql [1, nil]
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
- expect(Proc.loose_call(->(*args) {args}, [1, 2])).to eql [1, 2]
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
- it 'returns a procedure with loose arity semantics' do
80
- p = ->(a, b, c) { [a, b, c] }
81
- lp = p.loosen_args
82
- expect(lp.call(1, 2)).to eql [1, 2, nil]
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.0
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-05 00:00:00.000000000 Z
11
+ date: 2015-11-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler