hashie 3.4.2 → 5.0.0

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