transproc 0.2.4 → 0.3.0

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