bblib 0.3.0 → 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.gitignore +11 -10
- data/.rspec +2 -2
- data/.travis.yml +4 -4
- data/CODE_OF_CONDUCT.md +13 -13
- data/Gemfile +4 -4
- data/LICENSE.txt +21 -21
- data/README.md +247 -757
- data/Rakefile +6 -6
- data/bblib.gemspec +34 -34
- data/bin/console +14 -14
- data/bin/setup +7 -7
- data/lib/array/bbarray.rb +71 -29
- data/lib/bblib.rb +12 -12
- data/lib/bblib/version.rb +3 -3
- data/lib/class/effortless.rb +23 -0
- data/lib/error/abstract.rb +3 -0
- data/lib/file/bbfile.rb +93 -52
- data/lib/hash/bbhash.rb +130 -46
- data/lib/hash/hash_struct.rb +24 -0
- data/lib/hash/tree_hash.rb +364 -0
- data/lib/hash_path/hash_path.rb +210 -0
- data/lib/hash_path/part.rb +83 -0
- data/lib/hash_path/path_hash.rb +84 -0
- data/lib/hash_path/proc.rb +93 -0
- data/lib/hash_path/processors.rb +239 -0
- data/lib/html/bbhtml.rb +2 -0
- data/lib/html/builder.rb +34 -0
- data/lib/html/tag.rb +49 -0
- data/lib/logging/bblogging.rb +42 -0
- data/lib/mixins/attrs.rb +422 -0
- data/lib/mixins/bbmixins.rb +7 -0
- data/lib/mixins/bridge.rb +17 -0
- data/lib/mixins/family_tree.rb +41 -0
- data/lib/mixins/hooks.rb +139 -0
- data/lib/mixins/logger.rb +31 -0
- data/lib/mixins/serializer.rb +71 -0
- data/lib/mixins/simple_init.rb +160 -0
- data/lib/number/bbnumber.rb +15 -7
- data/lib/object/bbobject.rb +46 -19
- data/lib/opal/bbopal.rb +0 -4
- data/lib/os/bbos.rb +24 -16
- data/lib/os/bbsys.rb +60 -43
- data/lib/string/bbstring.rb +165 -66
- data/lib/string/cases.rb +37 -29
- data/lib/string/fuzzy_matcher.rb +48 -50
- data/lib/string/matching.rb +43 -30
- data/lib/string/pluralization.rb +156 -0
- data/lib/string/regexp.rb +45 -0
- data/lib/string/roman.rb +17 -30
- data/lib/system/bbsystem.rb +42 -0
- data/lib/time/bbtime.rb +79 -58
- data/lib/time/cron.rb +174 -132
- data/lib/time/task_timer.rb +86 -70
- metadata +27 -10
- data/lib/gem/bbgem.rb +0 -28
- data/lib/hash/hash_path.rb +0 -344
- data/lib/hash/hash_path_proc.rb +0 -256
- data/lib/hash/path_hash.rb +0 -81
- data/lib/object/attr.rb +0 -182
- data/lib/object/hooks.rb +0 -69
- data/lib/object/lazy_class.rb +0 -73
data/lib/hash/bbhash.rb
CHANGED
@@ -1,73 +1,157 @@
|
|
1
|
-
require_relative 'hash_path'
|
2
|
-
require_relative 'path_hash'
|
3
1
|
|
4
|
-
|
2
|
+
require_relative 'tree_hash'
|
3
|
+
require_relative 'hash_struct'
|
5
4
|
|
5
|
+
class Hash
|
6
6
|
# Merges with another hash but also merges all nested hashes and arrays/values.
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
def deep_merge! with, merge_arrays: true, overwrite: true
|
24
|
-
replace self.deep_merge(with, merge_arrays: merge_arrays, overwrite: overwrite)
|
7
|
+
def deep_merge(with, merge_arrays: true, overwrite: true, uniq: false)
|
8
|
+
merger = proc do |_k, v1, v2|
|
9
|
+
if BBLib.are_all?(Hash, v1, v2)
|
10
|
+
v1.merge(v2, &merger)
|
11
|
+
elsif merge_arrays && BBLib.are_all?(Array, v1, v2)
|
12
|
+
uniq ? (v1 + v2).uniq : v1 + v2
|
13
|
+
else
|
14
|
+
overwrite || v1 == v2 ? v2 : (uniq ? [v1, v2].flatten.uniq : [v1, v2].flatten)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
merge(with, &merger)
|
18
|
+
end
|
19
|
+
|
20
|
+
# In place version of deep_merge
|
21
|
+
def deep_merge!(*args)
|
22
|
+
replace deep_merge(*args)
|
25
23
|
end
|
26
24
|
|
27
25
|
# Converts the keys of the hash as well as any nested hashes to symbols.
|
28
|
-
|
29
|
-
|
30
|
-
|
26
|
+
def keys_to_sym(clean: false, recursive: true)
|
27
|
+
each_with_object({}) do |(k, v), memo|
|
28
|
+
key = clean ? k.to_s.to_clean_sym : k.to_s.to_sym
|
29
|
+
memo[key] = recursive && v.respond_to?(:keys_to_sym) ? v.keys_to_sym(clean: clean) : v
|
30
|
+
end
|
31
31
|
end
|
32
32
|
|
33
|
-
|
34
|
-
|
33
|
+
# In place version of keys_to_sym
|
34
|
+
def keys_to_sym!(clean: false, recursive: true)
|
35
|
+
replace(keys_to_sym(clean: clean, recursive: recursive))
|
35
36
|
end
|
36
37
|
|
37
38
|
# Converts the keys of the hash as well as any nested hashes to strings.
|
38
|
-
def keys_to_s
|
39
|
-
|
39
|
+
def keys_to_s(recursive: true)
|
40
|
+
each_with_object({}) do |(k, v), memo|
|
41
|
+
memo[k.to_s] = recursive && v.respond_to?(:keys_to_sym) ? v.keys_to_s : v
|
42
|
+
end
|
40
43
|
end
|
41
44
|
|
42
|
-
|
43
|
-
|
45
|
+
# In place version of keys_to_s
|
46
|
+
def keys_to_s!(recursive: true)
|
47
|
+
replace(keys_to_s(recursive: recursive))
|
44
48
|
end
|
45
49
|
|
46
50
|
# Reverses the order of keys in the Hash
|
47
51
|
def reverse
|
48
|
-
|
52
|
+
to_a.reverse.to_h
|
49
53
|
end
|
50
54
|
|
55
|
+
# In place version of reverse
|
51
56
|
def reverse!
|
52
|
-
replace
|
57
|
+
replace(reverse)
|
58
|
+
end
|
59
|
+
|
60
|
+
# Like unshift for Arrays. Adds a key to the beginning of a Hash rather than the end.
|
61
|
+
def unshift(hash, value = nil)
|
62
|
+
hash = { hash => value } unless hash.is_a?(Hash)
|
63
|
+
replace(hash.merge(self).merge(hash))
|
64
|
+
end
|
65
|
+
|
66
|
+
# Displays all of the differences between this hash and another.
|
67
|
+
# Checks both key and value pairs.
|
68
|
+
def diff(hash)
|
69
|
+
to_a.diff(hash.to_a).to_h
|
70
|
+
end
|
71
|
+
|
72
|
+
# Returns all matching values with a specific key (or keys) recursively within a Hash (including nested Arrays)
|
73
|
+
def dive(*keys)
|
74
|
+
matches = []
|
75
|
+
each do |k, v|
|
76
|
+
matches << v if keys.any? { |key| (key.is_a?(Regexp) ? key =~ k : key == k) }
|
77
|
+
matches += v.dive(*keys) if v.respond_to?(:dive)
|
78
|
+
end
|
79
|
+
matches
|
80
|
+
end
|
81
|
+
|
82
|
+
# Navigate a hash using a dot delimited path.
|
83
|
+
def path_nav(obj, path = '', delimiter = '.', &block)
|
84
|
+
case obj
|
85
|
+
when Hash
|
86
|
+
obj.each do |k, v|
|
87
|
+
path_nav(
|
88
|
+
v,
|
89
|
+
(path ? [path, k.to_s.gsub(delimiter, "\\#{delimiter}")].join(delimiter) : k.to_s.gsub(delimiter, "\\#{delimiter}")).to_s,
|
90
|
+
delimiter,
|
91
|
+
&block
|
92
|
+
)
|
93
|
+
end
|
94
|
+
when Array
|
95
|
+
obj.each_with_index do |ob, index|
|
96
|
+
path_nav(
|
97
|
+
ob,
|
98
|
+
(path ? [path, "[#{index}]"].join(delimiter) : "[#{index}]").to_s,
|
99
|
+
delimiter,
|
100
|
+
&block
|
101
|
+
)
|
102
|
+
end
|
103
|
+
else
|
104
|
+
yield path, obj
|
105
|
+
end
|
53
106
|
end
|
54
107
|
|
55
|
-
|
56
|
-
|
57
|
-
|
108
|
+
# Turns nested values' keys into delimiter separated paths
|
109
|
+
def squish(delimiter: '.')
|
110
|
+
sh = {}
|
111
|
+
path_nav(dup, nil, delimiter) { |k, v| sh[k] = v }
|
112
|
+
sh
|
58
113
|
end
|
59
114
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
"#{value}\n" +
|
68
|
-
"\t" * level +
|
69
|
-
(array ? '' : "</#{k}>\n")
|
70
|
-
end.join.split("\n").reject{ |r| r.strip == '' }.join("\n")
|
115
|
+
# Expands keys in a hash using a delimiter. Opposite of squish.
|
116
|
+
def expand
|
117
|
+
{}.to_tree_hash.tap do |hash|
|
118
|
+
each do |k, v|
|
119
|
+
hash.bridge(k => v)
|
120
|
+
end
|
121
|
+
end.value
|
71
122
|
end
|
72
123
|
|
124
|
+
# Returns a version of the hash not including the specified keys
|
125
|
+
def only(*args)
|
126
|
+
select { |k, _v| args.include?(k) }
|
127
|
+
end
|
128
|
+
|
129
|
+
# Returns a version of the hash with the specified keys removed.
|
130
|
+
def except(*args)
|
131
|
+
reject { |k, _v| args.include?(k) }
|
132
|
+
end
|
133
|
+
|
134
|
+
# Convert this hash into a TreeHash object.
|
135
|
+
def to_tree_hash
|
136
|
+
TreeHash.new(self)
|
137
|
+
end
|
138
|
+
|
139
|
+
# Run a map iterator over the values in the hash without changing the keys.
|
140
|
+
def vmap
|
141
|
+
return map unless block_given?
|
142
|
+
map { |k, v| [k, yield(v)] }.to_h
|
143
|
+
end
|
144
|
+
|
145
|
+
# Run a map iterator over the keys in the hash without changing the values.
|
146
|
+
def kmap
|
147
|
+
return map unless block_given?
|
148
|
+
map { |k, v| [yield(k), v] }.to_h
|
149
|
+
end
|
150
|
+
|
151
|
+
# Map for hash that automatically converts the yield block to a hash.
|
152
|
+
# Each yield must produce an array with exactly two elements.
|
153
|
+
def hmap
|
154
|
+
return map unless block_given?
|
155
|
+
map { |k, v| yield(k, v) }.to_h
|
156
|
+
end
|
73
157
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# Similar to Ruby's OpenStruct but uses hash as its parent class providing
|
2
|
+
# all of the typical Hash methods. Useful for storing settings on objects.
|
3
|
+
module BBLib
|
4
|
+
class HashStruct < Hash
|
5
|
+
|
6
|
+
protected
|
7
|
+
|
8
|
+
def method_missing(method, *args, &block)
|
9
|
+
if args.empty?
|
10
|
+
define_singleton_method(method) do
|
11
|
+
self[method]
|
12
|
+
end
|
13
|
+
self[method]
|
14
|
+
elsif method.to_s.end_with?('=')
|
15
|
+
define_singleton_method(method) do |arg|
|
16
|
+
self[method[0..-2].to_sym] = arg
|
17
|
+
end
|
18
|
+
self[method[0..-2].to_sym] = args.first
|
19
|
+
else
|
20
|
+
super
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,364 @@
|
|
1
|
+
class TreeHash
|
2
|
+
attr_reader :node_class, :parent, :children
|
3
|
+
|
4
|
+
def initialize(object = {}, parent = nil)
|
5
|
+
@children = {}
|
6
|
+
@parent = parent
|
7
|
+
object = object.value if object.is_a?(TreeHash)
|
8
|
+
replace_with(object)
|
9
|
+
end
|
10
|
+
|
11
|
+
def replace_with(object)
|
12
|
+
@node_class = object.class
|
13
|
+
build_from(object)
|
14
|
+
end
|
15
|
+
|
16
|
+
def root?
|
17
|
+
@parent.nil?
|
18
|
+
end
|
19
|
+
|
20
|
+
def child?
|
21
|
+
!root?
|
22
|
+
end
|
23
|
+
|
24
|
+
def child(key, symbol_sensitive = false)
|
25
|
+
case [node_class]
|
26
|
+
when [Hash]
|
27
|
+
children[key] || (symbol_sensitive ? nil : children[key.to_s.to_sym])
|
28
|
+
when [Array]
|
29
|
+
children[key.to_i]
|
30
|
+
else
|
31
|
+
nil
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def child_exists?(key, symbol_sensitive = false)
|
36
|
+
!case [node_class]
|
37
|
+
when [Hash]
|
38
|
+
children[key] || (!symbol_sensitive && children[key.to_s.to_sym])
|
39
|
+
when [Array]
|
40
|
+
[0...children.size] === key.to_i
|
41
|
+
else
|
42
|
+
false
|
43
|
+
end.nil?
|
44
|
+
end
|
45
|
+
|
46
|
+
def children?
|
47
|
+
(node_class == Hash || node_class == Array) && !children.empty?
|
48
|
+
end
|
49
|
+
|
50
|
+
def descendants
|
51
|
+
return [] unless children?
|
52
|
+
desc = []
|
53
|
+
children.each do |key, child|
|
54
|
+
desc << child
|
55
|
+
desc += child.descendants if child.children?
|
56
|
+
end
|
57
|
+
desc
|
58
|
+
end
|
59
|
+
|
60
|
+
def [](key)
|
61
|
+
children[key]
|
62
|
+
end
|
63
|
+
|
64
|
+
def []=(key, value)
|
65
|
+
add_child(key, value)
|
66
|
+
end
|
67
|
+
|
68
|
+
def find(path)
|
69
|
+
path = HashPath.new(*path) unless path.is_a?(HashPath)
|
70
|
+
matches = [self]
|
71
|
+
path.parts.each do |part|
|
72
|
+
break if matches.empty?
|
73
|
+
matches = matches.flat_map do |match|
|
74
|
+
part.matches(match)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
matches
|
78
|
+
end
|
79
|
+
|
80
|
+
def find_multi(*paths)
|
81
|
+
paths.map do |path|
|
82
|
+
find(path)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def find_join(*paths)
|
87
|
+
results = find_multi(*paths)
|
88
|
+
(0..(results.max_by(&:size).size - 1)).map do |index|
|
89
|
+
results.map do |result|
|
90
|
+
result[index]
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def find_join_hash(key_path, val_path)
|
96
|
+
find_join(key_path, val_path).to_h
|
97
|
+
end
|
98
|
+
|
99
|
+
def set(paths)
|
100
|
+
paths.each do |path, value|
|
101
|
+
find(path).each { |child| child.replace_with(value) }
|
102
|
+
end
|
103
|
+
self
|
104
|
+
end
|
105
|
+
|
106
|
+
# Generate a path using a dot (.) delimited path
|
107
|
+
# e.g. bridge('cats.jackson' => :my_value)
|
108
|
+
# This modifies the underlying container in place
|
109
|
+
def bridge(paths)
|
110
|
+
paths = paths.map { |a| [a, nil] }.to_h if paths.is_a?(Array)
|
111
|
+
paths.each do |path, value|
|
112
|
+
parts = path.to_s.split(/(?<=[^\\])\./)
|
113
|
+
node = self
|
114
|
+
next_part = false
|
115
|
+
until next_part.nil?
|
116
|
+
part = next_part || process_bridge_part(parts.shift)
|
117
|
+
next_part = process_bridge_part(parts.shift)
|
118
|
+
if node.child_exists?(part)
|
119
|
+
if next_part.is_a?(Integer)
|
120
|
+
next_next = process_bridge_part(parts.first)
|
121
|
+
if next_next.is_a?(Integer)
|
122
|
+
node[part][next_part] = [] unless node[part][next_part] && node[part][next_part].node_class == Array
|
123
|
+
else
|
124
|
+
node[part][next_part] = {} unless node[part][next_part] && node[part][next_part].node_class == Hash
|
125
|
+
end
|
126
|
+
end
|
127
|
+
next_part.nil? ? node[part] = value : node = node.child(part)
|
128
|
+
else
|
129
|
+
if next_part.nil?
|
130
|
+
node[part] = value
|
131
|
+
else
|
132
|
+
node[part] = next_part.is_a?(Integer) ? Array.new(next_part) : {}
|
133
|
+
end
|
134
|
+
node[part][next_part] = process_bridge_part(parts.first).is_a?(Integer) ? [] : {} if next_part.is_a?(Integer)
|
135
|
+
node = node[part]
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
self
|
140
|
+
end
|
141
|
+
|
142
|
+
def copy(paths)
|
143
|
+
paths.each do |from, to|
|
144
|
+
value = find(from).first
|
145
|
+
bridge(to => value)
|
146
|
+
end
|
147
|
+
self
|
148
|
+
end
|
149
|
+
|
150
|
+
def copy_all(paths)
|
151
|
+
paths.each do |from, to|
|
152
|
+
value = find(from)
|
153
|
+
bridge(to => value)
|
154
|
+
end
|
155
|
+
self
|
156
|
+
end
|
157
|
+
|
158
|
+
def move(paths)
|
159
|
+
paths.each do |from, to|
|
160
|
+
values = find(from)
|
161
|
+
next if values.empty?
|
162
|
+
value = values.first
|
163
|
+
bridge(to => value)
|
164
|
+
value.kill if value
|
165
|
+
end
|
166
|
+
self
|
167
|
+
end
|
168
|
+
|
169
|
+
def move_all(paths)
|
170
|
+
paths.each do |from, to|
|
171
|
+
value = find(from)
|
172
|
+
bridge(to => value)
|
173
|
+
value.map(&:kill)
|
174
|
+
end
|
175
|
+
self
|
176
|
+
end
|
177
|
+
|
178
|
+
def copy_to(hash, *paths)
|
179
|
+
hash = TreeHash.new(hash) unless hash.is_a?(TreeHash)
|
180
|
+
paths.each do |path|
|
181
|
+
hash.bridge(path => find(path).first)
|
182
|
+
end
|
183
|
+
hash
|
184
|
+
end
|
185
|
+
|
186
|
+
def move_to(hash, *paths)
|
187
|
+
hash = TreeHash.new(hash) unless hash.is_a?(TreeHash)
|
188
|
+
paths.each do |path|
|
189
|
+
value = find(path).first
|
190
|
+
hash.bridge(path => value)
|
191
|
+
value.kill if value
|
192
|
+
end
|
193
|
+
hash
|
194
|
+
end
|
195
|
+
|
196
|
+
def to_tree_hash
|
197
|
+
self
|
198
|
+
end
|
199
|
+
|
200
|
+
def process(processor)
|
201
|
+
|
202
|
+
end
|
203
|
+
|
204
|
+
def size
|
205
|
+
@children.respond_to?(:size) ? @children.size : 1
|
206
|
+
end
|
207
|
+
|
208
|
+
def paths
|
209
|
+
case [node_class]
|
210
|
+
when [Array], [Hash]
|
211
|
+
value.squish.keys
|
212
|
+
else
|
213
|
+
[]
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def ancestors
|
218
|
+
return [] if root?
|
219
|
+
return [parent] if parent.root?
|
220
|
+
parent.ancestors + [parent]
|
221
|
+
end
|
222
|
+
|
223
|
+
def absolute_path
|
224
|
+
anc = ancestors[1..-1]
|
225
|
+
(anc.nil? ? [] : anc.map(&:path) + [path]).join('.')
|
226
|
+
end
|
227
|
+
|
228
|
+
def inspect
|
229
|
+
value
|
230
|
+
end
|
231
|
+
|
232
|
+
def to_s
|
233
|
+
value.to_s
|
234
|
+
end
|
235
|
+
|
236
|
+
def siblings
|
237
|
+
parent.children.values.map { |c| c == self ? nil : c }.compact
|
238
|
+
end
|
239
|
+
|
240
|
+
def index
|
241
|
+
parent.children.values.index(self)
|
242
|
+
end
|
243
|
+
|
244
|
+
def delete(*paths)
|
245
|
+
paths.flat_map do |path|
|
246
|
+
find(path).map do |child|
|
247
|
+
if child.root?
|
248
|
+
delete_child(path)
|
249
|
+
else
|
250
|
+
child.parent.delete_child(child.key)
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
def kill
|
257
|
+
root.delete(absolute_path)
|
258
|
+
end
|
259
|
+
|
260
|
+
def value
|
261
|
+
case [node_class]
|
262
|
+
when [Hash]
|
263
|
+
children.map { |k, v| [k, v.value] }.to_h
|
264
|
+
when [Array]
|
265
|
+
children.values.map(&:value)
|
266
|
+
else
|
267
|
+
children
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
def key
|
272
|
+
return nil if root?
|
273
|
+
case [parent.node_class]
|
274
|
+
when [Hash]
|
275
|
+
parent.keys[index]
|
276
|
+
when [Array]
|
277
|
+
index
|
278
|
+
else
|
279
|
+
nil
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
def path
|
284
|
+
parent.node_class == Array ? "[#{key}]" : key.to_s.gsub('.', '\\.')
|
285
|
+
end
|
286
|
+
|
287
|
+
def absolute_paths
|
288
|
+
root.paths
|
289
|
+
end
|
290
|
+
|
291
|
+
def leaf_children
|
292
|
+
return self unless children?
|
293
|
+
children.map do |k, v|
|
294
|
+
v.children? ? v.leaf_children : v
|
295
|
+
end.flatten
|
296
|
+
end
|
297
|
+
|
298
|
+
def keys
|
299
|
+
return [] unless @children.respond_to?(:keys)
|
300
|
+
@children.keys
|
301
|
+
end
|
302
|
+
|
303
|
+
def following_siblings(limit = 0)
|
304
|
+
siblings[(index + 1)..-(limit.to_i + 1)]
|
305
|
+
end
|
306
|
+
|
307
|
+
def preceeding_siblings(limit = 0)
|
308
|
+
limit = limit.to_i
|
309
|
+
siblings[(limit.zero? ? limit : (index - limit))..(index - 1)]
|
310
|
+
end
|
311
|
+
|
312
|
+
def sibling(offset)
|
313
|
+
siblings[index + offset.to_i]
|
314
|
+
end
|
315
|
+
|
316
|
+
def next_sibling
|
317
|
+
sibling(1)
|
318
|
+
end
|
319
|
+
|
320
|
+
def previous_sibling
|
321
|
+
sibling(-1)
|
322
|
+
end
|
323
|
+
|
324
|
+
def root
|
325
|
+
return self if root?
|
326
|
+
ancestors.find(&:root?)
|
327
|
+
end
|
328
|
+
|
329
|
+
def delete_child(key, symbol_sensitive = false)
|
330
|
+
case [node_class]
|
331
|
+
when [Hash]
|
332
|
+
child = symbol_sensitive ? nil : children.delete(key.to_s.to_sym)
|
333
|
+
child = children.delete(key) unless child
|
334
|
+
when [Array]
|
335
|
+
children.delete(key.to_i)
|
336
|
+
else
|
337
|
+
nil
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
341
|
+
protected
|
342
|
+
|
343
|
+
def build_from(object)
|
344
|
+
@children = {}
|
345
|
+
case object
|
346
|
+
when Hash
|
347
|
+
object.each { |k, v| @children[k] = TreeHash.new(v, self) }
|
348
|
+
when Array
|
349
|
+
object.each_with_index { |a, i| @children[i] = TreeHash.new(a, self) }
|
350
|
+
else
|
351
|
+
@children = object
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
def add_child(key, child)
|
356
|
+
@children[key] = TreeHash.new(child, self)
|
357
|
+
end
|
358
|
+
|
359
|
+
def process_bridge_part(part)
|
360
|
+
return unless part
|
361
|
+
part =~ /^\[\d+\]$/ ? part.uncapsulate('[').to_i : part.to_sym
|
362
|
+
end
|
363
|
+
|
364
|
+
end
|