dry-transformer 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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