dry-transformer 0.1.0 → 0.1.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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +8 -0
  3. data/LICENSE +1 -1
  4. data/README.md +1 -1
  5. data/dry-transformer.gemspec +17 -10
  6. data/lib/dry/transformer/hash.rb +2 -1
  7. data/lib/dry/transformer/version.rb +1 -1
  8. metadata +10 -56
  9. data/.codeclimate.yml +0 -12
  10. data/.github/ISSUE_TEMPLATE/----please-don-t-ask-for-support-via-issues.md +0 -10
  11. data/.github/ISSUE_TEMPLATE/---bug-report.md +0 -30
  12. data/.github/ISSUE_TEMPLATE/---feature-request.md +0 -18
  13. data/.github/workflows/custom_ci.yml +0 -66
  14. data/.github/workflows/docsite.yml +0 -34
  15. data/.github/workflows/sync_configs.yml +0 -34
  16. data/.gitignore +0 -16
  17. data/.rspec +0 -4
  18. data/.rubocop.yml +0 -95
  19. data/CODE_OF_CONDUCT.md +0 -13
  20. data/CONTRIBUTING.md +0 -29
  21. data/Gemfile +0 -19
  22. data/Rakefile +0 -6
  23. data/docsite/source/built-in-transformations.html.md +0 -47
  24. data/docsite/source/index.html.md +0 -15
  25. data/docsite/source/transformation-objects.html.md +0 -32
  26. data/docsite/source/using-standalone-functions.html.md +0 -82
  27. data/spec/spec_helper.rb +0 -31
  28. data/spec/unit/array/combine_spec.rb +0 -224
  29. data/spec/unit/array_transformations_spec.rb +0 -233
  30. data/spec/unit/class_transformations_spec.rb +0 -50
  31. data/spec/unit/coercions_spec.rb +0 -132
  32. data/spec/unit/conditional_spec.rb +0 -48
  33. data/spec/unit/function_not_found_error_spec.rb +0 -12
  34. data/spec/unit/function_spec.rb +0 -193
  35. data/spec/unit/hash_transformations_spec.rb +0 -490
  36. data/spec/unit/proc_transformations_spec.rb +0 -20
  37. data/spec/unit/recursion_spec.rb +0 -145
  38. data/spec/unit/registry_spec.rb +0 -202
  39. data/spec/unit/store_spec.rb +0 -198
  40. data/spec/unit/transformer/class_interface_spec.rb +0 -350
  41. data/spec/unit/transformer/dsl_spec.rb +0 -15
  42. data/spec/unit/transformer/instance_methods_spec.rb +0 -25
@@ -1,12 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe Dry::Transformer::FunctionNotFoundError do
4
- it 'complains that the function not registered' do
5
- Foo = Module.new { extend Dry::Transformer::Registry }
6
-
7
- expect { Foo[:foo] }.to raise_error do |error|
8
- expect(error).to be_kind_of described_class
9
- expect(error.message['function Foo[:foo]']).not_to be_nil
10
- end
11
- end
12
- end
@@ -1,193 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe Dry::Transformer::Function do
4
- let(:container) do
5
- Module.new do
6
- extend Dry::Transformer::Registry
7
-
8
- import Dry::Transformer::HashTransformations
9
- end
10
- end
11
-
12
- describe '#name' do
13
- let(:block) { proc { |v| v } }
14
-
15
- it 'returns the name of the module function' do
16
- expect(container[:symbolize_keys].name).to eql :symbolize_keys
17
- end
18
-
19
- it 'returns the explicitly assigned name' do
20
- expect(described_class.new(block, name: :identity).name).to eql :identity
21
- end
22
-
23
- it 'returns the unnamed closure' do
24
- expect(described_class.new(block).name).to eql block
25
- end
26
- end
27
-
28
- describe '#>>' do
29
- it 'composes named functions' do
30
- f1 = container[:symbolize_keys]
31
- f2 = container[:rename_keys, user_name: :name]
32
-
33
- f3 = f1 >> f2
34
-
35
- expect(f3.to_ast).to eql(
36
- [
37
- :symbolize_keys, [],
38
- [
39
- :rename_keys, [user_name: :name]
40
- ]
41
- ]
42
- )
43
-
44
- expect(f3['user_name' => 'Jane']).to eql(name: 'Jane')
45
-
46
- f4 = f3 >> container[:nest, :details, [:name]]
47
-
48
- expect(f4.to_ast).to eql(
49
- [
50
- :symbolize_keys, [],
51
- [
52
- :rename_keys, [user_name: :name]
53
- ],
54
- [
55
- :nest, [:details, [:name]]
56
- ]
57
- ]
58
- )
59
-
60
- expect(f4['user_name' => 'Jane']).to eql(details: { name: 'Jane' })
61
- end
62
-
63
- it 'composes anonymous functions' do
64
- f1 = container[->(v, m) { v * m }, 2]
65
- f2 = container[:to_s.to_proc]
66
-
67
- f3 = f1 >> f2
68
-
69
- expect(f3.to_ast).to eql(
70
- [
71
- f1.fn, [2],
72
- [
73
- f2.fn, []
74
- ]
75
- ]
76
- )
77
- end
78
-
79
- it 'plays well with registered compositions' do
80
- container.register(:user_names, container[:symbolize_keys] + container[:rename_keys, user_name: :name])
81
- f = container[:user_names]
82
-
83
- expect(f['user_name' => 'Jane']).to eql(name: 'Jane')
84
- expect(f.to_ast).to eql(
85
- [
86
- :symbolize_keys, [],
87
- [
88
- :rename_keys, [user_name: :name]
89
- ]
90
- ]
91
- )
92
- end
93
-
94
- it 'plays well with registered functions' do
95
- container.register(:to_string, Dry::Transformer::Coercions.t(:to_string))
96
- fn = container.t(:to_string)
97
-
98
- expect(fn[:ok]).to eql('ok')
99
- expect(fn.to_ast).to eql([:to_string, []])
100
- end
101
-
102
- it 'plays well with functions as arguments' do
103
- container.register(:map_array, Dry::Transformer::ArrayTransformations.t(:map_array))
104
- container.register(:to_symbol, Dry::Transformer::Coercions.t(:to_symbol))
105
- fn = container.t(:map_array, container.t(:to_symbol))
106
-
107
- expect(fn.call(%w(a b c))).to eql([:a, :b, :c])
108
- expect(fn.to_ast).to eql(
109
- [
110
- :map_array, [
111
- [:to_symbol, []]
112
- ]
113
- ]
114
- )
115
- end
116
- end
117
-
118
- describe '#==' do
119
- let(:fns) do
120
- Module.new do
121
- extend Dry::Transformer::Registry
122
- import :wrap, from: Dry::Transformer::ArrayTransformations
123
- end
124
- end
125
-
126
- it 'returns true when the other is equal' do
127
- left = fns[:wrap, :user, [:name, :email]]
128
- right = fns[:wrap, :user, [:name, :email]]
129
-
130
- expect(left == right).to be(true)
131
- end
132
-
133
- it 'returns false when the other is not a fn' do
134
- left = fns[:wrap, :user, [:name, :email]]
135
- right = 'boo!'
136
-
137
- expect(left == right).to be(false)
138
- end
139
- end
140
-
141
- describe '#to_proc' do
142
- shared_examples :providing_a_proc do
143
- let(:fn) { described_class.new(source) }
144
- subject { fn.to_proc }
145
-
146
- it 'returns a proc' do
147
- expect(subject).to be_instance_of Proc
148
- end
149
-
150
- it 'works fine' do
151
- expect(subject.call :foo).to eql('foo')
152
- end
153
- end
154
-
155
- context 'from a method' do
156
- let(:source) do
157
- mod = Module.new do
158
- def self.get(x)
159
- x.to_s
160
- end
161
- end
162
- mod.method(:get)
163
- end
164
- it_behaves_like :providing_a_proc
165
- end
166
-
167
- context 'from a proc' do
168
- let(:source) { -> value { value.to_s } }
169
- it_behaves_like :providing_a_proc
170
- end
171
-
172
- context 'from a transproc' do
173
- let(:source) { Dry::Transformer::Function.new -> value { value.to_s } }
174
- it_behaves_like :providing_a_proc
175
-
176
- it 'can be applied to collection' do
177
- expect([:foo, :bar].map(&source)).to eql(%w(foo bar))
178
- end
179
- end
180
-
181
- context 'with curried args' do
182
- let(:source) { -> i, j { [i, j].join(' ') } }
183
-
184
- it 'works fine' do
185
- fn = described_class.new(source, args: ['world'])
186
-
187
- result = fn.to_proc.call('hello')
188
-
189
- expect(result).to eql('hello world')
190
- end
191
- end
192
- end
193
- end
@@ -1,490 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe Dry::Transformer::HashTransformations do
4
- describe '.map_keys' do
5
- it 'returns a new hash with given proc applied to keys' do
6
- map_keys = described_class.t(:map_keys, ->(key) { key.strip })
7
-
8
- input = { ' foo ' => 'bar' }.freeze
9
- output = { 'foo' => 'bar' }
10
-
11
- expect(map_keys[input]).to eql(output)
12
- end
13
- end
14
-
15
- it { expect(described_class).not_to be_contain(:map_keys!) }
16
-
17
- describe '.symbolize_keys' do
18
- it 'returns a new hash with symbolized keys' do
19
- symbolize_keys = described_class.t(:symbolize_keys)
20
-
21
- input = { 1 => 'bar' }.freeze
22
- output = { '1'.to_sym => 'bar' }
23
-
24
- expect(symbolize_keys[input]).to eql(output)
25
- end
26
- end
27
-
28
- describe '.deep_symbolize_keys' do
29
- it 'returns a new hash with symbolized keys' do
30
- symbolize_keys = described_class.t(:deep_symbolize_keys)
31
-
32
- input = { 'foo' => 'bar', 'baz' => [{ 'one' => 1 }, 'two'] }
33
- output = { foo: 'bar', baz: [{ one: 1 }, 'two'] }
34
-
35
- expect(symbolize_keys[input]).to eql(output)
36
- expect(input).to eql('foo' => 'bar', 'baz' => [{ 'one' => 1 }, 'two'])
37
- end
38
- end
39
-
40
- it { expect(described_class).not_to be_contain(:symbolize_keys!) }
41
-
42
- describe '.stringify_keys' do
43
- it 'returns a new hash with stringified keys' do
44
- stringify_keys = described_class.t(:stringify_keys)
45
-
46
- input = { foo: 'bar' }.freeze
47
- output = { 'foo' => 'bar' }
48
-
49
- expect(stringify_keys[input]).to eql(output)
50
- end
51
- end
52
-
53
- describe '.deep_stringify_keys' do
54
- it 'returns a new hash with symbolized keys' do
55
- stringify_keys = described_class.t(:deep_stringify_keys)
56
-
57
- input = { foo: 'bar', baz: [{ one: 1 }, 'two'] }
58
- output = { 'foo' => 'bar', 'baz' => [{ 'one' => 1 }, 'two'] }
59
-
60
- expect(stringify_keys[input]).to eql(output)
61
- end
62
- end
63
-
64
- it { expect(described_class).not_to be_contain(:stringify_keys!) }
65
-
66
- describe '.map_values' do
67
- it 'returns a new hash with given proc applied to values' do
68
- map_values = described_class.t(:map_values, ->(value) { value.strip })
69
-
70
- input = { 'foo' => ' bar ' }.freeze
71
- output = { 'foo' => 'bar' }
72
-
73
- expect(map_values[input]).to eql(output)
74
- end
75
- end
76
-
77
- it { expect(described_class).not_to be_contain(:map_values!) }
78
-
79
- describe '.rename_keys' do
80
- it 'returns a new hash with applied functions' do
81
- map = described_class.t(:rename_keys, 'foo' => :foo)
82
-
83
- input = { 'foo' => 'bar', :bar => 'baz' }.freeze
84
- output = { foo: 'bar', bar: 'baz' }
85
-
86
- expect(map[input]).to eql(output)
87
- end
88
-
89
- it 'only renames keys and never creates new ones' do
90
- map = described_class.t(:rename_keys, 'foo' => :foo, 'bar' => :bar)
91
-
92
- input = { 'bar' => 'baz' }.freeze
93
- output = { bar: 'baz' }
94
-
95
- expect(map[input]).to eql(output)
96
- end
97
- end
98
-
99
- it { expect(described_class).not_to be_contain(:rename_keys!) }
100
-
101
- describe '.copy_keys' do
102
- context 'with single destination key' do
103
- it 'returns a new hash with applied functions' do
104
- map = described_class.t(:copy_keys, 'foo' => :foo)
105
-
106
- input = { 'foo' => 'bar', :bar => 'baz' }.freeze
107
- output = { 'foo' => 'bar', foo: 'bar', bar: 'baz' }
108
-
109
- expect(map[input]).to eql(output)
110
- end
111
- end
112
-
113
- context 'with multiple destination keys' do
114
- it 'returns a new hash with applied functions' do
115
- map = described_class.t(:copy_keys, 'foo' => [:foo, :baz])
116
-
117
- input = { 'foo' => 'bar', :bar => 'baz' }.freeze
118
- output = { 'foo' => 'bar', foo: 'bar', baz: 'bar', bar: 'baz' }
119
-
120
- expect(map[input]).to eql(output)
121
- end
122
- end
123
- end
124
-
125
- it { expect(described_class).not_to be_contain(:copy_keys!) }
126
-
127
- describe '.map_value' do
128
- it 'applies function to value under specified key' do
129
- transformation =
130
- described_class.t(:map_value, :user, described_class.t(:symbolize_keys))
131
-
132
- input = { user: { 'name' => 'Jane' }.freeze }.freeze
133
- output = { user: { name: 'Jane' } }
134
-
135
- expect(transformation[input]).to eql(output)
136
- end
137
- end
138
-
139
- it { expect(described_class).not_to be_contain(:map_value!) }
140
-
141
- describe '.nest' do
142
- it 'returns new hash with keys nested under a new key' do
143
- nest = described_class.t(:nest, :baz, ['foo'])
144
-
145
- input = { 'foo' => 'bar' }.freeze
146
- output = { baz: { 'foo' => 'bar' } }
147
-
148
- expect(nest[input]).to eql(output)
149
- end
150
-
151
- it 'returns new hash with keys nested under the existing key' do
152
- nest = described_class.t(:nest, :baz, ['two'])
153
-
154
- input = {
155
- 'foo' => 'bar',
156
- baz: { 'one' => nil }.freeze,
157
- 'two' => false
158
- }.freeze
159
-
160
- output = { 'foo' => 'bar', baz: { 'one' => nil, 'two' => false } }
161
-
162
- expect(nest[input]).to eql(output)
163
- end
164
-
165
- it 'rewrites the existing key if its value is not a hash' do
166
- nest = described_class.t(:nest, :baz, ['two'])
167
-
168
- input = { 'foo' => 'bar', baz: 'one', 'two' => false }.freeze
169
- output = { 'foo' => 'bar', baz: { 'two' => false } }
170
-
171
- expect(nest[input]).to eql(output)
172
- end
173
-
174
- it 'returns new hash with an empty hash under a new key when nest-keys are missing' do
175
- nest = described_class.t(:nest, :baz, ['foo'])
176
-
177
- input = { 'bar' => 'foo' }.freeze
178
- output = { 'bar' => 'foo', baz: {} }
179
-
180
- expect(nest[input]).to eql(output)
181
- end
182
- end
183
-
184
- it { expect(described_class).not_to be_contain(:nest!) }
185
-
186
- describe '.unwrap' do
187
- it 'returns new hash with nested keys lifted to the root' do
188
- unwrap = described_class.t(:unwrap, 'wrapped', %w(one))
189
-
190
- input = {
191
- 'foo' => 'bar',
192
- 'wrapped' => { 'one' => nil, 'two' => false }.freeze
193
- }.freeze
194
-
195
- output = { 'foo' => 'bar', 'one' => nil, 'wrapped' => { 'two' => false } }
196
-
197
- expect(unwrap[input]).to eql(output)
198
- end
199
-
200
- it 'lifts all keys if none are passed' do
201
- unwrap = described_class.t(:unwrap, 'wrapped')
202
-
203
- input = { 'wrapped' => { 'one' => nil, 'two' => false }.freeze }.freeze
204
- output = { 'one' => nil, 'two' => false }
205
-
206
- expect(unwrap[input]).to eql(output)
207
- end
208
-
209
- it 'ignores unknown keys' do
210
- unwrap = described_class.t(:unwrap, 'wrapped', %w(one two three))
211
-
212
- input = { 'wrapped' => { 'one' => nil, 'two' => false }.freeze }.freeze
213
- output = { 'one' => nil, 'two' => false }
214
-
215
- expect(unwrap[input]).to eql(output)
216
- end
217
-
218
- it 'prefixes unwrapped keys and retains root string type if prefix option is truthy' do
219
- unwrap = described_class.t(:unwrap, 'wrapped', prefix: true)
220
-
221
- input = { 'wrapped' => { one: nil, two: false }.freeze }.freeze
222
- output = { 'wrapped_one' => nil, 'wrapped_two' => false }
223
-
224
- expect(unwrap[input]).to eql(output)
225
- end
226
-
227
- it 'prefixes unwrapped keys and retains root type if prefix option is truthy' do
228
- unwrap = described_class.t(:unwrap, :wrapped, prefix: true)
229
-
230
- input = { wrapped: { 'one' => nil, 'two' => false }.freeze }.freeze
231
- output = { wrapped_one: nil, wrapped_two: false }
232
-
233
- expect(unwrap[input]).to eql(output)
234
- end
235
- end
236
-
237
- it { expect(described_class).not_to be_contain(:unwrap!) }
238
-
239
- describe 'nested transform' do
240
- it 'applies functions to nested hashes' do
241
- symbolize_keys = described_class.t(:symbolize_keys)
242
- map_user_key = described_class.t(:map_value, :user, symbolize_keys)
243
-
244
- transformation = symbolize_keys >> map_user_key
245
-
246
- input = { 'user' => { 'name' => 'Jane' } }
247
- output = { user: { name: 'Jane' } }
248
-
249
- expect(transformation[input]).to eql(output)
250
- end
251
- end
252
-
253
- describe 'combining transformations' do
254
- it 'applies functions to the hash' do
255
- symbolize_keys = described_class.t(:symbolize_keys)
256
- map = described_class.t :rename_keys, user_name: :name, user_email: :email
257
-
258
- transformation = symbolize_keys >> map
259
-
260
- input = { 'user_name' => 'Jade', 'user_email' => 'jade@doe.org' }
261
- output = { name: 'Jade', email: 'jade@doe.org' }
262
-
263
- result = transformation[input]
264
-
265
- expect(result).to eql(output)
266
- end
267
- end
268
-
269
- describe '.reject_keys' do
270
- it 'returns a new hash with rejected keys' do
271
- reject_keys = described_class.t(:reject_keys, [:name, :age])
272
-
273
- input = { name: 'Jane', email: 'jane@doe.org', age: 21 }.freeze
274
- output = { email: 'jane@doe.org' }
275
-
276
- expect(reject_keys[input]).to eql(output)
277
- end
278
- end
279
-
280
- it { expect(described_class).not_to be_contain(:reject_keys!) }
281
-
282
- describe '.accept_keys' do
283
- it 'returns a new hash with rejected keys' do
284
- accept_keys = described_class.t(:accept_keys, [:age])
285
-
286
- input = { name: 'Jane', email: 'jane@doe.org', age: 21 }.freeze
287
- output = { age: 21 }
288
-
289
- expect(accept_keys[input]).to eql(output)
290
- end
291
- end
292
-
293
- it { expect(described_class).not_to be_contain(:accept_keys!) }
294
-
295
- describe '.fold' do
296
- let(:input) do
297
- {
298
- name: 'Jane',
299
- tasks: [
300
- { title: 'be nice', priority: 1 }.freeze,
301
- { title: 'sleep well' }.freeze
302
- ].freeze
303
- }.freeze
304
- end
305
-
306
- it 'returns a new hash with folded values' do
307
- fold = described_class.t(:fold, :tasks, :title)
308
-
309
- output = { name: 'Jane', tasks: ['be nice', 'sleep well'] }
310
-
311
- expect(fold[input]).to eql(output)
312
- end
313
-
314
- it 'uses nil if there was not such attribute' do
315
- fold = described_class.t(:fold, :tasks, :priority)
316
-
317
- output = { name: 'Jane', tasks: [1, nil] }
318
-
319
- expect(fold[input]).to eql(output)
320
- end
321
- end
322
-
323
- it { expect(described_class).not_to be_contain(:fold!) }
324
-
325
- describe '.split' do
326
- let(:input) do
327
- {
328
- name: 'Joe',
329
- tasks: [
330
- { title: 'sleep well', priority: 1 },
331
- { title: 'be nice', priority: 2 },
332
- { priority: 2 },
333
- { title: 'be cool' },
334
- {}
335
- ]
336
- }
337
- end
338
-
339
- it 'splits a tuple into array partially by given keys' do
340
- split = described_class.t(:split, :tasks, [:priority])
341
-
342
- output = [
343
- {
344
- name: 'Joe', priority: 1,
345
- tasks: [{ title: 'sleep well' }]
346
- },
347
- {
348
- name: 'Joe', priority: 2,
349
- tasks: [{ title: 'be nice' }, { title: nil }]
350
- },
351
- {
352
- name: 'Joe', priority: nil,
353
- tasks: [{ title: 'be cool' }, { title: nil }]
354
- }
355
- ]
356
-
357
- expect(split[input]).to eql output
358
- end
359
-
360
- it 'splits a tuple into array fully by all subkeys' do
361
- split = described_class.t(:split, :tasks, [:priority, :title])
362
-
363
- output = [
364
- { name: 'Joe', title: 'sleep well', priority: 1 },
365
- { name: 'Joe', title: 'be nice', priority: 2 },
366
- { name: 'Joe', title: nil, priority: 2 },
367
- { name: 'Joe', title: 'be cool', priority: nil },
368
- { name: 'Joe', title: nil, priority: nil }
369
- ]
370
-
371
- expect(split[input]).to eql output
372
- end
373
-
374
- it 'returns an array of one tuple with updated keys when there is nothing to split by' do
375
- output = [
376
- {
377
- name: 'Joe',
378
- tasks: [
379
- { title: 'sleep well', priority: 1 },
380
- { title: 'be nice', priority: 2 },
381
- { title: nil, priority: 2 },
382
- { title: 'be cool', priority: nil },
383
- { title: nil, priority: nil }
384
- ]
385
- }
386
- ]
387
-
388
- split = described_class.t(:split, :tasks, [])
389
- expect(split[input]).to eql output
390
-
391
- split = described_class.t(:split, :tasks, [:absent])
392
- expect(split[input]).to eql output
393
- end
394
-
395
- it 'returns an array of initial tuple when attribute is absent' do
396
- split = described_class.t(:split, :absent, [:priority, :title])
397
- expect(split[input]).to eql [input]
398
- end
399
-
400
- it 'ignores empty array' do
401
- input = { name: 'Joe', tasks: [] }
402
-
403
- split = described_class.t(:split, :tasks, [:title])
404
-
405
- expect(split[input]).to eql [{ name: 'Joe' }]
406
- end
407
- end
408
-
409
- describe ':eval_values' do
410
- it 'recursively evaluates values' do
411
- evaluate = described_class.t(:eval_values, 1)
412
-
413
- input = {
414
- one: 1, two: -> i { i + 1 },
415
- three: -> i { i + 2 }, four: 4,
416
- more: [{ one: -> i { i }, two: 2 }]
417
- }
418
-
419
- output = {
420
- one: 1, two: 2,
421
- three: 3, four: 4,
422
- more: [{ one: 1, two: 2 }]
423
- }
424
-
425
- expect(evaluate[input]).to eql(output)
426
- end
427
-
428
- it 'recursively evaluates values matching key names' do
429
- evaluate = described_class.t(:eval_values, 1, [:one, :two])
430
-
431
- input = {
432
- one: 1, two: -> i { i + 1 },
433
- three: -> i { i + 2 }, four: 4,
434
- array: [{ one: -> i { i }, two: 2 }],
435
- hash: { one: -> i { i } }
436
- }
437
-
438
- result = evaluate[input]
439
-
440
- expect(result[:three]).to be_a(Proc)
441
- expect(result).to include(two: 2)
442
- expect(result[:array]).to eql([{ one: 1, two: 2 }])
443
- expect(result[:hash]).to eql(one: 1)
444
- end
445
- end
446
-
447
- describe '.deep_merge' do
448
- let(:hash) {
449
- {
450
- name: 'Jane',
451
- email: 'jane@doe.org',
452
- favorites:
453
- {
454
- food: 'stroopwafel'
455
- }
456
- }
457
- }
458
-
459
- let(:update) {
460
- {
461
- email: 'jane@example.org',
462
- favorites:
463
- {
464
- color: 'orange'
465
- }
466
- }
467
- }
468
-
469
- it 'recursively merges hash values' do
470
- deep_merge = described_class.t(:deep_merge)
471
- output = {
472
- name: 'Jane',
473
- email: 'jane@example.org',
474
- favorites: { food: 'stroopwafel', color: 'orange' }
475
- }
476
-
477
- expect(deep_merge[hash, update]).to eql(output)
478
- end
479
-
480
- it 'does not alter the provided arguments' do
481
- original_hash = hash.dup
482
- original_update = update.dup
483
-
484
- described_class.t(:deep_merge)[hash, update]
485
-
486
- expect(hash).to eql(original_hash)
487
- expect(update).to eql(original_update)
488
- end
489
- end
490
- end