hashie 4.0.0 → 4.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +213 -187
  3. data/CONTRIBUTING.md +13 -6
  4. data/README.md +33 -9
  5. data/UPGRADING.md +5 -5
  6. data/hashie.gemspec +11 -6
  7. data/lib/hashie.rb +1 -0
  8. data/lib/hashie/extensions/dash/property_translation.rb +1 -1
  9. data/lib/hashie/extensions/deep_merge.rb +18 -1
  10. data/lib/hashie/extensions/mash/permissive_respond_to.rb +61 -0
  11. data/lib/hashie/extensions/parsers/yaml_erb_parser.rb +19 -2
  12. data/lib/hashie/extensions/ruby_version_check.rb +5 -1
  13. data/lib/hashie/mash.rb +31 -26
  14. data/lib/hashie/utils.rb +28 -0
  15. data/lib/hashie/version.rb +1 -1
  16. metadata +16 -131
  17. data/spec/hashie/array_spec.rb +0 -29
  18. data/spec/hashie/clash_spec.rb +0 -70
  19. data/spec/hashie/dash_spec.rb +0 -608
  20. data/spec/hashie/extensions/autoload_spec.rb +0 -24
  21. data/spec/hashie/extensions/coercion_spec.rb +0 -648
  22. data/spec/hashie/extensions/dash/coercion_spec.rb +0 -13
  23. data/spec/hashie/extensions/dash/indifferent_access_spec.rb +0 -84
  24. data/spec/hashie/extensions/deep_fetch_spec.rb +0 -97
  25. data/spec/hashie/extensions/deep_find_spec.rb +0 -144
  26. data/spec/hashie/extensions/deep_locate_spec.rb +0 -138
  27. data/spec/hashie/extensions/deep_merge_spec.rb +0 -74
  28. data/spec/hashie/extensions/ignore_undeclared_spec.rb +0 -48
  29. data/spec/hashie/extensions/indifferent_access_spec.rb +0 -295
  30. data/spec/hashie/extensions/indifferent_access_with_rails_hwia_spec.rb +0 -208
  31. data/spec/hashie/extensions/key_conversion_spec.rb +0 -12
  32. data/spec/hashie/extensions/mash/define_accessors_spec.rb +0 -90
  33. data/spec/hashie/extensions/mash/keep_original_keys_spec.rb +0 -46
  34. data/spec/hashie/extensions/mash/safe_assignment_spec.rb +0 -50
  35. data/spec/hashie/extensions/mash/symbolize_keys_spec.rb +0 -39
  36. data/spec/hashie/extensions/merge_initializer_spec.rb +0 -23
  37. data/spec/hashie/extensions/method_access_spec.rb +0 -233
  38. data/spec/hashie/extensions/strict_key_access_spec.rb +0 -109
  39. data/spec/hashie/extensions/stringify_keys_spec.rb +0 -124
  40. data/spec/hashie/extensions/symbolize_keys_spec.rb +0 -131
  41. data/spec/hashie/hash_spec.rb +0 -123
  42. data/spec/hashie/mash_spec.rb +0 -1077
  43. data/spec/hashie/parsers/yaml_erb_parser_spec.rb +0 -46
  44. data/spec/hashie/rash_spec.rb +0 -83
  45. data/spec/hashie/trash_spec.rb +0 -334
  46. data/spec/hashie/utils_spec.rb +0 -25
  47. data/spec/hashie/version_spec.rb +0 -7
  48. data/spec/hashie_spec.rb +0 -13
  49. data/spec/integration/elasticsearch/integration_spec.rb +0 -41
  50. data/spec/integration/omniauth-oauth2/app.rb +0 -52
  51. data/spec/integration/omniauth-oauth2/integration_spec.rb +0 -26
  52. data/spec/integration/omniauth-oauth2/some_site.rb +0 -38
  53. data/spec/integration/omniauth/app.rb +0 -11
  54. data/spec/integration/omniauth/integration_spec.rb +0 -38
  55. data/spec/integration/rails-without-dependency/integration_spec.rb +0 -15
  56. data/spec/integration/rails/app.rb +0 -40
  57. data/spec/integration/rails/integration_spec.rb +0 -47
  58. data/spec/spec_helper.rb +0 -23
  59. data/spec/support/integration_specs.rb +0 -36
  60. data/spec/support/logger.rb +0 -24
  61. data/spec/support/module_context.rb +0 -11
  62. data/spec/support/ruby_version_check.rb +0 -6
@@ -1,24 +0,0 @@
1
- require 'spec_helper'
2
- require 'hashie'
3
-
4
- describe Hashie::Extensions do
5
- describe 'autloads constants' do
6
- it { is_expected.to be_const_defined(:MethodAccess) }
7
- it { is_expected.to be_const_defined(:Coercion) }
8
- it { is_expected.to be_const_defined(:DeepMerge) }
9
- it { is_expected.to be_const_defined(:IgnoreUndeclared) }
10
- it { is_expected.to be_const_defined(:IndifferentAccess) }
11
- it { is_expected.to be_const_defined(:MergeInitializer) }
12
- it { is_expected.to be_const_defined(:MethodAccess) }
13
- it { is_expected.to be_const_defined(:MethodQuery) }
14
- it { is_expected.to be_const_defined(:MethodReader) }
15
- it { is_expected.to be_const_defined(:MethodWriter) }
16
- it { is_expected.to be_const_defined(:StringifyKeys) }
17
- it { is_expected.to be_const_defined(:SymbolizeKeys) }
18
- it { is_expected.to be_const_defined(:DeepFetch) }
19
- it { is_expected.to be_const_defined(:DeepFind) }
20
- it { is_expected.to be_const_defined(:PrettyInspect) }
21
- it { is_expected.to be_const_defined(:KeyConversion) }
22
- it { is_expected.to be_const_defined(:MethodAccessWithOverride) }
23
- end
24
- end
@@ -1,648 +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' }
287
- .to raise_error(Hashie::CoercionError, /NotInitializable is not a coercable type/)
288
- end
289
-
290
- it 'can coerce false' do
291
- subject.coerce_key :foo, Coercable
292
-
293
- instance[:foo] = false
294
- expect(instance[:foo]).to be_coerced
295
- expect(instance[:foo].value).to eq('FalseClass')
296
- end
297
-
298
- it 'does not coerce nil' do
299
- subject.coerce_key :foo, String
300
-
301
- instance[:foo] = nil
302
- expect(instance[:foo]).to_not eq('')
303
- expect(instance[:foo]).to be_nil
304
- end
305
- end
306
-
307
- it 'calls #new if no coerce method is available' do
308
- subject.coerce_key :foo, Initializable
309
-
310
- instance[:foo] = 'bar'
311
- expect(instance[:foo].value).to eq 'String'
312
- expect(instance[:foo]).not_to be_coerced
313
- end
314
-
315
- it 'coerces when the merge initializer is used' do
316
- subject.coerce_key :foo, Coercable
317
- instance = subject.new(foo: 'bar')
318
-
319
- expect(instance[:foo]).to be_coerced
320
- end
321
-
322
- context 'when #replace is used' do
323
- before { subject.coerce_key :foo, :bar, Coercable }
324
-
325
- let(:instance) do
326
- subject.new(foo: 'bar').replace(foo: 'foz', bar: 'baz', hi: 'bye')
327
- end
328
-
329
- it 'coerces relevant keys' do
330
- expect(instance[:foo]).to be_coerced
331
- expect(instance[:bar]).to be_coerced
332
- expect(instance[:hi]).not_to respond_to(:coerced?)
333
- end
334
-
335
- it 'sets correct values' do
336
- expect(instance[:hi]).to eq 'bye'
337
- end
338
- end
339
-
340
- context 'when used with a Mash' do
341
- class UserMash < Hashie::Mash
342
- end
343
- class TweetMash < Hashie::Mash
344
- include Hashie::Extensions::Coercion
345
- coerce_key :user, UserMash
346
- end
347
-
348
- it 'coerces with instance initialization' do
349
- tweet = TweetMash.new(user: { email: 'foo@bar.com' })
350
- expect(tweet[:user]).to be_a(UserMash)
351
- end
352
-
353
- it 'coerces when setting with attribute style' do
354
- tweet = TweetMash.new
355
- tweet.user = { email: 'foo@bar.com' }
356
- expect(tweet[:user]).to be_a(UserMash)
357
- end
358
-
359
- it 'coerces when setting with string index' do
360
- tweet = TweetMash.new
361
- tweet['user'] = { email: 'foo@bar.com' }
362
- expect(tweet[:user]).to be_a(UserMash)
363
- end
364
-
365
- it 'coerces when setting with symbol index' do
366
- tweet = TweetMash.new
367
- tweet[:user] = { email: 'foo@bar.com' }
368
- expect(tweet[:user]).to be_a(UserMash)
369
- end
370
- end
371
-
372
- context 'when used with a Trash' do
373
- class UserTrash < Hashie::Trash
374
- property :email
375
- end
376
- class TweetTrash < Hashie::Trash
377
- include Hashie::Extensions::Coercion
378
-
379
- property :user, from: :user_data
380
- coerce_key :user, UserTrash
381
- end
382
-
383
- it 'coerces with instance initialization' do
384
- tweet = TweetTrash.new(user_data: { email: 'foo@bar.com' })
385
- expect(tweet[:user]).to be_a(UserTrash)
386
- end
387
- end
388
-
389
- context 'when used with IndifferentAccess to coerce a Mash' do
390
- class MyHash < Hash
391
- include Hashie::Extensions::Coercion
392
- include Hashie::Extensions::IndifferentAccess
393
- include Hashie::Extensions::MergeInitializer
394
- end
395
-
396
- class UserHash < MyHash
397
- end
398
-
399
- class TweetHash < MyHash
400
- coerce_key :user, UserHash
401
- end
402
-
403
- it 'coerces with instance initialization' do
404
- tweet = TweetHash.new(user: Hashie::Mash.new(email: 'foo@bar.com'))
405
- expect(tweet[:user]).to be_a(UserHash)
406
- end
407
-
408
- it 'coerces when setting with string index' do
409
- tweet = TweetHash.new
410
- tweet['user'] = Hashie::Mash.new(email: 'foo@bar.com')
411
- expect(tweet[:user]).to be_a(UserHash)
412
- end
413
-
414
- it 'coerces when setting with symbol index' do
415
- tweet = TweetHash.new
416
- tweet[:user] = Hashie::Mash.new(email: 'foo@bar.com')
417
- expect(tweet[:user]).to be_a(UserHash)
418
- end
419
- end
420
-
421
- context 'when subclassing' do
422
- class MyOwnBase < Hash
423
- include Hashie::Extensions::Coercion
424
- end
425
-
426
- class MyOwnHash < MyOwnBase
427
- coerce_key :value, Integer
428
- end
429
-
430
- class MyOwnSubclass < MyOwnHash
431
- end
432
-
433
- it 'inherits key coercions' do
434
- expect(MyOwnHash.key_coercions).to eql(MyOwnSubclass.key_coercions)
435
- end
436
-
437
- it 'the superclass does not accumulate coerced attributes from subclasses' do
438
- expect(MyOwnBase.key_coercions).to eq({})
439
- end
440
- end
441
-
442
- context 'when using circular coercion' do
443
- context 'with a proc on one side' do
444
- class CategoryHash < Hash
445
- include Hashie::Extensions::Coercion
446
- include Hashie::Extensions::MergeInitializer
447
-
448
- coerce_key :products, lambda { |value|
449
- return value.map { |v| ProductHash.new(v) } if value.respond_to?(:map)
450
-
451
- ProductHash.new(v)
452
- }
453
- end
454
-
455
- class ProductHash < Hash
456
- include Hashie::Extensions::Coercion
457
- include Hashie::Extensions::MergeInitializer
458
-
459
- coerce_key :categories, Array[CategoryHash]
460
- end
461
-
462
- let(:category) do
463
- CategoryHash.new(type: 'rubygem', products: [Hashie::Mash.new(name: 'Hashie')])
464
- end
465
- let(:product) do
466
- ProductHash.new(name: 'Hashie', categories: [Hashie::Mash.new(type: 'rubygem')])
467
- end
468
-
469
- it 'coerces CategoryHash[:products] correctly' do
470
- expected = [ProductHash]
471
- actual = category[:products].map(&:class)
472
-
473
- expect(actual).to eq(expected)
474
- end
475
-
476
- it 'coerces ProductHash[:categories] correctly' do
477
- expected = [CategoryHash]
478
- actual = product[:categories].map(&:class)
479
-
480
- expect(actual).to eq(expected)
481
- end
482
- end
483
-
484
- context 'without a proc on either side' do
485
- it 'fails with a NameError since the other class is not defined yet' do
486
- attempted_code = lambda do
487
- class AnotherCategoryHash < Hash
488
- include Hashie::Extensions::Coercion
489
- include Hashie::Extensions::MergeInitializer
490
-
491
- coerce_key :products, Array[AnotherProductHash]
492
- end
493
-
494
- class AnotherProductHash < Hash
495
- include Hashie::Extensions::Coercion
496
- include Hashie::Extensions::MergeInitializer
497
-
498
- coerce_key :categories, Array[AnotherCategoryHash]
499
- end
500
- end
501
-
502
- expect { attempted_code.call }.to raise_error(NameError)
503
- end
504
- end
505
- end
506
- end
507
-
508
- describe '#coerce_value' do
509
- context 'with strict: true' do
510
- it 'coerces any value of the exact right class' do
511
- subject.coerce_value String, Coercable
512
-
513
- instance[:foo] = 'bar'
514
- instance[:bar] = 'bax'
515
- instance[:hi] = :bye
516
- expect(instance[:foo]).to be_coerced
517
- expect(instance[:bar]).to be_coerced
518
- expect(instance[:hi]).not_to respond_to(:coerced?)
519
- end
520
-
521
- it 'coerces values from a #replace call' do
522
- subject.coerce_value String, Coercable
523
-
524
- instance[:foo] = :bar
525
- instance.replace(foo: 'bar', bar: 'bax')
526
- expect(instance[:foo]).to be_coerced
527
- expect(instance[:bar]).to be_coerced
528
- end
529
-
530
- it 'does not coerce superclasses' do
531
- klass = Class.new(String)
532
- subject.coerce_value klass, Coercable
533
-
534
- instance[:foo] = 'bar'
535
- expect(instance[:foo]).not_to be_kind_of(Coercable)
536
- instance[:foo] = klass.new
537
- expect(instance[:foo]).to be_kind_of(Coercable)
538
- end
539
- end
540
-
541
- context 'core types' do
542
- it 'coerces String to Integer when possible' do
543
- subject.coerce_value String, Integer
544
-
545
- instance[:foo] = '2'
546
- instance[:bar] = '2.7'
547
- instance[:hi] = 'hi'
548
- expect(instance[:foo]).to be_a(Integer)
549
- expect(instance[:foo]).to eq(2)
550
- expect(instance[:bar]).to be_a(Integer)
551
- expect(instance[:bar]).to eq(2)
552
- expect(instance[:hi]).to be_a(Integer)
553
- expect(instance[:hi]).to eq(0) # not what I expected...
554
- end
555
-
556
- it 'coerces non-numeric from String to Integer' do
557
- # This was surprising, but I guess it's "correct"
558
- # unless there is a stricter `to_i` alternative
559
- subject.coerce_value String, Integer
560
- instance[:hi] = 'hi'
561
- expect(instance[:hi]).to be_a(Integer)
562
- expect(instance[:hi]).to eq(0)
563
- end
564
-
565
- it 'raises a CoercionError when coercion is not possible' do
566
- type =
567
- if Hashie::Extensions::RubyVersion.new(RUBY_VERSION) >=
568
- Hashie::Extensions::RubyVersion.new('2.4.0')
569
- Integer
570
- else
571
- Fixnum
572
- end
573
-
574
- subject.coerce_value type, Symbol
575
- expect { instance[:hi] = 1 }.to raise_error(
576
- Hashie::CoercionError, /Cannot coerce property :hi from #{type} to Symbol/
577
- )
578
- end
579
-
580
- it 'coerces Integer to String' do
581
- type =
582
- if Hashie::Extensions::RubyVersion.new(RUBY_VERSION) >=
583
- Hashie::Extensions::RubyVersion.new('2.4.0')
584
- Integer
585
- else
586
- Fixnum
587
- end
588
-
589
- subject.coerce_value type, String
590
-
591
- {
592
- fixnum: 2,
593
- bignum: 12_345_667_890_987_654_321,
594
- float: 2.7,
595
- rational: Rational(2, 3),
596
- complex: Complex(1)
597
- }.each do |k, v|
598
- instance[k] = v
599
- if v.is_a? type
600
- expect(instance[k]).to be_a(String)
601
- expect(instance[k]).to eq(v.to_s)
602
- else
603
- expect(instance[k]).to_not be_a(String)
604
- expect(instance[k]).to eq(v)
605
- end
606
- end
607
- end
608
-
609
- it 'coerces Numeric to String' do
610
- subject.coerce_value Numeric, String
611
-
612
- {
613
- fixnum: 2,
614
- bignum: 12_345_667_890_987_654_321,
615
- float: 2.7,
616
- rational: Rational(2, 3),
617
- complex: Complex(1)
618
- }.each do |k, v|
619
- instance[k] = v
620
- expect(instance[k]).to be_a(String)
621
- expect(instance[k]).to eq(v.to_s)
622
- end
623
- end
624
-
625
- it 'can coerce via a proc' do
626
- subject.coerce_value(String, lambda do |v|
627
- return !!(v =~ /^(true|t|yes|y|1)$/i)
628
- end)
629
-
630
- true_values = %w[true t yes y 1]
631
- false_values = %w[false f no n 0]
632
-
633
- true_values.each do |v|
634
- instance[:foo] = v
635
- expect(instance[:foo]).to be_a(TrueClass)
636
- end
637
- false_values.each do |v|
638
- instance[:foo] = v
639
- expect(instance[:foo]).to be_a(FalseClass)
640
- end
641
- end
642
- end
643
- end
644
-
645
- after(:each) do
646
- Object.send(:remove_const, :ExampleCoercableHash)
647
- end
648
- end