hashie 2.1.2 → 4.1.0

Sign up to get free protection for your applications and to get access to all the features.
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