hashie 3.6.0 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +28 -0
  3. data/README.md +95 -7
  4. data/UPGRADING.md +78 -2
  5. data/hashie.gemspec +2 -1
  6. data/lib/hashie.rb +20 -19
  7. data/lib/hashie/dash.rb +2 -1
  8. data/lib/hashie/extensions/active_support/core_ext/hash.rb +14 -0
  9. data/lib/hashie/extensions/coercion.rb +23 -16
  10. data/lib/hashie/extensions/dash/indifferent_access.rb +20 -1
  11. data/lib/hashie/extensions/dash/property_translation.rb +5 -2
  12. data/lib/hashie/extensions/deep_fetch.rb +4 -2
  13. data/lib/hashie/extensions/deep_find.rb +12 -3
  14. data/lib/hashie/extensions/deep_locate.rb +22 -7
  15. data/lib/hashie/extensions/indifferent_access.rb +1 -3
  16. data/lib/hashie/extensions/key_conflict_warning.rb +55 -0
  17. data/lib/hashie/extensions/mash/define_accessors.rb +90 -0
  18. data/lib/hashie/extensions/mash/keep_original_keys.rb +2 -1
  19. data/lib/hashie/extensions/mash/safe_assignment.rb +3 -1
  20. data/lib/hashie/extensions/method_access.rb +5 -2
  21. data/lib/hashie/extensions/parsers/yaml_erb_parser.rb +9 -4
  22. data/lib/hashie/extensions/strict_key_access.rb +8 -4
  23. data/lib/hashie/hash.rb +16 -9
  24. data/lib/hashie/mash.rb +99 -43
  25. data/lib/hashie/railtie.rb +7 -0
  26. data/lib/hashie/rash.rb +1 -1
  27. data/lib/hashie/version.rb +1 -1
  28. data/spec/hashie/dash_spec.rb +18 -8
  29. data/spec/hashie/extensions/coercion_spec.rb +17 -8
  30. data/spec/hashie/extensions/deep_find_spec.rb +12 -6
  31. data/spec/hashie/extensions/deep_locate_spec.rb +2 -1
  32. data/spec/hashie/extensions/deep_merge_spec.rb +6 -2
  33. data/spec/hashie/extensions/ignore_undeclared_spec.rb +2 -1
  34. data/spec/hashie/extensions/mash/define_accessors_spec.rb +90 -0
  35. data/spec/hashie/extensions/method_access_spec.rb +8 -1
  36. data/spec/hashie/extensions/strict_key_access_spec.rb +9 -10
  37. data/spec/hashie/extensions/symbolize_keys_spec.rb +3 -1
  38. data/spec/hashie/hash_spec.rb +45 -6
  39. data/spec/hashie/mash_spec.rb +314 -8
  40. data/spec/hashie/trash_spec.rb +9 -3
  41. data/spec/integration/elasticsearch/integration_spec.rb +3 -2
  42. data/spec/integration/rails/app.rb +5 -12
  43. data/spec/integration/rails/integration_spec.rb +22 -1
  44. metadata +8 -4
@@ -41,7 +41,8 @@ describe Hashie::Extensions::IgnoreUndeclared do
41
41
  property :some_other_key
42
42
  end
43
43
  hash = ForgivingTrashWithMergeAndProperty.new(some_ignored_key: 17, some_key: 12)
44
- expect(hash.deep_merge(some_other_key: 55, some_ignored_key: 18)).to eq(some_key: 12, some_other_key: 55)
44
+ expect(hash.deep_merge(some_other_key: 55, some_ignored_key: 18))
45
+ .to eq(some_key: 12, some_other_key: 55)
45
46
  end
46
47
  end
47
48
  end
@@ -0,0 +1,90 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hashie::Extensions::Mash::DefineAccessors do
4
+ let(:args) { [] }
5
+
6
+ shared_examples 'class with dynamically defined accessors' do
7
+ it 'defines reader on demand' do
8
+ expect(subject.method_defined?(:foo)).to be_falsey
9
+ instance.foo
10
+ expect(subject.method_defined?(:foo)).to be_truthy
11
+ end
12
+
13
+ it 'defines writer on demand' do
14
+ expect(subject.method_defined?(:foo=)).to be_falsey
15
+ instance.foo = :bar
16
+ expect(subject.method_defined?(:foo=)).to be_truthy
17
+ end
18
+
19
+ it 'defines predicate on demand' do
20
+ expect(subject.method_defined?(:foo?)).to be_falsey
21
+ instance.foo?
22
+ expect(subject.method_defined?(:foo?)).to be_truthy
23
+ end
24
+
25
+ it 'defines initializing reader on demand' do
26
+ expect(subject.method_defined?(:foo!)).to be_falsey
27
+ instance.foo!
28
+ expect(subject.method_defined?(:foo!)).to be_truthy
29
+ end
30
+
31
+ it 'defines underbang reader on demand' do
32
+ expect(subject.method_defined?(:foo_)).to be_falsey
33
+ instance.foo_
34
+ expect(subject.method_defined?(:foo_)).to be_truthy
35
+ end
36
+
37
+ context 'when initializing from another hash' do
38
+ let(:args) { [{ foo: :bar }] }
39
+
40
+ it 'does not define any accessors' do
41
+ expect(subject.method_defined?(:foo)).to be_falsey
42
+ expect(subject.method_defined?(:foo=)).to be_falsey
43
+ expect(subject.method_defined?(:foo?)).to be_falsey
44
+ expect(subject.method_defined?(:foo!)).to be_falsey
45
+ expect(subject.method_defined?(:foo_)).to be_falsey
46
+ expect(instance.foo).to eq :bar
47
+ end
48
+ end
49
+ end
50
+
51
+ context 'when included in Mash subclass' do
52
+ subject { Class.new(Hashie::Mash) { include Hashie::Extensions::Mash::DefineAccessors } }
53
+ let(:instance) { subject.new(*args) }
54
+
55
+ describe 'this subclass' do
56
+ it_behaves_like 'class with dynamically defined accessors'
57
+
58
+ describe 'when accessors are overrided in class' do
59
+ before do
60
+ subject.class_eval do
61
+ def foo
62
+ if self[:foo] != 1
63
+ :bar
64
+ else
65
+ super
66
+ end
67
+ end
68
+ end
69
+ end
70
+
71
+ it 'allows to call super' do
72
+ expect(instance.foo).to eq :bar
73
+ instance.foo = 2
74
+ expect(instance.foo).to eq :bar
75
+ instance.foo = 1
76
+ expect(instance.foo).to eq 1
77
+ end
78
+ end
79
+ end
80
+ end
81
+
82
+ context 'when Mash instance is extended' do
83
+ let(:instance) { Hashie::Mash.new(*args).with_accessors! }
84
+ subject { instance.singleton_class }
85
+
86
+ describe 'its singleton class' do
87
+ it_behaves_like 'class with dynamically defined accessors'
88
+ end
89
+ end
90
+ end
@@ -128,7 +128,14 @@ describe Hashie::Extensions::MethodAccess do
128
128
  it 'includes all of the other method mixins' do
129
129
  klass = Class.new(Hash)
130
130
  klass.send :include, Hashie::Extensions::MethodAccess
131
- expect((klass.ancestors & [Hashie::Extensions::MethodReader, Hashie::Extensions::MethodWriter, Hashie::Extensions::MethodQuery]).size).to eq 3
131
+
132
+ included_modules = klass.ancestors & [
133
+ Hashie::Extensions::MethodReader,
134
+ Hashie::Extensions::MethodWriter,
135
+ Hashie::Extensions::MethodQuery
136
+ ]
137
+
138
+ expect(included_modules.size).to eq 3
132
139
  end
133
140
  end
134
141
 
@@ -35,34 +35,33 @@ describe Hashie::Extensions::StrictKeyAccess do
35
35
  context 'lookup' do
36
36
  it('raises an error') do
37
37
  # Formatting of the error message does not vary here because raised by StrictKeyAccess
38
- expect { instance.key(invalid_value) }.to raise_error KeyError,
39
- %(key not found with value of #{invalid_value.inspect})
38
+ expect { instance.key(invalid_value) }.to raise_error KeyError
40
39
  end
41
40
  end
42
41
  end
43
42
  shared_examples_for 'StrictKeyAccess raises KeyError instead of allowing defaults' do
44
43
  context '#default' do
45
44
  it 'raises an error' do
46
- expect { instance.default(invalid_key) }.to raise_error Hashie::Extensions::StrictKeyAccess::DefaultError,
47
- 'Setting or using a default with Hashie::Extensions::StrictKeyAccess does not make sense'
45
+ expect { instance.default(invalid_key) }
46
+ .to raise_error Hashie::Extensions::StrictKeyAccess::DefaultError
48
47
  end
49
48
  end
50
49
  context '#default=' do
51
50
  it 'raises an error' do
52
- expect { instance.default = invalid_key }.to raise_error Hashie::Extensions::StrictKeyAccess::DefaultError,
53
- 'Setting or using a default with Hashie::Extensions::StrictKeyAccess does not make sense'
51
+ expect { instance.default = invalid_key }
52
+ .to raise_error Hashie::Extensions::StrictKeyAccess::DefaultError
54
53
  end
55
54
  end
56
55
  context '#default_proc' do
57
56
  it 'raises an error' do
58
- expect { instance.default_proc }.to raise_error Hashie::Extensions::StrictKeyAccess::DefaultError,
59
- 'Setting or using a default with Hashie::Extensions::StrictKeyAccess does not make sense'
57
+ expect { instance.default_proc }
58
+ .to raise_error Hashie::Extensions::StrictKeyAccess::DefaultError
60
59
  end
61
60
  end
62
61
  context '#default_proc=' do
63
62
  it 'raises an error' do
64
- expect { instance.default_proc = proc {} }.to raise_error Hashie::Extensions::StrictKeyAccess::DefaultError,
65
- 'Setting or using a default with Hashie::Extensions::StrictKeyAccess does not make sense'
63
+ expect { instance.default_proc = proc {} }
64
+ .to raise_error Hashie::Extensions::StrictKeyAccess::DefaultError
66
65
  end
67
66
  end
68
67
  end
@@ -88,7 +88,9 @@ describe Hashie::Extensions::SymbolizeKeys do
88
88
 
89
89
  context 'singleton methods' do
90
90
  subject { Hash }
91
- let(:object) { subject.new.merge('a' => 1, 'b' => { 'c' => 2 }).extend(Hashie::Extensions::SymbolizeKeys) }
91
+ let(:object) do
92
+ subject.new.merge('a' => 1, 'b' => { 'c' => 2 }).extend(Hashie::Extensions::SymbolizeKeys)
93
+ end
92
94
  let(:expected_hash) { { a: 1, b: { c: 2 } } }
93
95
 
94
96
  describe '.symbolize_keys' do
@@ -64,21 +64,60 @@ describe Hash do
64
64
  end
65
65
 
66
66
  it '#to_hash returns a hash with same keys' do
67
- hash = Hashie::Hash['a' => 'hey', 123 => 'bob', 'array' => [1, 2, 3], subhash: ClassRespondsToHash.new]
67
+ hash = Hashie::Hash[
68
+ 'a' => 'hey',
69
+ 123 => 'bob',
70
+ 'array' => [1, 2, 3],
71
+ subhash: ClassRespondsToHash.new
72
+ ]
68
73
  stringified_hash = hash.to_hash
69
- expect(stringified_hash).to eq('a' => 'hey', 123 => 'bob', 'array' => [1, 2, 3], subhash: { 'a' => 'hey', b: 'bar', 123 => 'bob', 'array' => [1, 2, 3] })
74
+
75
+ expected = {
76
+ 'a' => 'hey',
77
+ 123 => 'bob',
78
+ 'array' => [1, 2, 3],
79
+ subhash: { 'a' => 'hey', b: 'bar', 123 => 'bob', 'array' => [1, 2, 3] }
80
+ }
81
+
82
+ expect(stringified_hash).to eq(expected)
70
83
  end
71
84
 
72
85
  it '#to_hash with stringify_keys set to true returns a hash with stringified_keys' do
73
- hash = Hashie::Hash['a' => 'hey', 123 => 'bob', 'array' => [1, 2, 3], subhash: ClassRespondsToHash.new]
86
+ hash = Hashie::Hash[
87
+ 'a' => 'hey',
88
+ 123 => 'bob',
89
+ 'array' => [1, 2, 3],
90
+ subhash: ClassRespondsToHash.new
91
+ ]
74
92
  symbolized_hash = hash.to_hash(stringify_keys: true)
75
- expect(symbolized_hash).to eq('a' => 'hey', '123' => 'bob', 'array' => [1, 2, 3], 'subhash' => { 'a' => 'hey', 'b' => 'bar', '123' => 'bob', 'array' => [1, 2, 3] })
93
+
94
+ expected = {
95
+ 'a' => 'hey',
96
+ '123' => 'bob',
97
+ 'array' => [1, 2, 3],
98
+ 'subhash' => { 'a' => 'hey', 'b' => 'bar', '123' => 'bob', 'array' => [1, 2, 3] }
99
+ }
100
+
101
+ expect(symbolized_hash).to eq(expected)
76
102
  end
77
103
 
78
104
  it '#to_hash with symbolize_keys set to true returns a hash with symbolized keys' do
79
- hash = Hashie::Hash['a' => 'hey', 123 => 'bob', 'array' => [1, 2, 3], subhash: ClassRespondsToHash.new]
105
+ hash = Hashie::Hash[
106
+ 'a' => 'hey',
107
+ 123 => 'bob',
108
+ 'array' => [1, 2, 3],
109
+ subhash: ClassRespondsToHash.new
110
+ ]
80
111
  symbolized_hash = hash.to_hash(symbolize_keys: true)
81
- expect(symbolized_hash).to eq(a: 'hey', :"123" => 'bob', array: [1, 2, 3], subhash: { a: 'hey', b: 'bar', :'123' => 'bob', array: [1, 2, 3] })
112
+
113
+ expected = {
114
+ a: 'hey',
115
+ :"123" => 'bob',
116
+ array: [1, 2, 3],
117
+ subhash: { a: 'hey', b: 'bar', :'123' => 'bob', array: [1, 2, 3] }
118
+ }
119
+
120
+ expect(symbolized_hash).to eq(expected)
82
121
  end
83
122
  end
84
123
  end
@@ -154,14 +154,15 @@ describe Hashie::Mash do
154
154
  mash_class = Class.new(Hashie::Mash) do
155
155
  disable_warnings
156
156
  end
157
-
158
157
  mash_class.new('trust' => { 'two' => 2 })
159
158
 
160
159
  expect(logger_output).to be_blank
161
160
  end
162
161
 
163
162
  it 'cannot disable logging on the base Mash' do
164
- expect { Hashie::Mash.disable_warnings }.to raise_error(Hashie::Mash::CannotDisableMashWarnings)
163
+ expected_error = Hashie::Extensions::KeyConflictWarning::CannotDisableMashWarnings
164
+
165
+ expect { Hashie::Mash.disable_warnings }.to raise_error(expected_error)
165
166
  end
166
167
 
167
168
  it 'carries over the disable for warnings on grandchild classes' do
@@ -174,6 +175,78 @@ describe Hashie::Mash do
174
175
 
175
176
  expect(logger_output).to be_blank
176
177
  end
178
+
179
+ it 'writes to logger when a key is overridden that is not ignored' do
180
+ mash_class = Class.new(Hashie::Mash) do
181
+ disable_warnings :merge
182
+ end
183
+
184
+ mash_class.new('address' => { 'zip' => '90210' })
185
+ expect(logger_output).not_to be_blank
186
+ end
187
+
188
+ it 'does not write to logger when a key is overridden that is ignored' do
189
+ mash_class = Class.new(Hashie::Mash) do
190
+ disable_warnings :zip
191
+ end
192
+
193
+ mash_class.new('address' => { 'zip' => '90210' })
194
+ expect(logger_output).to be_blank
195
+ end
196
+
197
+ it 'carries over the ignored warnings list for warnings on grandchild classes' do
198
+ child_class = Class.new(Hashie::Mash) do
199
+ disable_warnings :zip, :merge
200
+ end
201
+ grandchild_class = Class.new(child_class)
202
+
203
+ grandchild_class.new('address' => { 'zip' => '90210' }, 'merge' => true)
204
+
205
+ expect(grandchild_class.disabled_warnings).to eq(%i[zip merge])
206
+ expect(logger_output).to be_blank
207
+ end
208
+
209
+ context 'multiple disable_warnings calls' do
210
+ context 'calling disable_warnings multiple times with parameters' do
211
+ it 'appends each new parameter to the ignore list' do
212
+ child_class = Class.new(Hashie::Mash) do
213
+ disable_warnings :zip
214
+ disable_warnings :merge
215
+ disable_warnings :cycle
216
+ end
217
+
218
+ expect(child_class.disabled_warnings).to eq(%i[zip merge cycle])
219
+ end
220
+ end
221
+
222
+ context 'calling disable_warnings without keys after calling with keys' do
223
+ it 'uses the last call to determine the ignore list' do
224
+ child_class = Class.new(Hashie::Mash) do
225
+ disable_warnings :zip
226
+ disable_warnings
227
+ end
228
+
229
+ child_class.new('address' => { 'zip' => '90210' }, 'merge' => true, 'cycle' => 'bi')
230
+
231
+ expect(child_class.disabled_warnings).to eq([])
232
+ expect(logger_output).to be_blank
233
+ end
234
+ end
235
+
236
+ context 'calling disable_parameters with keys after calling without keys' do
237
+ it 'only ignores logging for ignored methods' do
238
+ child_class = Class.new(Hashie::Mash) do
239
+ disable_warnings
240
+ disable_warnings :zip
241
+ end
242
+
243
+ child_class.new('address' => { 'zip' => '90210' }, 'merge' => true)
244
+
245
+ expect(logger_output).to match(/#{child_class}#merge/)
246
+ expect(logger_output).not_to match(/#{child_class}#zip/)
247
+ end
248
+ end
249
+ end
177
250
  end
178
251
 
179
252
  context 'updating' do
@@ -229,15 +302,29 @@ describe Hashie::Mash do
229
302
 
230
303
  # http://www.ruby-doc.org/core-1.9.3/Hash.html#method-i-update
231
304
  it 'accepts a block' do
232
- duped = subject.merge(details: { address: 'Pasadena CA' }) { |_, oldv, newv| [oldv, newv].join(', ') }
305
+ duped = subject.merge(details: { address: 'Pasadena CA' }) do |_, oldv, newv|
306
+ [oldv, newv].join(', ')
307
+ end
308
+
233
309
  expect(duped.details.address).to eq 'Nowhere road, Pasadena CA'
234
310
  end
235
311
 
236
312
  it 'copies values for non-duplicate keys when a block is supplied' do
237
- duped = subject.merge(details: { address: 'Pasadena CA', state: 'West Thoughtleby' }) { |_, oldv, _| oldv }
313
+ m_hash = { details: { address: 'Pasadena CA', state: 'West Thoughtleby' } }
314
+ duped = subject.merge(m_hash) { |_, oldv, _| oldv }
315
+
238
316
  expect(duped.details.address).to eq 'Nowhere road'
239
317
  expect(duped.details.state).to eq 'West Thoughtleby'
240
318
  end
319
+
320
+ it 'does not raise an exception when default_proc raises an error' do
321
+ hash = described_class.new(a: 1) { |_k, _v| raise('Should not be raise I') }
322
+ other_has = described_class.new(a: 2, b: 2) { |_k, _v| raise('Should not be raise II') }
323
+ expected_hash = described_class.new(a: 2, b: 2)
324
+
325
+ res = hash.merge(other_has)
326
+ expect(res).to eq(expected_hash)
327
+ end
241
328
  end
242
329
 
243
330
  describe 'shallow update' do
@@ -633,7 +720,7 @@ describe Hashie::Mash do
633
720
  context 'if the file exists' do
634
721
  before do
635
722
  expect(File).to receive(:file?).with(path).and_return(true)
636
- expect(parser).to receive(:perform).with(path).and_return(config)
723
+ expect(parser).to receive(:perform).with(path, {}).and_return(config)
637
724
  end
638
725
 
639
726
  it { is_expected.to be_a(Hashie::Mash) }
@@ -665,7 +752,7 @@ describe Hashie::Mash do
665
752
 
666
753
  before do
667
754
  expect(File).to receive(:file?).with(path).and_return(true)
668
- expect(parser).to receive(:perform).with(path).and_return(config)
755
+ expect(parser).to receive(:perform).with(path, {}).and_return(config)
669
756
  end
670
757
 
671
758
  it 'return a Mash from a file' do
@@ -681,8 +768,8 @@ describe Hashie::Mash do
681
768
  before do
682
769
  expect(File).to receive(:file?).with(path).and_return(true)
683
770
  expect(File).to receive(:file?).with("#{path}+1").and_return(true)
684
- expect(parser).to receive(:perform).once.with(path).and_return(config)
685
- expect(parser).to receive(:perform).once.with("#{path}+1").and_return(config)
771
+ expect(parser).to receive(:perform).once.with(path, {}).and_return(config)
772
+ expect(parser).to receive(:perform).once.with("#{path}+1", {}).and_return(config)
686
773
  end
687
774
 
688
775
  it 'cache the loaded yml file', :test_cache do
@@ -694,6 +781,50 @@ describe Hashie::Mash do
694
781
  expect(subject.object_id).to eq subject.object_id
695
782
  end
696
783
  end
784
+
785
+ context 'when the file has aliases in it' do
786
+ it 'can use the aliases and does not raise an error' do
787
+ mash = Hashie::Mash.load('spec/fixtures/yaml_with_aliases.yml')
788
+ expect(mash.company_a.accounts.admin.password).to eq('secret')
789
+ end
790
+ it 'can override the value of aliases' do
791
+ expect do
792
+ Hashie::Mash.load('spec/fixtures/yaml_with_aliases.yml', aliases: false)
793
+ end.to raise_error Psych::BadAlias, /base_accounts/
794
+ end
795
+ end
796
+
797
+ context 'when the file has symbols' do
798
+ it 'can override the value of permitted_classes' do
799
+ mash = Hashie::Mash.load('spec/fixtures/yaml_with_symbols.yml', permitted_classes: [Symbol])
800
+ expect(mash.user_icon.width).to eq(200)
801
+ end
802
+ it 'uses defaults for permitted_classes' do
803
+ expect do
804
+ Hashie::Mash.load('spec/fixtures/yaml_with_symbols.yml')
805
+ end.to raise_error Psych::DisallowedClass, /Symbol/
806
+ end
807
+ it 'can override the value of permitted_symbols' do
808
+ mash = Hashie::Mash.load('spec/fixtures/yaml_with_symbols.yml',
809
+ permitted_classes: [Symbol],
810
+ permitted_symbols: %i[
811
+ user_icon
812
+ width
813
+ height
814
+ ])
815
+ expect(mash.user_icon.width).to eq(200)
816
+ end
817
+ it 'raises an error on insufficient permitted_symbols' do
818
+ expect do
819
+ Hashie::Mash.load('spec/fixtures/yaml_with_symbols.yml',
820
+ permitted_classes: [Symbol],
821
+ permitted_symbols: %i[
822
+ user_icon
823
+ width
824
+ ])
825
+ end.to raise_error Psych::DisallowedClass, /Symbol/
826
+ end
827
+ end
697
828
  end
698
829
 
699
830
  describe '#to_module(mash_method_name)' do
@@ -751,6 +882,103 @@ describe Hashie::Mash do
751
882
  end
752
883
  end
753
884
 
885
+ describe '#compact' do
886
+ subject(:mash) { described_class.new(a: 1, b: nil) }
887
+
888
+ it 'returns a Hashie::Mash' do
889
+ expect(mash.compact).to be_kind_of(described_class)
890
+ end
891
+
892
+ it 'removes keys with nil values' do
893
+ expect(mash.compact).to eq('a' => 1)
894
+ end
895
+
896
+ context 'when using with subclass' do
897
+ let(:subclass) { Class.new(Hashie::Mash) }
898
+ subject(:sub_mash) { subclass.new(a: 1, b: nil) }
899
+
900
+ it 'creates an instance of subclass' do
901
+ expect(sub_mash.compact).to be_kind_of(subclass)
902
+ end
903
+ end
904
+ end
905
+
906
+ describe '#invert' do
907
+ subject(:mash) { described_class.new(a: 'apple', b: 4) }
908
+
909
+ it 'returns a Hashie::Mash' do
910
+ expect(mash.invert).to be_kind_of(described_class)
911
+ end
912
+
913
+ it 'returns a mash with the keys and values inverted' do
914
+ expect(mash.invert).to eq('apple' => 'a', '4' => 'b')
915
+ end
916
+
917
+ context 'when using with subclass' do
918
+ let(:subclass) { Class.new(Hashie::Mash) }
919
+ subject(:sub_mash) { subclass.new(a: 1, b: nil) }
920
+
921
+ it 'creates an instance of subclass' do
922
+ expect(sub_mash.invert).to be_kind_of(subclass)
923
+ end
924
+ end
925
+ end
926
+
927
+ describe '#reject' do
928
+ subject(:mash) { described_class.new(a: 1, b: nil) }
929
+
930
+ it 'returns a Hashie::Mash' do
931
+ expect(mash.reject { |_k, v| v.nil? }).to be_kind_of(described_class)
932
+ end
933
+
934
+ it 'rejects keys for which the block returns true' do
935
+ expect(mash.reject { |_k, v| v.nil? }).to eq('a' => 1)
936
+ end
937
+
938
+ context 'when using with subclass' do
939
+ let(:subclass) { Class.new(Hashie::Mash) }
940
+ subject(:sub_mash) { subclass.new(a: 1, b: nil) }
941
+
942
+ it 'creates an instance of subclass' do
943
+ expect(sub_mash.reject { |_k, v| v.nil? }).to be_kind_of(subclass)
944
+ end
945
+ end
946
+ end
947
+
948
+ describe '#select' do
949
+ subject(:mash) { described_class.new(a: 'apple', b: 4) }
950
+
951
+ it 'returns a Hashie::Mash' do
952
+ expect(mash.select { |_k, v| v.is_a? String }).to be_kind_of(described_class)
953
+ end
954
+
955
+ it 'selects keys for which the block returns true' do
956
+ expect(mash.select { |_k, v| v.is_a? String }).to eq('a' => 'apple')
957
+ end
958
+
959
+ context 'when using with subclass' do
960
+ let(:subclass) { Class.new(Hashie::Mash) }
961
+ subject(:sub_mash) { subclass.new(a: 1, b: nil) }
962
+
963
+ it 'creates an instance of subclass' do
964
+ expect(sub_mash.select { |_k, v| v.is_a? String }).to be_kind_of(subclass)
965
+ end
966
+ end
967
+ end
968
+
969
+ describe '.quiet' do
970
+ it 'returns a subclass of the calling class' do
971
+ expect(Hashie::Mash.quiet.new).to be_a(Hashie::Mash)
972
+ end
973
+
974
+ it 'memoizes and returns classes' do
975
+ call_one = Hashie::Mash.quiet
976
+ call_two = Hashie::Mash.quiet
977
+ expect(Hashie::Mash.instance_variable_get('@memoized_classes').count).to eq(1)
978
+ expect(call_one).to eq(call_two)
979
+ end
980
+ end
981
+
754
982
  with_minimum_ruby('2.3.0') do
755
983
  describe '#dig' do
756
984
  subject { described_class.new(a: { b: 1 }) }
@@ -768,4 +996,82 @@ describe Hashie::Mash do
768
996
  end
769
997
  end
770
998
  end
999
+
1000
+ with_minimum_ruby('2.4.0') do
1001
+ describe '#transform_values' do
1002
+ subject(:mash) { described_class.new(a: 1) }
1003
+
1004
+ it 'returns a Hashie::Mash' do
1005
+ expect(mash.transform_values(&:to_s)).to be_kind_of(described_class)
1006
+ end
1007
+
1008
+ it 'transforms the value' do
1009
+ expect(mash.transform_values(&:to_s).a).to eql('1')
1010
+ end
1011
+
1012
+ context 'when using with subclass' do
1013
+ let(:subclass) { Class.new(Hashie::Mash) }
1014
+ subject(:sub_mash) { subclass.new(a: 1).transform_values { |a| a + 2 } }
1015
+
1016
+ it 'creates an instance of subclass' do
1017
+ expect(sub_mash).to be_kind_of(subclass)
1018
+ end
1019
+ end
1020
+ end
1021
+ end
1022
+
1023
+ with_minimum_ruby('2.5.0') do
1024
+ describe '#slice' do
1025
+ subject(:mash) { described_class.new(a: 1, b: 2) }
1026
+
1027
+ it 'returns a Hashie::Mash' do
1028
+ expect(mash.slice(:a)).to be_kind_of(described_class)
1029
+ end
1030
+
1031
+ it 'returns a Mash with only the keys passed' do
1032
+ expect(mash.slice(:a).to_hash).to eq('a' => 1)
1033
+ end
1034
+
1035
+ context 'when using with subclass' do
1036
+ let(:subclass) { Class.new(Hashie::Mash) }
1037
+ subject(:sub_mash) { subclass.new(a: 1, b: 2) }
1038
+
1039
+ it 'creates an instance of subclass' do
1040
+ expect(sub_mash.slice(:a)).to be_kind_of(subclass)
1041
+ end
1042
+ end
1043
+ end
1044
+
1045
+ describe '#transform_keys' do
1046
+ subject(:mash) { described_class.new(a: 1, b: 2) }
1047
+
1048
+ it 'returns a Hashie::Mash' do
1049
+ expect(mash.transform_keys { |k| k + k }).to be_kind_of(described_class)
1050
+ end
1051
+
1052
+ it 'returns a Mash with transformed keys' do
1053
+ expect(mash.transform_keys { |k| k + k }).to eq('aa' => 1, 'bb' => 2)
1054
+ end
1055
+
1056
+ context 'when using with subclass' do
1057
+ let(:subclass) { Class.new(Hashie::Mash) }
1058
+ subject(:sub_mash) { subclass.new(a: 1, b: 2) }
1059
+
1060
+ it 'creates an instance of subclass' do
1061
+ expect(sub_mash.transform_keys { |k| k + k }).to be_kind_of(subclass)
1062
+ end
1063
+ end
1064
+ end
1065
+ end
1066
+
1067
+ with_minimum_ruby('2.6.0') do
1068
+ context 'ruby 2.6 merging' do
1069
+ subject(:mash) { Hashie::Mash.new(model: 'Honda') }
1070
+ it 'merges multiple hashes and mashes passeed to #merge' do
1071
+ first_hash = { model: 'Ford' }
1072
+ second_hash = { model: 'DeLorean' }
1073
+ expect(mash.merge(first_hash, second_hash)).to eq('model' => 'DeLorean')
1074
+ end
1075
+ end
1076
+ end
771
1077
  end