lib/ihash.rb in subhash-0.1.4 vs lib/ihash.rb in subhash-0.1.5
- old
+ new
@@ -153,184 +153,225 @@
#
# * *Args* :
# - +p+ : Array of String/Symbol or Regexp. It contains the list of keys
# tree to follow and check existence in self.
#
- # In the subhash structure, each hierachie tree level is a Hash or an
+ # In the subhash structure, each hierachy tree level is a Hash or an
# Array.
#
# 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.
+ # - RegExp match: (Version 0.1.2)
+ # You can define key matching with Regexp or with a structured string:
+ # - Regexp or '/<Regexp>/<opts>' 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.
#
- # - '{<regexp>}' :
- # For each key in self tree, matching <Regexp>, the value and the key
- # will be added as a new item (key => value) in a Hash
+ # - '{/<Regexp>/<opts>}' :
+ # For each key in self tree, matching <Regexp>, the value and the
+ # key will be added as a new item (key => value) in a Hash
#
+ # '<Regexp' is a string representing the Regexp to use.
+ # Please note that :
+ # data.rh_get(/test/) is equivalent to data.rh_get('/test/'). This
+ # second syntax provide additional feature, with <opts>
+ #
+ # '<opts>' is an optional string representing a list of characters
+ # to set options for matching results.
+ # Options supported are:
+ # - 'e' : If nothing match, the selection will return an empty Array
+ # By default, it return nil, Usually, it is eliminated in the
+ # final result. (Version 0.1.3)
+ # - '0' : If only one match, the selection will return the value
+ # of that alone matching RegExp. (Version 0.1.5)
+ #
+ # - ERB can be used to select a subhash or extract a key.
+ # (Version 0.1.3)
+ # - ERB Selection
+ # The ERB selection is detected by a string containing
+ # '<%= ... %>opts|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)
+ # '<opts>' is an optional string representing a list of characters
+ # to set options for matching results. (Version 0.1.5)
+ # Options supported are:
+ # - 'e' : If nothing match, the selection will return an empty Array
+ # By default, it return nil, Usually, it is eliminated in the
+ # final result.
+ # - '0' : If only one match, the selection will return the value
+ # of that alone matching RegExp.
+ # - 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']
+ #
# - Array:
- # If the top key type is:
+ # *Data selection :* If the top key type is:
# - Fixnum : The key is considered as the Array index.
- # it will get in self[p[0]]
+ # it will get in self[p[0]] (Version 0.1.3)
#
- # - String/Symbol : loop in array to find in Hash, the key to get value
- # and go on in the tree if possible. it must return an array of result
- # In case of symbol matching, the symbol is converted in string with a
- # ':' at pos 0 in the string, then start match process.
+ # # Data selection:
+ # data.rh_get(:arr2, 1, :test5) # => 'value8'
#
+ # - Range : Range extract only some element from the Array.
+ # (Version 0.1.3)
+ #
+ # # Data selection:
+ # data.rh_get(:arr2, 0..1, :test5) # => ['value5', 'value8']
+ # data.rh_get(:arr2, 1..2, :test5) # => ['value8', 'value9']
+ #
+ # *Data extraction :* If the top key type is:
+ # - '=[<Fixnum|Range>]' where
+ # - Fixnum : From found result, return the content of result[<Fixnum>]
+ # => subhash data found. It can return nil (Version 0.1.3)
+ # - Range : From found result, return the Range context of
+ # result[<Range>]
+ # => Array of (subhash data found) (Version 0.1.3)
+ #
+ # # 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']
+ #
+ # - String/Symbol : loop in array to find in elements an Hash to apply.
+ # So, for each Hash elements, it follows the Hash rule.
+ # The result will be stored in an Array of matching elements.
+ # (Version 0.1.3)
+ #
# * *Returns* :
# - +value+ : Represents the data found in the tree. Can be of any type.
#
# * *Raises* :
# No exceptions
#
# Example:(implemented in spec)
#
- # data =
- # :test:
- # :test2 = 'value1'
- # :test3 => 'value2'
- # :test4 = 'value3'
- # :arr1: [ 4, 'value4']
- # :arr2:
- # - :test5 = 'value5'
- # :test6 = 'value6'
- # - :test7 = 'value7'
- # :test8
- # :test5 = 'value8'
+ # data = {
+ # :test => {
+ # :test2 = 'value1'
+ # :test3 => 'value2' },
+ # :test4 => 'value3'
+ # :arr1 => [ 4, 'value4']
+ # :arr2 => [{ :test5 = 'value5', :test6 = 'value6'},
+ # { :test7 = 'value7'},
+ # :test8,
+ # { :test5 = 'value8' }
+ # ]
+ # }
#
# so:
- # data.rh_get(:test) => {:test2 => 'value1', :test3 => 'value2'}
- # data.rh_get(:test5) => nil
- # data.rh_get(:test, :test2) => 'value1'
- # data.rh_get(:test, :test2, :test5) => nil
- # data.rh_get(:test, :test5 ) => nil
- # data.rh_get => { :test => {:test2 => 'value1', :test3 => 'value2'},
+ # data.rh_get(:test) => {:test2 => 'value1', :test3 => 'value2'}
+ # data.rh_get(:test5) => nil
+ # data.rh_get(:test, :test2) => 'value1'
+ # 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: 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']
- # data.rh_get(:test, '[/:test.*/]') # => ['value1', 'value2']
- # data.rh_get(:test, '{/:test2/}') # => {:test2 => 'value1'}
- # data.rh_get(:test, '{/test/}')
- # # => {:test2 => 'value1', :test3 => 'value2'}
- # data.rh_get(:test, '{:test2}') # => {:test2 => 'value1'}
- # data.rh_get(:test, '{:test2}') # => {:test2 => 'value1'}
+ # 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']
+ # data.rh_get(:test, '[/:test.*/]') # => ['value1', 'value2']
+ # data.rh_get(:test, '{/:test2/}') # => {:test2 => 'value1'}
+ # data.rh_get(:test, '{/test/}')
+ # # => {:test2 => 'value1', :test3 => 'value2'}
+ # data.rh_get(:test, '{:test2}') # => {:test2 => 'value1'}
+ # data.rh_get(:test, '{:test2}') # => {:test2 => 'value1'}
#
- # data.rh_get(:arr2, :test6) # => ['value6']
- # data.rh_get(:arr2, :test8) # => nil
- # data.rh_get(:arr2, :test5) # => ['value5', 'value8']
- # data.rh_get(/arr/, :test5) # => [['value5', 'value8']]
- # data.rh_get('{/arr/}', :test5) # => { :arr2 => ['value5', 'value8']}
- # data.rh_get('{/arr/}', '{:test5}')
- # # => { :arr2 => {:test5 => ['value5', 'value8']}}
+ # data.rh_get(:arr2, :test6) # => ['value6']
+ # data.rh_get(:arr2, :test8) # => nil
+ # data.rh_get(:arr2, :test5) # => ['value5', 'value8']
+ # data.rh_get(/arr/, :test5) # => [['value5', 'value8']]
+ # data.rh_get('{/arr/}', :test5) # => { :arr2 => ['value5', 'value8']}
+ # data.rh_get('{/arr/}', '{:test5}')
+ # # => { :arr2 => {:test5 => ['value5', 'value8']}}
#
- # data.rh_get(:arr2, 2) # => nil
- # data.rh_get(:arr2, 0) # => { :test5 = 'value5',
- # # :test6 = 'value6'}
- # data.rh_get(:arr2, 1) # => { :test7 = 'value7',
- # # :test8
- # # :test5 = 'value8' }
- # data.rh_get(:arr2, 1, :test7) # => 'value7'
- # data.rh_get(:arr2, 0, :test7) # => nil
+ # data.rh_get(:arr2, 2) # => nil
+ # data.rh_get(:arr2, 0) # => { :test5 = 'value5',
+ # # :test6 = 'value6'}
+ # data.rh_get(:arr2, 1) # => { :test7 = 'value7',
+ # # :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.
+ # - Introduce ERB context rh_get/exist?/lexist? functions:
+ # - Introduce Array extraction (Fixnum and Range)
#
- # - 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.
+ # New features: 0.1.5
#
- # 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:
+ # A new option '0' has been added to the RegExp match
+ # It permits to return the value of element 0 of the array built by the
+ # matching result.
#
- # 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]
@@ -630,29 +671,31 @@
end
end
end
def _keys_match(re, res, sp, opts)
- empty = false
- empty = opts.include?('e') if opts
+ empty, one = _key_options(opts)
- _keys_match_loop(re, res, sp)
+ _keys_match_loop(re, res, sp, opts)
+ return res[0] if one && res.is_a?(Array) && res.length == 1
return res if empty || res.length > 0
nil
end
- def _keys_match_loop(re, res, sp)
+ def _keys_match_loop(re, res, sp, opts)
+ _, one = _key_options(opts)
+
keys.sort.each do |k|
k_re = _key_to_s(k)
next unless re.match(k_re)
if sp.length == 0
- _update_res(res, k, self[k])
+ _update_res(res, k, self[k], one)
else
v = self[k].rh_get(sp) if [Array, Hash].include?(self[k].class)
- _update_res(res, k, v) unless v.nil?
+ _update_res(res, k, v, one) unless v.nil?
end
end
end
# Internal function which do the real merge task by #rh_merge and #rh_merge!