dense 0.0.1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +27 -0
- data/LICENSE.txt +3 -0
- data/README.md +4 -1
- data/dense.gemspec +1 -1
- data/lib/dense.rb +6 -1
- data/lib/dense/methods.rb +143 -0
- data/lib/dense/path.rb +384 -0
- metadata +10 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bebe1c11161f7007a745279fd573cb11591b9325
|
4
|
+
data.tar.gz: '082cb4ac9f1e6412ecf44445bd1e51206a094faf'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2e5bbd4c1f7e915b66228cb2e494d46fd43338b9d0f822e42051c36864a45d08a18e2637d6b320cf238512fedad0e10d0f289b99727d7c850c2d18e204f7b63f
|
7
|
+
data.tar.gz: f7250e6416b6f781d00c7c1f094adfea7dc4983aad5fc37984f64614748a0c1e36a5b8623d52eed5056e37fa71464a4d4cf550ad16ec0e26451365e4c9cbf6a2
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
|
2
|
+
# dense
|
3
|
+
|
4
|
+
|
5
|
+
## dense 1.0.0 released 2017-09-29
|
6
|
+
|
7
|
+
* Accept `owner[age]` (unquoted key name in bracket index)
|
8
|
+
* Accept '=' and '?' in key names
|
9
|
+
* Introduce Dense::Path#last
|
10
|
+
* Introduce Dense::Path indexation and equality
|
11
|
+
* Introduce Dense::Path #length and #size
|
12
|
+
* Introduce Dense::Path::NotIndexableError#relabel
|
13
|
+
* Introduce Dense::Path::NotIndexableError
|
14
|
+
* Differentiate `Dense.get(col, path)` from `Dense.fetch(col, path[, default])`
|
15
|
+
* Provide Dense::Path.to_s
|
16
|
+
* Introduce Dense.has_key?(collection, path)
|
17
|
+
* Introduce Dense.insert(collection, path, value)
|
18
|
+
* Accept `.first` and `.last` when indexing arrays
|
19
|
+
* Introduce Dense.unset(collection, path)
|
20
|
+
* Introduce Dense.set(collection, path, value)
|
21
|
+
* Introduce Dense.get(collection, path)
|
22
|
+
|
23
|
+
|
24
|
+
## dense 0.1.0 released 2017-08-06
|
25
|
+
|
26
|
+
* initial release
|
27
|
+
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -1,7 +1,10 @@
|
|
1
1
|
|
2
2
|
# dense
|
3
3
|
|
4
|
-
|
4
|
+
[![Build Status](https://secure.travis-ci.org/floraison/dense.svg)](http://travis-ci.org/floraison/dense)
|
5
|
+
[![Gem Version](https://badge.fury.io/rb/dense.svg)](http://badge.fury.io/rb/dense)
|
6
|
+
|
7
|
+
Fetching deep in a dense structure. A kind of bastard of [JSONPath](http://goessner.net/articles/JsonPath/).
|
5
8
|
|
6
9
|
## LICENSE
|
7
10
|
|
data/dense.gemspec
CHANGED
data/lib/dense.rb
CHANGED
@@ -0,0 +1,143 @@
|
|
1
|
+
|
2
|
+
module Dense; class << self
|
3
|
+
|
4
|
+
def get(o, path)
|
5
|
+
|
6
|
+
Dense::Path.new(path).walk(o) { nil }
|
7
|
+
end
|
8
|
+
|
9
|
+
def fetch(o, path, default=IndexError, &block)
|
10
|
+
|
11
|
+
Dense::Path.new(path).walk(o, default, &block)
|
12
|
+
end
|
13
|
+
|
14
|
+
def set(o, path, value)
|
15
|
+
|
16
|
+
path = Dense::Path.new(path)
|
17
|
+
key = path.pop
|
18
|
+
|
19
|
+
case c = path.walk(o)
|
20
|
+
when Array then array_set(c, key, value)
|
21
|
+
when Hash then c[key.to_s] = value
|
22
|
+
else fail KeyError.new("Found no collection at #{path.to_s.inspect}")
|
23
|
+
end
|
24
|
+
|
25
|
+
value
|
26
|
+
end
|
27
|
+
|
28
|
+
def unset(o, path)
|
29
|
+
|
30
|
+
path = Dense::Path.new(path)
|
31
|
+
key = path.pop
|
32
|
+
|
33
|
+
case c = path.walk(o)
|
34
|
+
when Array then array_unset(c, key)
|
35
|
+
when Hash then hash_unset(c, key.to_s)
|
36
|
+
else fail KeyError.new("Found no collection at #{path.to_s.inspect}")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def insert(o, path, value)
|
41
|
+
|
42
|
+
path = Dense::Path.new(path)
|
43
|
+
key = path.pop
|
44
|
+
|
45
|
+
case c = path.walk(o)
|
46
|
+
when Array then array_insert(c, key, value)
|
47
|
+
when Hash then c[key.to_s] = value
|
48
|
+
else fail KeyError.new("Found no collection at #{path.to_s.inspect}")
|
49
|
+
end
|
50
|
+
|
51
|
+
value
|
52
|
+
end
|
53
|
+
|
54
|
+
def has_key?(o, path)
|
55
|
+
|
56
|
+
path = Dense::Path.new(path)
|
57
|
+
key = path.pop
|
58
|
+
|
59
|
+
case c = path.walk(o)
|
60
|
+
when Array then array_has_key?(c, key)
|
61
|
+
when Hash then hash_has_key?(c, key)
|
62
|
+
else fail IndexError.new("Found no collection at #{path.to_s.inspect}")
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
protected
|
67
|
+
|
68
|
+
def array_i(k, may_fail=true)
|
69
|
+
|
70
|
+
case k
|
71
|
+
when 'first' then 0
|
72
|
+
when 'last' then -1
|
73
|
+
when Integer then k
|
74
|
+
else
|
75
|
+
may_fail ?
|
76
|
+
fail(IndexError.new("Cannot index array at #{k.inspect}")) :
|
77
|
+
nil
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def array_index(a, k)
|
82
|
+
|
83
|
+
i = array_i(k)
|
84
|
+
i = a.length + i if i < 0
|
85
|
+
|
86
|
+
fail IndexError.new(
|
87
|
+
"Array has length of #{a.length}, index is at #{k.inspect}"
|
88
|
+
) if i < 0 || i >= a.length
|
89
|
+
|
90
|
+
i
|
91
|
+
end
|
92
|
+
|
93
|
+
def array_set(a, k, v)
|
94
|
+
|
95
|
+
i = array_index(a, k)
|
96
|
+
|
97
|
+
a[i] = v
|
98
|
+
end
|
99
|
+
|
100
|
+
def array_unset(a, k)
|
101
|
+
|
102
|
+
i = array_index(a, k)
|
103
|
+
|
104
|
+
a.delete_at(i)
|
105
|
+
end
|
106
|
+
|
107
|
+
def hash_unset(h, k)
|
108
|
+
|
109
|
+
fail KeyError.new("No key #{k.inspect} for hash") unless h.has_key?(k)
|
110
|
+
|
111
|
+
h.delete(k)
|
112
|
+
end
|
113
|
+
|
114
|
+
def array_insert(a, k, v)
|
115
|
+
|
116
|
+
i = array_i(k)
|
117
|
+
|
118
|
+
a.insert(i, v)
|
119
|
+
end
|
120
|
+
|
121
|
+
def array_has_key?(a, k)
|
122
|
+
|
123
|
+
i =
|
124
|
+
array_i(k, false)
|
125
|
+
i =
|
126
|
+
if i.nil?
|
127
|
+
-1
|
128
|
+
elsif i < 0
|
129
|
+
a.length + i
|
130
|
+
else
|
131
|
+
i
|
132
|
+
end
|
133
|
+
|
134
|
+
i > -1 && i < a.length
|
135
|
+
end
|
136
|
+
|
137
|
+
def hash_has_key?(h, k)
|
138
|
+
|
139
|
+
return true if k.is_a?(Integer) && h.has_key?(k.to_s)
|
140
|
+
h.has_key?(k)
|
141
|
+
end
|
142
|
+
end; end
|
143
|
+
|
data/lib/dense/path.rb
ADDED
@@ -0,0 +1,384 @@
|
|
1
|
+
|
2
|
+
class Dense::Path
|
3
|
+
|
4
|
+
attr_reader :original
|
5
|
+
|
6
|
+
def initialize(s)
|
7
|
+
|
8
|
+
@original = s
|
9
|
+
|
10
|
+
fail ArgumentError.new(
|
11
|
+
"Argument is a #{s.class}, not a String"
|
12
|
+
) unless s.is_a?(String)
|
13
|
+
|
14
|
+
s = ".#{s}" unless s[0, 1] == '[' || s[0, 2] == '.['
|
15
|
+
|
16
|
+
#Raabro.pp(Parser.parse(s, debug: 3))
|
17
|
+
@path = Parser.parse(s)
|
18
|
+
|
19
|
+
#Raabro.pp(Parser.parse(s, debug: 3), colors: true) unless @path
|
20
|
+
fail ArgumentError.new(
|
21
|
+
"couldn't determine path from #{s.inspect}"
|
22
|
+
) unless @path
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.make(path_array)
|
26
|
+
|
27
|
+
return nil if path_array.nil?
|
28
|
+
return path_array if path_array.is_a?(Dense::Path)
|
29
|
+
|
30
|
+
path = Dense::Path.allocate
|
31
|
+
path.instance_eval { @path = path_array }
|
32
|
+
path.instance_eval { @original = path.to_s }
|
33
|
+
|
34
|
+
path
|
35
|
+
end
|
36
|
+
|
37
|
+
module Parser include Raabro
|
38
|
+
|
39
|
+
# piece parsers bottom to top
|
40
|
+
|
41
|
+
def dqname(i)
|
42
|
+
|
43
|
+
rex(:qname, i, %r{
|
44
|
+
"(
|
45
|
+
\\["\\\/bfnrt] |
|
46
|
+
\\u[0-9a-fA-F]{4} |
|
47
|
+
[^"\\\b\f\n\r\t]
|
48
|
+
)*"
|
49
|
+
}x)
|
50
|
+
end
|
51
|
+
|
52
|
+
def sqname(i)
|
53
|
+
|
54
|
+
rex(:qname, i, %r{
|
55
|
+
'(
|
56
|
+
\\['\\\/bfnrt] |
|
57
|
+
\\u[0-9a-fA-F]{4} |
|
58
|
+
[^'\\\b\f\n\r\t]
|
59
|
+
)*'
|
60
|
+
}x)
|
61
|
+
end
|
62
|
+
|
63
|
+
def dot(i); str(nil, i, '.'); end
|
64
|
+
def comma(i); rex(nil, i, / *, */); end
|
65
|
+
def bend(i); str(nil, i, ']'); end
|
66
|
+
def bstart(i); str(nil, i, '['); end
|
67
|
+
def blank(i); str(:blank, i, ''); end
|
68
|
+
|
69
|
+
def name(i); rex(:name, i, /[-+%^<>a-zA-Z0-9_\/\\=?]+/); end
|
70
|
+
def off(i); rex(:off, i, /-?\d+/); end
|
71
|
+
|
72
|
+
def star(i); str(:star, i, '*'); end
|
73
|
+
|
74
|
+
def ses(i) # start:end:step
|
75
|
+
rex(
|
76
|
+
:ses,
|
77
|
+
i,
|
78
|
+
/(
|
79
|
+
(-?\d+)?:(-?\d+)?:(-?\d+)? |
|
80
|
+
(-?\d+)?:(-?\d+)? |
|
81
|
+
-?\d+
|
82
|
+
)/x)
|
83
|
+
end
|
84
|
+
|
85
|
+
def escape(i); rex(:esc, i, /\\[.*]/); end
|
86
|
+
|
87
|
+
def bindex(i); alt(:index, i, :dqname, :sqname, :star, :ses, :name, :blank); end
|
88
|
+
def bindexes(i); jseq(:bindexes, i, :bindex, :comma); end
|
89
|
+
def bracket_index(i); seq(nil, i, :bstart, :bindexes, :bend); end
|
90
|
+
def simple_index(i); alt(:index, i, :off, :escape, :star, :name); end
|
91
|
+
|
92
|
+
def dotdot(i); str(:dotdot, i, '.'); end
|
93
|
+
|
94
|
+
def dot_then_index(i); seq(nil, i, :dot, :simple_index); end
|
95
|
+
def index(i); alt(nil, i, :dot_then_index, :bracket_index, :dotdot); end
|
96
|
+
|
97
|
+
def path(i); rep(:path, i, :index, 1); end
|
98
|
+
|
99
|
+
# rewrite parsed tree
|
100
|
+
|
101
|
+
def rewrite_ses(t)
|
102
|
+
a = t.string.split(':').collect { |e| e.empty? ? nil : e.to_i }
|
103
|
+
return a[0] if a[1] == nil && a[2] == nil
|
104
|
+
{ start: a[0], end: a[1], step: a[2] }
|
105
|
+
end
|
106
|
+
def rewrite_esc(t); t.string[1, 1]; end
|
107
|
+
def rewrite_star(t); :star; end
|
108
|
+
def rewrite_dotdot(t); :dot; end
|
109
|
+
def rewrite_off(t); t.string.to_i; end
|
110
|
+
def rewrite_index(t); rewrite(t.sublookup); end
|
111
|
+
def rewrite_bindexes(t);
|
112
|
+
indexes = t.subgather.collect { |tt| rewrite(tt) }
|
113
|
+
indexes.length == 1 ? indexes[0] : indexes.compact
|
114
|
+
end
|
115
|
+
|
116
|
+
def rewrite_blank(t); nil; end
|
117
|
+
|
118
|
+
def rewrite_qname(t); t.string[1..-2]; end
|
119
|
+
def rewrite_name(t); t.string; end
|
120
|
+
|
121
|
+
def rewrite_path(t)
|
122
|
+
t.subgather.collect { |tt| rewrite(tt) }
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
|
127
|
+
def to_a
|
128
|
+
|
129
|
+
@path
|
130
|
+
end
|
131
|
+
|
132
|
+
def length; @path.length; end
|
133
|
+
alias size length
|
134
|
+
|
135
|
+
def to_s
|
136
|
+
|
137
|
+
o = StringIO.new
|
138
|
+
|
139
|
+
@path.each { |e|
|
140
|
+
s = _to_s(e, false)
|
141
|
+
o << '.' unless o.size == 0 || '[.'.index(s[0, 1])
|
142
|
+
o << s }
|
143
|
+
|
144
|
+
s = o.string
|
145
|
+
|
146
|
+
s[0, 2] == '..' ? s[1..-1] : s
|
147
|
+
end
|
148
|
+
|
149
|
+
def walk(data, default=nil, &block)
|
150
|
+
|
151
|
+
_walk(data, @path)
|
152
|
+
|
153
|
+
rescue IndexError => ie
|
154
|
+
|
155
|
+
return yield(@original, self) if block
|
156
|
+
return default if default != nil && default != IndexError
|
157
|
+
|
158
|
+
fail ie.expand(self) if ie.respond_to?(:expand)
|
159
|
+
|
160
|
+
raise
|
161
|
+
end
|
162
|
+
|
163
|
+
def [](offset, count=nil)
|
164
|
+
|
165
|
+
if count == nil && offset.is_a?(Integer)
|
166
|
+
@path[offset]
|
167
|
+
elsif count
|
168
|
+
self.class.make(@path[offset, count])
|
169
|
+
else
|
170
|
+
self.class.make(@path[offset])
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def ==(other)
|
175
|
+
|
176
|
+
other.class == self.class &&
|
177
|
+
other.to_a == @path
|
178
|
+
end
|
179
|
+
|
180
|
+
def last
|
181
|
+
|
182
|
+
@path.last
|
183
|
+
end
|
184
|
+
|
185
|
+
def pop
|
186
|
+
|
187
|
+
@path.pop
|
188
|
+
end
|
189
|
+
|
190
|
+
def -(path)
|
191
|
+
|
192
|
+
self.class.make(subtract(@path.dup, path.to_a.dup))
|
193
|
+
end
|
194
|
+
|
195
|
+
protected
|
196
|
+
|
197
|
+
class NotIndexableError < ::IndexError
|
198
|
+
|
199
|
+
attr_reader :container_class, :root_path, :remaining_path
|
200
|
+
|
201
|
+
def initialize(container, root_path, remaining_path, message=nil)
|
202
|
+
|
203
|
+
@container_class = container.is_a?(Class) ? container : container.class
|
204
|
+
|
205
|
+
@root_path = Dense::Path.make(root_path)
|
206
|
+
@remaining_path = Dense::Path.make(remaining_path)
|
207
|
+
|
208
|
+
if message
|
209
|
+
super(
|
210
|
+
message)
|
211
|
+
elsif @root_path
|
212
|
+
super(
|
213
|
+
"Found nothing at #{fail_path.to_s.inspect} " +
|
214
|
+
"(#{@remaining_path.original.inspect} remains)")
|
215
|
+
else
|
216
|
+
super(
|
217
|
+
"Cannot index instance of #{container_class} " +
|
218
|
+
"with #{@remaining_path.original.inspect}")
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
def expand(root_path)
|
223
|
+
|
224
|
+
err = self.class.new(container_class, root_path, remaining_path, nil)
|
225
|
+
err.set_backtrace(self.backtrace)
|
226
|
+
|
227
|
+
err
|
228
|
+
end
|
229
|
+
|
230
|
+
def relabel(message)
|
231
|
+
|
232
|
+
err = self.class.new(container_class, root_path, remaining_path, message)
|
233
|
+
err.set_backtrace(self.backtrace)
|
234
|
+
|
235
|
+
err
|
236
|
+
end
|
237
|
+
|
238
|
+
def fail_path
|
239
|
+
|
240
|
+
@fail_path ||= (@root_path ? @root_path - @remaining_path : nil)
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
def subtract(apath0, apath1)
|
245
|
+
|
246
|
+
while apath0.any? && apath1.any? && apath0.last == apath1.last
|
247
|
+
apath0.pop
|
248
|
+
apath1.pop
|
249
|
+
end
|
250
|
+
|
251
|
+
apath0
|
252
|
+
end
|
253
|
+
|
254
|
+
def _to_s(elt, in_array)
|
255
|
+
|
256
|
+
case elt
|
257
|
+
when Hash
|
258
|
+
s = [ "#{elt[:start]}:#{elt[:end]}", elt[:step] ].compact.join(':')
|
259
|
+
in_array ? s : "[#{s}]"
|
260
|
+
when Array
|
261
|
+
"[#{elt.map { |e| _to_s(e, true) }.join(',')}#{elt.size < 2 ? ',' : ''}]"
|
262
|
+
when String
|
263
|
+
#in_array ? elt.inspect : elt.to_s
|
264
|
+
#in_array ? _quote_s(elt) : _maybe_quote_s(elt)
|
265
|
+
_str_to_s(elt, in_array)
|
266
|
+
when :star
|
267
|
+
'*'
|
268
|
+
when :dot
|
269
|
+
'.'
|
270
|
+
else
|
271
|
+
elt.to_s
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
def _str_to_s(elt, in_array)
|
276
|
+
|
277
|
+
return elt.inspect if in_array
|
278
|
+
|
279
|
+
s = elt.to_s
|
280
|
+
|
281
|
+
return "\\#{s}" if s == '.' || s == '*'
|
282
|
+
return "[#{elt.inspect}]" if s =~ /["']/
|
283
|
+
s
|
284
|
+
end
|
285
|
+
|
286
|
+
def _walk(data, path)
|
287
|
+
|
288
|
+
return data if path.empty?
|
289
|
+
|
290
|
+
case pa = path.first
|
291
|
+
when :dot then _walk_dot(data, pa, path)
|
292
|
+
when :star then _walk_star(data, pa, path)
|
293
|
+
when Hash then _walk_start_end_step(data, pa, path)
|
294
|
+
when Integer then _walk_int(data, pa, path)
|
295
|
+
when String then _walk(_sindex(data, pa), path[1..-1])
|
296
|
+
else fail IndexError.new("Unwalkable index in path: #{pa.inspect}")
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
def _walk_star(data, pa, path)
|
301
|
+
|
302
|
+
case data
|
303
|
+
when Array then data.collect { |d| _walk(d, path[1..-1]) }
|
304
|
+
when Hash then data.values.collect { |d| _walk(d, path[1..-1]) }
|
305
|
+
else data
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
def _walk_dot(data, pa, path)
|
310
|
+
|
311
|
+
_run(data, path[1])
|
312
|
+
.inject([]) { |a, d|
|
313
|
+
a.concat(
|
314
|
+
begin
|
315
|
+
[ _walk(d, path[2..-1]) ]
|
316
|
+
rescue NotIndexableError
|
317
|
+
[]
|
318
|
+
end) }
|
319
|
+
end
|
320
|
+
|
321
|
+
def _walk_start_end_step(data, pa, path)
|
322
|
+
|
323
|
+
be = pa[:start] || 0
|
324
|
+
en = pa[:end] || data.length - 1
|
325
|
+
st = pa[:step] || 1
|
326
|
+
Range.new(be, en).step(st).collect { |i| _walk(data[i], path[1..-1]) }
|
327
|
+
end
|
328
|
+
|
329
|
+
def _walk_int(data, pa, path)
|
330
|
+
|
331
|
+
if data.is_a?(Array)
|
332
|
+
return _walk(data[pa], path[1..-1])
|
333
|
+
end
|
334
|
+
|
335
|
+
if data.is_a?(Hash)
|
336
|
+
return _walk(data[pa], path[1..-1]) if data.has_key?(pa)
|
337
|
+
pa = pa.to_s
|
338
|
+
return _walk(data[pa], path[1..-1]) if data.has_key?(pa)
|
339
|
+
end
|
340
|
+
|
341
|
+
fail NotIndexableError.new(data, nil, path)
|
342
|
+
end
|
343
|
+
|
344
|
+
def _sindex(data, key)
|
345
|
+
|
346
|
+
case data
|
347
|
+
when Hash
|
348
|
+
data[key]
|
349
|
+
when Array
|
350
|
+
case key
|
351
|
+
when /\Afirst\z/i then data[0]
|
352
|
+
when /\Alast\z/i then data[-1]
|
353
|
+
else fail IndexError.new("Cannot index array with #{key.inspect}")
|
354
|
+
end
|
355
|
+
else
|
356
|
+
fail IndexError.new("Cannot index #{data.class} with #{key.inspect}")
|
357
|
+
end
|
358
|
+
end
|
359
|
+
|
360
|
+
def _run(d, key)
|
361
|
+
|
362
|
+
case d
|
363
|
+
when Hash then _run_hash(d, key)
|
364
|
+
when Array then _run_array(d, key)
|
365
|
+
else key == :star ? [ d ] : []
|
366
|
+
end
|
367
|
+
end
|
368
|
+
|
369
|
+
def _run_hash(d, key)
|
370
|
+
|
371
|
+
if key == :star
|
372
|
+
[ d ] + d.values.inject([]) { |a, v| a.concat(_run(v, key)) }
|
373
|
+
else
|
374
|
+
d.inject([]) { |a, (k, v)| a.concat(k == key ? [ v ] : _run(v, key)) }
|
375
|
+
end
|
376
|
+
end
|
377
|
+
|
378
|
+
def _run_array(d, key)
|
379
|
+
|
380
|
+
(key == :star ? [ d ] : []) +
|
381
|
+
d.inject([]) { |r, e| r.concat(_run(e, key)) }
|
382
|
+
end
|
383
|
+
end
|
384
|
+
|
metadata
CHANGED
@@ -1,29 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dense
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Mettraux
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-09-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: raabro
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 1.1.5
|
20
20
|
type: :runtime
|
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: 1.1.5
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rspec
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -45,11 +45,14 @@ executables: []
|
|
45
45
|
extensions: []
|
46
46
|
extra_rdoc_files: []
|
47
47
|
files:
|
48
|
+
- CHANGELOG.md
|
48
49
|
- LICENSE.txt
|
49
50
|
- Makefile
|
50
51
|
- README.md
|
51
52
|
- dense.gemspec
|
52
53
|
- lib/dense.rb
|
54
|
+
- lib/dense/methods.rb
|
55
|
+
- lib/dense/path.rb
|
53
56
|
homepage: http://github.com/floraison/dense
|
54
57
|
licenses:
|
55
58
|
- MIT
|
@@ -70,7 +73,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
70
73
|
version: '0'
|
71
74
|
requirements: []
|
72
75
|
rubyforge_project:
|
73
|
-
rubygems_version: 2.
|
76
|
+
rubygems_version: 2.6.13
|
74
77
|
signing_key:
|
75
78
|
specification_version: 4
|
76
79
|
summary: fetching deep in a dense structure
|