transproc 0.2.2 → 0.2.3

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: 8df075878909a89bb9304ac442044a20d768860b
4
- data.tar.gz: 2ebde290094e7cf302891f536c043af3c933f6b3
3
+ metadata.gz: a5ce7c814504a4dbdb337c06e34e0d8ba76240c2
4
+ data.tar.gz: 7634b2a430e84411fdf9b3454f8a9e530167efef
5
5
  SHA512:
6
- metadata.gz: 5baa7f8034bb8e727c1a0f8870a4f5e9f5ec9c9d4f04ad39400572473fa543aca6ab6540ba6658a75570acf068010f0637102ac973772e349cc8a59949c602d8
7
- data.tar.gz: 86a696d76233333a933bec5fd4ac83052cc4ce04bab8ae9bb9c11204caae818fa9020392846ff7a948b3c30fdaaa28257cad8448cf46a0d90a3a166b26b4962c
6
+ metadata.gz: 03cdc29204c0aeec12217dfc88a838d052fa3dbed8be7b66c1c8326ccd09bb9a0a2445dd4c535916da0e0fa2546340882867adcc897586e1e0f2b42244cf1c74
7
+ data.tar.gz: a0c2124b3455b1d240a27420214a0ba5b31c8114a2002032fba26e02938fdb1ed99183a2b4a01bfdc250e16fd58a06b92612a78f5b7c8e017e95ab20bf6efabe
data/.rspec CHANGED
@@ -1,2 +1,3 @@
1
1
  --color
2
2
  --require spec_helper
3
+ --warnings
@@ -1,3 +1,19 @@
1
+ ## v0.2.3 2015-06-02
2
+
3
+ ### Added
4
+
5
+ * `:split` splits a hash to array by a list of values from a specified key of the hash (nepalez)
6
+ * `:ungroup` is an inverse array transormation with respect to the `:group` (nepalez)
7
+ * `:insert_key` (and `:insert_key!`) is the partial inversion of `:extract_key`.
8
+ The method converts array of values into array of tuples with given key (nepalez)
9
+ * `:add_keys` (and `:add_keys!`) adds missing keys to all tuples in array (nepalez)
10
+
11
+ ### Fixed
12
+
13
+ * Warning about circular requires is gone now (splattael)
14
+
15
+ [Compare v0.2.2...v0.2.3](https://github.com/solnic/transproc/compare/v0.2.2...v0.2.3)
16
+
1
17
  ## v0.2.2 2015-05-22
2
18
 
3
19
  ### Added
@@ -1,5 +1,6 @@
1
1
  require 'transproc/version'
2
2
  require 'transproc/function'
3
+ require 'transproc/functions'
3
4
  require 'transproc/composer'
4
5
  require 'transproc/error'
5
6
 
@@ -42,29 +43,11 @@ module Transproc
42
43
  def functions
43
44
  @_functions ||= {}
44
45
  end
45
-
46
- # Function container extension
47
- #
48
- # @example
49
- # module MyTransformations
50
- # extend Transproc::Functions
51
- #
52
- # def boom!(value)
53
- # "#{value} BOOM!"
54
- # end
55
- # end
56
- #
57
- # Transproc(:boom!)['w00t!'] # => "w00t! BOOM!"
58
- #
59
- # @api public
60
- module Functions
61
- def method_added(meth)
62
- module_function meth
63
- Transproc.register(meth, method(meth))
64
- end
65
- end
66
46
  end
67
47
 
48
+ require 'transproc/array'
49
+ require 'transproc/hash'
50
+
68
51
  # Access registered functions
69
52
  #
70
53
  # @example
@@ -1,5 +1,3 @@
1
- require 'transproc/hash'
2
-
3
1
  module Transproc
4
2
  # Transformation functions for Array objects
5
3
  #
@@ -99,6 +97,30 @@ module Transproc
99
97
  end
100
98
  end
101
99
 
100
+ # Ungroup array values using provided root key and value keys
101
+ #
102
+ # @example
103
+ # fn = Transproc(:group, :tags, [:tag_name])
104
+ #
105
+ # fn.call [
106
+ # { task: 'Group it', tags: [{ tag: 'task' }, { tag: 'important' }]
107
+ # ]
108
+ # # => [
109
+ # { task: 'Group it', tag: 'task' },
110
+ # { task: 'Group it', tag: 'important' }
111
+ # ]
112
+ #
113
+ # @param [Array] array The input array
114
+ # @param [Object] key The nesting root key
115
+ # @param [Object] keys The nesting value keys
116
+ #
117
+ # @return [Array]
118
+ #
119
+ # @api public
120
+ def ungroup(array, key, keys)
121
+ array.flat_map { |item| HashTransformations.split(item, key, keys) }
122
+ end
123
+
102
124
  # Combines two arrays by merging child items from right array using join keys
103
125
  #
104
126
  # @example
@@ -175,5 +197,55 @@ module Transproc
175
197
  def extract_key!(array, key)
176
198
  map_array!(array, -> v { v[key] })
177
199
  end
200
+
201
+ # Wraps every value of the array to tuple with given key
202
+ #
203
+ # The transformation partially inverses the `extract_key`.
204
+ #
205
+ # @example
206
+ # fn = t(:insert_key, 'name')
207
+ # fn.call ['Alice', 'Bob', nil]
208
+ # # => [{ 'name' => 'Alice' }, { 'name' => 'Bob' }, { 'name' => nil }]
209
+ #
210
+ # @param [Array<Hash>] array The input array of hashes
211
+ # @param [Object] key The key to extract values by
212
+ #
213
+ # @return [Array]
214
+ #
215
+ # @api public
216
+ def insert_key(array, key)
217
+ insert_key!(Array[*array], key)
218
+ end
219
+
220
+ # Same as `insert_key` but mutates the array
221
+ #
222
+ # @see ArrayTransformations.insert_key
223
+ #
224
+ # @api public
225
+ def insert_key!(array, key)
226
+ map_array!(array, -> v { { key => v } })
227
+ end
228
+
229
+ # Adds missing keys with nil value to all tuples in array
230
+ #
231
+ # @param [Array] keys
232
+ #
233
+ # @return [Array]
234
+ #
235
+ # @api public
236
+ #
237
+ def add_keys(array, keys)
238
+ add_keys!(Array[*array], keys)
239
+ end
240
+
241
+ # Same as `add_keys` but mutates the array
242
+ #
243
+ # @see ArrayTransformations.add_keys
244
+ #
245
+ # @api public
246
+ def add_keys!(array, keys)
247
+ base = keys.inject({}) { |a, e| a.merge(e => nil) }
248
+ map_array!(array, -> v { base.merge(v) })
249
+ end
178
250
  end
179
251
  end
@@ -0,0 +1,22 @@
1
+ module Transproc
2
+ # Function container extension
3
+ #
4
+ # @example
5
+ # module MyTransformations
6
+ # extend Transproc::Functions
7
+ #
8
+ # def boom!(value)
9
+ # "#{value} BOOM!"
10
+ # end
11
+ # end
12
+ #
13
+ # Transproc(:boom!)['w00t!'] # => "w00t! BOOM!"
14
+ #
15
+ # @api public
16
+ module Functions
17
+ def method_added(meth)
18
+ module_function meth
19
+ Transproc.register(meth, method(meth))
20
+ end
21
+ end
22
+ end
@@ -1,4 +1,3 @@
1
- require 'transproc/array'
2
1
  require 'transproc/coercions'
3
2
 
4
3
  module Transproc
@@ -146,15 +145,6 @@ module Transproc
146
145
  hash
147
146
  end
148
147
 
149
- # Same as `:reject_keys` but mutates the hash
150
- #
151
- # @see HashTransformations.reject_keys
152
- #
153
- # @api public
154
- def reject_keys!(hash, keys)
155
- hash.reject { |k, _| keys.include?(k) }
156
- end
157
-
158
148
  # Rejects specified keys from a hash
159
149
  #
160
150
  # @example
@@ -171,13 +161,13 @@ module Transproc
171
161
  reject_keys!(Hash[hash], keys)
172
162
  end
173
163
 
174
- # Same as `:accept_keys` but mutates the hash
164
+ # Same as `:reject_keys` but mutates the hash
175
165
  #
176
166
  # @see HashTransformations.reject_keys
177
167
  #
178
168
  # @api public
179
- def accept_keys!(hash, keys)
180
- reject_keys!(hash, hash.keys - keys)
169
+ def reject_keys!(hash, keys)
170
+ hash.reject { |k, _| keys.include?(k) }
181
171
  end
182
172
 
183
173
  # Accepts specified keys from a hash
@@ -196,6 +186,15 @@ module Transproc
196
186
  accept_keys!(Hash[hash], keys)
197
187
  end
198
188
 
189
+ # Same as `:accept_keys` but mutates the hash
190
+ #
191
+ # @see HashTransformations.accept
192
+ #
193
+ # @api public
194
+ def accept_keys!(hash, keys)
195
+ reject_keys!(hash, hash.keys - keys)
196
+ end
197
+
199
198
  # Map a key in a hash with the provided transformation function
200
199
  #
201
200
  # @example
@@ -213,7 +212,7 @@ module Transproc
213
212
 
214
213
  # Same as `:map_value` but mutates the hash
215
214
  #
216
- # @see HashTransformations.map_value!
215
+ # @see HashTransformations.map_value
217
216
  #
218
217
  # @api public
219
218
  def map_value!(hash, key, fn)
@@ -272,9 +271,10 @@ module Transproc
272
271
  # @see HashTransformations.unwrap
273
272
  #
274
273
  # @api public
275
- def unwrap!(hash, root, keys = nil)
274
+ def unwrap!(hash, root, selected = nil)
276
275
  if nested_hash = hash[root]
277
- keys ||= nested_hash.keys
276
+ keys = nested_hash.keys
277
+ keys &= selected if selected
278
278
  hash.update(Hash[keys.zip(keys.map { |key| nested_hash.delete(key) })])
279
279
  hash.delete(root) if nested_hash.empty?
280
280
  end
@@ -284,8 +284,6 @@ module Transproc
284
284
 
285
285
  # Folds array of tuples to array of values from a specified key
286
286
  #
287
- # The second argument defines the key to fold tuples by
288
- #
289
287
  # @example
290
288
  # source = {
291
289
  # name: "Jane",
@@ -296,7 +294,9 @@ module Transproc
296
294
  # Transproc(:fold, :tasks, :priority)[source]
297
295
  # # => { name: "Jane", tasks: [1, nil] }
298
296
  #
299
- # @param [Hash]
297
+ # @param [Hash] hash
298
+ # @param [Object] key The key to fold values to
299
+ # @param [Object] tuple_key The key to take folded values from
300
300
  #
301
301
  # @return [Hash]
302
302
  #
@@ -313,5 +313,46 @@ module Transproc
313
313
  def fold!(hash, key, tuple_key)
314
314
  hash.merge!(key => ArrayTransformations.extract_key(hash[key], tuple_key))
315
315
  end
316
+
317
+ # Splits hash to array by all values from a specified key
318
+ #
319
+ # The operation adds missing keys extracted from the array to regularize the output.
320
+ #
321
+ # @example
322
+ # input = {
323
+ # name: 'Joe',
324
+ # tasks: [
325
+ # { title: 'sleep well', priority: 1 },
326
+ # { title: 'be nice', priority: 2 },
327
+ # { priority: 2 },
328
+ # { title: 'be cool' }
329
+ # ]
330
+ # }
331
+ # Transproc(:split, :tasks, [:priority])
332
+ # => [
333
+ # { name: 'Joe', priority: 1, tasks: [{ title: 'sleep well' }] },
334
+ # { name: 'Joe', priority: 2, tasks: [{ title: 'be nice' }, { title: nil }] },
335
+ # { name: 'Joe', priority: nil, tasks: [{ title: 'be cool' }] }
336
+ # ]
337
+ #
338
+ # @param [Hash] hash
339
+ # @param [Object] key The key to split a hash by
340
+ # @param [Array] subkeys The list of subkeys to be extracted from key
341
+ #
342
+ # @return [Array<Hash>]
343
+ #
344
+ # @api public
345
+ def split(hash, key, keys)
346
+ list = Array(hash[key])
347
+ return [hash.reject { |k, _| k == key }] if list.empty?
348
+
349
+ existing = list.flat_map(&:keys).uniq
350
+ grouped = existing - keys
351
+ ungrouped = existing & keys
352
+
353
+ list = ArrayTransformations.group(list, key, grouped) if grouped.any?
354
+ list = list.map { |item| item.merge(reject_keys(hash, [key])) }
355
+ ArrayTransformations.add_keys(list, ungrouped)
356
+ end
316
357
  end
317
358
  end
@@ -1,3 +1,3 @@
1
1
  module Transproc
2
- VERSION = '0.2.2'.freeze
2
+ VERSION = '0.2.3'.freeze
3
3
  end
@@ -37,6 +37,80 @@ describe Transproc::ArrayTransformations do
37
37
  end
38
38
  end
39
39
 
40
+ describe '.insert_key' do
41
+ it 'wraps values to tuples with given key' do
42
+ insert_key = t(:insert_key, 'name')
43
+
44
+ original = ['Alice', 'Bob', nil]
45
+
46
+ input = original
47
+
48
+ output = [
49
+ { 'name' => 'Alice' },
50
+ { 'name' => 'Bob' },
51
+ { 'name' => nil }
52
+ ]
53
+
54
+ expect(insert_key[input]).to eql(output)
55
+ expect(input).to eql(original)
56
+ end
57
+ end
58
+
59
+ describe '.insert_key!' do
60
+ it 'wraps values to tuples with given key' do
61
+ insert_key = t(:insert_key!, 'name')
62
+
63
+ original = ['Alice', 'Bob', nil]
64
+
65
+ input = original
66
+
67
+ output = [
68
+ { 'name' => 'Alice' },
69
+ { 'name' => 'Bob' },
70
+ { 'name' => nil }
71
+ ]
72
+
73
+ expect(insert_key[input]).to eql(output)
74
+ expect(input).to eql(output)
75
+ end
76
+ end
77
+
78
+ describe '.add_keys' do
79
+ it 'returns a new array with missed keys added to tuples' do
80
+ add_keys = t(:add_keys, [:foo, :bar, :baz])
81
+
82
+ original = [{ foo: 'bar' }, { bar: 'baz' }]
83
+
84
+ input = original
85
+
86
+ output = [
87
+ { foo: 'bar', bar: nil, baz: nil },
88
+ { foo: nil, bar: 'baz', baz: nil }
89
+ ]
90
+
91
+ expect(add_keys[input]).to eql(output)
92
+ expect(input).to eql(original)
93
+ end
94
+ end
95
+
96
+ describe '.add_keys!' do
97
+ it 'adds missed keys to tuples' do
98
+ add_keys = t(:add_keys!, [:foo, :bar, :baz])
99
+
100
+ original = [{ foo: 'bar' }, { bar: 'baz' }]
101
+
102
+ input = original
103
+
104
+ output = [
105
+ { foo: 'bar', bar: nil, baz: nil },
106
+ { foo: nil, bar: 'baz', baz: nil }
107
+ ]
108
+
109
+ expect(add_keys[input]).to eql(output)
110
+ expect(input).to eql(output)
111
+ end
112
+ end
113
+
40
114
  describe '.map_array' do
41
115
  it 'applies funtions to all values' do
42
116
  map = t(:map_array, t(:symbolize_keys))
@@ -114,6 +188,31 @@ describe Transproc::ArrayTransformations do
114
188
  end
115
189
  end
116
190
 
191
+ describe '.ungroup' do
192
+ subject(:ungroup) { t(:ungroup, :tasks, [:title]) }
193
+
194
+ it 'returns a new array with ungrouped hashes' do
195
+ input = [{ name: 'Jane', tasks: [{ title: 'One' }, { title: 'Two' }] }]
196
+ output = [{ name: 'Jane', title: 'One' }, { name: 'Jane', title: 'Two' }]
197
+
198
+ expect(ungroup[input]).to eql(output)
199
+ end
200
+
201
+ it 'returns an input with empty array removed' do
202
+ input = [{ name: 'Jane', tasks: [] }]
203
+ output = [{ name: 'Jane' }]
204
+
205
+ expect(ungroup[input]).to eql(output)
206
+ end
207
+
208
+ it 'returns an input when a key is absent' do
209
+ input = [{ name: 'Jane' }]
210
+ output = [{ name: 'Jane' }]
211
+
212
+ expect(ungroup[input]).to eql(output)
213
+ end
214
+ end
215
+
117
216
  describe '.combine' do
118
217
  let(:input) do
119
218
  [
@@ -206,6 +206,17 @@ describe Transproc::HashTransformations do
206
206
 
207
207
  expect(input).to eql(output)
208
208
  end
209
+
210
+ it 'ignores unknown keys' do
211
+ unwrap = t(:unwrap!, 'wrapped', %w(one two three))
212
+
213
+ input = { 'wrapped' => { 'one' => nil, 'two' => false } }
214
+ output = { 'one' => nil, 'two' => false }
215
+
216
+ unwrap[input]
217
+
218
+ expect(input).to eql(output)
219
+ end
209
220
  end
210
221
 
211
222
  describe '.unwrap' do
@@ -354,4 +365,88 @@ describe Transproc::HashTransformations do
354
365
  expect(input).to eql output
355
366
  end
356
367
  end
368
+
369
+ describe '.split' do
370
+ let(:input) do
371
+ {
372
+ name: 'Joe',
373
+ tasks: [
374
+ { title: 'sleep well', priority: 1 },
375
+ { title: 'be nice', priority: 2 },
376
+ { priority: 2 },
377
+ { title: 'be cool' },
378
+ {}
379
+ ]
380
+ }
381
+ end
382
+
383
+ it 'splits a tuple into array partially by given keys' do
384
+ split = t(:split, :tasks, [:priority])
385
+
386
+ output = [
387
+ {
388
+ name: 'Joe', priority: 1,
389
+ tasks: [{ title: 'sleep well' }]
390
+ },
391
+ {
392
+ name: 'Joe', priority: 2,
393
+ tasks: [{ title: 'be nice' }, { title: nil }]
394
+ },
395
+ {
396
+ name: 'Joe', priority: nil,
397
+ tasks: [{ title: 'be cool' }, { title: nil }]
398
+ }
399
+ ]
400
+
401
+ expect(split[input]).to eql output
402
+ end
403
+
404
+ it 'splits a tuple into array fully by all subkeys' do
405
+ split = t(:split, :tasks, [:priority, :title])
406
+
407
+ output = [
408
+ { name: 'Joe', title: 'sleep well', priority: 1 },
409
+ { name: 'Joe', title: 'be nice', priority: 2 },
410
+ { name: 'Joe', title: nil, priority: 2 },
411
+ { name: 'Joe', title: 'be cool', priority: nil },
412
+ { name: 'Joe', title: nil, priority: nil }
413
+ ]
414
+
415
+ expect(split[input]).to eql output
416
+ end
417
+
418
+ it 'returns an array of one tuple with updated keys when there is nothing to split by' do
419
+ output = [
420
+ {
421
+ name: 'Joe',
422
+ tasks: [
423
+ { title: 'sleep well', priority: 1 },
424
+ { title: 'be nice', priority: 2 },
425
+ { title: nil, priority: 2 },
426
+ { title: 'be cool', priority: nil },
427
+ { title: nil, priority: nil }
428
+ ]
429
+ }
430
+ ]
431
+
432
+ split = t(:split, :tasks, [])
433
+ expect(split[input]).to eql output
434
+
435
+ split = t(:split, :tasks, [:absent])
436
+ expect(split[input]).to eql output
437
+ end
438
+
439
+ it 'returns an array of initial tuple when attribute is absent' do
440
+ split = t(:split, :absent, [:priority, :title])
441
+ expect(split[input]).to eql [input]
442
+ end
443
+
444
+ it 'ignores empty array' do
445
+ input = { name: 'Joe', tasks: [] }
446
+
447
+ split = t(:split, :tasks, [:title])
448
+
449
+ expect(split[input]).to eql [{ name: 'Joe' }]
450
+ end
451
+ end
357
452
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: transproc
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Piotr Solnica
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-05-22 00:00:00.000000000 Z
11
+ date: 2015-06-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -80,6 +80,7 @@ files:
80
80
  - lib/transproc/conditional.rb
81
81
  - lib/transproc/error.rb
82
82
  - lib/transproc/function.rb
83
+ - lib/transproc/functions.rb
83
84
  - lib/transproc/hash.rb
84
85
  - lib/transproc/recursion.rb
85
86
  - lib/transproc/version.rb