subhash 0.1.2 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +3 -0
- data/build/build_with_proxy.sh +1 -1
- data/lib/iarray.rb +86 -28
- data/lib/ihash.rb +137 -13
- data/lib/rh.rb +80 -0
- data/lib/subhash/version.rb +2 -2
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9f0a051059b11209331fa863d593e6e7d2b0714c
|
4
|
+
data.tar.gz: 989f14524b03c77768a03e8d30588548d477980a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: db164809d9c9bdd2e6f0c68e6e74c995d4ceb845c03ca2eaeef5e3f26ca00fed1123f39c1dd098e1e1cb63f58a4bc664121c084845039993e8d754d0892e9b21
|
7
|
+
data.tar.gz: cdcbc82160fa22fb53d1beca45df9cc2e0c43fa899151d87b3d6ae5fa6b9b62a279ed74ef65a4332acad9b297c75a9b3fc37f93c66f2a768923062620e4a7544
|
data/.rubocop.yml
CHANGED
data/build/build_with_proxy.sh
CHANGED
data/lib/iarray.rb
CHANGED
@@ -26,9 +26,12 @@ class Array
|
|
26
26
|
key = p[0]
|
27
27
|
sp = p.drop(1)
|
28
28
|
|
29
|
-
|
29
|
+
re, _, opts = _regexp(key)
|
30
|
+
return _keys_match_lexist(re, [], sp, opts) unless re.nil?
|
30
31
|
|
31
|
-
|
32
|
+
return _lexist_array(sp, key) if [Fixnum, Range].include?(key.class)
|
33
|
+
|
34
|
+
_loop_lexist_array(p, key)
|
32
35
|
end
|
33
36
|
|
34
37
|
# Recursive Hash deep level existence
|
@@ -54,9 +57,9 @@ class Array
|
|
54
57
|
re, res, opts = _regexp(key)
|
55
58
|
return _keys_match(re, res, sp, opts) unless re.nil?
|
56
59
|
|
57
|
-
return _get_array(sp, key) if
|
60
|
+
return _get_array(sp, key) if [Fixnum, Range].include?(key.class)
|
58
61
|
|
59
|
-
_loop_get_array(
|
62
|
+
_loop_get_array(key, p)
|
60
63
|
end
|
61
64
|
|
62
65
|
# return an exact clone of the recursive Array and Hash contents.
|
@@ -189,49 +192,78 @@ class Array
|
|
189
192
|
private
|
190
193
|
|
191
194
|
# Loop in array
|
192
|
-
def _loop_get_array(
|
195
|
+
def _loop_get_array(key, p)
|
196
|
+
if key =~ /=\[[0-9]+\.\.[0-9]+\]/
|
197
|
+
found = /=\[([0-9]+)\.\.([0-9]+)\]/.match(key)
|
198
|
+
extract = Range.new(found[1].to_i, found[2].to_i)
|
199
|
+
sp = p.drop(1)
|
200
|
+
elsif key =~ /=\[[0-9]+\]/
|
201
|
+
extract = /=\[([0-9]+)\]/.match(key)[1].to_i
|
202
|
+
sp = p.drop(1)
|
203
|
+
else
|
204
|
+
sp = p.clone
|
205
|
+
end
|
206
|
+
|
207
|
+
ret = _loop_array(sp)
|
208
|
+
|
209
|
+
return ret[extract] unless extract.nil?
|
210
|
+
return ret if ret.length > 0
|
211
|
+
nil
|
212
|
+
end
|
213
|
+
|
214
|
+
def _loop_array(sp)
|
193
215
|
ret = []
|
194
216
|
each do |e|
|
195
|
-
next unless e.is_a?(Hash)
|
196
|
-
next unless e.key?(key)
|
197
|
-
|
198
217
|
if sp.length == 0
|
199
|
-
ret << e
|
200
|
-
|
201
|
-
ret << e[key].rh_get(sp) if [Array, Hash].include?(e[key].class)
|
218
|
+
ret << e
|
219
|
+
next
|
202
220
|
end
|
221
|
+
|
222
|
+
next unless e.structured?
|
223
|
+
found = e.rh_get(*sp)
|
224
|
+
ret << found unless found.nil?
|
203
225
|
end
|
204
|
-
|
205
|
-
nil
|
226
|
+
ret
|
206
227
|
end
|
207
|
-
|
208
228
|
# Index provided. return the value of the index.
|
209
229
|
def _get_array(sp, key)
|
210
230
|
return self[key] if sp.length == 0
|
211
231
|
|
212
|
-
|
232
|
+
if key.is_a?(Range)
|
233
|
+
res = []
|
234
|
+
self[key].each do |k|
|
235
|
+
next unless k.structured?
|
236
|
+
res << k.rh_get(sp) if k.rh_exist?(sp)
|
237
|
+
end
|
238
|
+
res
|
239
|
+
else
|
240
|
+
self[key].rh_get(sp) if self[key].structured?
|
241
|
+
end
|
213
242
|
end
|
214
243
|
|
244
|
+
# Check in existing Array, Fixnum and Range data.
|
215
245
|
def _lexist_array(sp, key)
|
216
|
-
return 0
|
246
|
+
return 0 if _key_out_of_array(key)
|
247
|
+
|
217
248
|
return 1 if sp.length == 0
|
218
249
|
|
219
|
-
1 + self[key].rh_lexist?(sp) if
|
250
|
+
1 + self[key].rh_lexist?(sp) if self[key].structured?
|
251
|
+
end
|
252
|
+
|
253
|
+
def _key_out_of_array(key)
|
254
|
+
return !(key >= 0 && key < length) if key.is_a?(Fixnum)
|
255
|
+
|
256
|
+
test = 0..(length - 1)
|
257
|
+
!(test.include?(key.first) && test.include?(key.last))
|
220
258
|
end
|
221
259
|
|
222
|
-
|
260
|
+
# Check under each array element to get result.
|
261
|
+
def _loop_lexist_array(p, _key)
|
223
262
|
ret = []
|
224
263
|
each do |e|
|
225
|
-
next unless e.
|
226
|
-
next unless e.key?(key)
|
264
|
+
next unless e.structured?
|
227
265
|
|
228
|
-
|
229
|
-
ret << 1
|
230
|
-
else
|
231
|
-
res = 1
|
232
|
-
res += e[key].rh_lexist?(sp) if [Array, Hash].include?(e[key].class)
|
233
|
-
ret << res
|
234
|
-
end
|
266
|
+
ret << e.rh_lexist?(*p)
|
235
267
|
end
|
236
268
|
ret.length > 0 ? ret.max : 0
|
237
269
|
end
|
@@ -249,6 +281,27 @@ class Array
|
|
249
281
|
nil
|
250
282
|
end
|
251
283
|
|
284
|
+
def _keys_match_lexist(re, res, sp, _opts)
|
285
|
+
each do |e|
|
286
|
+
next unless e.is_a?(Hash)
|
287
|
+
|
288
|
+
_keys_match_lexist_hash(re, res, sp, e)
|
289
|
+
end
|
290
|
+
return res.max if res.length > 0
|
291
|
+
0
|
292
|
+
end
|
293
|
+
|
294
|
+
def _keys_match_lexist_hash(re, res, sp, e)
|
295
|
+
e.keys.sort.each do |k|
|
296
|
+
k_re = _key_to_s(k)
|
297
|
+
next unless re.match(k_re)
|
298
|
+
|
299
|
+
v = 1
|
300
|
+
v += e[k].rh_lexist?(sp) if sp.length > 0 && e[k].structured?
|
301
|
+
res << v
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
252
305
|
def _keys_match_hash(re, res, sp, e)
|
253
306
|
e.keys.sort.each do |k|
|
254
307
|
k_re = _key_to_s(k)
|
@@ -257,13 +310,18 @@ class Array
|
|
257
310
|
if sp.length == 0
|
258
311
|
_update_res(res, k, e[k])
|
259
312
|
else
|
260
|
-
v = e[k].rh_get(sp) if
|
313
|
+
v = e[k].rh_get(sp) if e[k].structured?
|
261
314
|
|
262
315
|
_update_res(res, k, v) unless v.nil?
|
263
316
|
end
|
264
317
|
end
|
265
318
|
res
|
266
319
|
end
|
320
|
+
end
|
321
|
+
|
322
|
+
# Internal function for merge.
|
323
|
+
class Array
|
324
|
+
private
|
267
325
|
|
268
326
|
def _rh_merge(result, data)
|
269
327
|
data = data.clone
|
data/lib/ihash.rb
CHANGED
@@ -64,21 +64,31 @@ class Hash
|
|
64
64
|
#
|
65
65
|
# # it is like searching for nothing...
|
66
66
|
# yVal.rh_lexist? => 0
|
67
|
-
|
67
|
+
#
|
68
|
+
# New features 0.1.3
|
69
|
+
#
|
68
70
|
def rh_lexist?(*p)
|
69
71
|
p = p.flatten
|
70
72
|
|
71
73
|
return 0 if p.length == 0
|
72
74
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
return 0 unless
|
75
|
+
key = p[0]
|
76
|
+
sp = p.drop(1)
|
77
|
+
|
78
|
+
selected, key = _erb_select(key)
|
79
|
+
return 0 unless selected
|
80
|
+
|
81
|
+
key = _erb_extract(key)
|
82
|
+
|
83
|
+
re, _, opts = _regexp(key)
|
84
|
+
return _keys_match_lexist(re, [], sp, opts) unless re.nil?
|
85
|
+
|
86
|
+
return 0 unless self.key?(key)
|
87
|
+
|
88
|
+
return 1 if p.length == 1
|
89
|
+
|
78
90
|
ret = 0
|
79
|
-
|
80
|
-
ret = self[p[0]].rh_lexist?(p.drop(1))
|
81
|
-
end
|
91
|
+
ret = self[key].rh_lexist?(*sp) if self[key].structured?
|
82
92
|
1 + ret
|
83
93
|
end
|
84
94
|
|
@@ -148,8 +158,8 @@ class Hash
|
|
148
158
|
# In the subhash structure, each hierachie tree level is a Hash or an
|
149
159
|
# Array.
|
150
160
|
#
|
151
|
-
# At a given level the top key will be interpreted as follow
|
152
|
-
# is:
|
161
|
+
# At a given level the top key will be interpreted as follow and used as
|
162
|
+
# data selection if the object is:
|
153
163
|
# - Hash:
|
154
164
|
# You can define key matching with Regexp or with a structured string:
|
155
165
|
# - Regexp or '/<Regexp>/' or '[<Regexp>]' :
|
@@ -200,7 +210,7 @@ class Hash
|
|
200
210
|
# data.rh_get => { :test => {:test2 => 'value1', :test3 => 'value2'},
|
201
211
|
# :test4 => 'value3'}
|
202
212
|
#
|
203
|
-
# New features:
|
213
|
+
# New features: 0.1.2
|
204
214
|
# data.rh_get(:test, /^test/) # => []
|
205
215
|
# data.rh_get(:test, /^:test/) # => ['value1', 'value2']
|
206
216
|
# data.rh_get(:test, /^:test.*/) # => ['value1', 'value2']
|
@@ -230,6 +240,95 @@ class Hash
|
|
230
240
|
# data.rh_get(:arr2, 1, :test7) # => 'value7'
|
231
241
|
# data.rh_get(:arr2, 0, :test7) # => nil
|
232
242
|
#
|
243
|
+
# New features: 0.1.3
|
244
|
+
#
|
245
|
+
# Introduce ERB context rh_get/exist?/lexist? functions:
|
246
|
+
# ERB can be used to select a subhash or extract a key.
|
247
|
+
#
|
248
|
+
# - ERB Selection
|
249
|
+
# The ERB selection is detected by a string containing
|
250
|
+
# '<%= ... %>|something'
|
251
|
+
# The ERB code must return a boolean or 'true' to consider the current
|
252
|
+
# data context as queriable with a key.
|
253
|
+
# 'something' can be any key (string, symbol or even an ERB extraction)
|
254
|
+
# - ERB Extraction
|
255
|
+
# The ERB selection is detected by a string containing simply
|
256
|
+
# '<%= ... %>'
|
257
|
+
# The result of that ERB call should return a string which will become a
|
258
|
+
# key to extract data from the current data context.
|
259
|
+
#
|
260
|
+
# NOTE! ERB convert any symbol using to_s. If you need to get a key as a
|
261
|
+
# symbol, you will to add : in front of the context string:
|
262
|
+
#
|
263
|
+
# Ex:
|
264
|
+
# RhContext.context = :test
|
265
|
+
# data.rh_get('<%= context =>') # is equivalent to data.rh_get('test')
|
266
|
+
#
|
267
|
+
# RhContext.context = ':test'
|
268
|
+
# data.rh_get('<%= context =>') # is equivalent to data.rh_get(:test)
|
269
|
+
#
|
270
|
+
# The ERB context by default contains:
|
271
|
+
# - at least a 'data' attribute. It contains the current Hash/Array
|
272
|
+
# level data in the data structure hierarchy.
|
273
|
+
# - optionally a 'context' attribute. Contains any kind of data.
|
274
|
+
# This is typically set before any call to rh_* functions.
|
275
|
+
#
|
276
|
+
# you can introduce more data in the context, by creating a derived class
|
277
|
+
# from RhContext.ERBConfig. This will ensure attribute data/context exist
|
278
|
+
# in the context.
|
279
|
+
# Ex:
|
280
|
+
#
|
281
|
+
# class MyContext < RhContext::ERBConfig
|
282
|
+
# attr_accessor :config # Added config in context
|
283
|
+
# end
|
284
|
+
#
|
285
|
+
# RhContext.erb = MyContext.new
|
286
|
+
# RhContext.erb.config = my_config
|
287
|
+
# data.rh_get(...)
|
288
|
+
#
|
289
|
+
# data = YAML.parse("---
|
290
|
+
# :test:
|
291
|
+
# :test2: value1
|
292
|
+
# :test3: value2
|
293
|
+
# :test4: value3
|
294
|
+
# :arr1: [ 4, value4]
|
295
|
+
# :arr2:
|
296
|
+
# - :test5: value5
|
297
|
+
# :test6: value6
|
298
|
+
# - :test7: value7
|
299
|
+
# :test8
|
300
|
+
# :test5: value8
|
301
|
+
# - :test5: value9")
|
302
|
+
#
|
303
|
+
# # Default context:
|
304
|
+
# RhContext.erb = nil
|
305
|
+
# # Filtering using |
|
306
|
+
# data.rh_get(:arr2, '<%= data.key?(:test8) %>|:test5')
|
307
|
+
# # => ['value8']
|
308
|
+
# RhContext.context = :test6
|
309
|
+
# data.rh_get(:arr2, '<%= context %>')
|
310
|
+
# # => ['value6']
|
311
|
+
#
|
312
|
+
# Introduce Array extraction (Fixnum and Range)
|
313
|
+
# When a data at a current level is an Array, get/exist?/lexist? interpret
|
314
|
+
# - the string '=[<Fixnum|Range>]' where
|
315
|
+
# - Fixnum : From found result, return the content of result[<Fixnum>]
|
316
|
+
# => subhash data found. It can return nil
|
317
|
+
# - Range : From found result, return the Range context of result[<Range>]
|
318
|
+
# => Array of (subhash data found)
|
319
|
+
# - the Range. complete the Array index selection.
|
320
|
+
# ex: [:test1, {:test2 => :value1}].rh_get(0..1, :test2)
|
321
|
+
#
|
322
|
+
# # data extraction. By default:
|
323
|
+
# # data.rh_get(:arr2, :test5) return ['value5', 'value8', 'value9']
|
324
|
+
# # then
|
325
|
+
# data.rh_get(:arr2, '=[0]', :test5) # => 'value5'
|
326
|
+
# data.rh_get(:arr2, '=[0..1]', :test5) # => ['value5', 'value8']
|
327
|
+
# data.rh_get(:arr2, '=[0..3]', :test5) # => ['value5', 'value8','value9']
|
328
|
+
#
|
329
|
+
# # Data selection:
|
330
|
+
# data.rh_get(:arr2, 0..1, :test5) # => ['value5', 'value8']
|
331
|
+
# data.rh_get(:arr2, 1..2, :test5) # => ['value8', 'value9']
|
233
332
|
def rh_get(*p)
|
234
333
|
p = p.flatten
|
235
334
|
return self if p.length == 0
|
@@ -237,6 +336,11 @@ class Hash
|
|
237
336
|
key = p[0]
|
238
337
|
sp = p.drop(1)
|
239
338
|
|
339
|
+
selected, key = _erb_select(key)
|
340
|
+
return nil unless selected
|
341
|
+
|
342
|
+
key = _erb_extract(key)
|
343
|
+
|
240
344
|
re, res, opts = _regexp(key)
|
241
345
|
return _keys_match(re, res, sp, opts) unless re.nil?
|
242
346
|
|
@@ -245,7 +349,7 @@ class Hash
|
|
245
349
|
return nil
|
246
350
|
end
|
247
351
|
|
248
|
-
return self[key].rh_get(sp) if [Array, Hash].include?(self[key].class)
|
352
|
+
return self[key].rh_get(*sp) if [Array, Hash].include?(self[key].class)
|
249
353
|
nil
|
250
354
|
end
|
251
355
|
|
@@ -505,6 +609,26 @@ end
|
|
505
609
|
class Hash
|
506
610
|
private
|
507
611
|
|
612
|
+
def _keys_match_lexist(re, res, sp, _opts)
|
613
|
+
_keys_match_loop_lexist(re, res, sp)
|
614
|
+
|
615
|
+
return 1 + res.max if res.length > 0
|
616
|
+
0
|
617
|
+
end
|
618
|
+
|
619
|
+
def _keys_match_loop_lexist(re, res, sp)
|
620
|
+
keys.sort.each do |k|
|
621
|
+
k_re = _key_to_s(k)
|
622
|
+
next unless re.match(k_re)
|
623
|
+
|
624
|
+
if sp.length == 0
|
625
|
+
res << 1
|
626
|
+
else
|
627
|
+
res << self[k].rh_lexist?(sp) if [Array, Hash].include?(self[k].class)
|
628
|
+
end
|
629
|
+
end
|
630
|
+
end
|
631
|
+
|
508
632
|
def _keys_match(re, res, sp, opts)
|
509
633
|
empty = false
|
510
634
|
empty = opts.include?('e') if opts
|
data/lib/rh.rb
CHANGED
@@ -14,6 +14,48 @@
|
|
14
14
|
# See the License for the specific language governing permissions and
|
15
15
|
# limitations under the License.
|
16
16
|
|
17
|
+
# Recursive Hash context
|
18
|
+
module RhContext
|
19
|
+
# Module attributes
|
20
|
+
class << self
|
21
|
+
attr_accessor :erb
|
22
|
+
end
|
23
|
+
|
24
|
+
# ERBConfig context for subhash selection, or filter
|
25
|
+
class ERBConfig
|
26
|
+
attr_accessor :data
|
27
|
+
attr_accessor :context
|
28
|
+
|
29
|
+
# Bind this limited class with ERB templates
|
30
|
+
def get_binding # rubocop: disable AccessorMethodName
|
31
|
+
binding
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
module_function
|
36
|
+
|
37
|
+
def get(str)
|
38
|
+
return str if @erb.nil?
|
39
|
+
ERB.new(str).result(@erb.get_binding)
|
40
|
+
end
|
41
|
+
|
42
|
+
def data=(data)
|
43
|
+
@erb = ERBConfig.new if @erb.nil?
|
44
|
+
|
45
|
+
@erb.data = data
|
46
|
+
rescue
|
47
|
+
return
|
48
|
+
end
|
49
|
+
|
50
|
+
def context=(data)
|
51
|
+
@erb = ERBConfig.new if @erb.nil?
|
52
|
+
|
53
|
+
@erb.context = data
|
54
|
+
rescue
|
55
|
+
return
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
17
59
|
# Rh common module included in Hash and Array class.
|
18
60
|
module Rh
|
19
61
|
public
|
@@ -26,8 +68,39 @@ module Rh
|
|
26
68
|
_rh_remove_control(rh_clone)
|
27
69
|
end
|
28
70
|
|
71
|
+
def structured?
|
72
|
+
true
|
73
|
+
end
|
74
|
+
|
29
75
|
private
|
30
76
|
|
77
|
+
# Return the erb call return
|
78
|
+
# true otherwise ie Selected by default.
|
79
|
+
def _erb_select(key)
|
80
|
+
return true, key unless key.is_a?(String) && key =~ /^<%=.*%>\|/
|
81
|
+
RhContext.data = self
|
82
|
+
|
83
|
+
found = /^(<%=.*%>)\|/.match(key)
|
84
|
+
key = key.clone
|
85
|
+
key[found[0]] = ''
|
86
|
+
[RhContext.get(found[1]) == 'true', _convert_key(key)]
|
87
|
+
end
|
88
|
+
|
89
|
+
def _erb_extract(key)
|
90
|
+
return key unless key.is_a?(String) && key =~ /^<%=.*%>[^|]?/
|
91
|
+
RhContext.data = self
|
92
|
+
|
93
|
+
_convert_key(RhContext.get(key))
|
94
|
+
end
|
95
|
+
|
96
|
+
def _convert_key(key)
|
97
|
+
return key unless key.is_a?(String)
|
98
|
+
# Ruby 1.8 : 'ab'[1] => 98 and 'ab'[1, 1] => 'b'
|
99
|
+
# Ruby 1.9+ : 'ab'[1] => 'b' and 'ab'[1, 1] => 'b'
|
100
|
+
return key[1..-1].to_sym if key[0, 1] == ':'
|
101
|
+
key
|
102
|
+
end
|
103
|
+
|
31
104
|
# Function which will parse arrays in hierarchie and will remove any control
|
32
105
|
# element (index 0)
|
33
106
|
def _rh_remove_control(result)
|
@@ -155,3 +228,10 @@ module RhGet
|
|
155
228
|
res[k] = v if res.is_a?(Hash)
|
156
229
|
end
|
157
230
|
end
|
231
|
+
|
232
|
+
# By default all object are considered as unstructured, ie not Hash or Array.
|
233
|
+
class Object
|
234
|
+
def structured?
|
235
|
+
false
|
236
|
+
end
|
237
|
+
end
|
data/lib/subhash/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: subhash
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Christophe Larsonneur
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-06-
|
11
|
+
date: 2015-06-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|