dense 0.0.1 → 1.0.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.
- 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
|
+
[](http://travis-ci.org/floraison/dense)
|
5
|
+
[](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
|