hashie 3.4.2 → 5.0.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.
Files changed (73) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +518 -122
  3. data/CONTRIBUTING.md +24 -7
  4. data/LICENSE +1 -1
  5. data/README.md +455 -48
  6. data/Rakefile +18 -1
  7. data/UPGRADING.md +157 -7
  8. data/hashie.gemspec +14 -7
  9. data/lib/hashie/array.rb +21 -0
  10. data/lib/hashie/clash.rb +24 -12
  11. data/lib/hashie/dash.rb +56 -31
  12. data/lib/hashie/extensions/active_support/core_ext/hash.rb +14 -0
  13. data/lib/hashie/extensions/array/pretty_inspect.rb +19 -0
  14. data/lib/hashie/extensions/coercion.rb +91 -52
  15. data/lib/hashie/extensions/dash/coercion.rb +25 -0
  16. data/lib/hashie/extensions/dash/indifferent_access.rb +30 -1
  17. data/lib/hashie/extensions/dash/predefined_values.rb +88 -0
  18. data/lib/hashie/extensions/dash/property_translation.rb +59 -30
  19. data/lib/hashie/extensions/deep_fetch.rb +5 -3
  20. data/lib/hashie/extensions/deep_find.rb +14 -5
  21. data/lib/hashie/extensions/deep_locate.rb +40 -21
  22. data/lib/hashie/extensions/deep_merge.rb +28 -10
  23. data/lib/hashie/extensions/ignore_undeclared.rb +6 -4
  24. data/lib/hashie/extensions/indifferent_access.rb +49 -8
  25. data/lib/hashie/extensions/key_conflict_warning.rb +55 -0
  26. data/lib/hashie/extensions/mash/define_accessors.rb +90 -0
  27. data/lib/hashie/extensions/mash/keep_original_keys.rb +53 -0
  28. data/lib/hashie/extensions/mash/permissive_respond_to.rb +61 -0
  29. data/lib/hashie/extensions/mash/safe_assignment.rb +3 -1
  30. data/lib/hashie/extensions/mash/symbolize_keys.rb +38 -0
  31. data/lib/hashie/extensions/method_access.rb +77 -19
  32. data/lib/hashie/extensions/parsers/yaml_erb_parser.rb +29 -5
  33. data/lib/hashie/extensions/ruby_version.rb +60 -0
  34. data/lib/hashie/extensions/ruby_version_check.rb +21 -0
  35. data/lib/hashie/extensions/strict_key_access.rb +77 -0
  36. data/lib/hashie/extensions/stringify_keys.rb +8 -5
  37. data/lib/hashie/extensions/symbolize_keys.rb +21 -7
  38. data/lib/hashie/hash.rb +18 -11
  39. data/lib/hashie/logger.rb +18 -0
  40. data/lib/hashie/mash.rb +196 -55
  41. data/lib/hashie/railtie.rb +21 -0
  42. data/lib/hashie/rash.rb +7 -7
  43. data/lib/hashie/utils.rb +44 -0
  44. data/lib/hashie/version.rb +1 -1
  45. data/lib/hashie.rb +34 -16
  46. metadata +30 -79
  47. data/spec/hashie/clash_spec.rb +0 -48
  48. data/spec/hashie/dash_spec.rb +0 -513
  49. data/spec/hashie/extensions/autoload_spec.rb +0 -24
  50. data/spec/hashie/extensions/coercion_spec.rb +0 -625
  51. data/spec/hashie/extensions/dash/indifferent_access_spec.rb +0 -84
  52. data/spec/hashie/extensions/deep_fetch_spec.rb +0 -97
  53. data/spec/hashie/extensions/deep_find_spec.rb +0 -45
  54. data/spec/hashie/extensions/deep_locate_spec.rb +0 -124
  55. data/spec/hashie/extensions/deep_merge_spec.rb +0 -45
  56. data/spec/hashie/extensions/ignore_undeclared_spec.rb +0 -46
  57. data/spec/hashie/extensions/indifferent_access_spec.rb +0 -219
  58. data/spec/hashie/extensions/indifferent_access_with_rails_hwia_spec.rb +0 -208
  59. data/spec/hashie/extensions/key_conversion_spec.rb +0 -12
  60. data/spec/hashie/extensions/mash/safe_assignment_spec.rb +0 -23
  61. data/spec/hashie/extensions/merge_initializer_spec.rb +0 -23
  62. data/spec/hashie/extensions/method_access_spec.rb +0 -184
  63. data/spec/hashie/extensions/stringify_keys_spec.rb +0 -101
  64. data/spec/hashie/extensions/symbolize_keys_spec.rb +0 -106
  65. data/spec/hashie/hash_spec.rb +0 -84
  66. data/spec/hashie/mash_spec.rb +0 -683
  67. data/spec/hashie/parsers/yaml_erb_parser_spec.rb +0 -29
  68. data/spec/hashie/rash_spec.rb +0 -77
  69. data/spec/hashie/trash_spec.rb +0 -268
  70. data/spec/hashie/version_spec.rb +0 -7
  71. data/spec/spec_helper.rb +0 -15
  72. data/spec/support/module_context.rb +0 -11
  73. data/spec/support/ruby_version.rb +0 -10
@@ -1,683 +0,0 @@
1
- require 'spec_helper'
2
- require 'delegate'
3
- require 'support/ruby_version'
4
-
5
- describe Hashie::Mash do
6
- subject { Hashie::Mash.new }
7
-
8
- it 'inherits from Hash' do
9
- expect(subject.is_a?(Hash)).to be_truthy
10
- end
11
-
12
- it 'sets hash values through method= calls' do
13
- subject.test = 'abc'
14
- expect(subject['test']).to eq 'abc'
15
- end
16
-
17
- it 'retrieves set values through method calls' do
18
- subject['test'] = 'abc'
19
- expect(subject.test).to eq 'abc'
20
- end
21
-
22
- it 'retrieves set values through blocks' do
23
- subject['test'] = 'abc'
24
- value = nil
25
- subject.[]('test') { |v| value = v }
26
- expect(value).to eq 'abc'
27
- end
28
-
29
- it 'retrieves set values through blocks with method calls' do
30
- subject['test'] = 'abc'
31
- value = nil
32
- subject.test { |v| value = v }
33
- expect(value).to eq 'abc'
34
- end
35
-
36
- it 'tests for already set values when passed a ? method' do
37
- expect(subject.test?).to be_falsy
38
- subject.test = 'abc'
39
- expect(subject.test?).to be_truthy
40
- end
41
-
42
- it 'returns false on a ? method if a value has been set to nil or false' do
43
- subject.test = nil
44
- expect(subject).not_to be_test
45
- subject.test = false
46
- expect(subject).not_to be_test
47
- end
48
-
49
- it 'makes all [] and []= into strings for consistency' do
50
- subject['abc'] = 123
51
- expect(subject.key?('abc')).to be_truthy
52
- expect(subject['abc']).to eq 123
53
- end
54
-
55
- it 'has a to_s that is identical to its inspect' do
56
- subject.abc = 123
57
- expect(subject.to_s).to eq subject.inspect
58
- end
59
-
60
- it 'returns nil instead of raising an error for attribute-esque method calls' do
61
- expect(subject.abc).to be_nil
62
- end
63
-
64
- it 'returns the default value if set like Hash' do
65
- subject.default = 123
66
- expect(subject.abc).to eq 123
67
- end
68
-
69
- it 'gracefully handles being accessed with arguments' do
70
- expect(subject.abc('foobar')).to eq nil
71
- subject.abc = 123
72
- expect(subject.abc('foobar')).to eq 123
73
- end
74
-
75
- # Added due to downstream gems assuming indifferent access to be true for Mash
76
- # When this is not, bump major version so that downstream gems can target
77
- # correct version and fix accordingly.
78
- # See https://github.com/intridea/hashie/pull/197
79
- it 'maintains indifferent access when nested' do
80
- subject[:a] = { b: 'c' }
81
- expect(subject[:a][:b]).to eq 'c'
82
- expect(subject[:a]['b']).to eq 'c'
83
- end
84
-
85
- it 'returns a Hashie::Mash when passed a bang method to a non-existenct key' do
86
- expect(subject.abc!.is_a?(Hashie::Mash)).to be_truthy
87
- end
88
-
89
- it 'returns the existing value when passed a bang method for an existing key' do
90
- subject.name = 'Bob'
91
- expect(subject.name!).to eq 'Bob'
92
- end
93
-
94
- it 'returns a Hashie::Mash when passed an under bang method to a non-existenct key' do
95
- expect(subject.abc_.is_a?(Hashie::Mash)).to be_truthy
96
- end
97
-
98
- it 'returns the existing value when passed an under bang method for an existing key' do
99
- subject.name = 'Bob'
100
- expect(subject.name_).to eq 'Bob'
101
- end
102
-
103
- it '#initializing_reader returns a Hashie::Mash when passed a non-existent key' do
104
- expect(subject.initializing_reader(:abc).is_a?(Hashie::Mash)).to be_truthy
105
- end
106
-
107
- it 'allows for multi-level assignment through bang methods' do
108
- subject.author!.name = 'Michael Bleigh'
109
- expect(subject.author).to eq Hashie::Mash.new(name: 'Michael Bleigh')
110
- subject.author!.website!.url = 'http://www.mbleigh.com/'
111
- expect(subject.author.website).to eq Hashie::Mash.new(url: 'http://www.mbleigh.com/')
112
- end
113
-
114
- it 'allows for multi-level under bang testing' do
115
- expect(subject.author_.website_.url).to be_nil
116
- expect(subject.author_.website_.url?).to eq false
117
- expect(subject.author).to be_nil
118
- end
119
-
120
- it 'does not call super if id is not a key' do
121
- expect(subject.id).to eq nil
122
- end
123
-
124
- it 'returns the value if id is a key' do
125
- subject.id = 'Steve'
126
- expect(subject.id).to eq 'Steve'
127
- end
128
-
129
- it 'does not call super if type is not a key' do
130
- expect(subject.type).to eq nil
131
- end
132
-
133
- it 'returns the value if type is a key' do
134
- subject.type = 'Steve'
135
- expect(subject.type).to eq 'Steve'
136
- end
137
-
138
- context 'updating' do
139
- subject do
140
- described_class.new(
141
- first_name: 'Michael',
142
- last_name: 'Bleigh',
143
- details: {
144
- email: 'michael@asf.com',
145
- address: 'Nowhere road'
146
- })
147
- end
148
-
149
- describe '#deep_update' do
150
- it 'recursively Hashie::Mash Hashie::Mashes and hashes together' do
151
- subject.deep_update(details: { email: 'michael@intridea.com', city: 'Imagineton' })
152
- expect(subject.first_name).to eq 'Michael'
153
- expect(subject.details.email).to eq 'michael@intridea.com'
154
- expect(subject.details.address).to eq 'Nowhere road'
155
- expect(subject.details.city).to eq 'Imagineton'
156
- end
157
-
158
- it 'converts values only once' do
159
- class ConvertedMash < Hashie::Mash
160
- end
161
-
162
- rhs = ConvertedMash.new(email: 'foo@bar.com')
163
- expect(subject).to receive(:convert_value).exactly(1).times
164
- subject.deep_update(rhs)
165
- end
166
-
167
- it 'makes #update deep by default' do
168
- expect(subject.update(details: { address: 'Fake street' })).to eql(subject)
169
- expect(subject.details.address).to eq 'Fake street'
170
- expect(subject.details.email).to eq 'michael@asf.com'
171
- end
172
-
173
- it 'clones before a #deep_merge' do
174
- duped = subject.deep_merge(details: { address: 'Fake street' })
175
- expect(duped).not_to eql(subject)
176
- expect(duped.details.address).to eq 'Fake street'
177
- expect(subject.details.address).to eq 'Nowhere road'
178
- expect(duped.details.email).to eq 'michael@asf.com'
179
- end
180
-
181
- it 'default #merge is deep' do
182
- duped = subject.merge(details: { email: 'michael@intridea.com' })
183
- expect(duped).not_to eql(subject)
184
- expect(duped.details.email).to eq 'michael@intridea.com'
185
- expect(duped.details.address).to eq 'Nowhere road'
186
- end
187
-
188
- # http://www.ruby-doc.org/core-1.9.3/Hash.html#method-i-update
189
- it 'accepts a block' do
190
- duped = subject.merge(details: { address: 'Pasadena CA' }) { |_, oldv, newv| [oldv, newv].join(', ') }
191
- expect(duped.details.address).to eq 'Nowhere road, Pasadena CA'
192
- end
193
-
194
- it 'copies values for non-duplicate keys when a block is supplied' do
195
- duped = subject.merge(details: { address: 'Pasadena CA', state: 'West Thoughtleby' }) { |_, oldv, _| oldv }
196
- expect(duped.details.address).to eq 'Nowhere road'
197
- expect(duped.details.state).to eq 'West Thoughtleby'
198
- end
199
- end
200
-
201
- describe 'shallow update' do
202
- it 'shallowly Hashie::Mash Hashie::Mashes and hashes together' do
203
- expect(subject.shallow_update(details: { email: 'michael@intridea.com',
204
- city: 'Imagineton' })).to eql(subject)
205
-
206
- expect(subject.first_name).to eq 'Michael'
207
- expect(subject.details.email).to eq 'michael@intridea.com'
208
- expect(subject.details.address).to be_nil
209
- expect(subject.details.city).to eq 'Imagineton'
210
- end
211
-
212
- it 'clones before a #regular_merge' do
213
- duped = subject.shallow_merge(details: { address: 'Fake street' })
214
- expect(duped).not_to eql(subject)
215
- end
216
-
217
- it 'default #merge is shallow' do
218
- duped = subject.shallow_merge(details: { address: 'Fake street' })
219
- expect(duped.details.address).to eq 'Fake street'
220
- expect(subject.details.address).to eq 'Nowhere road'
221
- expect(duped.details.email).to be_nil
222
- end
223
- end
224
-
225
- describe '#replace' do
226
- before do
227
- subject.replace(
228
- middle_name: 'Cain',
229
- details: { city: 'Imagination' }
230
- )
231
- end
232
-
233
- it 'returns self' do
234
- expect(subject.replace(foo: 'bar').to_hash).to eq('foo' => 'bar')
235
- end
236
-
237
- it 'sets all specified keys to their corresponding values' do
238
- expect(subject.middle_name?).to be_truthy
239
- expect(subject.details?).to be_truthy
240
- expect(subject.middle_name).to eq 'Cain'
241
- expect(subject.details.city?).to be_truthy
242
- expect(subject.details.city).to eq 'Imagination'
243
- end
244
-
245
- it 'leaves only specified keys' do
246
- expect(subject.keys.sort).to eq %w(details middle_name)
247
- expect(subject.first_name?).to be_falsy
248
- expect(subject).not_to respond_to(:first_name)
249
- expect(subject.last_name?).to be_falsy
250
- expect(subject).not_to respond_to(:last_name)
251
- end
252
- end
253
-
254
- describe 'delete' do
255
- it 'deletes with String key' do
256
- subject.delete('details')
257
- expect(subject.details).to be_nil
258
- expect(subject).not_to be_respond_to :details
259
- end
260
-
261
- it 'deletes with Symbol key' do
262
- subject.delete(:details)
263
- expect(subject.details).to be_nil
264
- expect(subject).not_to be_respond_to :details
265
- end
266
- end
267
- end
268
-
269
- it 'converts hash assignments into Hashie::Mashes' do
270
- subject.details = { email: 'randy@asf.com', address: { state: 'TX' } }
271
- expect(subject.details.email).to eq 'randy@asf.com'
272
- expect(subject.details.address.state).to eq 'TX'
273
- end
274
-
275
- it 'does not convert the type of Hashie::Mashes childs to Hashie::Mash' do
276
- class MyMash < Hashie::Mash
277
- end
278
-
279
- record = MyMash.new
280
- record.son = MyMash.new
281
- expect(record.son.class).to eq MyMash
282
- end
283
-
284
- it 'does not change the class of Mashes when converted' do
285
- class SubMash < Hashie::Mash
286
- end
287
-
288
- record = Hashie::Mash.new
289
- son = SubMash.new
290
- record['submash'] = son
291
- expect(record['submash']).to be_kind_of(SubMash)
292
- end
293
-
294
- it 'respects the class when passed a bang method for a non-existent key' do
295
- record = Hashie::Mash.new
296
- expect(record.non_existent!).to be_kind_of(Hashie::Mash)
297
-
298
- class SubMash < Hashie::Mash
299
- end
300
-
301
- son = SubMash.new
302
- expect(son.non_existent!).to be_kind_of(SubMash)
303
- end
304
-
305
- it 'respects the class when passed an under bang method for a non-existent key' do
306
- record = Hashie::Mash.new
307
- expect(record.non_existent_).to be_kind_of(Hashie::Mash)
308
-
309
- class SubMash < Hashie::Mash
310
- end
311
-
312
- son = SubMash.new
313
- expect(son.non_existent_).to be_kind_of(SubMash)
314
- end
315
-
316
- it 'respects the class when converting the value' do
317
- record = Hashie::Mash.new
318
- record.details = Hashie::Mash.new(email: 'randy@asf.com')
319
- expect(record.details).to be_kind_of(Hashie::Mash)
320
- end
321
-
322
- it 'respects another subclass when converting the value' do
323
- record = Hashie::Mash.new
324
-
325
- class SubMash < Hashie::Mash
326
- end
327
-
328
- son = SubMash.new(email: 'foo@bar.com')
329
- record.details = son
330
- expect(record.details).to be_kind_of(SubMash)
331
- end
332
-
333
- describe '#respond_to?' do
334
- subject do
335
- Hashie::Mash.new(abc: 'def')
336
- end
337
-
338
- it 'responds to a normal method' do
339
- expect(subject).to be_respond_to(:key?)
340
- end
341
-
342
- it 'responds to a set key' do
343
- expect(subject).to be_respond_to(:abc)
344
- expect(subject.method(:abc)).to_not be_nil
345
- end
346
-
347
- it 'responds to a set key with a suffix' do
348
- %w(= ? ! _).each do |suffix|
349
- expect(subject).to be_respond_to(:"abc#{suffix}")
350
- end
351
- end
352
-
353
- it 'is able to access the suffixed key as a method' do
354
- %w(= ? ! _).each do |suffix|
355
- expect(subject.method(:"abc#{suffix}")).to_not be_nil
356
- end
357
- end
358
-
359
- it 'responds to an unknown key with a suffix' do
360
- %w(= ? ! _).each do |suffix|
361
- expect(subject).to be_respond_to(:"xyz#{suffix}")
362
- end
363
- end
364
-
365
- it 'is able to access an unknown suffixed key as a method' do
366
- # See https://github.com/intridea/hashie/pull/285 for more information
367
- if mri22?
368
- pending 'Bug in MRI 2.2.x means this behavior is broken in those versions'
369
- end
370
-
371
- %w(= ? ! _).each do |suffix|
372
- expect(subject.method(:"xyz#{suffix}")).to_not be_nil
373
- end
374
- end
375
-
376
- it 'does not respond to an unknown key without a suffix' do
377
- expect(subject).not_to be_respond_to(:xyz)
378
- expect { subject.method(:xyz) }.to raise_error(NameError)
379
- end
380
- end
381
-
382
- context '#initialize' do
383
- it 'converts an existing hash to a Hashie::Mash' do
384
- converted = Hashie::Mash.new(abc: 123, name: 'Bob')
385
- expect(converted.abc).to eq 123
386
- expect(converted.name).to eq 'Bob'
387
- end
388
-
389
- it 'converts hashes recursively into Hashie::Mashes' do
390
- converted = Hashie::Mash.new(a: { b: 1, c: { d: 23 } })
391
- expect(converted.a.is_a?(Hashie::Mash)).to be_truthy
392
- expect(converted.a.b).to eq 1
393
- expect(converted.a.c.d).to eq 23
394
- end
395
-
396
- it 'converts hashes in arrays into Hashie::Mashes' do
397
- converted = Hashie::Mash.new(a: [{ b: 12 }, 23])
398
- expect(converted.a.first.b).to eq 12
399
- expect(converted.a.last).to eq 23
400
- end
401
-
402
- it 'converts an existing Hashie::Mash into a Hashie::Mash' do
403
- initial = Hashie::Mash.new(name: 'randy', address: { state: 'TX' })
404
- copy = Hashie::Mash.new(initial)
405
- expect(initial.name).to eq copy.name
406
- expect(initial.__id__).not_to eq copy.__id__
407
- expect(copy.address.state).to eq 'TX'
408
- copy.address.state = 'MI'
409
- expect(initial.address.state).to eq 'TX'
410
- expect(copy.address.__id__).not_to eq initial.address.__id__
411
- end
412
-
413
- it 'accepts a default block' do
414
- initial = Hashie::Mash.new { |h, i| h[i] = [] }
415
- expect(initial.default_proc).not_to be_nil
416
- expect(initial.default).to be_nil
417
- expect(initial.test).to eq []
418
- expect(initial.test?).to be_truthy
419
- end
420
-
421
- it 'allows assignment of an empty array in a default block' do
422
- initial = Hashie::Mash.new { |h, k| h[k] = [] }
423
- initial.hello << 100
424
- expect(initial.hello).to eq [100]
425
- initial['hi'] << 100
426
- expect(initial['hi']).to eq [100]
427
- end
428
-
429
- it 'allows assignment of a non-empty array in a default block' do
430
- initial = Hashie::Mash.new { |h, k| h[k] = [100] }
431
- initial.hello << 200
432
- expect(initial.hello).to eq [100, 200]
433
- initial['hi'] << 200
434
- expect(initial['hi']).to eq [100, 200]
435
- end
436
-
437
- it 'allows assignment of an empty hash in a default block' do
438
- initial = Hashie::Mash.new { |h, k| h[k] = {} }
439
- initial.hello[:a] = 100
440
- expect(initial.hello).to eq Hashie::Mash.new(a: 100)
441
- initial[:hi][:a] = 100
442
- expect(initial[:hi]).to eq Hashie::Mash.new(a: 100)
443
- end
444
-
445
- it 'allows assignment of a non-empty hash in a default block' do
446
- initial = Hashie::Mash.new { |h, k| h[k] = { a: 100 } }
447
- initial.hello[:b] = 200
448
- expect(initial.hello).to eq Hashie::Mash.new(a: 100, b: 200)
449
- initial[:hi][:b] = 200
450
- expect(initial[:hi]).to eq Hashie::Mash.new(a: 100, b: 200)
451
- end
452
-
453
- it 'converts Hashie::Mashes within Arrays back to Hashes' do
454
- initial_hash = { 'a' => [{ 'b' => 12, 'c' => ['d' => 50, 'e' => 51] }, 23] }
455
- converted = Hashie::Mash.new(initial_hash)
456
- expect(converted.to_hash['a'].first.is_a?(Hashie::Mash)).to be_falsy
457
- expect(converted.to_hash['a'].first.is_a?(Hash)).to be_truthy
458
- expect(converted.to_hash['a'].first['c'].first.is_a?(Hashie::Mash)).to be_falsy
459
- end
460
- end
461
-
462
- describe '#fetch' do
463
- let(:hash) { { one: 1, other: false } }
464
- let(:mash) { Hashie::Mash.new(hash) }
465
-
466
- context 'when key exists' do
467
- it 'returns the value' do
468
- expect(mash.fetch(:one)).to eql(1)
469
- end
470
-
471
- it 'returns the value even if the value is falsy' do
472
- expect(mash.fetch(:other)).to eql(false)
473
- end
474
-
475
- context 'when key has other than original but acceptable type' do
476
- it 'returns the value' do
477
- expect(mash.fetch('one')).to eql(1)
478
- end
479
- end
480
- end
481
-
482
- context 'when key does not exist' do
483
- it 'raises KeyError' do
484
- error = RUBY_VERSION =~ /1.8/ ? IndexError : KeyError
485
- expect { mash.fetch(:two) }.to raise_error(error)
486
- end
487
-
488
- context 'with default value given' do
489
- it 'returns default value' do
490
- expect(mash.fetch(:two, 8)).to eql(8)
491
- end
492
-
493
- it 'returns default value even if it is falsy' do
494
- expect(mash.fetch(:two, false)).to eql(false)
495
- end
496
- end
497
-
498
- context 'with block given' do
499
- it 'returns default value' do
500
- expect(mash.fetch(:two) do
501
- 'block default value'
502
- end).to eql('block default value')
503
- end
504
- end
505
- end
506
- end
507
-
508
- describe '#to_hash' do
509
- let(:hash) { { 'outer' => { 'inner' => 42 }, 'testing' => [1, 2, 3] } }
510
- let(:mash) { Hashie::Mash.new(hash) }
511
-
512
- it 'returns a standard Hash' do
513
- expect(mash.to_hash).to be_a(::Hash)
514
- end
515
-
516
- it 'includes all keys' do
517
- expect(mash.to_hash.keys).to eql(%w(outer testing))
518
- end
519
-
520
- it 'converts keys to symbols when symbolize_keys option is true' do
521
- expect(mash.to_hash(symbolize_keys: true).keys).to include(:outer)
522
- expect(mash.to_hash(symbolize_keys: true).keys).not_to include('outer')
523
- end
524
-
525
- it 'leaves keys as strings when symbolize_keys option is false' do
526
- expect(mash.to_hash(symbolize_keys: false).keys).to include('outer')
527
- expect(mash.to_hash(symbolize_keys: false).keys).not_to include(:outer)
528
- end
529
-
530
- it 'symbolizes keys recursively' do
531
- expect(mash.to_hash(symbolize_keys: true)[:outer].keys).to include(:inner)
532
- expect(mash.to_hash(symbolize_keys: true)[:outer].keys).not_to include('inner')
533
- end
534
- end
535
-
536
- describe '#stringify_keys' do
537
- it 'turns all keys into strings recursively' do
538
- hash = Hashie::Mash[:a => 'hey', 123 => { 345 => 'hey' }]
539
- hash.stringify_keys!
540
- expect(hash).to eq Hashie::Hash['a' => 'hey', '123' => { '345' => 'hey' }]
541
- end
542
- end
543
-
544
- describe '#values_at' do
545
- let(:hash) { { 'key_one' => 1, :key_two => 2 } }
546
- let(:mash) { Hashie::Mash.new(hash) }
547
-
548
- context 'when the original type is given' do
549
- it 'returns the values' do
550
- expect(mash.values_at('key_one', :key_two)).to eq([1, 2])
551
- end
552
- end
553
-
554
- context 'when a different, but acceptable type is given' do
555
- it 'returns the values' do
556
- expect(mash.values_at(:key_one, 'key_two')).to eq([1, 2])
557
- end
558
- end
559
-
560
- context 'when a key is given that is not in the Mash' do
561
- it 'returns nil for that value' do
562
- expect(mash.values_at('key_one', :key_three)).to eq([1, nil])
563
- end
564
- end
565
- end
566
-
567
- describe '.load(filename, options = {})' do
568
- let(:config) do
569
- {
570
- 'production' => {
571
- 'foo' => 'production_foo'
572
- }
573
- }
574
- end
575
- let(:path) { 'database.yml' }
576
- let(:parser) { double(:parser) }
577
-
578
- subject { described_class.load(path, parser: parser) }
579
-
580
- before do |ex|
581
- unless ex.metadata == :test_cache
582
- described_class.instance_variable_set('@_mashes', nil) # clean the cached mashes
583
- end
584
- end
585
-
586
- context 'if the file exists' do
587
- before do
588
- expect(File).to receive(:file?).with(path).and_return(true)
589
- expect(parser).to receive(:perform).with(path).and_return(config)
590
- end
591
-
592
- it { is_expected.to be_a(Hashie::Mash) }
593
-
594
- it 'return a Mash from a file' do
595
- expect(subject.production).not_to be_nil
596
- expect(subject.production.keys).to eq config['production'].keys
597
- expect(subject.production.foo).to eq config['production']['foo']
598
- end
599
-
600
- it 'freeze the attribtues' do
601
- expect { subject.production = {} }.to raise_exception(RuntimeError, /can't modify frozen/)
602
- end
603
- end
604
-
605
- context 'if the fils does not exists' do
606
- before do
607
- expect(File).to receive(:file?).with(path).and_return(false)
608
- end
609
-
610
- it 'raise an ArgumentError' do
611
- expect { subject }.to raise_exception(ArgumentError)
612
- end
613
- end
614
-
615
- describe 'results are cached' do
616
- let(:parser) { double(:parser) }
617
-
618
- subject { described_class.load(path, parser: parser) }
619
-
620
- before do
621
- expect(File).to receive(:file?).with(path).and_return(true)
622
- expect(File).to receive(:file?).with("#{path}+1").and_return(true)
623
- expect(parser).to receive(:perform).once.with(path).and_return(config)
624
- expect(parser).to receive(:perform).once.with("#{path}+1").and_return(config)
625
- end
626
-
627
- it 'cache the loaded yml file', :test_cache do
628
- 2.times do
629
- expect(subject).to be_a(described_class)
630
- expect(described_class.load("#{path}+1", parser: parser)).to be_a(described_class)
631
- end
632
-
633
- expect(subject.object_id).to eq subject.object_id
634
- end
635
- end
636
- end
637
-
638
- describe '#to_module(mash_method_name)' do
639
- let(:mash) { described_class.new }
640
- subject { Class.new.extend mash.to_module }
641
-
642
- it 'defines a settings method on the klass class that extends the module' do
643
- expect(subject).to respond_to(:settings)
644
- expect(subject.settings).to eq mash
645
- end
646
-
647
- context 'when a settings_method_name is set' do
648
- let(:mash_method_name) { 'config' }
649
-
650
- subject { Class.new.extend mash.to_module(mash_method_name) }
651
-
652
- it 'defines a settings method on the klass class that extends the module' do
653
- expect(subject).to respond_to(mash_method_name.to_sym)
654
- expect(subject.send(mash_method_name.to_sym)).to eq mash
655
- end
656
- end
657
- end
658
-
659
- describe '#extractable_options?' do
660
- require 'active_support'
661
-
662
- subject { described_class.new(name: 'foo') }
663
- let(:args) { [101, 'bar', subject] }
664
-
665
- it 'can be extracted from an array' do
666
- expect(args.extract_options!).to eq subject
667
- expect(args).to eq [101, 'bar']
668
- end
669
- end
670
-
671
- describe '#reverse_merge' do
672
- subject { described_class.new(a: 1, b: 2) }
673
-
674
- it 'unifies strings and symbols' do
675
- expect(subject.reverse_merge(a: 2).length).to eq 2
676
- expect(subject.reverse_merge('a' => 2).length).to eq 2
677
- end
678
-
679
- it 'does not overwrite values' do
680
- expect(subject.reverse_merge(a: 5).a).to eq subject.a
681
- end
682
- end
683
- end
@@ -1,29 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Hashie::Extensions::Parsers::YamlErbParser do
4
- describe '.perform' do
5
- let(:config) do
6
- <<-EOF
7
- ---
8
- foo: verbatim
9
- bar: <%= "erb" %>
10
- baz: "<%= __FILE__ %>"
11
- EOF
12
- end
13
- let(:path) { 'template.yml' }
14
-
15
- subject { described_class.new(path).perform }
16
-
17
- before do
18
- expect(File).to receive(:read).with(path).and_return(config)
19
- end
20
-
21
- it { is_expected.to be_a(Hash) }
22
-
23
- it 'parses YAML after interpolating ERB' do
24
- expect(subject['foo']).to eq 'verbatim'
25
- expect(subject['bar']).to eq 'erb'
26
- expect(subject['baz']).to eq path
27
- end
28
- end
29
- end