subhash 0.1.2 → 0.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b002bfeab90a25d4208f184c8892ca90bd2a8761
4
- data.tar.gz: df518060d1c2ae5f428b7eaa2302a6dd6cb8ba3e
3
+ metadata.gz: 9f0a051059b11209331fa863d593e6e7d2b0714c
4
+ data.tar.gz: 989f14524b03c77768a03e8d30588548d477980a
5
5
  SHA512:
6
- metadata.gz: a183bffb2589c4f51b84de71436a2f562563e74b3a87f809471803303c490b9c005abc0320530c2c9f176c7f79bb0ed79e3fa3184e665b4699eeb237beeda106
7
- data.tar.gz: 04f7c7a78f569b549a21f5b058109a2502b7225ae8f3976826de3b5c8d6f998259d8f8f4eedd3fc8ae9de8ba51bf2bc8dd33c38db0352d1a4e9b6dc9d89e99ca
6
+ metadata.gz: db164809d9c9bdd2e6f0c68e6e74c995d4ceb845c03ca2eaeef5e3f26ca00fed1123f39c1dd098e1e1cb63f58a4bc664121c084845039993e8d754d0892e9b21
7
+ data.tar.gz: cdcbc82160fa22fb53d1beca45df9cc2e0c43fa899151d87b3d6ae5fa6b9b62a279ed74ef65a4332acad9b297c75a9b3fc37f93c66f2a768923062620e4a7544
data/.rubocop.yml CHANGED
@@ -31,3 +31,6 @@ Metrics/ClassLength:
31
31
  # allow arguments to be longer than 15
32
32
  Metrics/AbcSize:
33
33
  Max: 40
34
+
35
+ Metrics/CyclomaticComplexity:
36
+ Max: 7
@@ -41,7 +41,7 @@ fi
41
41
  if [ "$http_proxy" = "" ]
42
42
  then
43
43
  echo "Currently, no proxy is set. Running docker without proxy"
44
- docker build $TAG
44
+ docker build $TAG .
45
45
  exit
46
46
  fi
47
47
 
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
- return _lexist_array(sp, key) if key.is_a?(Fixnum)
29
+ re, _, opts = _regexp(key)
30
+ return _keys_match_lexist(re, [], sp, opts) unless re.nil?
30
31
 
31
- _loop_lexist_array(sp, key)
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 key.is_a?(Fixnum)
60
+ return _get_array(sp, key) if [Fixnum, Range].include?(key.class)
58
61
 
59
- _loop_get_array(sp, key)
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(sp, key)
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[key]
200
- else
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
- return ret if ret.length > 0
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
- self[key].rh_get(sp) if [Array, Hash].include?(self[key].class)
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 unless key >= 0 && key < length
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 [Array, Hash].include?(self[key].class)
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
- def _loop_lexist_array(sp, key)
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.is_a?(Hash)
226
- next unless e.key?(key)
264
+ next unless e.structured?
227
265
 
228
- if sp.length == 0
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 [Array, Hash].include?(e[k].class)
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
- if p.length == 1
74
- return 1 if self.key?(p[0])
75
- return 0
76
- end
77
- return 0 unless self.key?(p[0])
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
- if [Hash, Array].include?(self[p[0]].class)
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 if the object
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
@@ -1,5 +1,5 @@
1
1
  # Recursive Hash
2
2
  module SubHash
3
- VERSION = '0.1.2'
4
- DATE = '2015-06-03'
3
+ VERSION = '0.1.3'
4
+ DATE = '2015-06-15'
5
5
  end
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.2
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-03 00:00:00.000000000 Z
11
+ date: 2015-06-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler