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,625 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Hashie::Extensions::Coercion do
4
- class NotInitializable
5
- private_class_method :new
6
- end
7
-
8
- class Initializable
9
- attr_reader :coerced, :value
10
-
11
- def initialize(obj, coerced = nil)
12
- @coerced = coerced
13
- @value = obj.class.to_s
14
- end
15
-
16
- def coerced?
17
- !@coerced.nil?
18
- end
19
- end
20
-
21
- class Coercable < Initializable
22
- def self.coerce(obj)
23
- new(obj, true)
24
- end
25
- end
26
-
27
- before(:each) do
28
- class ExampleCoercableHash < Hash
29
- include Hashie::Extensions::Coercion
30
- include Hashie::Extensions::MergeInitializer
31
- end
32
- end
33
-
34
- subject { ExampleCoercableHash }
35
-
36
- let(:instance) { subject.new }
37
-
38
- describe '#coerce_key' do
39
- context 'nesting' do
40
- class BaseCoercableHash < Hash
41
- include Hashie::Extensions::Coercion
42
- include Hashie::Extensions::MergeInitializer
43
- end
44
-
45
- class NestedCoercableHash < BaseCoercableHash
46
- coerce_key :foo, String
47
- coerce_key :bar, Integer
48
- end
49
-
50
- class OtherNestedCoercableHash < BaseCoercableHash
51
- coerce_key :foo, Symbol
52
- end
53
-
54
- class RootCoercableHash < BaseCoercableHash
55
- coerce_key :nested, NestedCoercableHash
56
- coerce_key :other, OtherNestedCoercableHash
57
- coerce_key :nested_list, Array[NestedCoercableHash]
58
- coerce_key :nested_hash, Hash[String => NestedCoercableHash]
59
- end
60
-
61
- def test_nested_object(obj)
62
- expect(obj).to be_a(NestedCoercableHash)
63
- expect(obj[:foo]).to be_a(String)
64
- expect(obj[:bar]).to be_an(Integer)
65
- end
66
-
67
- subject { RootCoercableHash }
68
- let(:instance) { subject.new }
69
-
70
- it 'does not add coercions to superclass' do
71
- instance[:nested] = { foo: 'bar' }
72
- instance[:other] = { foo: 'bar' }
73
- expect(instance[:nested][:foo]).to be_a String
74
- expect(instance[:other][:foo]).to be_a Symbol
75
- end
76
-
77
- it 'coerces nested objects' do
78
- instance[:nested] = { foo: 123, bar: '456' }
79
- test_nested_object(instance[:nested])
80
- end
81
-
82
- it 'coerces nested arrays' do
83
- instance[:nested_list] = [
84
- { foo: 123, bar: '456' },
85
- { foo: 234, bar: '567' },
86
- { foo: 345, bar: '678' }
87
- ]
88
- expect(instance[:nested_list]).to be_a Array
89
- expect(instance[:nested_list].size).to eq(3)
90
- instance[:nested_list].each do | nested |
91
- test_nested_object nested
92
- end
93
- end
94
-
95
- it 'coerces nested hashes' do
96
- instance[:nested_hash] = {
97
- a: { foo: 123, bar: '456' },
98
- b: { foo: 234, bar: '567' },
99
- c: { foo: 345, bar: '678' }
100
- }
101
- expect(instance[:nested_hash]).to be_a Hash
102
- expect(instance[:nested_hash].size).to eq(3)
103
- instance[:nested_hash].each do | key, nested |
104
- expect(key).to be_a(String)
105
- test_nested_object nested
106
- end
107
- end
108
-
109
- context 'when repetitively including the module' do
110
- class RepetitiveCoercableHash < NestedCoercableHash
111
- include Hashie::Extensions::Coercion
112
- include Hashie::Extensions::MergeInitializer
113
-
114
- coerce_key :nested, NestedCoercableHash
115
- end
116
-
117
- subject { RepetitiveCoercableHash }
118
- let(:instance) { subject.new }
119
-
120
- it 'does not raise a stack overflow error' do
121
- expect do
122
- instance[:nested] = { foo: 123, bar: '456' }
123
- test_nested_object(instance[:nested])
124
- end.not_to raise_error
125
- end
126
- end
127
- end
128
-
129
- it { expect(subject).to be_respond_to(:coerce_key) }
130
-
131
- it 'runs through coerce on a specified key' do
132
- subject.coerce_key :foo, Coercable
133
-
134
- instance[:foo] = 'bar'
135
- expect(instance[:foo]).to be_coerced
136
- end
137
-
138
- it 'skips unnecessary coercions' do
139
- subject.coerce_key :foo, Coercable
140
-
141
- instance[:foo] = Coercable.new('bar')
142
- expect(instance[:foo]).to_not be_coerced
143
- end
144
-
145
- it 'supports an array of keys' do
146
- subject.coerce_keys :foo, :bar, Coercable
147
-
148
- instance[:foo] = 'bar'
149
- instance[:bar] = 'bax'
150
- expect(instance[:foo]).to be_coerced
151
- expect(instance[:bar]).to be_coerced
152
- end
153
-
154
- it 'supports coercion for Array' do
155
- subject.coerce_key :foo, Array[Coercable]
156
-
157
- instance[:foo] = %w('bar', 'bar2')
158
- expect(instance[:foo]).to all(be_coerced)
159
- expect(instance[:foo]).to be_a(Array)
160
- end
161
-
162
- it 'supports coercion for Set' do
163
- subject.coerce_key :foo, Set[Coercable]
164
-
165
- instance[:foo] = Set.new(%w('bar', 'bar2'))
166
- expect(instance[:foo]).to all(be_coerced)
167
- expect(instance[:foo]).to be_a(Set)
168
- end
169
-
170
- it 'supports coercion for Set of primitive' do
171
- subject.coerce_key :foo, Set[Initializable]
172
-
173
- instance[:foo] = %w('bar', 'bar2')
174
- expect(instance[:foo].map(&:value)).to all(eq 'String')
175
- expect(instance[:foo]).to be_none(&:coerced?)
176
- expect(instance[:foo]).to be_a(Set)
177
- end
178
-
179
- it 'supports coercion for Hash' do
180
- subject.coerce_key :foo, Hash[Coercable => Coercable]
181
-
182
- instance[:foo] = { 'bar_key' => 'bar_value', 'bar2_key' => 'bar2_value' }
183
- expect(instance[:foo].keys).to all(be_coerced)
184
- expect(instance[:foo].values).to all(be_coerced)
185
- expect(instance[:foo]).to be_a(Hash)
186
- end
187
-
188
- it 'supports coercion for Hash with primitive as value' do
189
- subject.coerce_key :foo, Hash[Coercable => Initializable]
190
-
191
- instance[:foo] = { 'bar_key' => '1', 'bar2_key' => '2' }
192
- expect(instance[:foo].values.map(&:value)).to all(eq 'String')
193
- expect(instance[:foo].keys).to all(be_coerced)
194
- end
195
-
196
- context 'coercing core types' do
197
- def test_coercion(literal, target_type, coerce_method)
198
- subject.coerce_key :foo, target_type
199
- instance[:foo] = literal
200
- expect(instance[:foo]).to be_a(target_type)
201
- expect(instance[:foo]).to eq(literal.send(coerce_method))
202
- end
203
-
204
- RSpec.shared_examples 'coerces from numeric types' do |target_type, coerce_method|
205
- it "coerces from String to #{target_type} via #{coerce_method}" do
206
- test_coercion '2.0', target_type, coerce_method
207
- end
208
-
209
- it "coerces from Integer to #{target_type} via #{coerce_method}" do
210
- # Fixnum
211
- test_coercion 2, target_type, coerce_method
212
- # Bignum
213
- test_coercion 12_345_667_890_987_654_321, target_type, coerce_method
214
- end
215
-
216
- it "coerces from Rational to #{target_type} via #{coerce_method}" do
217
- test_coercion Rational(2, 3), target_type, coerce_method
218
- end
219
- end
220
-
221
- RSpec.shared_examples 'coerces from alphabetical types' do |target_type, coerce_method|
222
- it "coerces from String to #{target_type} via #{coerce_method}" do
223
- test_coercion 'abc', target_type, coerce_method
224
- end
225
-
226
- it "coerces from Symbol to #{target_type} via #{coerce_method}" do
227
- test_coercion :abc, target_type, coerce_method
228
- end
229
- end
230
-
231
- include_examples 'coerces from numeric types', Integer, :to_i
232
- include_examples 'coerces from numeric types', Float, :to_f
233
- include_examples 'coerces from numeric types', String, :to_s
234
-
235
- include_examples 'coerces from alphabetical types', String, :to_s
236
- include_examples 'coerces from alphabetical types', Symbol, :to_sym
237
-
238
- it 'can coerce String to Rational when possible' do
239
- test_coercion '2/3', Rational, :to_r
240
- end
241
-
242
- it 'can coerce String to Complex when possible' do
243
- test_coercion '2/3+3/4i', Complex, :to_c
244
- end
245
-
246
- it 'coerces collections with core types' do
247
- subject.coerce_key :foo, Hash[String => String]
248
-
249
- instance[:foo] = {
250
- abc: 123,
251
- xyz: 987
252
- }
253
- expect(instance[:foo]).to eq(
254
- 'abc' => '123',
255
- 'xyz' => '987'
256
- )
257
- end
258
-
259
- it 'can coerce via a proc' do
260
- subject.coerce_key(:foo, lambda do |v|
261
- case v
262
- when String
263
- return !!(v =~ /^(true|t|yes|y|1)$/i)
264
- when Numeric
265
- return !v.to_i.zero?
266
- else
267
- return v == true
268
- end
269
- end)
270
-
271
- true_values = [true, 'true', 't', 'yes', 'y', '1', 1, -1]
272
- false_values = [false, 'false', 'f', 'no', 'n', '0', 0]
273
-
274
- true_values.each do |v|
275
- instance[:foo] = v
276
- expect(instance[:foo]).to be_a(TrueClass)
277
- end
278
- false_values.each do |v|
279
- instance[:foo] = v
280
- expect(instance[:foo]).to be_a(FalseClass)
281
- end
282
- end
283
-
284
- it 'raises errors for non-coercable types' do
285
- subject.coerce_key :foo, NotInitializable
286
- expect { instance[:foo] = 'true' }.to raise_error(Hashie::CoercionError, /NotInitializable is not a coercable type/)
287
- end
288
-
289
- it 'can coerce false' do
290
- subject.coerce_key :foo, Coercable
291
-
292
- instance[:foo] = false
293
- expect(instance[:foo]).to be_coerced
294
- expect(instance[:foo].value).to eq('FalseClass')
295
- end
296
-
297
- it 'does not coerce nil' do
298
- subject.coerce_key :foo, String
299
-
300
- instance[:foo] = nil
301
- expect(instance[:foo]).to_not eq('')
302
- expect(instance[:foo]).to be_nil
303
- end
304
- end
305
-
306
- it 'calls #new if no coerce method is available' do
307
- subject.coerce_key :foo, Initializable
308
-
309
- instance[:foo] = 'bar'
310
- expect(instance[:foo].value).to eq 'String'
311
- expect(instance[:foo]).not_to be_coerced
312
- end
313
-
314
- it 'coerces when the merge initializer is used' do
315
- subject.coerce_key :foo, Coercable
316
- instance = subject.new(foo: 'bar')
317
-
318
- expect(instance[:foo]).to be_coerced
319
- end
320
-
321
- context 'when #replace is used' do
322
- before { subject.coerce_key :foo, :bar, Coercable }
323
-
324
- let(:instance) do
325
- subject.new(foo: 'bar').replace(foo: 'foz', bar: 'baz', hi: 'bye')
326
- end
327
-
328
- it 'coerces relevant keys' do
329
- expect(instance[:foo]).to be_coerced
330
- expect(instance[:bar]).to be_coerced
331
- expect(instance[:hi]).not_to respond_to(:coerced?)
332
- end
333
-
334
- it 'sets correct values' do
335
- expect(instance[:hi]).to eq 'bye'
336
- end
337
- end
338
-
339
- context 'when used with a Mash' do
340
- class UserMash < Hashie::Mash
341
- end
342
- class TweetMash < Hashie::Mash
343
- include Hashie::Extensions::Coercion
344
- coerce_key :user, UserMash
345
- end
346
-
347
- it 'coerces with instance initialization' do
348
- tweet = TweetMash.new(user: { email: 'foo@bar.com' })
349
- expect(tweet[:user]).to be_a(UserMash)
350
- end
351
-
352
- it 'coerces when setting with attribute style' do
353
- tweet = TweetMash.new
354
- tweet.user = { email: 'foo@bar.com' }
355
- expect(tweet[:user]).to be_a(UserMash)
356
- end
357
-
358
- it 'coerces when setting with string index' do
359
- tweet = TweetMash.new
360
- tweet['user'] = { email: 'foo@bar.com' }
361
- expect(tweet[:user]).to be_a(UserMash)
362
- end
363
-
364
- it 'coerces when setting with symbol index' do
365
- tweet = TweetMash.new
366
- tweet[:user] = { email: 'foo@bar.com' }
367
- expect(tweet[:user]).to be_a(UserMash)
368
- end
369
- end
370
-
371
- context 'when used with a Trash' do
372
- class UserTrash < Hashie::Trash
373
- property :email
374
- end
375
- class TweetTrash < Hashie::Trash
376
- include Hashie::Extensions::Coercion
377
-
378
- property :user, from: :user_data
379
- coerce_key :user, UserTrash
380
- end
381
-
382
- it 'coerces with instance initialization' do
383
- tweet = TweetTrash.new(user_data: { email: 'foo@bar.com' })
384
- expect(tweet[:user]).to be_a(UserTrash)
385
- end
386
- end
387
-
388
- context 'when used with IndifferentAccess to coerce a Mash' do
389
- class MyHash < Hash
390
- include Hashie::Extensions::Coercion
391
- include Hashie::Extensions::IndifferentAccess
392
- include Hashie::Extensions::MergeInitializer
393
- end
394
-
395
- class UserHash < MyHash
396
- end
397
-
398
- class TweetHash < MyHash
399
- coerce_key :user, UserHash
400
- end
401
-
402
- it 'coerces with instance initialization' do
403
- tweet = TweetHash.new(user: Hashie::Mash.new(email: 'foo@bar.com'))
404
- expect(tweet[:user]).to be_a(UserHash)
405
- end
406
-
407
- it 'coerces when setting with string index' do
408
- tweet = TweetHash.new
409
- tweet['user'] = Hashie::Mash.new(email: 'foo@bar.com')
410
- expect(tweet[:user]).to be_a(UserHash)
411
- end
412
-
413
- it 'coerces when setting with symbol index' do
414
- tweet = TweetHash.new
415
- tweet[:user] = Hashie::Mash.new(email: 'foo@bar.com')
416
- expect(tweet[:user]).to be_a(UserHash)
417
- end
418
- end
419
-
420
- context 'when subclassing' do
421
- class MyOwnBase < Hash
422
- include Hashie::Extensions::Coercion
423
- end
424
-
425
- class MyOwnHash < MyOwnBase
426
- coerce_key :value, Integer
427
- end
428
-
429
- class MyOwnSubclass < MyOwnHash
430
- end
431
-
432
- it 'inherits key coercions' do
433
- expect(MyOwnHash.key_coercions).to eql(MyOwnSubclass.key_coercions)
434
- end
435
-
436
- it 'the superclass does not accumulate coerced attributes from subclasses' do
437
- expect(MyOwnBase.key_coercions).to eq({})
438
- end
439
- end
440
-
441
- context 'when using circular coercion' do
442
- context 'with a proc on one side' do
443
- class CategoryHash < Hash
444
- include Hashie::Extensions::Coercion
445
- include Hashie::Extensions::MergeInitializer
446
-
447
- coerce_key :products, lambda { |value|
448
- return value.map { |v| ProductHash.new(v) } if value.respond_to?(:map)
449
-
450
- ProductHash.new(v)
451
- }
452
- end
453
-
454
- class ProductHash < Hash
455
- include Hashie::Extensions::Coercion
456
- include Hashie::Extensions::MergeInitializer
457
-
458
- coerce_key :categories, Array[CategoryHash]
459
- end
460
-
461
- let(:category) { CategoryHash.new(type: 'rubygem', products: [Hashie::Mash.new(name: 'Hashie')]) }
462
- let(:product) { ProductHash.new(name: 'Hashie', categories: [Hashie::Mash.new(type: 'rubygem')]) }
463
-
464
- it 'coerces CategoryHash[:products] correctly' do
465
- expected = [ProductHash]
466
- actual = category[:products].map(&:class)
467
-
468
- expect(actual).to eq(expected)
469
- end
470
-
471
- it 'coerces ProductHash[:categories] correctly' do
472
- expected = [CategoryHash]
473
- actual = product[:categories].map(&:class)
474
-
475
- expect(actual).to eq(expected)
476
- end
477
- end
478
-
479
- context 'without a proc on either side' do
480
- it 'fails with a NameError since the other class is not defined yet' do
481
- attempted_code = lambda do
482
- class AnotherCategoryHash < Hash
483
- include Hashie::Extensions::Coercion
484
- include Hashie::Extensions::MergeInitializer
485
-
486
- coerce_key :products, Array[AnotherProductHash]
487
- end
488
-
489
- class AnotherProductHash < Hash
490
- include Hashie::Extensions::Coercion
491
- include Hashie::Extensions::MergeInitializer
492
-
493
- coerce_key :categories, Array[AnotherCategoryHash]
494
- end
495
- end
496
-
497
- expect { attempted_code.call }.to raise_error(NameError)
498
- end
499
- end
500
- end
501
- end
502
-
503
- describe '#coerce_value' do
504
- context 'with strict: true' do
505
- it 'coerces any value of the exact right class' do
506
- subject.coerce_value String, Coercable
507
-
508
- instance[:foo] = 'bar'
509
- instance[:bar] = 'bax'
510
- instance[:hi] = :bye
511
- expect(instance[:foo]).to be_coerced
512
- expect(instance[:bar]).to be_coerced
513
- expect(instance[:hi]).not_to respond_to(:coerced?)
514
- end
515
-
516
- it 'coerces values from a #replace call' do
517
- subject.coerce_value String, Coercable
518
-
519
- instance[:foo] = :bar
520
- instance.replace(foo: 'bar', bar: 'bax')
521
- expect(instance[:foo]).to be_coerced
522
- expect(instance[:bar]).to be_coerced
523
- end
524
-
525
- it 'does not coerce superclasses' do
526
- klass = Class.new(String)
527
- subject.coerce_value klass, Coercable
528
-
529
- instance[:foo] = 'bar'
530
- expect(instance[:foo]).not_to be_kind_of(Coercable)
531
- instance[:foo] = klass.new
532
- expect(instance[:foo]).to be_kind_of(Coercable)
533
- end
534
- end
535
-
536
- context 'core types' do
537
- it 'coerces String to Integer when possible' do
538
- subject.coerce_value String, Integer
539
-
540
- instance[:foo] = '2'
541
- instance[:bar] = '2.7'
542
- instance[:hi] = 'hi'
543
- expect(instance[:foo]).to be_a(Integer)
544
- expect(instance[:foo]).to eq(2)
545
- expect(instance[:bar]).to be_a(Integer)
546
- expect(instance[:bar]).to eq(2)
547
- expect(instance[:hi]).to be_a(Integer)
548
- expect(instance[:hi]).to eq(0) # not what I expected...
549
- end
550
-
551
- it 'coerces non-numeric from String to Integer' do
552
- # This was surprising, but I guess it's "correct"
553
- # unless there is a stricter `to_i` alternative
554
- subject.coerce_value String, Integer
555
- instance[:hi] = 'hi'
556
- expect(instance[:hi]).to be_a(Integer)
557
- expect(instance[:hi]).to eq(0)
558
- end
559
-
560
- it 'raises a CoercionError when coercion is not possible' do
561
- subject.coerce_value Fixnum, Symbol
562
- expect { instance[:hi] = 1 }.to raise_error(Hashie::CoercionError, /Cannot coerce property :hi from Fixnum to Symbol/)
563
- end
564
-
565
- it 'coerces Integer to String' do
566
- subject.coerce_value Integer, String
567
-
568
- {
569
- fixnum: 2,
570
- bignum: 12_345_667_890_987_654_321,
571
- float: 2.7,
572
- rational: Rational(2, 3),
573
- complex: Complex(1)
574
- }.each do | k, v |
575
- instance[k] = v
576
- if v.is_a? Integer
577
- expect(instance[k]).to be_a(String)
578
- expect(instance[k]).to eq(v.to_s)
579
- else
580
- expect(instance[k]).to_not be_a(String)
581
- expect(instance[k]).to eq(v)
582
- end
583
- end
584
- end
585
-
586
- it 'coerces Numeric to String' do
587
- subject.coerce_value Numeric, String
588
-
589
- {
590
- fixnum: 2,
591
- bignum: 12_345_667_890_987_654_321,
592
- float: 2.7,
593
- rational: Rational(2, 3),
594
- complex: Complex(1)
595
- }.each do | k, v |
596
- instance[k] = v
597
- expect(instance[k]).to be_a(String)
598
- expect(instance[k]).to eq(v.to_s)
599
- end
600
- end
601
-
602
- it 'can coerce via a proc' do
603
- subject.coerce_value(String, lambda do |v|
604
- return !!(v =~ /^(true|t|yes|y|1)$/i)
605
- end)
606
-
607
- true_values = %w(true t yes y 1)
608
- false_values = %w(false f no n 0)
609
-
610
- true_values.each do |v|
611
- instance[:foo] = v
612
- expect(instance[:foo]).to be_a(TrueClass)
613
- end
614
- false_values.each do |v|
615
- instance[:foo] = v
616
- expect(instance[:foo]).to be_a(FalseClass)
617
- end
618
- end
619
- end
620
- end
621
-
622
- after(:each) do
623
- Object.send(:remove_const, :ExampleCoercableHash)
624
- end
625
- end
@@ -1,84 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Hashie::Extensions::Dash::IndifferentAccess do
4
- class TrashWithIndifferentAccess < Hashie::Trash
5
- include Hashie::Extensions::Dash::IndifferentAccess
6
- property :per_page, transform_with: ->(v) { v.to_i }
7
- property :total, from: :total_pages
8
- end
9
-
10
- class DashWithIndifferentAccess < Hashie::Dash
11
- include Hashie::Extensions::Dash::IndifferentAccess
12
- property :name
13
- end
14
-
15
- context 'when included in Trash' do
16
- let(:params) { { per_page: '1', total_pages: 2 } }
17
- subject { TrashWithIndifferentAccess.new(params) }
18
-
19
- it 'gets the expected behaviour' do
20
- expect(subject.per_page).to eq params[:per_page].to_i
21
- expect(subject.total).to eq params[:total_pages]
22
- end
23
- end
24
-
25
- context 'when included in Dash' do
26
- let(:patch) { Hashie::Extensions::Dash::IndifferentAccess::ClassMethods }
27
- let(:dash_class) { Class.new(Hashie::Dash) }
28
-
29
- it 'extends with the patch once' do
30
- expect(patch).to receive(:extended).with(dash_class).once
31
- dash_class.send(:include, Hashie::Extensions::Dash::IndifferentAccess)
32
- end
33
- end
34
-
35
- context 'initialized with' do
36
- it 'string' do
37
- instance = DashWithIndifferentAccess.new('name' => 'Name')
38
- expect(instance.name).to eq('Name')
39
- expect(instance['name']).to eq('Name')
40
- expect(instance[:name]).to eq('Name')
41
- expect(instance.inspect).to eq('#<DashWithIndifferentAccess name="Name">')
42
- expect(instance.to_hash).to eq('name' => 'Name')
43
- end
44
-
45
- it 'key' do
46
- instance = DashWithIndifferentAccess.new(name: 'Name')
47
- expect(instance.name).to eq('Name')
48
- expect(instance['name']).to eq('Name')
49
- expect(instance[:name]).to eq('Name')
50
- expect(instance.inspect).to eq('#<DashWithIndifferentAccess name="Name">')
51
- expect(instance.to_hash).to eq('name' => 'Name')
52
- end
53
- end
54
-
55
- it 'updates' do
56
- instance = DashWithIndifferentAccess.new
57
- instance['name'] = 'Updated String'
58
- expect(instance.name).to eq('Updated String')
59
- instance[:name] = 'Updated Symbol'
60
- expect(instance.name).to eq('Updated Symbol')
61
- instance.name = 'Updated Method'
62
- expect(instance.name).to eq('Updated Method')
63
- end
64
-
65
- context 'initialized with both prefers last assignment' do
66
- it 'string, then symbol' do
67
- instance = DashWithIndifferentAccess.new('name' => 'First', name: 'Last')
68
- expect(instance.name).to eq('Last')
69
- expect(instance['name']).to eq('Last')
70
- expect(instance[:name]).to eq('Last')
71
- expect(instance.inspect).to eq('#<DashWithIndifferentAccess name="Last">')
72
- expect(instance.to_hash).to eq('name' => 'Last')
73
- end
74
-
75
- it 'symbol then string' do
76
- instance = DashWithIndifferentAccess.new(name: 'Last', 'name' => 'First')
77
- expect(instance.name).to eq('First')
78
- expect(instance['name']).to eq('First')
79
- expect(instance[:name]).to eq('First')
80
- expect(instance.inspect).to eq('#<DashWithIndifferentAccess name="First">')
81
- expect(instance.to_hash).to eq('name' => 'First')
82
- end
83
- end
84
- end