dottie 0.0.1 → 0.0.3
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.
- checksums.yaml +5 -5
- data/README.md +15 -0
- data/Rakefile +1 -1
- data/dottie.gemspec +1 -1
- data/lib/dottie/freckle.rb +6 -1
- data/lib/dottie/methods.rb +25 -0
- data/lib/dottie/version.rb +1 -1
- data/lib/dottie.rb +102 -4
- data/spec/dottie_spec.rb +207 -12
- data/spec/freckle_spec.rb +60 -8
- metadata +10 -12
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 56422c24e86966e8928fdd65ae47c56527a1ac72c75d0d6ff7674fa6c467088b
|
|
4
|
+
data.tar.gz: 815ece0b81778ef2ab9a66dbe8e467fffc1500a73ca7f30e2d13957d51ad83fe
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: '0878011a1e5a80b62a8aedcc81b48da2777768d5d2011829e4669db535c2999e6c2b41ab668bbfe7d209d35e123bcd669ee434c377c3344924ff462c6d349eca'
|
|
7
|
+
data.tar.gz: f371ca692a797e919b6f4ad89cd205365d4e06e8f1d422ce753221d151524ee9a4637c7ef9e63e77d5562618307b71cc31ed891b3ab82e1f4341fee59b08c20b
|
data/README.md
CHANGED
|
@@ -115,6 +115,21 @@ complex.hash # => {"a"=>[{"b"=>"x"}, {"d"=>"e"}]}
|
|
|
115
115
|
# add another array element
|
|
116
116
|
complex['a[2]'] = 'y'
|
|
117
117
|
complex.hash # => {"a"=>[{"b"=>"x"}, {"d"=>"e"}, "y"]}
|
|
118
|
+
|
|
119
|
+
# elements can also be prepended and appended to arrays
|
|
120
|
+
complex['a[+]'] = 'z' # can also use 'a[<<]' and 'a[append]'
|
|
121
|
+
complex.hash # => {"a"=>[{"b"=>"x"}, {"d"=>"e"}, "y", "z"]}
|
|
122
|
+
|
|
123
|
+
complex['a[-]'] = 'p' # can also use 'a[>>]' and 'a[prepend]'
|
|
124
|
+
complex.hash # => {"a"=>["p", {"b"=>"x"}, {"d"=>"e"}, "y", "z"]}
|
|
125
|
+
|
|
126
|
+
# use delete as you would on a Hash
|
|
127
|
+
complex.delete('a[1].b') # => "x"
|
|
128
|
+
complex.hash # => {"a"=>["p", {}, {"d"=>"e"}, "y", "z"]}
|
|
129
|
+
|
|
130
|
+
# delete an element at an array index
|
|
131
|
+
complex.delete('a[1]') # => {}
|
|
132
|
+
complex.hash # => {"a"=>["p", {"d"=>"e"}, "y", "z"]}
|
|
118
133
|
```
|
|
119
134
|
|
|
120
135
|
### Dottie Usage Options
|
data/Rakefile
CHANGED
data/dottie.gemspec
CHANGED
|
@@ -20,7 +20,7 @@ Gem::Specification.new do |spec|
|
|
|
20
20
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
21
21
|
spec.require_paths = ["lib"]
|
|
22
22
|
|
|
23
|
-
spec.add_development_dependency "bundler", "
|
|
23
|
+
spec.add_development_dependency "bundler", ">= 2.2.33"
|
|
24
24
|
spec.add_development_dependency "rake"
|
|
25
25
|
spec.add_development_dependency "rspec"
|
|
26
26
|
end
|
data/lib/dottie/freckle.rb
CHANGED
data/lib/dottie/methods.rb
CHANGED
|
@@ -56,6 +56,31 @@ module Dottie
|
|
|
56
56
|
end
|
|
57
57
|
end
|
|
58
58
|
|
|
59
|
+
##
|
|
60
|
+
# Deletes the value at the specified key and returns it.
|
|
61
|
+
|
|
62
|
+
def delete(key)
|
|
63
|
+
if Dottie.dottie_key?(key)
|
|
64
|
+
Dottie.delete(wrapped_object_or_self, key)
|
|
65
|
+
else
|
|
66
|
+
super
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
##
|
|
71
|
+
#
|
|
72
|
+
|
|
73
|
+
def dottie_flatten
|
|
74
|
+
Dottie.flatten(wrapped_object_or_self)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
##
|
|
78
|
+
#
|
|
79
|
+
|
|
80
|
+
def dottie_keys(intermediate = false)
|
|
81
|
+
Dottie.keys(wrapped_object_or_self, intermediate: intermediate)
|
|
82
|
+
end
|
|
83
|
+
|
|
59
84
|
private
|
|
60
85
|
|
|
61
86
|
##
|
data/lib/dottie/version.rb
CHANGED
data/lib/dottie.rb
CHANGED
|
@@ -10,7 +10,11 @@ module Dottie
|
|
|
10
10
|
# Creates a new Dottie::Freckle from a standard Ruby Hash or Array.
|
|
11
11
|
|
|
12
12
|
def self.[](obj)
|
|
13
|
-
Dottie::Freckle
|
|
13
|
+
if obj.is_a?(Dottie::Freckle)
|
|
14
|
+
obj
|
|
15
|
+
else
|
|
16
|
+
Dottie::Freckle.new(obj)
|
|
17
|
+
end
|
|
14
18
|
end
|
|
15
19
|
|
|
16
20
|
##
|
|
@@ -21,6 +25,8 @@ module Dottie
|
|
|
21
25
|
Dottie.key_parts(key).each do |k|
|
|
22
26
|
obj = case obj
|
|
23
27
|
when Hash, Array
|
|
28
|
+
# use an array index if it appears that's what was intended
|
|
29
|
+
k = k.to_i if obj.is_a?(Array) && k.to_i.to_s == k
|
|
24
30
|
obj[k]
|
|
25
31
|
else
|
|
26
32
|
nil
|
|
@@ -39,8 +45,17 @@ module Dottie
|
|
|
39
45
|
# set the value if this is the last key part
|
|
40
46
|
if i == key_parts.size - 1
|
|
41
47
|
case obj
|
|
42
|
-
when Hash
|
|
48
|
+
when Hash
|
|
43
49
|
obj[k] = value
|
|
50
|
+
when Array
|
|
51
|
+
case k
|
|
52
|
+
when '-', 'prepend', '>>'
|
|
53
|
+
obj.unshift(value)
|
|
54
|
+
when '+', 'append', '<<'
|
|
55
|
+
obj << value
|
|
56
|
+
else
|
|
57
|
+
obj[k] = value
|
|
58
|
+
end
|
|
44
59
|
else
|
|
45
60
|
raise TypeError.new("expected Hash or Array but got #{obj.class.name}")
|
|
46
61
|
end
|
|
@@ -49,7 +64,8 @@ module Dottie
|
|
|
49
64
|
obj = case obj
|
|
50
65
|
when Hash, Array
|
|
51
66
|
# look ahead at the next key to see if an array should be created
|
|
52
|
-
if key_parts[i + 1].is_a?(Integer)
|
|
67
|
+
if key_parts[i + 1].is_a?(Integer) ||
|
|
68
|
+
key_parts[i + 1] =~ /\A(-|\+|prepend|append|>>|<<)\z/
|
|
53
69
|
obj[k] ||= []
|
|
54
70
|
else
|
|
55
71
|
obj[k] ||= {}
|
|
@@ -57,7 +73,7 @@ module Dottie
|
|
|
57
73
|
when nil
|
|
58
74
|
# look at the key to see if an array should be created
|
|
59
75
|
case k
|
|
60
|
-
when Integer
|
|
76
|
+
when Integer, '-', '+', 'prepend', 'append', '>>', '<<'
|
|
61
77
|
obj[k] = []
|
|
62
78
|
else
|
|
63
79
|
obj[k] = {}
|
|
@@ -113,6 +129,71 @@ module Dottie
|
|
|
113
129
|
end
|
|
114
130
|
end
|
|
115
131
|
|
|
132
|
+
##
|
|
133
|
+
# Deletes the value at the specified key and returns it.
|
|
134
|
+
|
|
135
|
+
def self.delete(obj, key)
|
|
136
|
+
if Dottie.has_key?(obj, key)
|
|
137
|
+
key_parts = Dottie.key_parts(key)
|
|
138
|
+
if key_parts.size > 1
|
|
139
|
+
key = Dottie.build_key(key_parts[0..-2])
|
|
140
|
+
obj = Dottie.get(obj, key)
|
|
141
|
+
end
|
|
142
|
+
if obj.is_a?(Array) && key_parts.last.is_a?(Integer)
|
|
143
|
+
obj.delete_at(key_parts.last)
|
|
144
|
+
else
|
|
145
|
+
obj.delete(key_parts.last)
|
|
146
|
+
end
|
|
147
|
+
else
|
|
148
|
+
nil
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
##
|
|
153
|
+
# Flattens a Hash or Array to a single-depth Hash with Dottie-style keys.
|
|
154
|
+
|
|
155
|
+
def self.flatten(obj, options = {}, path = nil, flat = nil)
|
|
156
|
+
path ||= []
|
|
157
|
+
flat ||= {}
|
|
158
|
+
case obj
|
|
159
|
+
when Hash
|
|
160
|
+
obj.each do |k, v|
|
|
161
|
+
this_path = path + [k]
|
|
162
|
+
case v
|
|
163
|
+
when Hash, Array
|
|
164
|
+
flat[this_path.join('.')] = options[:keys_only] ? nil : v if options[:intermediate]
|
|
165
|
+
Dottie.flatten(v, options, this_path, flat)
|
|
166
|
+
else
|
|
167
|
+
flat[this_path.join('.')] = options[:keys_only] ? nil : v
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
when Array
|
|
171
|
+
obj.each_with_index do |v, i|
|
|
172
|
+
this_path = path.dup
|
|
173
|
+
if this_path.any?
|
|
174
|
+
this_path[-1] = this_path[-1].to_s + "[#{i}]"
|
|
175
|
+
else
|
|
176
|
+
this_path = ["[#{i}]"]
|
|
177
|
+
end
|
|
178
|
+
case v
|
|
179
|
+
when Hash, Array
|
|
180
|
+
flat[this_path.join('.')] = options[:keys_only] ? nil : v if options[:intermediate]
|
|
181
|
+
Dottie.flatten(v, options, this_path, flat)
|
|
182
|
+
else
|
|
183
|
+
flat[this_path.join('.')] = options[:keys_only] ? nil : v
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
flat
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
##
|
|
191
|
+
# Gets an array of the Dottie-style keys that exist in a Hash or Array.
|
|
192
|
+
|
|
193
|
+
def self.keys(obj, options = {})
|
|
194
|
+
Dottie.flatten(obj, { keys_only: true }.merge(options)).keys
|
|
195
|
+
end
|
|
196
|
+
|
|
116
197
|
##
|
|
117
198
|
# Checks whether a key looks like a key Dottie understands.
|
|
118
199
|
|
|
@@ -150,4 +231,21 @@ module Dottie
|
|
|
150
231
|
end
|
|
151
232
|
end
|
|
152
233
|
|
|
234
|
+
##
|
|
235
|
+
# Builds a Dottie key from an Array of strings and integers.
|
|
236
|
+
|
|
237
|
+
def self.build_key(parts)
|
|
238
|
+
key = ''
|
|
239
|
+
parts.each_with_index do |part, i|
|
|
240
|
+
case part
|
|
241
|
+
when String
|
|
242
|
+
key << '.' unless i == 0
|
|
243
|
+
key << part
|
|
244
|
+
when Integer
|
|
245
|
+
key << "[#{part}]"
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
key
|
|
249
|
+
end
|
|
250
|
+
|
|
153
251
|
end
|
data/spec/dottie_spec.rb
CHANGED
|
@@ -20,6 +20,30 @@ describe Dottie do
|
|
|
20
20
|
arr = ['a', 'b', 'c']
|
|
21
21
|
expect(Dottie(arr)).to be_a Dottie::Freckle
|
|
22
22
|
end
|
|
23
|
+
it 'returns a Dottie::Freckle<Hash> instead of rewrapping it using Dottie[]' do
|
|
24
|
+
dottie = Dottie[{ 'a' => 'b' }]
|
|
25
|
+
expect(Dottie[dottie]).to eq dottie
|
|
26
|
+
end
|
|
27
|
+
it 'returns a Dottie::Freckle<Array> instead of rewrapping it using Dottie[]' do
|
|
28
|
+
dottie = Dottie[['a', 'b', 'c']]
|
|
29
|
+
expect(Dottie[dottie]).to eq dottie
|
|
30
|
+
end
|
|
31
|
+
it 'returns a Dottie::Freckle<Hash> instead of rewrapping it using Dottie()' do
|
|
32
|
+
dottie = Dottie({ 'a' => 'b' })
|
|
33
|
+
expect(Dottie(dottie)).to eq dottie
|
|
34
|
+
end
|
|
35
|
+
it 'returns a Dottie::Freckle<Array> instead of rewrapping it using Dottie()' do
|
|
36
|
+
dottie = Dottie(['a', 'b', 'c'])
|
|
37
|
+
expect(Dottie(dottie)).to eq dottie
|
|
38
|
+
end
|
|
39
|
+
['a', nil, 1].each do |val|
|
|
40
|
+
it "fails to create a Dottie::Freckle from an invalid type (#{val.class}) using Dottie[]" do
|
|
41
|
+
expect{ Dottie[val] }.to raise_error(TypeError)
|
|
42
|
+
end
|
|
43
|
+
it "fails to create a Dottie::Freckle from an invalid type (#{val.class}) using Dottie()" do
|
|
44
|
+
expect{ Dottie(val) }.to raise_error(TypeError)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
23
47
|
|
|
24
48
|
end
|
|
25
49
|
|
|
@@ -120,34 +144,48 @@ describe Dottie do
|
|
|
120
144
|
end
|
|
121
145
|
end
|
|
122
146
|
|
|
147
|
+
context 'lax keys' do
|
|
148
|
+
let(:hash) {{ 'a' => 'b', 'c' => [{ 'd' => 'e', '0' => 1 }, { 'f' => 'g' }] }}
|
|
149
|
+
|
|
150
|
+
it 'reads a targeted array index number as an integer' do
|
|
151
|
+
expect(Dottie.get(hash, 'c.0.d')).to eq 'e'
|
|
152
|
+
end
|
|
153
|
+
it 'reads a targeted hash key number as a string' do
|
|
154
|
+
expect(Dottie.get(hash, 'c.0.0')).to eq 1
|
|
155
|
+
end
|
|
156
|
+
it 'reads an targeted hash key integer as an integer' do
|
|
157
|
+
expect(Dottie.get(hash, 'c.0[0]')).to be_nil
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
123
161
|
end
|
|
124
162
|
|
|
125
163
|
describe 'key existence' do
|
|
126
164
|
let(:hash) {{ 'a' => 'b', 'c' => { 'd' => ['e', 'f', 'g'] } }}
|
|
127
165
|
|
|
128
166
|
it "finds a standard key" do
|
|
129
|
-
expect(Dottie.has_key?(hash, 'a')).to
|
|
167
|
+
expect(Dottie.has_key?(hash, 'a')).to be true
|
|
130
168
|
end
|
|
131
169
|
it "does not find a missing standard key" do
|
|
132
|
-
expect(Dottie.has_key?(hash, 'x')).to
|
|
170
|
+
expect(Dottie.has_key?(hash, 'x')).to be false
|
|
133
171
|
end
|
|
134
172
|
it "finds a Dottie key (Hash value)" do
|
|
135
|
-
expect(Dottie.has_key?(hash, 'c.d')).to
|
|
173
|
+
expect(Dottie.has_key?(hash, 'c.d')).to be true
|
|
136
174
|
end
|
|
137
175
|
it "finds a Dottie key (Array element)" do
|
|
138
|
-
expect(Dottie.has_key?(hash, 'c.d[0]')).to
|
|
176
|
+
expect(Dottie.has_key?(hash, 'c.d[0]')).to be true
|
|
139
177
|
end
|
|
140
178
|
it "does not find a missing Dottie key (first part is a String)" do
|
|
141
|
-
expect(Dottie.has_key?(hash, 'a.b')).to
|
|
179
|
+
expect(Dottie.has_key?(hash, 'a.b')).to be false
|
|
142
180
|
end
|
|
143
181
|
it "does not find a missing Dottie key (first part exists)" do
|
|
144
|
-
expect(Dottie.has_key?(hash, 'c.x')).to
|
|
182
|
+
expect(Dottie.has_key?(hash, 'c.x')).to be false
|
|
145
183
|
end
|
|
146
184
|
it "does not find a missing Dottie key (outside Array bounds)" do
|
|
147
|
-
expect(Dottie.has_key?(hash, 'c.d[4]')).to
|
|
185
|
+
expect(Dottie.has_key?(hash, 'c.d[4]')).to be false
|
|
148
186
|
end
|
|
149
187
|
it "does not find a missing Dottie key (no part exists)" do
|
|
150
|
-
expect(Dottie.has_key?(hash, 'x.y')).to
|
|
188
|
+
expect(Dottie.has_key?(hash, 'x.y')).to be false
|
|
151
189
|
end
|
|
152
190
|
end
|
|
153
191
|
|
|
@@ -255,6 +293,31 @@ describe Dottie do
|
|
|
255
293
|
end
|
|
256
294
|
end
|
|
257
295
|
|
|
296
|
+
context 'array prepend/append' do
|
|
297
|
+
before :each do
|
|
298
|
+
@hash = { 'a' => 'b', 'c' => ['g', 'h', 'i'] }
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
%w( - prepend >> ).each do |key|
|
|
302
|
+
it "prepends an array element with [#{key}]" do
|
|
303
|
+
Dottie.set(@hash, "c[#{key}]", 'f')
|
|
304
|
+
expect(@hash).to eq({ 'a' => 'b', 'c' => ['f', 'g', 'h', 'i'] })
|
|
305
|
+
end
|
|
306
|
+
end
|
|
307
|
+
%w( + append << ).each do |key|
|
|
308
|
+
it "appends an array element with [#{key}]" do
|
|
309
|
+
Dottie.set(@hash, "c[#{key}]", 'j')
|
|
310
|
+
expect(@hash).to eq({ 'a' => 'b', 'c' => ['g', 'h', 'i', 'j'] })
|
|
311
|
+
end
|
|
312
|
+
end
|
|
313
|
+
%w( - + prepend append >> << ).each do |key|
|
|
314
|
+
it "creates an array at a non-existent key with [#{key}]" do
|
|
315
|
+
Dottie.set(@hash, "r[#{key}]", 's')
|
|
316
|
+
expect(@hash).to eq({ 'a' => 'b', 'c' => ['g', 'h', 'i'], 'r' => ['s'] })
|
|
317
|
+
end
|
|
318
|
+
end
|
|
319
|
+
end
|
|
320
|
+
|
|
258
321
|
context 'invalid' do
|
|
259
322
|
before :each do
|
|
260
323
|
@hash = { 'a' => 'b', 'c' => { 'd' => 'e' }, 'f' => ['g', 'h'] }
|
|
@@ -277,26 +340,130 @@ describe Dottie do
|
|
|
277
340
|
|
|
278
341
|
end
|
|
279
342
|
|
|
343
|
+
describe 'deleting' do
|
|
344
|
+
|
|
345
|
+
context 'simple' do
|
|
346
|
+
before :each do
|
|
347
|
+
@hash = { 'a' => 'b', 'c' => { 'd' => 'e' } }
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
it 'deletes a standard key' do
|
|
351
|
+
ret = Dottie.delete(@hash, 'c')
|
|
352
|
+
expect(ret).to eq({ 'd' => 'e' })
|
|
353
|
+
expect(@hash).to eq({ 'a' => 'b' })
|
|
354
|
+
end
|
|
355
|
+
it 'deletes a dotted key' do
|
|
356
|
+
ret = Dottie.delete(@hash, 'c.d')
|
|
357
|
+
expect(ret).to eq 'e'
|
|
358
|
+
expect(@hash).to eq({ 'a' => 'b', 'c' => {} })
|
|
359
|
+
end
|
|
360
|
+
it 'returns nil when attempting to delete a non-existent standard key' do
|
|
361
|
+
ret = Dottie.delete(@hash, 'x')
|
|
362
|
+
expect(ret).to be_nil
|
|
363
|
+
expect(@hash).to eq({ 'a' => 'b', 'c' => { 'd' => 'e' } })
|
|
364
|
+
end
|
|
365
|
+
it 'returns nil when attempting to delete a non-existent dotted key' do
|
|
366
|
+
ret = Dottie.delete(@hash, 'x.y')
|
|
367
|
+
expect(ret).to be_nil
|
|
368
|
+
expect(@hash).to eq({ 'a' => 'b', 'c' => { 'd' => 'e' } })
|
|
369
|
+
end
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
context 'array indexes' do
|
|
373
|
+
before :each do
|
|
374
|
+
@hash = { 'a' => 'b', 'c' => [{ 'd' => 'e', 'f' => 'g' }, { 'h' => 'i' }] }
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
it 'deletes an element from an array (positive index)' do
|
|
378
|
+
ret = Dottie.delete(@hash, 'c[0]')
|
|
379
|
+
expect(ret).to eq({ 'd' => 'e', 'f' => 'g' })
|
|
380
|
+
expect(@hash).to eq({ 'a' => 'b', 'c' => [{ 'h' => 'i' }] })
|
|
381
|
+
end
|
|
382
|
+
it 'deletes an element from an array (negative index)' do
|
|
383
|
+
ret = Dottie.delete(@hash, 'c[-1]')
|
|
384
|
+
expect(ret).to eq({ 'h' => 'i' })
|
|
385
|
+
expect(@hash).to eq({ 'a' => 'b', 'c' => [{ 'd' => 'e', 'f' => 'g' }] })
|
|
386
|
+
end
|
|
387
|
+
it 'deletes an element from an array (first)' do
|
|
388
|
+
ret = Dottie.delete(@hash, 'c[first]')
|
|
389
|
+
expect(ret).to eq({ 'd' => 'e', 'f' => 'g' })
|
|
390
|
+
expect(@hash).to eq({ 'a' => 'b', 'c' => [{ 'h' => 'i' }] })
|
|
391
|
+
end
|
|
392
|
+
it 'deletes an element from an array (last)' do
|
|
393
|
+
ret = Dottie.delete(@hash, 'c[last]')
|
|
394
|
+
expect(ret).to eq({ 'h' => 'i' })
|
|
395
|
+
expect(@hash).to eq({ 'a' => 'b', 'c' => [{ 'd' => 'e', 'f' => 'g' }] })
|
|
396
|
+
end
|
|
397
|
+
it 'deletes an element from a nested structure' do
|
|
398
|
+
ret = Dottie.delete(@hash, 'c[0].d')
|
|
399
|
+
expect(ret).to eq('e')
|
|
400
|
+
expect(@hash).to eq({ 'a' => 'b', 'c' => [{ 'f' => 'g' }, { 'h' => 'i' }] })
|
|
401
|
+
end
|
|
402
|
+
it 'returns nil when attempting to delete a non-existent array index' do
|
|
403
|
+
ret = Dottie.delete(@hash, 'c[3]')
|
|
404
|
+
expect(ret).to be_nil
|
|
405
|
+
expect(@hash).to eq({ 'a' => 'b', 'c' => [{ 'd' => 'e', 'f' => 'g' }, { 'h' => 'i' }] })
|
|
406
|
+
end
|
|
407
|
+
end
|
|
408
|
+
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
describe 'flattening' do
|
|
412
|
+
|
|
413
|
+
context 'hash' do
|
|
414
|
+
let(:hash) {{ 'a' => 'b', 'c' => { 'd' => ['e', 'f', 'g'] } }}
|
|
415
|
+
|
|
416
|
+
it 'flattens a hash' do
|
|
417
|
+
expect(Dottie.flatten(hash)).to eq({ 'a' => 'b', 'c.d[0]' => 'e', 'c.d[1]' => 'f', 'c.d[2]' => 'g' })
|
|
418
|
+
end
|
|
419
|
+
it 'gets flattened hash keys' do
|
|
420
|
+
expect(Dottie.keys(hash)).to eq ['a', 'c.d[0]', 'c.d[1]', 'c.d[2]']
|
|
421
|
+
end
|
|
422
|
+
it 'gets all flattened hash keys' do
|
|
423
|
+
expect(Dottie.keys(hash, intermediate: true)).to eq ['a', 'c', 'c.d', 'c.d[0]', 'c.d[1]', 'c.d[2]']
|
|
424
|
+
end
|
|
425
|
+
end
|
|
426
|
+
|
|
427
|
+
context 'array' do
|
|
428
|
+
let(:arr) { ['x', { 'a' => 'b', 'c' => { 'd' => ['e', 'f', 'g'] } }, 'y'] }
|
|
429
|
+
|
|
430
|
+
it 'flattens an array' do
|
|
431
|
+
expect(Dottie.flatten(arr)).to eq({
|
|
432
|
+
'[0]' => 'x',
|
|
433
|
+
'[1].a' => 'b', '[1].c.d[0]' => 'e', '[1].c.d[1]' => 'f', '[1].c.d[2]' => 'g',
|
|
434
|
+
'[2]' => 'y' })
|
|
435
|
+
end
|
|
436
|
+
it 'gets flattened array keys' do
|
|
437
|
+
expect(Dottie.keys(arr)).to eq ['[0]', '[1].a', '[1].c.d[0]', '[1].c.d[1]', '[1].c.d[2]', '[2]']
|
|
438
|
+
end
|
|
439
|
+
it 'gets all flattened array keys' do
|
|
440
|
+
expect(Dottie.keys(arr, intermediate: true)).to eq [
|
|
441
|
+
'[0]', '[1]', '[1].a', '[1].c', '[1].c.d', '[1].c.d[0]', '[1].c.d[1]', '[1].c.d[2]', '[2]']
|
|
442
|
+
end
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
end
|
|
446
|
+
|
|
280
447
|
describe 'key identification' do
|
|
281
448
|
|
|
282
449
|
it 'recognizes a dotted key' do
|
|
283
450
|
key = 'a.b.c'
|
|
284
|
-
expect(Dottie.dottie_key?(key)).to
|
|
451
|
+
expect(Dottie.dottie_key?(key)).to be true
|
|
285
452
|
end
|
|
286
453
|
|
|
287
454
|
it 'recognizes a bracketed key' do
|
|
288
455
|
key = 'a[0]b'
|
|
289
|
-
expect(Dottie.dottie_key?(key)).to
|
|
456
|
+
expect(Dottie.dottie_key?(key)).to be true
|
|
290
457
|
end
|
|
291
458
|
|
|
292
459
|
it 'recognizes an array as a Dottie key' do
|
|
293
460
|
key = ['a', 'b', 'c']
|
|
294
|
-
expect(Dottie.dottie_key?(key)).to
|
|
461
|
+
expect(Dottie.dottie_key?(key)).to be true
|
|
295
462
|
end
|
|
296
463
|
|
|
297
464
|
it 'does not recognize a normal key' do
|
|
298
465
|
key = 'a_b_c'
|
|
299
|
-
expect(Dottie.dottie_key?(key)).to
|
|
466
|
+
expect(Dottie.dottie_key?(key)).to be false
|
|
300
467
|
end
|
|
301
468
|
|
|
302
469
|
end
|
|
@@ -400,6 +567,34 @@ describe Dottie do
|
|
|
400
567
|
|
|
401
568
|
end
|
|
402
569
|
|
|
570
|
+
describe 'key building' do
|
|
571
|
+
|
|
572
|
+
it 'builds a single-element key' do
|
|
573
|
+
expect(Dottie.build_key(['a'])).to eq 'a'
|
|
574
|
+
end
|
|
575
|
+
|
|
576
|
+
it 'builds a dotted key' do
|
|
577
|
+
expect(Dottie.build_key(['a', 'b', 'c'])).to eq 'a.b.c'
|
|
578
|
+
end
|
|
579
|
+
|
|
580
|
+
it 'builds a dotted key a number' do
|
|
581
|
+
expect(Dottie.build_key(['a', '0', '1', 'b'])).to eq 'a.0.1.b'
|
|
582
|
+
end
|
|
583
|
+
|
|
584
|
+
it 'builds a complex key with a positive integer' do
|
|
585
|
+
expect(Dottie.build_key(['a', 0, 'b'])).to eq 'a[0].b'
|
|
586
|
+
end
|
|
587
|
+
|
|
588
|
+
it 'builds a complex key with a negative integer' do
|
|
589
|
+
expect(Dottie.build_key(['a', -1, 'b'])).to eq 'a[-1].b'
|
|
590
|
+
end
|
|
591
|
+
|
|
592
|
+
it 'builds a complex key with consecutive integers' do
|
|
593
|
+
expect(Dottie.build_key(['a', 0, 1, 'b'])).to eq 'a[0][1].b'
|
|
594
|
+
end
|
|
595
|
+
|
|
596
|
+
end
|
|
597
|
+
|
|
403
598
|
describe 'key format variants' do
|
|
404
599
|
let(:arr) { ['a', 0, 'b', 1, 'c', -2, 'd', -1, 'e'] }
|
|
405
600
|
|
data/spec/freckle_spec.rb
CHANGED
|
@@ -2,6 +2,22 @@ require 'spec_helper'
|
|
|
2
2
|
|
|
3
3
|
describe Dottie::Freckle do
|
|
4
4
|
|
|
5
|
+
describe 'instantiation' do
|
|
6
|
+
|
|
7
|
+
it 'creates a Dottie::Freckle from a Hash' do
|
|
8
|
+
expect(Dottie::Freckle.new({ 'a' => 'b' })).to be_a Dottie::Freckle
|
|
9
|
+
end
|
|
10
|
+
it 'creates a Dottie::Freckle from an Array' do
|
|
11
|
+
expect(Dottie::Freckle.new(['a', 'b', 'c'])).to be_a Dottie::Freckle
|
|
12
|
+
end
|
|
13
|
+
['a', nil, 1].each do |val|
|
|
14
|
+
it "fails to create a Dottie::Freckle from an invalid type (#{val.class})" do
|
|
15
|
+
expect{ Dottie::Freckle.new(val) }.to raise_error(TypeError)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
end
|
|
20
|
+
|
|
5
21
|
describe 'general' do
|
|
6
22
|
|
|
7
23
|
context 'Hash' do
|
|
@@ -131,28 +147,28 @@ describe Dottie::Freckle do
|
|
|
131
147
|
let(:freckle) { Dottie::Freckle.new({ 'a' => 'b', 'c' => { 'd' => ['e', 'f', 'g'] } }) }
|
|
132
148
|
|
|
133
149
|
it "finds a standard key" do
|
|
134
|
-
expect(freckle.has_key?('a')).to
|
|
150
|
+
expect(freckle.has_key?('a')).to be true
|
|
135
151
|
end
|
|
136
152
|
it "does not find a missing standard key" do
|
|
137
|
-
expect(freckle.has_key?('x')).to
|
|
153
|
+
expect(freckle.has_key?('x')).to be false
|
|
138
154
|
end
|
|
139
155
|
it "finds a Dottie key (Hash value)" do
|
|
140
|
-
expect(freckle.has_key?('c.d')).to
|
|
156
|
+
expect(freckle.has_key?('c.d')).to be true
|
|
141
157
|
end
|
|
142
158
|
it "finds a Dottie key (Array element)" do
|
|
143
|
-
expect(freckle.has_key?('c.d[0]')).to
|
|
159
|
+
expect(freckle.has_key?('c.d[0]')).to be true
|
|
144
160
|
end
|
|
145
161
|
it "does not find a missing Dottie key (first part is a String)" do
|
|
146
|
-
expect(freckle.has_key?('a.b')).to
|
|
162
|
+
expect(freckle.has_key?('a.b')).to be false
|
|
147
163
|
end
|
|
148
164
|
it "does not find a missing Dottie key (first part exists)" do
|
|
149
|
-
expect(freckle.has_key?('c.x')).to
|
|
165
|
+
expect(freckle.has_key?('c.x')).to be false
|
|
150
166
|
end
|
|
151
167
|
it "does not find a missing Dottie key (outside Array bounds)" do
|
|
152
|
-
expect(freckle.has_key?('c.d[4]')).to
|
|
168
|
+
expect(freckle.has_key?('c.d[4]')).to be false
|
|
153
169
|
end
|
|
154
170
|
it "does not find a missing Dottie key (no part exists)" do
|
|
155
|
-
expect(freckle.has_key?('x.y')).to
|
|
171
|
+
expect(freckle.has_key?('x.y')).to be false
|
|
156
172
|
end
|
|
157
173
|
end
|
|
158
174
|
|
|
@@ -271,4 +287,40 @@ describe Dottie::Freckle do
|
|
|
271
287
|
|
|
272
288
|
end
|
|
273
289
|
|
|
290
|
+
describe 'flattening' do
|
|
291
|
+
|
|
292
|
+
context 'hash' do
|
|
293
|
+
let(:freckle) { Dottie::Freckle.new({ 'a' => 'b', 'c' => { 'd' => ['e', 'f', 'g'] } }) }
|
|
294
|
+
|
|
295
|
+
it 'flattens a hash' do
|
|
296
|
+
expect(freckle.dottie_flatten).to eq({ 'a' => 'b', 'c.d[0]' => 'e', 'c.d[1]' => 'f', 'c.d[2]' => 'g' })
|
|
297
|
+
end
|
|
298
|
+
it 'gets flattened hash keys' do
|
|
299
|
+
expect(freckle.dottie_keys).to eq ['a', 'c.d[0]', 'c.d[1]', 'c.d[2]']
|
|
300
|
+
end
|
|
301
|
+
it 'gets all flattened hash keys' do
|
|
302
|
+
expect(freckle.dottie_keys(true)).to eq ['a', 'c', 'c.d', 'c.d[0]', 'c.d[1]', 'c.d[2]']
|
|
303
|
+
end
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
context 'array' do
|
|
307
|
+
let(:freckle) { Dottie::Freckle.new(['x', { 'a' => 'b', 'c' => { 'd' => ['e', 'f', 'g'] } }, 'y']) }
|
|
308
|
+
|
|
309
|
+
it 'flattens an array' do
|
|
310
|
+
expect(freckle.dottie_flatten).to eq({
|
|
311
|
+
'[0]' => 'x',
|
|
312
|
+
'[1].a' => 'b', '[1].c.d[0]' => 'e', '[1].c.d[1]' => 'f', '[1].c.d[2]' => 'g',
|
|
313
|
+
'[2]' => 'y' })
|
|
314
|
+
end
|
|
315
|
+
it 'gets flattened array keys' do
|
|
316
|
+
expect(freckle.dottie_keys).to eq ['[0]', '[1].a', '[1].c.d[0]', '[1].c.d[1]', '[1].c.d[2]', '[2]']
|
|
317
|
+
end
|
|
318
|
+
it 'gets all flattened array keys' do
|
|
319
|
+
expect(freckle.dottie_keys(true)).to eq [
|
|
320
|
+
'[0]', '[1]', '[1].a', '[1].c', '[1].c.d', '[1].c.d[0]', '[1].c.d[1]', '[1].c.d[2]', '[2]']
|
|
321
|
+
end
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
end
|
|
325
|
+
|
|
274
326
|
end
|
metadata
CHANGED
|
@@ -1,29 +1,29 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: dottie
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Nick Pearson
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2024-03-21 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
|
16
16
|
requirements:
|
|
17
|
-
- - "
|
|
17
|
+
- - ">="
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version:
|
|
19
|
+
version: 2.2.33
|
|
20
20
|
type: :development
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
|
-
- - "
|
|
24
|
+
- - ">="
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version:
|
|
26
|
+
version: 2.2.33
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: rake
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -85,7 +85,7 @@ homepage: https://github.com/nickpearson/dottie
|
|
|
85
85
|
licenses:
|
|
86
86
|
- MIT
|
|
87
87
|
metadata: {}
|
|
88
|
-
post_install_message:
|
|
88
|
+
post_install_message:
|
|
89
89
|
rdoc_options: []
|
|
90
90
|
require_paths:
|
|
91
91
|
- lib
|
|
@@ -100,9 +100,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
100
100
|
- !ruby/object:Gem::Version
|
|
101
101
|
version: '0'
|
|
102
102
|
requirements: []
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
signing_key:
|
|
103
|
+
rubygems_version: 3.2.33
|
|
104
|
+
signing_key:
|
|
106
105
|
specification_version: 4
|
|
107
106
|
summary: Deep Hash and Array access with dotted keys
|
|
108
107
|
test_files:
|
|
@@ -111,4 +110,3 @@ test_files:
|
|
|
111
110
|
- spec/freckle_spec.rb
|
|
112
111
|
- spec/hash_spec.rb
|
|
113
112
|
- spec/spec_helper.rb
|
|
114
|
-
has_rdoc:
|