hashie 4.0.0 → 4.1.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 (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