lib/ihash.rb in subhash-0.1.2 vs lib/ihash.rb in subhash-0.1.3
- old
+ new
@@ -62,25 +62,35 @@
# # :test was found. but :test/:test5 tree was not found. so level 1, ok.
# yVal.rh_lexist?(:test, :test5 ) => 1
#
# # it is like searching for nothing...
# yVal.rh_lexist? => 0
-
+ #
+ # New features 0.1.3
+ #
def rh_lexist?(*p)
p = p.flatten
return 0 if p.length == 0
- if p.length == 1
- return 1 if self.key?(p[0])
- return 0
- end
- return 0 unless self.key?(p[0])
+ key = p[0]
+ sp = p.drop(1)
+
+ selected, key = _erb_select(key)
+ return 0 unless selected
+
+ key = _erb_extract(key)
+
+ re, _, opts = _regexp(key)
+ return _keys_match_lexist(re, [], sp, opts) unless re.nil?
+
+ return 0 unless self.key?(key)
+
+ return 1 if p.length == 1
+
ret = 0
- if [Hash, Array].include?(self[p[0]].class)
- ret = self[p[0]].rh_lexist?(p.drop(1))
- end
+ ret = self[key].rh_lexist?(*sp) if self[key].structured?
1 + ret
end
# Recursive Hash deep level existence
#
@@ -146,12 +156,12 @@
# tree to follow and check existence in self.
#
# In the subhash structure, each hierachie tree level is a Hash or an
# Array.
#
- # At a given level the top key will be interpreted as follow if the object
- # is:
+ # At a given level the top key will be interpreted as follow and used as
+ # data selection if the object is:
# - Hash:
# You can define key matching with Regexp or with a structured string:
# - Regexp or '/<Regexp>/' or '[<Regexp>]' :
# For each key in self tree, matching <Regexp>, the value associated
# to the key will be added as a new item in an array.
@@ -198,11 +208,11 @@
# data.rh_get(:test, :test2, :test5) => nil
# data.rh_get(:test, :test5 ) => nil
# data.rh_get => { :test => {:test2 => 'value1', :test3 => 'value2'},
# :test4 => 'value3'}
#
- # New features:
+ # New features: 0.1.2
# data.rh_get(:test, /^test/) # => []
# data.rh_get(:test, /^:test/) # => ['value1', 'value2']
# data.rh_get(:test, /^:test.*/) # => ['value1', 'value2']
# data.rh_get(:test, '/:test.*/') # => ['value1', 'value2']
# data.rh_get(:test, '/:test.*/') # => ['value1', 'value2']
@@ -228,26 +238,120 @@
# # :test8
# # :test5 = 'value8' }
# data.rh_get(:arr2, 1, :test7) # => 'value7'
# data.rh_get(:arr2, 0, :test7) # => nil
#
+ # New features: 0.1.3
+ #
+ # Introduce ERB context rh_get/exist?/lexist? functions:
+ # ERB can be used to select a subhash or extract a key.
+ #
+ # - ERB Selection
+ # The ERB selection is detected by a string containing
+ # '<%= ... %>|something'
+ # The ERB code must return a boolean or 'true' to consider the current
+ # data context as queriable with a key.
+ # 'something' can be any key (string, symbol or even an ERB extraction)
+ # - ERB Extraction
+ # The ERB selection is detected by a string containing simply
+ # '<%= ... %>'
+ # The result of that ERB call should return a string which will become a
+ # key to extract data from the current data context.
+ #
+ # NOTE! ERB convert any symbol using to_s. If you need to get a key as a
+ # symbol, you will to add : in front of the context string:
+ #
+ # Ex:
+ # RhContext.context = :test
+ # data.rh_get('<%= context =>') # is equivalent to data.rh_get('test')
+ #
+ # RhContext.context = ':test'
+ # data.rh_get('<%= context =>') # is equivalent to data.rh_get(:test)
+ #
+ # The ERB context by default contains:
+ # - at least a 'data' attribute. It contains the current Hash/Array
+ # level data in the data structure hierarchy.
+ # - optionally a 'context' attribute. Contains any kind of data.
+ # This is typically set before any call to rh_* functions.
+ #
+ # you can introduce more data in the context, by creating a derived class
+ # from RhContext.ERBConfig. This will ensure attribute data/context exist
+ # in the context.
+ # Ex:
+ #
+ # class MyContext < RhContext::ERBConfig
+ # attr_accessor :config # Added config in context
+ # end
+ #
+ # RhContext.erb = MyContext.new
+ # RhContext.erb.config = my_config
+ # data.rh_get(...)
+ #
+ # data = YAML.parse("---
+ # :test:
+ # :test2: value1
+ # :test3: value2
+ # :test4: value3
+ # :arr1: [ 4, value4]
+ # :arr2:
+ # - :test5: value5
+ # :test6: value6
+ # - :test7: value7
+ # :test8
+ # :test5: value8
+ # - :test5: value9")
+ #
+ # # Default context:
+ # RhContext.erb = nil
+ # # Filtering using |
+ # data.rh_get(:arr2, '<%= data.key?(:test8) %>|:test5')
+ # # => ['value8']
+ # RhContext.context = :test6
+ # data.rh_get(:arr2, '<%= context %>')
+ # # => ['value6']
+ #
+ # Introduce Array extraction (Fixnum and Range)
+ # When a data at a current level is an Array, get/exist?/lexist? interpret
+ # - the string '=[<Fixnum|Range>]' where
+ # - Fixnum : From found result, return the content of result[<Fixnum>]
+ # => subhash data found. It can return nil
+ # - Range : From found result, return the Range context of result[<Range>]
+ # => Array of (subhash data found)
+ # - the Range. complete the Array index selection.
+ # ex: [:test1, {:test2 => :value1}].rh_get(0..1, :test2)
+ #
+ # # data extraction. By default:
+ # # data.rh_get(:arr2, :test5) return ['value5', 'value8', 'value9']
+ # # then
+ # data.rh_get(:arr2, '=[0]', :test5) # => 'value5'
+ # data.rh_get(:arr2, '=[0..1]', :test5) # => ['value5', 'value8']
+ # data.rh_get(:arr2, '=[0..3]', :test5) # => ['value5', 'value8','value9']
+ #
+ # # Data selection:
+ # data.rh_get(:arr2, 0..1, :test5) # => ['value5', 'value8']
+ # data.rh_get(:arr2, 1..2, :test5) # => ['value8', 'value9']
def rh_get(*p)
p = p.flatten
return self if p.length == 0
key = p[0]
sp = p.drop(1)
+ selected, key = _erb_select(key)
+ return nil unless selected
+
+ key = _erb_extract(key)
+
re, res, opts = _regexp(key)
return _keys_match(re, res, sp, opts) unless re.nil?
if sp.length == 0
return self[key] if self.key?(key)
return nil
end
- return self[key].rh_get(sp) if [Array, Hash].include?(self[key].class)
+ return self[key].rh_get(*sp) if [Array, Hash].include?(self[key].class)
nil
end
# Recursive Hash Set
# This function will build a recursive hash according to the '*p' key tree.
@@ -502,9 +606,29 @@
end
# Recursive Hash added to the Hash class
class Hash
private
+
+ def _keys_match_lexist(re, res, sp, _opts)
+ _keys_match_loop_lexist(re, res, sp)
+
+ return 1 + res.max if res.length > 0
+ 0
+ end
+
+ def _keys_match_loop_lexist(re, res, sp)
+ keys.sort.each do |k|
+ k_re = _key_to_s(k)
+ next unless re.match(k_re)
+
+ if sp.length == 0
+ res << 1
+ else
+ res << self[k].rh_lexist?(sp) if [Array, Hash].include?(self[k].class)
+ end
+ end
+ end
def _keys_match(re, res, sp, opts)
empty = false
empty = opts.include?('e') if opts