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