Class: Hash
Overview
Recursive Hash added to the Hash class
Instance Method Summary collapse
-
#rh_clone ⇒ Object
return an exact clone of the recursive Array and Hash contents.
-
#rh_del(*p) ⇒ Object
Recursive Hash delete This function will remove the last key defined by the key tree.
-
#rh_exist?(*p) ⇒ Boolean
Recursive Hash deep level existence.
-
#rh_get(*p) ⇒ Object
Recursive Hash/Array Get This function will returns the level of recursive subhash structure found.
-
#rh_key_to_symbol(levels = 1) ⇒ Object
Move levels (default level 1) of tree keys to become symbol.
-
#rh_key_to_symbol?(levels = 1) ⇒ Boolean
Check if levels of tree keys are all symbols.
-
#rh_lexist?(*p) ⇒ Boolean
Recursive Hash deep level found counter This function will returns the count of deep level of recursive hash.
-
#rh_merge(data) ⇒ Object
Merge the current Hash object (self) cloned with a Hash/Array tree contents (data).
-
#rh_merge!(data) ⇒ Object
Merge the current Hash object (self) with a Hash/Array tree contents (data).
-
#rh_set(value, *p) ⇒ Object
Recursive Hash Set This function will build a recursive hash according to the ‘*p’ key tree.
Methods included from RhGet
#_key_options, #_key_to_s, #_loop_on_regs, #_regexp, #_update_res
Methods included from Rh
#merge_cleanup, #merge_cleanup!, #structured?
Instance Method Details
#rh_clone ⇒ Object
return an exact clone of the recursive Array and Hash contents.
-
Args :
-
Returns :
-
Recursive Array/Hash cloned. Other kind of objects are kept referenced.
-
-
Raises : Nothing
examples:
hdata = { :test => { :test2 => { :test5 => :test,
'text' => 'blabla' },
'test5' => 'test' },
:array => [{ :test => :value1 }, 2, { :test => :value3 }]}
hclone = hdata.rh_clone
hclone[:test] = "test"
hdata[:test] == { :test2 => { :test5 => :test,'text' => 'blabla' }
# => true
hclone[:array].pop
hdata[:array].length != hclone[:array].length
# => true
hclone[:array][0][:test] = "value2"
hdata[:array][0][:test] != hclone[:array][0][:test]
# => true
579 580 581 582 583 584 585 586 587 588 589 |
# File 'lib/ihash.rb', line 579 def rh_clone result = {} each do |key, value| if [Array, Hash].include?(value.class) result[key] = value.rh_clone else result[key] = value end end result end |
#rh_del(*p) ⇒ Object
Recursive Hash delete This function will remove the last key defined by the key tree
-
Args :
-
p
: Array of string or symbols. keys tree to follow and checkexistence in self.
-
-
Returns :
-
value
: The Hash updated.
-
-
Raises : No exceptions
Example:(implemented in spec)
data = {{:test2 => { :test5 => :test,
'text' => 'blabla' },
:test5 => :test}}
data.rh_del(:test) # => nil
# data = no change
data.rh_del(:test, :test2) # => nil
# data = no change
data.rh_del(:test2, :test5) # => {:test5 => :test}
# data = { :test2 => { 'text' => 'blabla' },
# :test5 => :test} }
data.rh_del(:test2, 'text') # => { 'text' => 'blabla' }
# data = { :test2 => {},
# :test5 => :test} }
data.rh_del(:test5) # => {:test5 => :test}
# data = { :test2 => {} }
475 476 477 478 479 480 481 482 483 484 |
# File 'lib/ihash.rb', line 475 def rh_del(*p) p = p.flatten return nil if p.length == 0 return delete(p[0]) if p.length == 1 return nil if self[p[0]].nil? self[p[0]].rh_del(p.drop(1)) end |
#rh_exist?(*p) ⇒ Boolean
Recursive Hash deep level existence
-
Args :
-
p
: Array of string or symbols. keys tree to follow and checkexistence in yVal.
-
-
Returns :
-
boolean
: Returns True if the deep level of recursive hash is found.false otherwise
-
-
Raises : No exceptions
Example:(implemented in spec)
yVal = { :test => {:test2 => 'value1', :test3 => 'value2'},
:test4 => 'value3'}
yVal can be represented like:
yVal:
test:
test2 = 'value1'
test3 = 'value2'
test4 = 'value3'
so:
# test is found
yVal.rh_exist?(:test) => True
# no test5
yVal.rh_exist?(:test5) => False
# :test/:test2 tree is found
yVal.rh_exist?(:test, :test2) => True
# :test/:test2 is found (value = 2), but :test5 was not found in this tree
yVal.rh_exist?(:test, :test2, :test5) => False
# :test was found. but :test/:test5 tree was not found. so level 1, ok.
yVal.rh_exist?(:test, :test5 ) => False
# it is like searching for nothing...
yVal.rh_exist? => nil
139 140 141 142 143 144 145 146 |
# File 'lib/ihash.rb', line 139 def rh_exist?(*p) p = p.flatten return nil if p.length == 0 count = p.length (rh_lexist?(*p) == count) end |
#rh_get(*p) ⇒ Object
Recursive Hash/Array Get This function will returns the level of recursive subhash structure found. Thanks to Regexp support in keys or string interpretation, rh_get can extract data found in the tree matching and build a simplified structure as result.
-
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 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:
-
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>/<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: *Data selection :* If the top key type is:
-
Fixnum : The key is considered as the Array index. it will get in self[p] (Version 0.1.3)
# 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
> subhash data found. It can return nil (Version 0.1.3)
-
Range : From found result, return the Range context of result
> 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' }
]
}
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'},
: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(: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
New features: 0.1.3
-
Introduce ERB context rh_get/exist?/lexist? functions:
-
Introduce Array extraction (Fixnum and Range)
New features: 0.1.5
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.
373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 |
# File 'lib/ihash.rb', line 373 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) nil end |
#rh_key_to_symbol(levels = 1) ⇒ Object
Move levels (default level 1) of tree keys to become symbol.
-
Args :
-
levels
: level of key tree to update.
-
-
Returns :
-
a new hash of hashes updated. Original Hash is not updated anymore.
-
examples:
With hdata = { :test => { :test2 => { :test5 => :test,
'text' => 'blabla' },
'test5' => 'test' }}
hdata.rh_key_to_symbol(1) return no diff
hdata.rh_key_to_symbol(2) return "test5" is replaced by :test5
# hdata = { :test => { :test2 => { :test5 => :test,
# 'text' => 'blabla' },
# :test5 => 'test' }}
rh_key_to_symbol(3) return "test5" replaced by :test5, and "text" to :text
# hdata = { :test => { :test2 => { :test5 => :test,
# :text => 'blabla' },
# :test5 => 'test' }}
rh_key_to_symbol(4) same like rh_key_to_symbol(3)
509 510 511 512 513 514 515 516 517 518 519 520 |
# File 'lib/ihash.rb', line 509 def rh_key_to_symbol(levels = 1) result = {} each do |key, value| new_key = key new_key = key.to_sym if key.is_a?(String) if value.is_a?(Hash) && levels > 1 value = value.rh_key_to_symbol(levels - 1) end result[new_key] = value end result end |
#rh_key_to_symbol?(levels = 1) ⇒ Boolean
Check if levels of tree keys are all symbols.
-
Args :
-
levels
: level of key tree to update.
-
-
Returns :
-
true : one key path is not symbol.
-
false : all key path are symbols.
-
-
Raises : Nothing
examples:
With hdata = { :test => { :test2 => { :test5 => :test,
'text' => 'blabla' },
'test5' => 'test' }}
hdata.rh_key_to_symbol?(1) return false
hdata.rh_key_to_symbol?(2) return true
hdata.rh_key_to_symbol?(3) return true
hdata.rh_key_to_symbol?(4) return true
541 542 543 544 545 546 547 548 549 550 551 552 |
# File 'lib/ihash.rb', line 541 def rh_key_to_symbol?(levels = 1) each do |key, value| return true if key.is_a?(String) res = false if levels > 1 && value.is_a?(Hash) res = value.rh_key_to_symbol?(levels - 1) end return true if res end false end |
#rh_lexist?(*p) ⇒ Boolean
Recursive Hash deep level found counter This function will returns the count of deep level of recursive hash.
-
Args :
-
p
: Array of string or symbols. keys tree to follow and checkexistence in yVal.
-
-
Returns :
-
integer
: Represents how many deep level was found in the recursivehash
-
-
Raises : No exceptions
Example: (implemented in spec)
yVal = { :test => {:test2 => 'value1', :test3 => 'value2'},
:test4 => 'value3'}
yVal can be represented like:
yVal:
test:
test2 = 'value1'
test3 = 'value2'
test4 = 'value3'
so:
# test is found
yVal.rh_lexist?(:test) => 1
# no test5
yVal.rh_lexist?(:test5) => 0
# :test/:test2 tree is found
yVal.rh_lexist?(:test, :test2) => 2
# :test/:test2 is found (value = 2), but :test5 was not found in this tree
yVal.rh_lexist?(:test, :test2, :test5) => 2
# :test/:test2 is found (value = 2), and 'value1' was found in this tree,
# but not as a Hash
yVal.rh_lexist?(:test, :test2, 'value1') => 2
# :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
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
# File 'lib/ihash.rb', line 70 def rh_lexist?(*p) p = p.flatten return 0 if p.length == 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 ret = self[key].rh_lexist?(*sp) if self[key].structured? 1 + ret end |
#rh_merge(data) ⇒ Object
Merge the current Hash object (self) cloned with a Hash/Array tree contents (data).
‘self’ is used as original data to merge to. ‘data’ is used as data to merged to clone of ‘self’. If you want to update ‘self’, use rh_merge!
if ‘self’ or ‘data’ contains a Hash tree, the merge will be executed recursively.
The current function will execute the merge of the ‘self’ keys with the top keys in ‘data’
The merge can be controlled by an additionnal Hash key ‘__*’ in each ‘self’ key. If both a <key> exist in ‘self’ and ‘data’, the following decision is made:
-
if both ‘self’ and ‘data’ key contains an Hash or and Array, a recursive merge if Hash or update if Array, is started.
-
if ‘self’ <key> contains an Hash or an Array, but not ‘data’ <key>, then ‘self’ <key> will be set to the ‘data’ <key> except if ‘self’ <Key> has :__struct_changing: true data <key> value can set :unset value
-
if ‘self’ <key> is :unset and ‘data’ <key> is any value ‘self’ <key> value is set with ‘data’ <key> value. ‘data’ <key> value can contains a Hash with :__no_unset: true to
protect this key against the next merge. (next config layer merge)
-
if ‘data’ <key> exist but not in ‘self’, ‘data’ <key> is just added.
-
if ‘data’ & ‘self’ <key> exist, ‘self’<key> is updated except if key is in :__protected array list.
-
Args :
-
hash : Hash data to merge.
-
-
Returns :
-
Recursive Array/Hash merged.
-
-
Raises : Nothing
examples:
636 637 638 |
# File 'lib/ihash.rb', line 636 def rh_merge(data) _rh_merge(clone, data) end |
#rh_merge!(data) ⇒ Object
Merge the current Hash object (self) with a Hash/Array tree contents (data).
For details on this functions, see #rh_merge
644 645 646 |
# File 'lib/ihash.rb', line 644 def rh_merge!(data) _rh_merge(self, data) end |
#rh_set(value, *p) ⇒ Object
Recursive Hash Set This function will build a recursive hash according to the ‘*p’ key tree. if yVal is not nil, it will be updated.
-
Args :
-
p
: Array of string or symbols. keys tree to follow and checkexistence in yVal.
-
-
Returns :
-
value
: the value set.
-
-
Raises : No exceptions
Example:(implemented in spec)
data = {}.rh_set(:test) # => {}
data.rh_set(:test, :test2) # => {:test2 => :test}
data.rh_set(:test, :test2, :test5) # => {:test2 => {:test5 => :test} }
data.rh_set(:test, :test5 ) # => {:test2 => {:test5 => :test},
# :test5 => :test }
data.rh_set('blabla', :test2, 'text')
# => {:test2 => {:test5 => :test,
# 'text' => 'blabla'},
# :test5 => :test }
426 427 428 429 430 431 432 433 434 435 436 437 |
# File 'lib/ihash.rb', line 426 def rh_set(value, *p) p = p.flatten return nil if p.length == 0 if p.length == 1 self[p[0]] = value return value end self[p[0]] = {} unless self[p[0]].is_a?(Hash) self[p[0]].rh_set(value, p.drop(1)) end |