subhash 0.1.0
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 +7 -0
- data/.gitignore +9 -0
- data/.gitreview +4 -0
- data/.rspec +2 -0
- data/.rubocop.yml +33 -0
- data/Gemfile +4 -0
- data/README.md +115 -0
- data/Rakefile +27 -0
- data/bin/console +27 -0
- data/bin/setup +7 -0
- data/lib/subhash.rb +844 -0
- data/lib/subhash/version.rb +4 -0
- data/subhash.gemspec +25 -0
- metadata +112 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 4438f0e738721e6c962927ac4ca4c9d532bd1257
|
4
|
+
data.tar.gz: c70b1bb50ed69081842f8e27ec5f0589b0544d50
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0a6d52b5a2a87cc274534d49a5b53bcd9a834512ef3b6b25861eb660536e06614c84ad72c448b6ea237f913b1d9aa6d17e9fa985a2198f7f5bf398721bbdb172
|
7
|
+
data.tar.gz: 04c7cad6557626bcb02e479ae44bf155e9f810c843f80b7e2769c3f8cc99fd4062e3891eec8890dea383eb0a590115a6b43f6758180479e80d77938bf9cec72b
|
data/.gitignore
ADDED
data/.gitreview
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# Copyright 2013 Hewlett-Packard Development Company, L.P.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
# use: rubocop --show-cops
|
16
|
+
# to validate configuration.
|
17
|
+
# rubocop config
|
18
|
+
AllCops:
|
19
|
+
Include:
|
20
|
+
- '**/Rakefile'
|
21
|
+
Style/HashSyntax:
|
22
|
+
EnforcedStyle: hash_rockets
|
23
|
+
|
24
|
+
# lets start with 40, but 10 is way to small..
|
25
|
+
Metrics/MethodLength:
|
26
|
+
Max: 40
|
27
|
+
# If Method length is increased, class length need to be extended as well.
|
28
|
+
Metrics/ClassLength:
|
29
|
+
Max: 150
|
30
|
+
|
31
|
+
# allow arguments to be longer than 15
|
32
|
+
Metrics/AbcSize:
|
33
|
+
Max: 40
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
# Recursive Hash of Hashes/Arrays
|
2
|
+
|
3
|
+
SubHash is a set of tools which facilitate data structured as tree of Hash or Array to be
|
4
|
+
set and get.
|
5
|
+
|
6
|
+
Imagine you have a yaml file to load. If your yaml file is well structured, it will be stored
|
7
|
+
in memory as Hash of Hashes, or even Arrays or any kind of type recognized by ruby.
|
8
|
+
|
9
|
+
So, if you want to access a data in a strong tree of hash, how do you write this?
|
10
|
+
|
11
|
+
puts data[key_l1][key_l2][key_l3][mykey]
|
12
|
+
=> myvalue
|
13
|
+
|
14
|
+
What's happen if key\_l1 doesn't exist? or exist but contains a nil or any other value instead of a Hash?
|
15
|
+
An exception is generated. So, you need to add exception to avoid this.
|
16
|
+
|
17
|
+
But your code may become a little complex if you need to check if those layers exists or not...
|
18
|
+
|
19
|
+
So, imagine that instead of that, you do:
|
20
|
+
|
21
|
+
puts data.rh_get(key_l1, key_l2, key_l3, mykey)
|
22
|
+
|
23
|
+
Interesting, right?
|
24
|
+
|
25
|
+
Imagine the set, now:
|
26
|
+
|
27
|
+
data.rh_set(MyNewValue, key_l1, key_l2, key_l3, mykey)
|
28
|
+
|
29
|
+
Seems easier, right?
|
30
|
+
|
31
|
+
And you can check if keys exists, as well as until which level of Hash, I found the path to my key:
|
32
|
+
|
33
|
+
data.rh_exist?(key_l1, key_l2, key_l3, mykey)
|
34
|
+
=> true/false
|
35
|
+
|
36
|
+
data.rh_lexist?(key_l1, key_l2, key_l3, mykey)
|
37
|
+
=> can be 0, 1, 2 ,3 or 4, depending on the path existence to access mykey...
|
38
|
+
|
39
|
+
If you think this can help you, subhash provides the following functions to the Ruby Hash/Array object:
|
40
|
+
|
41
|
+
- Hash.rh\_get
|
42
|
+
- Hash.rh\_set
|
43
|
+
- Hash.rh\_exist? or Hash.rh\_lexist?
|
44
|
+
- Hash.rh\_del
|
45
|
+
- Hash.rh\_merge
|
46
|
+
- Array.rh\_merge
|
47
|
+
|
48
|
+
Thanks to set of functions, you can easily create a tree of Hash or Array.
|
49
|
+
|
50
|
+
Ex:
|
51
|
+
|
52
|
+
data = {}
|
53
|
+
|
54
|
+
data.rh_set(:value, :level1, :level2, :level3)
|
55
|
+
# => { :level1 => { :level2 => { :level3 => :value } } }
|
56
|
+
|
57
|
+
# And it is easy to add or remove
|
58
|
+
data.rh_set(:value2, :level1, :level2, :level3)
|
59
|
+
# => { :level1 => { :level2 => { :level3 => :value } } }
|
60
|
+
data.rh_set(:value, :level1, :level2, :level3_second)
|
61
|
+
# => { :level1 => { :level2 => { :level3 => :value, :level3_second => :value } } }
|
62
|
+
data.rh_del(:level1, :level2, :level3)
|
63
|
+
# => { :level1 => { :level2 => { } } }
|
64
|
+
|
65
|
+
## Installation
|
66
|
+
|
67
|
+
Add this line to your application's Gemfile:
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
gem 'subhash'
|
71
|
+
```
|
72
|
+
|
73
|
+
And then execute:
|
74
|
+
|
75
|
+
$ bundle
|
76
|
+
|
77
|
+
Or install it yourself as:
|
78
|
+
|
79
|
+
$ gem install subhash
|
80
|
+
|
81
|
+
## Usage
|
82
|
+
|
83
|
+
To use Recursive Hash, just add this require in your code:
|
84
|
+
|
85
|
+
require 'subhash'
|
86
|
+
|
87
|
+
Hash and Array object are enhanced and provide those features to any Hash or Array. So, you just need to
|
88
|
+
call wanted functions.
|
89
|
+
|
90
|
+
Ex:
|
91
|
+
|
92
|
+
data = Hash.new
|
93
|
+
data.rh_set(:value, :key_lvl1)
|
94
|
+
|
95
|
+
## Development
|
96
|
+
|
97
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt that will allow you to experiment.
|
98
|
+
|
99
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release` to create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
100
|
+
|
101
|
+
## Contributing
|
102
|
+
|
103
|
+
1. clone it ( https://review.forj.io/forj-oss/rhash )
|
104
|
+
2. add an alias to push your code
|
105
|
+
|
106
|
+
alias git-push='git push origin HEAD:refs/for/$(git branch -v|grep "^\*"|awk '\''{printf $2}'\'')'
|
107
|
+
|
108
|
+
or install git-review
|
109
|
+
|
110
|
+
3. push your code
|
111
|
+
* with git-push
|
112
|
+
* with git-review. See http://www.mediawiki.org/wiki/Gerrit/git-review
|
113
|
+
|
114
|
+
|
115
|
+
Enjoy!!!
|
data/Rakefile
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
require 'rspec/core/rake_task'
|
3
|
+
require 'rubocop/rake_task'
|
4
|
+
require 'rdoc/task'
|
5
|
+
|
6
|
+
task :default => [:lint, :spec]
|
7
|
+
|
8
|
+
desc 'Run the specs.'
|
9
|
+
RSpec::Core::RakeTask.new do |t|
|
10
|
+
t.pattern = 'spec/*_spec.rb'
|
11
|
+
t.rspec_opts = '-f doc'
|
12
|
+
end
|
13
|
+
|
14
|
+
desc 'Generate lorj documentation'
|
15
|
+
RDoc::Task.new do |rdoc|
|
16
|
+
rdoc.main = 'README.md'
|
17
|
+
rdoc.rdoc_files.include('README.md', 'lib', 'example', 'bin')
|
18
|
+
end
|
19
|
+
|
20
|
+
desc 'Run RuboCop on the project'
|
21
|
+
RuboCop::RakeTask.new(:lint) do |task|
|
22
|
+
task.formatters = ['progress']
|
23
|
+
task.verbose = true
|
24
|
+
task.fail_on_error = true
|
25
|
+
end
|
26
|
+
|
27
|
+
task :build => [:lint, :spec]
|
data/bin/console
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'rh'
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
puts "Recursive Hash module
|
14
|
+
Short list of functions:
|
15
|
+
- Hash.rh_set(value, *keys) => value
|
16
|
+
- Hash.rh_get(*keys) => value found or nil
|
17
|
+
- Hash.rh_exist?(*keys) => true/false
|
18
|
+
- Hash.rh_lexist?(*keys) => number
|
19
|
+
- Hash.rh_clone() => Hash
|
20
|
+
- Hash.rh_del(*keys) => data removed
|
21
|
+
- Hash.rh_merge(hash) => Hash/Array merged
|
22
|
+
- Hash.rh_merge!(hash) => Hash/Array merged
|
23
|
+
- Array.rh_merge(Array) => Array/Hash merged
|
24
|
+
- Array.rh_merge!(Array) => Array/Hash merged"
|
25
|
+
|
26
|
+
require 'irb'
|
27
|
+
IRB.start
|
data/bin/setup
ADDED
data/lib/subhash.rb
ADDED
@@ -0,0 +1,844 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: UTF-8
|
3
|
+
|
4
|
+
# (c) Copyright 2014 Hewlett-Packard Development Company, L.P.
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
|
18
|
+
require 'rubygems'
|
19
|
+
require 'yaml'
|
20
|
+
|
21
|
+
# Adding rh_clone at object level. This be able to use a generic rh_clone
|
22
|
+
# redefined per object Hash and Array.
|
23
|
+
class Object
|
24
|
+
alias_method :rh_clone, :clone
|
25
|
+
end
|
26
|
+
|
27
|
+
# Rh common module included in Hash and Array class.
|
28
|
+
module Rh
|
29
|
+
public
|
30
|
+
|
31
|
+
# Function which will parse arrays in hierarchie and will remove any control
|
32
|
+
# element (index 0)
|
33
|
+
def rh_remove_control(result)
|
34
|
+
return unless [Hash, Array].include?(result.class)
|
35
|
+
|
36
|
+
if result.is_a?(Hash)
|
37
|
+
result.each { |elem| rh_remove_control(elem) }
|
38
|
+
else
|
39
|
+
result.delete_at(0) if result[0].is_a?(Hash) && result[0].key?(:__control)
|
40
|
+
result.each_index { |index| rh_remove_control(result[index]) }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
# Internal function to determine if result and data key contains both Hash or
|
47
|
+
# Array and if so, do the merge task on those sub Hash/Array
|
48
|
+
#
|
49
|
+
def _rh_merge_recursive(result, key, data)
|
50
|
+
return false unless [Array, Hash].include?(data.class)
|
51
|
+
|
52
|
+
value = data[key]
|
53
|
+
return false unless [Array, Hash].include?(value.class) &&
|
54
|
+
value.class == result[key].class
|
55
|
+
|
56
|
+
if object_id == result.object_id
|
57
|
+
result[key].rh_merge!(value)
|
58
|
+
else
|
59
|
+
result[key] = result[key].rh_merge(value)
|
60
|
+
end
|
61
|
+
|
62
|
+
true
|
63
|
+
end
|
64
|
+
|
65
|
+
# Internal function to determine if changing from Hash/Array to anything else
|
66
|
+
# is authorized or not.
|
67
|
+
#
|
68
|
+
# The structure is changing if `result` or `value` move from Hash/Array to any
|
69
|
+
# other type.
|
70
|
+
#
|
71
|
+
# * *Args*:
|
72
|
+
# - result: Merged Hash or Array structure.
|
73
|
+
# - key : Key in result and data.
|
74
|
+
# - data : Hash or Array structure to merge.
|
75
|
+
#
|
76
|
+
# * *returns*:
|
77
|
+
# - +true+ : if :__struct_changing == true
|
78
|
+
# - +false+ : otherwise.
|
79
|
+
def _rh_struct_changing_ok?(result, key, data)
|
80
|
+
return true unless [Array, Hash].include?(data[key].class) ||
|
81
|
+
[Array, Hash].include?(result[key].class)
|
82
|
+
|
83
|
+
# result or value are structure (Hash or Array)
|
84
|
+
if result.is_a?(Hash)
|
85
|
+
control = result[:__struct_changing]
|
86
|
+
else
|
87
|
+
control = result[0][:__struct_changing]
|
88
|
+
key -= 1
|
89
|
+
end
|
90
|
+
return true if control.is_a?(Array) && control.include?(key)
|
91
|
+
|
92
|
+
false
|
93
|
+
end
|
94
|
+
|
95
|
+
# Internal function to determine if a data merged can be updated by any
|
96
|
+
# other object like Array, String, etc...
|
97
|
+
#
|
98
|
+
# The decision is given by a :__unset setting.
|
99
|
+
#
|
100
|
+
# * *Args*:
|
101
|
+
# - Hash/Array data to replace.
|
102
|
+
# - key: string or symbol.
|
103
|
+
#
|
104
|
+
# * *returns*:
|
105
|
+
# - +false+ : if key is found in :__protected Array.
|
106
|
+
# - +true+ : otherwise.
|
107
|
+
def _rh_merge_ok?(result, key)
|
108
|
+
if result.is_a?(Hash)
|
109
|
+
control = result[:__protected]
|
110
|
+
else
|
111
|
+
control = result[0][:__protected]
|
112
|
+
key -= 1
|
113
|
+
end
|
114
|
+
|
115
|
+
return false if control.is_a?(Array) && control.include?(key)
|
116
|
+
|
117
|
+
true
|
118
|
+
end
|
119
|
+
|
120
|
+
def _rh_control_tags
|
121
|
+
[:__remove, :__remove_index, :__add, :__add_index,
|
122
|
+
:__protected, :__struct_changing, :__control]
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
# Recursive Hash added to the Hash class
|
127
|
+
class Hash
|
128
|
+
# Recursive Hash deep level found counter
|
129
|
+
# This function will returns the count of deep level of recursive hash.
|
130
|
+
# * *Args* :
|
131
|
+
# - +p+ : Array of string or symbols. keys tree to follow and check
|
132
|
+
# existence in yVal.
|
133
|
+
#
|
134
|
+
# * *Returns* :
|
135
|
+
# - +integer+ : Represents how many deep level was found in the recursive
|
136
|
+
# hash
|
137
|
+
#
|
138
|
+
# * *Raises* :
|
139
|
+
# No exceptions
|
140
|
+
#
|
141
|
+
# Example: (implemented in spec)
|
142
|
+
#
|
143
|
+
# yVal = { :test => {:test2 => 'value1', :test3 => 'value2'},
|
144
|
+
# :test4 => 'value3'}
|
145
|
+
#
|
146
|
+
# yVal can be represented like:
|
147
|
+
#
|
148
|
+
# yVal:
|
149
|
+
# test:
|
150
|
+
# test2 = 'value1'
|
151
|
+
# test3 = 'value2'
|
152
|
+
# test4 = 'value3'
|
153
|
+
#
|
154
|
+
# so:
|
155
|
+
# # test is found
|
156
|
+
# yVal.rh_lexist?(:test) => 1
|
157
|
+
#
|
158
|
+
# # no test5
|
159
|
+
# yVal.rh_lexist?(:test5) => 0
|
160
|
+
#
|
161
|
+
# # :test/:test2 tree is found
|
162
|
+
# yVal.rh_lexist?(:test, :test2) => 2
|
163
|
+
#
|
164
|
+
# # :test/:test2 is found (value = 2), but :test5 was not found in this tree
|
165
|
+
# yVal.rh_lexist?(:test, :test2, :test5) => 2
|
166
|
+
#
|
167
|
+
# # :test was found. but :test/:test5 tree was not found. so level 1, ok.
|
168
|
+
# yVal.rh_lexist?(:test, :test5 ) => 1
|
169
|
+
#
|
170
|
+
# # it is like searching for nothing...
|
171
|
+
# yVal.rh_lexist? => 0
|
172
|
+
|
173
|
+
def rh_lexist?(*p)
|
174
|
+
p = p.flatten
|
175
|
+
|
176
|
+
return 0 if p.length == 0
|
177
|
+
|
178
|
+
if p.length == 1
|
179
|
+
return 1 if self.key?(p[0])
|
180
|
+
return 0
|
181
|
+
end
|
182
|
+
return 0 unless self.key?(p[0])
|
183
|
+
ret = 0
|
184
|
+
ret = self[p[0]].rh_lexist?(p.drop(1)) if self[p[0]].is_a?(Hash)
|
185
|
+
1 + ret
|
186
|
+
end
|
187
|
+
|
188
|
+
# Recursive Hash deep level existence
|
189
|
+
#
|
190
|
+
# * *Args* :
|
191
|
+
# - +p+ : Array of string or symbols. keys tree to follow and check
|
192
|
+
# existence in yVal.
|
193
|
+
#
|
194
|
+
# * *Returns* :
|
195
|
+
# - +boolean+ : Returns True if the deep level of recursive hash is found.
|
196
|
+
# false otherwise
|
197
|
+
#
|
198
|
+
# * *Raises* :
|
199
|
+
# No exceptions
|
200
|
+
#
|
201
|
+
# Example:(implemented in spec)
|
202
|
+
#
|
203
|
+
# yVal = { :test => {:test2 => 'value1', :test3 => 'value2'},
|
204
|
+
# :test4 => 'value3'}
|
205
|
+
#
|
206
|
+
# yVal can be represented like:
|
207
|
+
#
|
208
|
+
# yVal:
|
209
|
+
# test:
|
210
|
+
# test2 = 'value1'
|
211
|
+
# test3 = 'value2'
|
212
|
+
# test4 = 'value3'
|
213
|
+
#
|
214
|
+
# so:
|
215
|
+
# # test is found
|
216
|
+
# yVal.rh_exist?(:test) => True
|
217
|
+
#
|
218
|
+
# # no test5
|
219
|
+
# yVal.rh_exist?(:test5) => False
|
220
|
+
#
|
221
|
+
# # :test/:test2 tree is found
|
222
|
+
# yVal.rh_exist?(:test, :test2) => True
|
223
|
+
#
|
224
|
+
# # :test/:test2 is found (value = 2), but :test5 was not found in this tree
|
225
|
+
# yVal.rh_exist?(:test, :test2, :test5) => False
|
226
|
+
#
|
227
|
+
# # :test was found. but :test/:test5 tree was not found. so level 1, ok.
|
228
|
+
# yVal.rh_exist?(:test, :test5 ) => False
|
229
|
+
#
|
230
|
+
# # it is like searching for nothing...
|
231
|
+
# yVal.rh_exist? => nil
|
232
|
+
def rh_exist?(*p)
|
233
|
+
p = p.flatten
|
234
|
+
|
235
|
+
return nil if p.length == 0
|
236
|
+
|
237
|
+
count = p.length
|
238
|
+
(rh_lexist?(*p) == count)
|
239
|
+
end
|
240
|
+
|
241
|
+
# Recursive Hash Get
|
242
|
+
# This function will returns the level of recursive hash was found.
|
243
|
+
# * *Args* :
|
244
|
+
# - +p+ : Array of string or symbols. keys tree to follow and check
|
245
|
+
# existence in yVal.
|
246
|
+
#
|
247
|
+
# * *Returns* :
|
248
|
+
# - +value+ : Represents the data found in the tree. Can be of any type.
|
249
|
+
#
|
250
|
+
# * *Raises* :
|
251
|
+
# No exceptions
|
252
|
+
#
|
253
|
+
# Example:(implemented in spec)
|
254
|
+
#
|
255
|
+
# yVal = { :test => {:test2 => 'value1', :test3 => 'value2'},
|
256
|
+
# :test4 => 'value3'}
|
257
|
+
#
|
258
|
+
# yVal can be represented like:
|
259
|
+
#
|
260
|
+
# yVal:
|
261
|
+
# test:
|
262
|
+
# test2 = 'value1'
|
263
|
+
# test3 = 'value2'
|
264
|
+
# test4 = 'value3'
|
265
|
+
#
|
266
|
+
# so:
|
267
|
+
# yVal.rh_get(:test) => {:test2 => 'value1', :test3 => 'value2'}
|
268
|
+
# yVal.rh_get(:test5) => nil
|
269
|
+
# yVal.rh_get(:test, :test2) => 'value1'
|
270
|
+
# yVal.rh_get(:test, :test2, :test5) => nil
|
271
|
+
# yVal.rh_get(:test, :test5 ) => nil
|
272
|
+
# yVal.rh_get => { :test => {:test2 => 'value1', :test3 => 'value2'},
|
273
|
+
# :test4 => 'value3'}
|
274
|
+
def rh_get(*p)
|
275
|
+
p = p.flatten
|
276
|
+
return self if p.length == 0
|
277
|
+
|
278
|
+
if p.length == 1
|
279
|
+
return self[p[0]] if self.key?(p[0])
|
280
|
+
return nil
|
281
|
+
end
|
282
|
+
return self[p[0]].rh_get(p.drop(1)) if self[p[0]].is_a?(Hash)
|
283
|
+
nil
|
284
|
+
end
|
285
|
+
|
286
|
+
# Recursive Hash Set
|
287
|
+
# This function will build a recursive hash according to the '*p' key tree.
|
288
|
+
# if yVal is not nil, it will be updated.
|
289
|
+
#
|
290
|
+
# * *Args* :
|
291
|
+
# - +p+ : Array of string or symbols. keys tree to follow and check
|
292
|
+
# existence in yVal.
|
293
|
+
#
|
294
|
+
# * *Returns* :
|
295
|
+
# - +value+ : the value set.
|
296
|
+
#
|
297
|
+
# * *Raises* :
|
298
|
+
# No exceptions
|
299
|
+
#
|
300
|
+
# Example:(implemented in spec)
|
301
|
+
#
|
302
|
+
# yVal = {}
|
303
|
+
#
|
304
|
+
# yVal.rh_set(:test) => nil
|
305
|
+
# # yVal = {}
|
306
|
+
#
|
307
|
+
# yVal.rh_set(:test5) => nil
|
308
|
+
# # yVal = {}
|
309
|
+
#
|
310
|
+
# yVal.rh_set(:test, :test2) => :test
|
311
|
+
# # yVal = {:test2 => :test}
|
312
|
+
#
|
313
|
+
# yVal.rh_set(:test, :test2, :test5) => :test
|
314
|
+
# # yVal = {:test2 => {:test5 => :test} }
|
315
|
+
#
|
316
|
+
# yVal.rh_set(:test, :test5 ) => :test
|
317
|
+
# # yVal = {:test2 => {:test5 => :test}, :test5 => :test }
|
318
|
+
#
|
319
|
+
# yVal.rh_set('blabla', :test2, 'text') => :test
|
320
|
+
# # yVal = {:test2 => {:test5 => :test, 'text' => 'blabla'},
|
321
|
+
# :test5 => :test }
|
322
|
+
def rh_set(value, *p)
|
323
|
+
p = p.flatten
|
324
|
+
return nil if p.length == 0
|
325
|
+
|
326
|
+
if p.length == 1
|
327
|
+
self[p[0]] = value
|
328
|
+
return value
|
329
|
+
end
|
330
|
+
|
331
|
+
self[p[0]] = {} unless self[p[0]].is_a?(Hash)
|
332
|
+
self[p[0]].rh_set(value, p.drop(1))
|
333
|
+
end
|
334
|
+
|
335
|
+
# Recursive Hash delete
|
336
|
+
# This function will remove the last key defined by the key tree
|
337
|
+
#
|
338
|
+
# * *Args* :
|
339
|
+
# - +p+ : Array of string or symbols. keys tree to follow and check
|
340
|
+
# existence in yVal.
|
341
|
+
#
|
342
|
+
# * *Returns* :
|
343
|
+
# - +value+ : The Hash updated.
|
344
|
+
#
|
345
|
+
# * *Raises* :
|
346
|
+
# No exceptions
|
347
|
+
#
|
348
|
+
# Example:(implemented in spec)
|
349
|
+
#
|
350
|
+
# yVal = {{:test2 => { :test5 => :test,
|
351
|
+
# 'text' => 'blabla' },
|
352
|
+
# :test5 => :test}}
|
353
|
+
#
|
354
|
+
#
|
355
|
+
# yVal.rh_del(:test) => nil
|
356
|
+
# # yVal = no change
|
357
|
+
#
|
358
|
+
# yVal.rh_del(:test, :test2) => nil
|
359
|
+
# # yVal = no change
|
360
|
+
#
|
361
|
+
# yVal.rh_del(:test2, :test5) => {:test5 => :test}
|
362
|
+
# # yVal = {:test2 => {:test5 => :test} }
|
363
|
+
#
|
364
|
+
# yVal.rh_del(:test, :test2)
|
365
|
+
# # yVal = {:test2 => {:test5 => :test} }
|
366
|
+
#
|
367
|
+
# yVal.rh_del(:test, :test5)
|
368
|
+
# # yVal = {:test2 => {} }
|
369
|
+
#
|
370
|
+
def rh_del(*p)
|
371
|
+
p = p.flatten
|
372
|
+
|
373
|
+
return nil if p.length == 0
|
374
|
+
|
375
|
+
return delete(p[0]) if p.length == 1
|
376
|
+
|
377
|
+
return nil if self[p[0]].nil?
|
378
|
+
self[p[0]].rh_del(p.drop(1))
|
379
|
+
end
|
380
|
+
|
381
|
+
# Move levels (default level 1) of tree keys to become symbol.
|
382
|
+
#
|
383
|
+
# * *Args* :
|
384
|
+
# - +levels+: level of key tree to update.
|
385
|
+
# * *Returns* :
|
386
|
+
# - a new hash of hashes updated. Original Hash is not updated anymore.
|
387
|
+
#
|
388
|
+
# examples:
|
389
|
+
# With hdata = { :test => { :test2 => { :test5 => :test,
|
390
|
+
# 'text' => 'blabla' },
|
391
|
+
# 'test5' => 'test' }}
|
392
|
+
#
|
393
|
+
# hdata.rh_key_to_symbol(1) return no diff
|
394
|
+
# hdata.rh_key_to_symbol(2) return "test5" is replaced by :test5
|
395
|
+
# # hdata = { :test => { :test2 => { :test5 => :test,
|
396
|
+
# # 'text' => 'blabla' },
|
397
|
+
# # :test5 => 'test' }}
|
398
|
+
# rh_key_to_symbol(3) return "test5" replaced by :test5, and "text" to :text
|
399
|
+
# # hdata = { :test => { :test2 => { :test5 => :test,
|
400
|
+
# # :text => 'blabla' },
|
401
|
+
# # :test5 => 'test' }}
|
402
|
+
# rh_key_to_symbol(4) same like rh_key_to_symbol(3)
|
403
|
+
|
404
|
+
def rh_key_to_symbol(levels = 1)
|
405
|
+
result = {}
|
406
|
+
each do |key, value|
|
407
|
+
new_key = key
|
408
|
+
new_key = key.to_sym if key.is_a?(String)
|
409
|
+
if value.is_a?(Hash) && levels > 1
|
410
|
+
value = value.rh_key_to_symbol(levels - 1)
|
411
|
+
end
|
412
|
+
result[new_key] = value
|
413
|
+
end
|
414
|
+
result
|
415
|
+
end
|
416
|
+
|
417
|
+
# Check if levels of tree keys are all symbols.
|
418
|
+
#
|
419
|
+
# * *Args* :
|
420
|
+
# - +levels+: level of key tree to update.
|
421
|
+
# * *Returns* :
|
422
|
+
# - true : one key path is not symbol.
|
423
|
+
# - false : all key path are symbols.
|
424
|
+
# * *Raises* :
|
425
|
+
# Nothing
|
426
|
+
#
|
427
|
+
# examples:
|
428
|
+
# With hdata = { :test => { :test2 => { :test5 => :test,
|
429
|
+
# 'text' => 'blabla' },
|
430
|
+
# 'test5' => 'test' }}
|
431
|
+
#
|
432
|
+
# hdata.rh_key_to_symbol?(1) return false
|
433
|
+
# hdata.rh_key_to_symbol?(2) return true
|
434
|
+
# hdata.rh_key_to_symbol?(3) return true
|
435
|
+
# hdata.rh_key_to_symbol?(4) return true
|
436
|
+
def rh_key_to_symbol?(levels = 1)
|
437
|
+
each do |key, value|
|
438
|
+
return true if key.is_a?(String)
|
439
|
+
|
440
|
+
res = false
|
441
|
+
if levels > 1 && value.is_a?(Hash)
|
442
|
+
res = value.rh_key_to_symbol?(levels - 1)
|
443
|
+
end
|
444
|
+
return true if res
|
445
|
+
end
|
446
|
+
false
|
447
|
+
end
|
448
|
+
|
449
|
+
# return an exact clone of the recursive Array and Hash contents.
|
450
|
+
#
|
451
|
+
# * *Args* :
|
452
|
+
#
|
453
|
+
# * *Returns* :
|
454
|
+
# - Recursive Array/Hash cloned. Other kind of objects are kept referenced.
|
455
|
+
# * *Raises* :
|
456
|
+
# Nothing
|
457
|
+
#
|
458
|
+
# examples:
|
459
|
+
# hdata = { :test => { :test2 => { :test5 => :test,
|
460
|
+
# 'text' => 'blabla' },
|
461
|
+
# 'test5' => 'test' },
|
462
|
+
# :array => [{ :test => :value1 }, 2, { :test => :value3 }]}
|
463
|
+
#
|
464
|
+
# hclone = hdata.rh_clone
|
465
|
+
# hclone[:test] = "test"
|
466
|
+
# hdata[:test] == { :test2 => { :test5 => :test,'text' => 'blabla' }
|
467
|
+
# # => true
|
468
|
+
# hclone[:array].pop
|
469
|
+
# hdata[:array].length != hclone[:array].length
|
470
|
+
# # => true
|
471
|
+
# hclone[:array][0][:test] = "value2"
|
472
|
+
# hdata[:array][0][:test] != hclone[:array][0][:test]
|
473
|
+
# # => true
|
474
|
+
def rh_clone
|
475
|
+
result = {}
|
476
|
+
each do |key, value|
|
477
|
+
if [Array, Hash].include?(value.class)
|
478
|
+
result[key] = value.rh_clone
|
479
|
+
else
|
480
|
+
result[key] = value
|
481
|
+
end
|
482
|
+
end
|
483
|
+
result
|
484
|
+
end
|
485
|
+
|
486
|
+
# Merge the current Hash object (self) cloned with a Hash/Array tree contents
|
487
|
+
# (data).
|
488
|
+
#
|
489
|
+
# 'self' is used as original data to merge to.
|
490
|
+
# 'data' is used as data to merged to clone of 'self'. If you want to update
|
491
|
+
# 'self', use rh_merge!
|
492
|
+
#
|
493
|
+
# if 'self' or 'data' contains a Hash tree, the merge will be executed
|
494
|
+
# recursively.
|
495
|
+
#
|
496
|
+
# The current function will execute the merge of the 'self' keys with the top
|
497
|
+
# keys in 'data'
|
498
|
+
#
|
499
|
+
# The merge can be controlled by an additionnal Hash key '__*' in each
|
500
|
+
# 'self' key.
|
501
|
+
# If both a <key> exist in 'self' and 'data', the following decision is made:
|
502
|
+
# - if both 'self' and 'data' key contains an Hash or and Array, a recursive
|
503
|
+
# merge if Hash or update if Array, is started.
|
504
|
+
#
|
505
|
+
# - if 'self' <key> contains an Hash or an Array, but not 'data' <key>, then
|
506
|
+
# 'self' <key> will be set to the 'data' <key> except if 'self' <Key> has
|
507
|
+
# :__struct_changing: true
|
508
|
+
# data <key> value can set :unset value
|
509
|
+
#
|
510
|
+
# - if 'self' <key> is :unset and 'data' <key> is any value
|
511
|
+
# 'self' <key> value is set with 'data' <key> value.
|
512
|
+
# 'data' <key> value can contains a Hash with :__no_unset: true to
|
513
|
+
# protect this key against the next merge. (next config layer merge)
|
514
|
+
#
|
515
|
+
# - if 'data' <key> exist but not in 'self', 'data' <key> is just added.
|
516
|
+
#
|
517
|
+
# - if 'data' & 'self' <key> exist, 'self'<key> is updated except if key is in
|
518
|
+
# :__protected array list.
|
519
|
+
#
|
520
|
+
# * *Args* :
|
521
|
+
# - hash : Hash data to merge.
|
522
|
+
#
|
523
|
+
# * *Returns* :
|
524
|
+
# - Recursive Array/Hash merged.
|
525
|
+
#
|
526
|
+
# * *Raises* :
|
527
|
+
# Nothing
|
528
|
+
#
|
529
|
+
# examples:
|
530
|
+
#
|
531
|
+
def rh_merge(data)
|
532
|
+
_rh_merge(clone, data)
|
533
|
+
end
|
534
|
+
|
535
|
+
# Merge the current Hash object (self) with a Hash/Array tree contents (data).
|
536
|
+
#
|
537
|
+
# For details on this functions, see #rh_merge
|
538
|
+
#
|
539
|
+
def rh_merge!(data)
|
540
|
+
_rh_merge(self, data)
|
541
|
+
end
|
542
|
+
end
|
543
|
+
|
544
|
+
# Recursive Hash added to the Hash class
|
545
|
+
class Hash
|
546
|
+
private
|
547
|
+
|
548
|
+
# Internal function which do the real merge task by #rh_merge and #rh_merge!
|
549
|
+
#
|
550
|
+
# See #rh_merge for details
|
551
|
+
#
|
552
|
+
def _rh_merge(result, data)
|
553
|
+
return _rh_merge_choose_data(result, data) unless data.is_a?(Hash)
|
554
|
+
|
555
|
+
data.each do |key, _value|
|
556
|
+
next if [:__struct_changing, :__protected].include?(key)
|
557
|
+
|
558
|
+
_do_rh_merge(result, key, data)
|
559
|
+
end
|
560
|
+
[:__struct_changing, :__protected].each do |key|
|
561
|
+
# Refuse merge by default if key data type are different.
|
562
|
+
# This assume that the first layer merge has set
|
563
|
+
# :__unset as a Hash, and :__protected as an Array.
|
564
|
+
|
565
|
+
_do_rh_merge(result, key, data, true) if data.key?(key)
|
566
|
+
|
567
|
+
# Remove all control element in arrays
|
568
|
+
rh_remove_control(result[key]) if result.key?(key)
|
569
|
+
end
|
570
|
+
|
571
|
+
result
|
572
|
+
end
|
573
|
+
|
574
|
+
def _rh_merge_choose_data(result, data)
|
575
|
+
# return result as first one impose the type between Hash/Array.
|
576
|
+
return result if [Hash, Array].include?(result.class) ||
|
577
|
+
[Hash, Array].include?(data.class)
|
578
|
+
|
579
|
+
data
|
580
|
+
end
|
581
|
+
# Internal function to execute the merge on one key provided by #_rh_merge
|
582
|
+
#
|
583
|
+
# if refuse_discordance is true, then result[key] can't be updated if
|
584
|
+
# stricly not of same type.
|
585
|
+
def _do_rh_merge(result, key, data, refuse_discordance = false)
|
586
|
+
value = data[key]
|
587
|
+
|
588
|
+
return if _rh_merge_do_add_key(result, key, value)
|
589
|
+
|
590
|
+
return if _rh_merge_recursive(result, key, data)
|
591
|
+
|
592
|
+
return if refuse_discordance
|
593
|
+
|
594
|
+
return unless _rh_struct_changing_ok?(result, key, data)
|
595
|
+
|
596
|
+
return unless _rh_merge_ok?(result, key)
|
597
|
+
|
598
|
+
_rh_merge_do_upd_key(result, key, value)
|
599
|
+
end
|
600
|
+
|
601
|
+
def _rh_merge_do_add_key(result, key, value)
|
602
|
+
unless result.key?(key) || value == :unset
|
603
|
+
result[key] = value # New key added
|
604
|
+
return true
|
605
|
+
end
|
606
|
+
false
|
607
|
+
end
|
608
|
+
|
609
|
+
def _rh_merge_do_upd_key(result, key, value)
|
610
|
+
if value == :unset
|
611
|
+
result.delete(key) if result.key?(key)
|
612
|
+
return
|
613
|
+
end
|
614
|
+
|
615
|
+
result[key] = value # Key updated
|
616
|
+
end
|
617
|
+
|
618
|
+
include Rh
|
619
|
+
end
|
620
|
+
|
621
|
+
# Defines rh_clone for Array
|
622
|
+
class Array
|
623
|
+
# return an exact clone of the recursive Array and Hash contents.
|
624
|
+
#
|
625
|
+
# * *Args* :
|
626
|
+
#
|
627
|
+
# * *Returns* :
|
628
|
+
# - Recursive Array/Hash cloned.
|
629
|
+
# * *Raises* :
|
630
|
+
# Nothing
|
631
|
+
#
|
632
|
+
# examples:
|
633
|
+
# hdata = { :test => { :test2 => { :test5 => :test,
|
634
|
+
# 'text' => 'blabla' },
|
635
|
+
# 'test5' => 'test' },
|
636
|
+
# :array => [{ :test => :value1 }, 2, { :test => :value3 }]}
|
637
|
+
#
|
638
|
+
# hclone = hdata.rh_clone
|
639
|
+
# hclone[:test] = "test"
|
640
|
+
# hdata[:test] == { :test2 => { :test5 => :test,'text' => 'blabla' }
|
641
|
+
# # => true
|
642
|
+
# hclone[:array].pop
|
643
|
+
# hdata[:array].length != hclone[:array].length
|
644
|
+
# # => true
|
645
|
+
# hclone[:array][0][:test] = "value2"
|
646
|
+
# hdata[:array][0][:test] != hclone[:array][0][:test]
|
647
|
+
# # => true
|
648
|
+
def rh_clone
|
649
|
+
result = []
|
650
|
+
each do |value|
|
651
|
+
begin
|
652
|
+
result << value.rh_clone
|
653
|
+
rescue
|
654
|
+
result << value
|
655
|
+
end
|
656
|
+
end
|
657
|
+
result
|
658
|
+
end
|
659
|
+
|
660
|
+
# This function is part of the rh_merge functionnality adapted for Array.
|
661
|
+
#
|
662
|
+
# To provide Array recursivity, we uses the element index.
|
663
|
+
#
|
664
|
+
# **Warning!** If the Array order has changed (sort/random) the index changed
|
665
|
+
# and can generate unwanted result.
|
666
|
+
#
|
667
|
+
# To implement recursivity, and some specific Array management (add/remove)
|
668
|
+
# you have to create an Hash and insert it at position 0 in the 'self' Array.
|
669
|
+
#
|
670
|
+
# **Warning!** If you create an Array, where index 0 contains a Hash, this
|
671
|
+
# Hash will be considered as the Array control element.
|
672
|
+
# If the first index of your Array is not a Hash, an empty Hash will be
|
673
|
+
# inserted at position 0.
|
674
|
+
#
|
675
|
+
# 'data' has the same restriction then 'self' about the first element.
|
676
|
+
# 'data' can influence the rh_merge Array behavior, by updating the first
|
677
|
+
# element.
|
678
|
+
#
|
679
|
+
# The first Hash element has the following attributes:
|
680
|
+
#
|
681
|
+
# - :__struct_changing: Array of index which accepts to move from a structured
|
682
|
+
# data (Hash/Array) to another structure or type.
|
683
|
+
#
|
684
|
+
# Ex: Hash => Array, Array => Integer
|
685
|
+
#
|
686
|
+
# - :__protected: Array of index which protects against update from 'data'
|
687
|
+
#
|
688
|
+
# - :__remove: Array of elements to remove. each element are remove with
|
689
|
+
# Array.delete function. See Array delete function for details.
|
690
|
+
#
|
691
|
+
# - :__remove_index: Array of indexes to remove.
|
692
|
+
# Each element are removed with Array.delete_at function.
|
693
|
+
# It starts from the highest index until the lowest.
|
694
|
+
# See Array delete function for details.
|
695
|
+
#
|
696
|
+
# **NOTE**: __remove and __remove_index cannot be used together.
|
697
|
+
# if both are set, __remove is choosen
|
698
|
+
#
|
699
|
+
# **NOTE** : __remove* is executed before __add*
|
700
|
+
#
|
701
|
+
# - :__add: Array of elements to add. Those elements are systematically added
|
702
|
+
# at the end of the Array. See Array.<< for details.
|
703
|
+
#
|
704
|
+
# - :__add_index: Hash of index(key) + Array of data(value) to add.
|
705
|
+
# The index listed refer to merged 'self' Array. several elements with same
|
706
|
+
# index are grouply inserted in the index.
|
707
|
+
# ex:
|
708
|
+
# [:data3].rh_merge({:__add_index => [0 => [:data1, :data2]]})
|
709
|
+
# => [{}, :data1, :data2, :data3]
|
710
|
+
#
|
711
|
+
# **NOTE**: __add and __add_index cannot be used together.
|
712
|
+
# if both are set, __add is choosen
|
713
|
+
#
|
714
|
+
# How merge is executed:
|
715
|
+
#
|
716
|
+
# Starting at index 0, each index of 'data' and 'self' are used to compare
|
717
|
+
# indexed data.
|
718
|
+
# - If 'data' index 0 has not an Hash, the 'self' index 0 is just skipped.
|
719
|
+
# - If 'data' index 0 has the 'control' Hash, the array will be updated
|
720
|
+
# according to :__add and :__remove arrays.
|
721
|
+
# when done, those attributes are removed
|
722
|
+
#
|
723
|
+
# - For all next index (1 => 'data'.length), data are compared
|
724
|
+
#
|
725
|
+
# - If the 'data' length is > than 'self' length
|
726
|
+
# addtionnal indexed data are added to 'self'
|
727
|
+
#
|
728
|
+
# - If index element exist in both 'data' and 'self',
|
729
|
+
# 'self' indexed data is updated/merged according to control.
|
730
|
+
# 'data' indexed data can use :unset to remove the data at this index
|
731
|
+
# nil is also supported. But the index won't be removed. data will just
|
732
|
+
# be set to nil
|
733
|
+
#
|
734
|
+
# when all Arrays elements are merged, rh_merge will:
|
735
|
+
# - remove 'self' elements containing ':unset'
|
736
|
+
#
|
737
|
+
# - merge 'self' data at index 0 with 'data' found index 0
|
738
|
+
#
|
739
|
+
def rh_merge(data)
|
740
|
+
_rh_merge(clone, data)
|
741
|
+
end
|
742
|
+
|
743
|
+
def rh_merge!(data)
|
744
|
+
_rh_merge(self, data)
|
745
|
+
end
|
746
|
+
|
747
|
+
private
|
748
|
+
|
749
|
+
def _rh_merge(result, data)
|
750
|
+
data = data.clone
|
751
|
+
data_control = _rh_merge_control(data)
|
752
|
+
result_control = _rh_merge_control(result)
|
753
|
+
|
754
|
+
_rh_do_control_merge(result_control, result, data_control, data)
|
755
|
+
|
756
|
+
(1..(data.length - 1)).each do |index|
|
757
|
+
_rh_do_array_merge(result, index, data)
|
758
|
+
end
|
759
|
+
|
760
|
+
(-(result.length - 1)..-1).each do |index|
|
761
|
+
result.delete_at(index.abs) if result[index.abs] == :unset
|
762
|
+
end
|
763
|
+
|
764
|
+
_rh_do_array_merge(result, 0, [data_control])
|
765
|
+
rh_remove_control(result[0]) # Remove all control elements in tree of arrays
|
766
|
+
|
767
|
+
result
|
768
|
+
end
|
769
|
+
|
770
|
+
def _rh_do_array_merge(result, index, data)
|
771
|
+
return if _rh_merge_recursive(result, index, data)
|
772
|
+
|
773
|
+
return unless _rh_struct_changing_ok?(result, index, data)
|
774
|
+
|
775
|
+
return unless _rh_merge_ok?(result, index)
|
776
|
+
|
777
|
+
result[index] = data[index] unless data[index] == :kept
|
778
|
+
end
|
779
|
+
|
780
|
+
# Get the control element. or create it if missing.
|
781
|
+
def _rh_merge_control(array)
|
782
|
+
unless array[0].is_a?(Hash)
|
783
|
+
array.insert(0, :__control => true)
|
784
|
+
return array[0]
|
785
|
+
end
|
786
|
+
|
787
|
+
_rh_control_tags.each do |prop|
|
788
|
+
if array[0].key?(prop)
|
789
|
+
array[0][:__control] = true
|
790
|
+
return array[0]
|
791
|
+
end
|
792
|
+
end
|
793
|
+
|
794
|
+
array.insert(0, :__control => true)
|
795
|
+
|
796
|
+
array[0]
|
797
|
+
end
|
798
|
+
|
799
|
+
# Do the merge according to :__add and :__remove
|
800
|
+
def _rh_do_control_merge(_result_control, result, data_control, _data)
|
801
|
+
if data_control[:__remove].is_a?(Array)
|
802
|
+
_rh_do_control_remove(result, data_control[:__remove])
|
803
|
+
elsif data_control[:__remove_index].is_a?(Array)
|
804
|
+
index_to_remove = data_control[:__remove_index].uniq.sort.reverse
|
805
|
+
_rh_do_control_remove_index(result, index_to_remove)
|
806
|
+
end
|
807
|
+
|
808
|
+
data_control.delete(:__remove)
|
809
|
+
data_control.delete(:__remove_index)
|
810
|
+
|
811
|
+
if data_control[:__add].is_a?(Array)
|
812
|
+
data_control[:__add].each { |element| result << element }
|
813
|
+
elsif data_control[:__add_index].is_a?(Hash)
|
814
|
+
_rh_do_control_add_index(result, data_control[:__add_index].sort)
|
815
|
+
end
|
816
|
+
|
817
|
+
data_control.delete(:__add)
|
818
|
+
data_control.delete(:__add_index)
|
819
|
+
end
|
820
|
+
|
821
|
+
def _rh_do_control_add_index(result, add_index)
|
822
|
+
add_index.reverse_each do |elements_to_insert|
|
823
|
+
next unless elements_to_insert.is_a?(Array) &&
|
824
|
+
elements_to_insert[0].is_a?(Fixnum) &&
|
825
|
+
elements_to_insert[1].is_a?(Array)
|
826
|
+
|
827
|
+
index = elements_to_insert[0] + 1
|
828
|
+
elements = elements_to_insert[1]
|
829
|
+
|
830
|
+
elements.reverse_each { |element| result.insert(index, element) }
|
831
|
+
end
|
832
|
+
end
|
833
|
+
|
834
|
+
# do the element removal.
|
835
|
+
def _rh_do_control_remove(result, remove)
|
836
|
+
remove.each { |element| result.delete(element) }
|
837
|
+
end
|
838
|
+
|
839
|
+
def _rh_do_control_remove_index(result, index_to_remove)
|
840
|
+
index_to_remove.each { |index| result.delete_at(index + 1) }
|
841
|
+
end
|
842
|
+
|
843
|
+
include Rh
|
844
|
+
end
|
data/subhash.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'subhash/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'subhash'
|
8
|
+
spec.version = SubHash::VERSION
|
9
|
+
spec.authors = ['Christophe Larsonneur']
|
10
|
+
spec.email = ['clarsonneur@gmail.com']
|
11
|
+
|
12
|
+
spec.summary = 'Recursive Hash of hashes/Array management'
|
13
|
+
spec.description = 'Hash and Array object enhanced to manage Hash of Hash/Array easily.'
|
14
|
+
spec.homepage = "https://github.com/forj-oss/rhash"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
17
|
+
spec.bindir = 'exe'
|
18
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
19
|
+
spec.require_paths = ['lib']
|
20
|
+
|
21
|
+
spec.add_development_dependency 'bundler', '~> 1.9'
|
22
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
23
|
+
spec.add_development_dependency "rspec", "~> 3.1.0"
|
24
|
+
spec.add_development_dependency "rubocop", "~> 0.30.0"
|
25
|
+
end
|
metadata
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: subhash
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Christophe Larsonneur
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-04-17 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.9'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.9'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 3.1.0
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 3.1.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rubocop
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.30.0
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ~>
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 0.30.0
|
69
|
+
description: Hash and Array object enhanced to manage Hash of Hash/Array easily.
|
70
|
+
email:
|
71
|
+
- clarsonneur@gmail.com
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- .gitignore
|
77
|
+
- .gitreview
|
78
|
+
- .rspec
|
79
|
+
- .rubocop.yml
|
80
|
+
- Gemfile
|
81
|
+
- README.md
|
82
|
+
- Rakefile
|
83
|
+
- bin/console
|
84
|
+
- bin/setup
|
85
|
+
- lib/subhash.rb
|
86
|
+
- lib/subhash/version.rb
|
87
|
+
- subhash.gemspec
|
88
|
+
homepage: https://github.com/forj-oss/rhash
|
89
|
+
licenses: []
|
90
|
+
metadata: {}
|
91
|
+
post_install_message:
|
92
|
+
rdoc_options: []
|
93
|
+
require_paths:
|
94
|
+
- lib
|
95
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
96
|
+
requirements:
|
97
|
+
- - '>='
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: '0'
|
100
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
101
|
+
requirements:
|
102
|
+
- - '>='
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: '0'
|
105
|
+
requirements: []
|
106
|
+
rubyforge_project:
|
107
|
+
rubygems_version: 2.1.11
|
108
|
+
signing_key:
|
109
|
+
specification_version: 4
|
110
|
+
summary: Recursive Hash of hashes/Array management
|
111
|
+
test_files: []
|
112
|
+
has_rdoc:
|