hashie 2.1.2 → 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 (71) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +524 -59
  3. data/CONTRIBUTING.md +24 -7
  4. data/README.md +781 -90
  5. data/Rakefile +19 -2
  6. data/UPGRADING.md +245 -0
  7. data/hashie.gemspec +21 -13
  8. data/lib/hashie.rb +60 -21
  9. data/lib/hashie/array.rb +21 -0
  10. data/lib/hashie/clash.rb +24 -12
  11. data/lib/hashie/dash.rb +96 -33
  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 +124 -18
  15. data/lib/hashie/extensions/dash/coercion.rb +25 -0
  16. data/lib/hashie/extensions/dash/indifferent_access.rb +56 -0
  17. data/lib/hashie/extensions/dash/property_translation.rb +191 -0
  18. data/lib/hashie/extensions/deep_fetch.rb +7 -5
  19. data/lib/hashie/extensions/deep_find.rb +69 -0
  20. data/lib/hashie/extensions/deep_locate.rb +113 -0
  21. data/lib/hashie/extensions/deep_merge.rb +35 -12
  22. data/lib/hashie/extensions/ignore_undeclared.rb +11 -5
  23. data/lib/hashie/extensions/indifferent_access.rb +28 -16
  24. data/lib/hashie/extensions/key_conflict_warning.rb +55 -0
  25. data/lib/hashie/extensions/key_conversion.rb +0 -82
  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 +18 -0
  30. data/lib/hashie/extensions/mash/symbolize_keys.rb +38 -0
  31. data/lib/hashie/extensions/method_access.rb +154 -11
  32. data/lib/hashie/extensions/parsers/yaml_erb_parser.rb +48 -0
  33. data/lib/hashie/extensions/pretty_inspect.rb +19 -0
  34. data/lib/hashie/extensions/ruby_version.rb +60 -0
  35. data/lib/hashie/extensions/ruby_version_check.rb +21 -0
  36. data/lib/hashie/extensions/strict_key_access.rb +77 -0
  37. data/lib/hashie/extensions/stringify_keys.rb +71 -0
  38. data/lib/hashie/extensions/symbolize_keys.rb +71 -0
  39. data/lib/hashie/hash.rb +27 -8
  40. data/lib/hashie/logger.rb +18 -0
  41. data/lib/hashie/mash.rb +235 -57
  42. data/lib/hashie/railtie.rb +21 -0
  43. data/lib/hashie/rash.rb +40 -16
  44. data/lib/hashie/trash.rb +2 -88
  45. data/lib/hashie/utils.rb +44 -0
  46. data/lib/hashie/version.rb +1 -1
  47. metadata +42 -81
  48. data/.gitignore +0 -9
  49. data/.rspec +0 -2
  50. data/.rubocop.yml +0 -36
  51. data/.travis.yml +0 -15
  52. data/Gemfile +0 -11
  53. data/Guardfile +0 -5
  54. data/lib/hashie/hash_extensions.rb +0 -47
  55. data/spec/hashie/clash_spec.rb +0 -48
  56. data/spec/hashie/dash_spec.rb +0 -338
  57. data/spec/hashie/extensions/coercion_spec.rb +0 -156
  58. data/spec/hashie/extensions/deep_fetch_spec.rb +0 -70
  59. data/spec/hashie/extensions/deep_merge_spec.rb +0 -22
  60. data/spec/hashie/extensions/ignore_undeclared_spec.rb +0 -23
  61. data/spec/hashie/extensions/indifferent_access_spec.rb +0 -152
  62. data/spec/hashie/extensions/key_conversion_spec.rb +0 -103
  63. data/spec/hashie/extensions/merge_initializer_spec.rb +0 -23
  64. data/spec/hashie/extensions/method_access_spec.rb +0 -121
  65. data/spec/hashie/hash_spec.rb +0 -66
  66. data/spec/hashie/mash_spec.rb +0 -467
  67. data/spec/hashie/rash_spec.rb +0 -44
  68. data/spec/hashie/trash_spec.rb +0 -193
  69. data/spec/hashie/version_spec.rb +0 -7
  70. data/spec/spec.opts +0 -3
  71. data/spec/spec_helper.rb +0 -8
data/.gitignore DELETED
@@ -1,9 +0,0 @@
1
- *.sw?
2
- .DS_Store
3
- coverage
4
- rdoc
5
- pkg
6
- *.gem
7
- .bundle
8
- .rvmrc
9
- Gemfile.lock
data/.rspec DELETED
@@ -1,2 +0,0 @@
1
- --colour
2
- --format=documentation
@@ -1,36 +0,0 @@
1
- AllCops:
2
- Exclude:
3
- - vendor/**
4
- - bin/**
5
- - .bundle/**
6
-
7
- LineLength:
8
- Enabled: false
9
-
10
- MethodLength:
11
- Enabled: false
12
-
13
- ClassLength:
14
- Enabled: false
15
-
16
- Documentation:
17
- # don't require classes to be documented
18
- Enabled: false
19
-
20
- Encoding:
21
- # no need to always specify encoding
22
- Enabled: false
23
-
24
- Lambda:
25
- # TODO: replace all lambda with -> or Proc
26
- Enabled: false
27
-
28
- CyclomaticComplexity:
29
- Enabled: false
30
-
31
- DoubleNegation:
32
- Enabled: false
33
-
34
- CaseEquality:
35
- Enabled: false
36
-
@@ -1,15 +0,0 @@
1
- language: ruby
2
- rvm:
3
- - ruby-head
4
- - 2.1.1
5
- - 2.1.0
6
- - 2.0.0
7
- - 1.9.3
8
- - jruby-19mode
9
- - jruby-head
10
- - rbx-2.1
11
- - rbx-2.2
12
- matrix:
13
- allow_failures:
14
- - rvm: ruby-head
15
- - rvm: jruby-head
data/Gemfile DELETED
@@ -1,11 +0,0 @@
1
- source 'http://rubygems.org'
2
-
3
- platforms :rbx do
4
- gem 'rubysl'
5
- gem 'rubinius-developer_tools'
6
- gem 'racc'
7
- end
8
-
9
- gemspec
10
-
11
- gem 'rubocop', '0.20.0'
data/Guardfile DELETED
@@ -1,5 +0,0 @@
1
- guard 'rspec' do
2
- watch(%r{^spec/.+_spec\.rb})
3
- watch(%r{^lib/(.+)\.rb}) { |m| "spec/#{m[1]}_spec.rb" }
4
- watch('spec/spec_helper.rb') { "spec" }
5
- end
@@ -1,47 +0,0 @@
1
- module Hashie
2
- module HashExtensions
3
- def self.included(base)
4
- # Don't tread on existing extensions of Hash by
5
- # adding methods that are likely to exist.
6
- %w(stringify_keys stringify_keys!).each do |hashie_method|
7
- base.send :alias_method, hashie_method, "hashie_#{hashie_method}" unless base.instance_methods.include?(hashie_method)
8
- end
9
- end
10
-
11
- # Destructively convert all of the keys of a Hash
12
- # to their string representations.
13
- def hashie_stringify_keys!
14
- keys.each do |k|
15
- self[k.to_s] = delete(k) unless String === k
16
- end
17
- self
18
- end
19
-
20
- # Convert all of the keys of a Hash
21
- # to their string representations.
22
- def hashie_stringify_keys
23
- dup.stringify_keys!
24
- end
25
-
26
- # Convert this hash into a Mash
27
- def to_mash
28
- ::Hashie::Mash.new(self)
29
- end
30
- end
31
-
32
- module PrettyInspect
33
- def self.included(base)
34
- base.send :alias_method, :hash_inspect, :inspect
35
- base.send :alias_method, :inspect, :hashie_inspect
36
- end
37
-
38
- def hashie_inspect
39
- ret = "#<#{self.class}"
40
- stringify_keys.keys.sort.each do |key|
41
- ret << " #{key}=#{self[key].inspect}"
42
- end
43
- ret << '>'
44
- ret
45
- end
46
- end
47
- end
@@ -1,48 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Hashie::Clash do
4
- subject { Hashie::Clash.new }
5
-
6
- it 'is able to set an attribute via method_missing' do
7
- subject.foo('bar')
8
- expect(subject[:foo]).to eq 'bar'
9
- end
10
-
11
- it 'is able to set multiple attributes' do
12
- subject.foo('bar').baz('wok')
13
- expect(subject).to eq(foo: 'bar', baz: 'wok')
14
- end
15
-
16
- it 'converts multiple arguments into an array' do
17
- subject.foo(1, 2, 3)
18
- expect(subject[:foo]).to eq [1, 2, 3]
19
- end
20
-
21
- it 'is able to use bang notation to create a new Clash on a key' do
22
- subject.foo!
23
- expect(subject[:foo]).to be_kind_of(Hashie::Clash)
24
- end
25
-
26
- it 'is able to chain onto the new Clash when using bang notation' do
27
- subject.foo!.bar('abc').baz(123)
28
- expect(subject).to eq(foo: { bar: 'abc', baz: 123 })
29
- end
30
-
31
- it 'is able to jump back up to the parent in the chain with #_end!' do
32
- subject.foo!.bar('abc')._end!.baz(123)
33
- expect(subject).to eq(foo: { bar: 'abc' }, baz: 123)
34
- end
35
-
36
- it 'merges rather than replaces existing keys' do
37
- subject.where(abc: 'def').where(hgi: 123)
38
- expect(subject).to eq(where: { abc: 'def', hgi: 123 })
39
- end
40
-
41
- it 'is able to replace all of its own keys with #replace' do
42
- subject.foo(:bar).hello(:world)
43
- expect(subject.replace(baz: 123, hgi: 123)).to eq(baz: 123, hgi: 123)
44
- expect(subject).to eq(baz: 123, hgi: 123)
45
- expect(subject[:foo]).to be_nil
46
- expect(subject[:hello]).to be_nil
47
- end
48
- end
@@ -1,338 +0,0 @@
1
- require 'spec_helper'
2
-
3
- Hashie::Hash.class_eval do
4
- def self.inherited(klass)
5
- klass.instance_variable_set('@inheritance_test', true)
6
- end
7
- end
8
-
9
- class DashTest < Hashie::Dash
10
- property :first_name, required: true
11
- property :email
12
- property :count, default: 0
13
- end
14
-
15
- class DashNoRequiredTest < Hashie::Dash
16
- property :first_name
17
- property :email
18
- property :count, default: 0
19
- end
20
-
21
- class PropertyBangTest < Hashie::Dash
22
- property :important!
23
- end
24
-
25
- class Subclassed < DashTest
26
- property :last_name, required: true
27
- end
28
-
29
- class DashDefaultTest < Hashie::Dash
30
- property :aliases, default: ['Snake']
31
- end
32
-
33
- class DeferredTest < Hashie::Dash
34
- property :created_at, default: proc { Time.now }
35
- end
36
-
37
- describe DashTest do
38
-
39
- subject { DashTest.new(first_name: 'Bob', email: 'bob@example.com') }
40
-
41
- it('subclasses Hashie::Hash') { should respond_to(:to_mash) }
42
-
43
- describe '#to_s' do
44
- subject { super().to_s }
45
- it { should eq '#<DashTest count=0 email="bob@example.com" first_name="Bob">' }
46
- end
47
-
48
- it 'lists all set properties in inspect' do
49
- subject.first_name = 'Bob'
50
- subject.email = 'bob@example.com'
51
- expect(subject.inspect).to eq '#<DashTest count=0 email="bob@example.com" first_name="Bob">'
52
- end
53
-
54
- describe '#count' do
55
- subject { super().count }
56
- it { should be_zero }
57
- end
58
-
59
- it { should respond_to(:first_name) }
60
- it { should respond_to(:first_name=) }
61
- it { should_not respond_to(:nonexistent) }
62
-
63
- it 'errors out for a non-existent property' do
64
- expect { subject['nonexistent'] }.to raise_error(NoMethodError)
65
- end
66
-
67
- it 'errors out when attempting to set a required property to nil' do
68
- expect { subject.first_name = nil }.to raise_error(ArgumentError)
69
- end
70
-
71
- context 'writing to properties' do
72
- it 'fails writing a required property to nil' do
73
- expect { subject.first_name = nil }.to raise_error(ArgumentError)
74
- end
75
-
76
- it 'fails writing a required property to nil using []=' do
77
- expect { subject['first_name'] = nil }.to raise_error(ArgumentError)
78
- end
79
-
80
- it 'fails writing to a non-existent property using []=' do
81
- expect { subject['nonexistent'] = 123 }.to raise_error(NoMethodError)
82
- end
83
-
84
- it 'works for an existing property using []=' do
85
- subject['first_name'] = 'Bob'
86
- expect(subject['first_name']).to eq 'Bob'
87
- expect(subject[:first_name]).to eq 'Bob'
88
- end
89
-
90
- it 'works for an existing property using a method call' do
91
- subject.first_name = 'Franklin'
92
- expect(subject.first_name).to eq 'Franklin'
93
- end
94
- end
95
-
96
- context 'reading from properties' do
97
- it 'fails reading from a non-existent property using []' do
98
- expect { subject['nonexistent'] }.to raise_error(NoMethodError)
99
- end
100
-
101
- it 'is able to retrieve properties through blocks' do
102
- subject['first_name'] = 'Aiden'
103
- value = nil
104
- subject.[]('first_name') { |v| value = v }
105
- expect(value).to eq 'Aiden'
106
- end
107
-
108
- it 'is able to retrieve properties through blocks with method calls' do
109
- subject['first_name'] = 'Frodo'
110
- value = nil
111
- subject.first_name { |v| value = v }
112
- expect(value).to eq 'Frodo'
113
- end
114
- end
115
-
116
- context 'reading from deferred properties' do
117
- it 'evaluates proc after initial read' do
118
- expect(DeferredTest.new['created_at']).to be_instance_of(Time)
119
- end
120
-
121
- it 'does not evalute proc after subsequent reads' do
122
- deferred = DeferredTest.new
123
- expect(deferred['created_at'].object_id).to eq deferred['created_at'].object_id
124
- end
125
- end
126
-
127
- describe '#new' do
128
- it 'fails with non-existent properties' do
129
- expect { described_class.new(bork: '') }.to raise_error(NoMethodError)
130
- end
131
-
132
- it 'sets properties that it is able to' do
133
- obj = described_class.new first_name: 'Michael'
134
- expect(obj.first_name).to eq 'Michael'
135
- end
136
-
137
- it 'accepts nil' do
138
- expect { DashNoRequiredTest.new(nil) }.not_to raise_error
139
- end
140
-
141
- it 'accepts block to define a global default' do
142
- obj = described_class.new { |hash, key| key.to_s.upcase }
143
- expect(obj.first_name).to eq 'FIRST_NAME'
144
- expect(obj.count).to be_zero
145
- end
146
-
147
- it 'fails when required values are missing' do
148
- expect { DashTest.new }.to raise_error(ArgumentError)
149
- end
150
-
151
- it 'does not overwrite default values' do
152
- obj1 = DashDefaultTest.new
153
- obj1.aliases << 'El Rey'
154
- obj2 = DashDefaultTest.new
155
- expect(obj2.aliases).not_to include 'El Rey'
156
- end
157
- end
158
-
159
- describe '#merge' do
160
- it 'creates a new instance of the Dash' do
161
- new_dash = subject.merge(first_name: 'Robert')
162
- expect(subject.object_id).not_to eq new_dash.object_id
163
- end
164
-
165
- it 'merges the given hash' do
166
- new_dash = subject.merge(first_name: 'Robert', email: 'robert@example.com')
167
- expect(new_dash.first_name).to eq 'Robert'
168
- expect(new_dash.email).to eq 'robert@example.com'
169
- end
170
-
171
- it 'fails with non-existent properties' do
172
- expect { subject.merge(middle_name: 'James') }.to raise_error(NoMethodError)
173
- end
174
-
175
- it 'errors out when attempting to set a required property to nil' do
176
- expect { subject.merge(first_name: nil) }.to raise_error(ArgumentError)
177
- end
178
-
179
- context 'given a block' do
180
- it "sets merged key's values to the block's return value" do
181
- expect(subject.merge(first_name: 'Jim') do |key, oldval, newval|
182
- "#{key}: #{newval} #{oldval}"
183
- end.first_name).to eq 'first_name: Jim Bob'
184
- end
185
- end
186
- end
187
-
188
- describe '#merge!' do
189
- it 'modifies the existing instance of the Dash' do
190
- original_dash = subject.merge!(first_name: 'Robert')
191
- expect(subject.object_id).to eq original_dash.object_id
192
- end
193
-
194
- it 'merges the given hash' do
195
- subject.merge!(first_name: 'Robert', email: 'robert@example.com')
196
- expect(subject.first_name).to eq 'Robert'
197
- expect(subject.email).to eq 'robert@example.com'
198
- end
199
-
200
- it 'fails with non-existent properties' do
201
- expect { subject.merge!(middle_name: 'James') }.to raise_error(NoMethodError)
202
- end
203
-
204
- it 'errors out when attempting to set a required property to nil' do
205
- expect { subject.merge!(first_name: nil) }.to raise_error(ArgumentError)
206
- end
207
-
208
- context 'given a block' do
209
- it "sets merged key's values to the block's return value" do
210
- expect(subject.merge!(first_name: 'Jim') do |key, oldval, newval|
211
- "#{key}: #{newval} #{oldval}"
212
- end.first_name).to eq 'first_name: Jim Bob'
213
- end
214
- end
215
- end
216
-
217
- describe 'properties' do
218
- it 'lists defined properties' do
219
- expect(described_class.properties).to eq Set.new([:first_name, :email, :count])
220
- end
221
-
222
- it 'checks if a property exists' do
223
- expect(described_class.property?('first_name')).to be true
224
- expect(described_class.property?(:first_name)).to be true
225
- end
226
-
227
- it 'checks if a property is required' do
228
- expect(described_class.required?('first_name')).to be true
229
- expect(described_class.required?(:first_name)).to be true
230
- end
231
-
232
- it 'doesnt include property from subclass' do
233
- expect(described_class.property?(:last_name)).to be false
234
- end
235
-
236
- it 'lists declared defaults' do
237
- expect(described_class.defaults).to eq(count: 0)
238
- end
239
-
240
- it 'allows properties that end in bang' do
241
- expect(PropertyBangTest.property?(:important!)).to be true
242
- end
243
- end
244
-
245
- describe '#replace' do
246
- before { subject.replace(first_name: 'Cain') }
247
-
248
- it 'return self' do
249
- expect(subject.replace(email: 'bar').to_hash).to eq('email' => 'bar', 'count' => 0)
250
- end
251
-
252
- it 'sets all specified keys to their corresponding values' do
253
- expect(subject.first_name).to eq 'Cain'
254
- end
255
-
256
- it 'leaves only specified keys and keys with default values' do
257
- expect(subject.keys.sort).to eq %w(count first_name)
258
- expect(subject.email).to be_nil
259
- expect(subject.count).to eq 0
260
- end
261
-
262
- context 'when replacing keys with default values' do
263
- before { subject.replace(count: 3) }
264
-
265
- it 'sets all specified keys to their corresponding values' do
266
- expect(subject.count).to eq 3
267
- end
268
- end
269
- end
270
- end
271
-
272
- describe Hashie::Dash, 'inheritance' do
273
- before do
274
- @top = Class.new(Hashie::Dash)
275
- @middle = Class.new(@top)
276
- @bottom = Class.new(@middle)
277
- end
278
-
279
- it 'reports empty properties when nothing defined' do
280
- expect(@top.properties).to be_empty
281
- expect(@top.defaults).to be_empty
282
- end
283
-
284
- it 'inherits properties downwards' do
285
- @top.property :echo
286
- expect(@middle.properties).to include(:echo)
287
- expect(@bottom.properties).to include(:echo)
288
- end
289
-
290
- it 'doesnt inherit properties upwards' do
291
- @middle.property :echo
292
- expect(@top.properties).not_to include(:echo)
293
- expect(@bottom.properties).to include(:echo)
294
- end
295
-
296
- it 'allows overriding a default on an existing property' do
297
- @top.property :echo
298
- @middle.property :echo, default: 123
299
- expect(@bottom.properties.to_a).to eq [:echo]
300
- expect(@bottom.new.echo).to eq 123
301
- end
302
-
303
- it 'allows clearing an existing default' do
304
- @top.property :echo
305
- @middle.property :echo, default: 123
306
- @bottom.property :echo
307
- expect(@bottom.properties.to_a).to eq [:echo]
308
- expect(@bottom.new.echo).to be_nil
309
- end
310
-
311
- it 'allows nil defaults' do
312
- @bottom.property :echo, default: nil
313
- expect(@bottom.new).to have_key('echo')
314
- end
315
-
316
- end
317
-
318
- describe Subclassed do
319
- subject { Subclassed.new(first_name: 'Bob', last_name: 'McNob', email: 'bob@example.com') }
320
-
321
- describe '#count' do
322
- subject { super().count }
323
- it { should be_zero }
324
- end
325
-
326
- it { should respond_to(:first_name) }
327
- it { should respond_to(:first_name=) }
328
- it { should respond_to(:last_name) }
329
- it { should respond_to(:last_name=) }
330
-
331
- it 'has one additional property' do
332
- expect(described_class.property?(:last_name)).to be true
333
- end
334
-
335
- it "didn't override superclass inheritance logic" do
336
- expect(described_class.instance_variable_get('@inheritance_test')).to be true
337
- end
338
- end