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,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