subhash 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|