transproc 0.2.4 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,5 @@
1
+ require 'transproc/support/deprecations'
2
+
1
3
  module Transproc
2
4
  # Transproc helper that adds `t` method as a shortcut for `Transproc` method
3
5
  #
@@ -8,6 +10,24 @@ module Transproc
8
10
  #
9
11
  # @api public
10
12
  module Helper
13
+ # @api private
14
+ def self.included(*)
15
+ Transproc::Deprecations.announce(
16
+ 'Transproc::Helper',
17
+ 'Define your own function registry using Transproc::Registry extension'
18
+ )
19
+ super
20
+ end
21
+
22
+ # @api private
23
+ def self.included(*)
24
+ Transproc::Deprecations.announce(
25
+ 'Transproc::Helper',
26
+ 'Define your own function registry using Transproc::Registry extension'
27
+ )
28
+ super
29
+ end
30
+
11
31
  # @see Transproc
12
32
  #
13
33
  # @api public
@@ -31,8 +51,6 @@ module Transproc
31
51
  #
32
52
  # @api public
33
53
  module Composer
34
- include Helper
35
-
36
54
  # @api private
37
55
  class Factory
38
56
  attr_reader :fns, :default
@@ -53,6 +71,12 @@ module Transproc
53
71
  def to_fn
54
72
  fns.reduce(:+) || default
55
73
  end
74
+
75
+ # @deprecated
76
+ # @api public
77
+ def t(*args, &block)
78
+ Transproc(*args, &block)
79
+ end
56
80
  end
57
81
 
58
82
  # Gather and compose functions and fall-back to a default one if provided
@@ -13,7 +13,7 @@ module Transproc
13
13
  #
14
14
  # @api public
15
15
  module Conditional
16
- extend Functions
16
+ extend Registry
17
17
 
18
18
  # Apply the transformation function to subject if the predicate returns true, or return un-modified
19
19
  #
@@ -28,7 +28,7 @@ module Transproc
28
28
  # @return [Mixed]
29
29
  #
30
30
  # @api public
31
- def guard(value, predicate, fn)
31
+ def self.guard(value, predicate, fn)
32
32
  predicate[value] ? fn[value] : value
33
33
  end
34
34
 
@@ -48,8 +48,12 @@ module Transproc
48
48
  # @return [Object]
49
49
  #
50
50
  # @api public
51
- def is(value, type, fn)
51
+ def self.is(value, type, fn)
52
52
  guard(value, -> v { v.is_a?(type) }, fn)
53
53
  end
54
+
55
+ # @deprecated Register methods globally
56
+ (methods - Registry.methods - Registry.instance_methods)
57
+ .each { |name| Transproc.register name, t(name) }
54
58
  end
55
59
  end
@@ -1,8 +1,14 @@
1
1
  module Transproc
2
2
  Error = Class.new(StandardError)
3
- FunctionNotFoundError = Class.new(Error)
4
3
  FunctionAlreadyRegisteredError = Class.new(Error)
5
4
 
5
+ class FunctionNotFoundError < Error
6
+ def initialize(function, source = nil)
7
+ return super "No registered function #{source}[:#{function}]" if source
8
+ super "No globally registered function for #{function}"
9
+ end
10
+ end
11
+
6
12
  class MalformedInputError < Error
7
13
  attr_reader :function, :value, :original_error
8
14
 
@@ -22,10 +22,18 @@ module Transproc
22
22
  # @api private
23
23
  attr_reader :args
24
24
 
25
+ # @!attribute [r] name
26
+ #
27
+ # @return [<type] The name of the function
28
+ #
29
+ # @api public
30
+ attr_reader :name
31
+
25
32
  # @api private
26
- def initialize(fn, options)
33
+ def initialize(fn, options = {})
27
34
  @fn = fn
28
- @args = options[:args]
35
+ @args = options.fetch(:args, [])
36
+ @name = options.fetch(:name, fn)
29
37
  end
30
38
 
31
39
  # Call the wrapped proc
@@ -38,7 +46,7 @@ module Transproc
38
46
  def call(*value)
39
47
  fn[*value, *args]
40
48
  rescue => e
41
- raise MalformedInputError.new(@fn, value, e)
49
+ raise MalformedInputError.new(@name, value, e)
42
50
  end
43
51
  alias_method :[], :call
44
52
 
@@ -57,14 +65,29 @@ module Transproc
57
65
  alias_method :+, :compose
58
66
  alias_method :>>, :compose
59
67
 
68
+ # Return a new fn with curried args
69
+ #
70
+ # @return [Function]
71
+ #
72
+ # @api private
73
+ def with(*args)
74
+ self.class.new(fn, name: name, args: args)
75
+ end
76
+
77
+ # @api public
78
+ def ==(other)
79
+ return false unless other.instance_of?(self.class)
80
+ [fn, name, args] == [other.fn, other.name, other.args]
81
+ end
82
+ alias_method :eql?, :==
83
+
60
84
  # Return a simple AST representation of this function
61
85
  #
62
86
  # @return [Array]
63
87
  #
64
88
  # @api public
65
89
  def to_ast
66
- identifier = fn.instance_of?(Proc) ? fn : fn.name
67
- [identifier, args]
90
+ [name, args]
68
91
  end
69
92
  end
70
93
  end
@@ -14,6 +14,11 @@ module Transproc
14
14
  #
15
15
  # @api public
16
16
  module Functions
17
+ def self.extended(mod)
18
+ warn 'Transproc::Functions is deprecated please switch to Transproc::Registry'
19
+ super
20
+ end
21
+
17
22
  def method_added(meth)
18
23
  module_function meth
19
24
  Transproc.register(meth, method(meth))
@@ -15,7 +15,7 @@ module Transproc
15
15
  #
16
16
  # @api public
17
17
  module HashTransformations
18
- extend Functions
18
+ extend Registry
19
19
 
20
20
  # Map all keys in a hash with the provided transformation function
21
21
  #
@@ -28,7 +28,7 @@ module Transproc
28
28
  # @return [Hash]
29
29
  #
30
30
  # @api public
31
- def map_keys(hash, fn)
31
+ def self.map_keys(hash, fn)
32
32
  map_keys!(Hash[hash], fn)
33
33
  end
34
34
 
@@ -37,7 +37,7 @@ module Transproc
37
37
  # @see HashTransformations.map_keys
38
38
  #
39
39
  # @api public
40
- def map_keys!(hash, fn)
40
+ def self.map_keys!(hash, fn)
41
41
  hash.keys.each { |key| hash[fn[key]] = hash.delete(key) }
42
42
  hash
43
43
  end
@@ -53,7 +53,7 @@ module Transproc
53
53
  # @return [Hash]
54
54
  #
55
55
  # @api public
56
- def symbolize_keys(hash)
56
+ def self.symbolize_keys(hash)
57
57
  symbolize_keys!(Hash[hash])
58
58
  end
59
59
 
@@ -62,8 +62,38 @@ module Transproc
62
62
  # @see HashTransformations.symbolize_keys!
63
63
  #
64
64
  # @api public
65
- def symbolize_keys!(hash)
66
- map_keys!(hash, Transproc(:to_symbol).fn)
65
+ def self.symbolize_keys!(hash)
66
+ map_keys!(hash, Coercions[:to_symbol].fn)
67
+ end
68
+
69
+ # Symbolize keys in a hash recursively
70
+ #
71
+ # @example
72
+ #
73
+ # input = { 'foo' => 'bar', 'baz' => [{ 'one' => 1 }] }
74
+ #
75
+ # t(:deep_symbolize_keys)[input]
76
+ # # => { :foo => "bar", :baz => [{ :one => 1 }] }
77
+ #
78
+ # @param [Hash]
79
+ #
80
+ # @return [Hash]
81
+ #
82
+ # @api public
83
+ def self.deep_symbolize_keys(hash)
84
+ hash.each_with_object({}) do |(key, value), output|
85
+ output[key.to_sym] =
86
+ case value
87
+ when Hash
88
+ deep_symbolize_keys(value)
89
+ when Array
90
+ value.map { |item|
91
+ item.is_a?(Hash) ? deep_symbolize_keys(item) : item
92
+ }
93
+ else
94
+ value
95
+ end
96
+ end
67
97
  end
68
98
 
69
99
  # Stringify all keys in a hash
@@ -77,7 +107,7 @@ module Transproc
77
107
  # @return [Hash]
78
108
  #
79
109
  # @api public
80
- def stringify_keys(hash)
110
+ def self.stringify_keys(hash)
81
111
  stringify_keys!(Hash[hash])
82
112
  end
83
113
 
@@ -86,8 +116,8 @@ module Transproc
86
116
  # @see HashTransformations.stringify_keys
87
117
  #
88
118
  # @api public
89
- def stringify_keys!(hash)
90
- map_keys!(hash, Transproc(:to_string).fn)
119
+ def self.stringify_keys!(hash)
120
+ map_keys!(hash, Coercions[:to_string].fn)
91
121
  end
92
122
 
93
123
  # Map all values in a hash using transformation function
@@ -101,7 +131,7 @@ module Transproc
101
131
  # @return [Hash]
102
132
  #
103
133
  # @api public
104
- def map_values(hash, fn)
134
+ def self.map_values(hash, fn)
105
135
  map_values!(Hash[hash], fn)
106
136
  end
107
137
 
@@ -114,7 +144,7 @@ module Transproc
114
144
  # @return [Hash]
115
145
  #
116
146
  # @api public
117
- def map_values!(hash, fn)
147
+ def self.map_values!(hash, fn)
118
148
  hash.each { |key, value| hash[key] = fn[value] }
119
149
  hash
120
150
  end
@@ -131,7 +161,7 @@ module Transproc
131
161
  # @return [Hash]
132
162
  #
133
163
  # @api public
134
- def rename_keys(hash, mapping)
164
+ def self.rename_keys(hash, mapping)
135
165
  rename_keys!(Hash[hash], mapping)
136
166
  end
137
167
 
@@ -140,7 +170,7 @@ module Transproc
140
170
  # @see HashTransformations.rename_keys
141
171
  #
142
172
  # @api public
143
- def rename_keys!(hash, mapping)
173
+ def self.rename_keys!(hash, mapping)
144
174
  mapping.each { |k, v| hash[v] = hash.delete(k) }
145
175
  hash
146
176
  end
@@ -157,7 +187,7 @@ module Transproc
157
187
  # @return [Hash]
158
188
  #
159
189
  # @api public
160
- def reject_keys(hash, keys)
190
+ def self.reject_keys(hash, keys)
161
191
  reject_keys!(Hash[hash], keys)
162
192
  end
163
193
 
@@ -166,7 +196,7 @@ module Transproc
166
196
  # @see HashTransformations.reject_keys
167
197
  #
168
198
  # @api public
169
- def reject_keys!(hash, keys)
199
+ def self.reject_keys!(hash, keys)
170
200
  hash.reject { |k, _| keys.include?(k) }
171
201
  end
172
202
 
@@ -182,7 +212,7 @@ module Transproc
182
212
  # @return [Hash]
183
213
  #
184
214
  # @api public
185
- def accept_keys(hash, keys)
215
+ def self.accept_keys(hash, keys)
186
216
  accept_keys!(Hash[hash], keys)
187
217
  end
188
218
 
@@ -191,7 +221,7 @@ module Transproc
191
221
  # @see HashTransformations.accept
192
222
  #
193
223
  # @api public
194
- def accept_keys!(hash, keys)
224
+ def self.accept_keys!(hash, keys)
195
225
  reject_keys!(hash, hash.keys - keys)
196
226
  end
197
227
 
@@ -206,7 +236,7 @@ module Transproc
206
236
  # @return [Hash]
207
237
  #
208
238
  # @api public
209
- def map_value(hash, key, fn)
239
+ def self.map_value(hash, key, fn)
210
240
  hash.merge(key => fn[hash[key]])
211
241
  end
212
242
 
@@ -215,7 +245,7 @@ module Transproc
215
245
  # @see HashTransformations.map_value
216
246
  #
217
247
  # @api public
218
- def map_value!(hash, key, fn)
248
+ def self.map_value!(hash, key, fn)
219
249
  hash.update(key => fn[hash[key]])
220
250
  end
221
251
 
@@ -230,7 +260,7 @@ module Transproc
230
260
  # @return [Hash]
231
261
  #
232
262
  # @api public
233
- def nest(hash, key, keys)
263
+ def self.nest(hash, key, keys)
234
264
  nest!(Hash[hash], key, keys)
235
265
  end
236
266
 
@@ -239,7 +269,7 @@ module Transproc
239
269
  # @see HashTransformations.nest
240
270
  #
241
271
  # @api public
242
- def nest!(hash, root, keys)
272
+ def self.nest!(hash, root, keys)
243
273
  nest_keys = hash.keys & keys
244
274
 
245
275
  if nest_keys.size > 0
@@ -263,7 +293,7 @@ module Transproc
263
293
  # @return [Hash]
264
294
  #
265
295
  # @api public
266
- def unwrap(hash, root, keys = nil)
296
+ def self.unwrap(hash, root, keys = nil)
267
297
  copy = Hash[hash].merge(root => Hash[hash[root]])
268
298
  unwrap!(copy, root, keys)
269
299
  end
@@ -273,7 +303,7 @@ module Transproc
273
303
  # @see HashTransformations.unwrap
274
304
  #
275
305
  # @api public
276
- def unwrap!(hash, root, selected = nil)
306
+ def self.unwrap!(hash, root, selected = nil)
277
307
  if nested_hash = hash[root]
278
308
  keys = nested_hash.keys
279
309
  keys &= selected if selected
@@ -303,7 +333,7 @@ module Transproc
303
333
  # @return [Hash]
304
334
  #
305
335
  # @api public
306
- def fold(hash, key, tuple_key)
336
+ def self.fold(hash, key, tuple_key)
307
337
  fold!(Hash[hash], key, tuple_key)
308
338
  end
309
339
 
@@ -312,7 +342,7 @@ module Transproc
312
342
  # @see HashTransformations.fold
313
343
  #
314
344
  # @api public
315
- def fold!(hash, key, tuple_key)
345
+ def self.fold!(hash, key, tuple_key)
316
346
  hash.update(key => ArrayTransformations.extract_key(hash[key], tuple_key))
317
347
  end
318
348
 
@@ -344,7 +374,7 @@ module Transproc
344
374
  # @return [Array<Hash>]
345
375
  #
346
376
  # @api public
347
- def split(hash, key, keys)
377
+ def self.split(hash, key, keys)
348
378
  list = Array(hash[key])
349
379
  return [hash.reject { |k, _| k == key }] if list.empty?
350
380
 
@@ -356,5 +386,51 @@ module Transproc
356
386
  list = list.map { |item| item.merge(reject_keys(hash, [key])) }
357
387
  ArrayTransformations.add_keys(list, ungrouped)
358
388
  end
389
+
390
+ # Recursively evaluate hash values if they are procs/lambdas
391
+ #
392
+ # @example
393
+ # hash = {
394
+ # num: -> i { i + 1 },
395
+ # str: -> i { "num #{i}" }
396
+ # }
397
+ #
398
+ # t(:eval_values, 1)[hash]
399
+ # # => {:num => 2, :str => "num 1" }
400
+ #
401
+ # # with filters
402
+ # t(:eval_values, 1, [:str])[hash]
403
+ # # => {:num => #{still a proc}, :str => "num 1" }
404
+ #
405
+ # @param [Hash]
406
+ # @param [Array,Object] args Anything that should be passed to procs
407
+ # @param [Array] filters A list of attribute names that should be evaluated
408
+ #
409
+ # @api public
410
+ def self.eval_values(hash, args, filters = [])
411
+ hash.each_with_object({}) do |(key, value), output|
412
+ output[key] =
413
+ case value
414
+ when Proc
415
+ if filters.empty? || filters.include?(key)
416
+ value.call(*args)
417
+ else
418
+ value
419
+ end
420
+ when Hash
421
+ eval_values(value, args, filters)
422
+ when Array
423
+ value.map { |item|
424
+ item.is_a?(Hash) ? eval_values(item, args, filters) : item
425
+ }
426
+ else
427
+ value
428
+ end
429
+ end
430
+ end
431
+
432
+ # @deprecated Register methods globally
433
+ (methods - Registry.instance_methods - Registry.methods)
434
+ .each { |name| Transproc.register name, t(name) }
359
435
  end
360
436
  end