bblib 0.3.0 → 0.4.1

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.
Files changed (62) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +11 -10
  3. data/.rspec +2 -2
  4. data/.travis.yml +4 -4
  5. data/CODE_OF_CONDUCT.md +13 -13
  6. data/Gemfile +4 -4
  7. data/LICENSE.txt +21 -21
  8. data/README.md +247 -757
  9. data/Rakefile +6 -6
  10. data/bblib.gemspec +34 -34
  11. data/bin/console +14 -14
  12. data/bin/setup +7 -7
  13. data/lib/array/bbarray.rb +71 -29
  14. data/lib/bblib.rb +12 -12
  15. data/lib/bblib/version.rb +3 -3
  16. data/lib/class/effortless.rb +23 -0
  17. data/lib/error/abstract.rb +3 -0
  18. data/lib/file/bbfile.rb +93 -52
  19. data/lib/hash/bbhash.rb +130 -46
  20. data/lib/hash/hash_struct.rb +24 -0
  21. data/lib/hash/tree_hash.rb +364 -0
  22. data/lib/hash_path/hash_path.rb +210 -0
  23. data/lib/hash_path/part.rb +83 -0
  24. data/lib/hash_path/path_hash.rb +84 -0
  25. data/lib/hash_path/proc.rb +93 -0
  26. data/lib/hash_path/processors.rb +239 -0
  27. data/lib/html/bbhtml.rb +2 -0
  28. data/lib/html/builder.rb +34 -0
  29. data/lib/html/tag.rb +49 -0
  30. data/lib/logging/bblogging.rb +42 -0
  31. data/lib/mixins/attrs.rb +422 -0
  32. data/lib/mixins/bbmixins.rb +7 -0
  33. data/lib/mixins/bridge.rb +17 -0
  34. data/lib/mixins/family_tree.rb +41 -0
  35. data/lib/mixins/hooks.rb +139 -0
  36. data/lib/mixins/logger.rb +31 -0
  37. data/lib/mixins/serializer.rb +71 -0
  38. data/lib/mixins/simple_init.rb +160 -0
  39. data/lib/number/bbnumber.rb +15 -7
  40. data/lib/object/bbobject.rb +46 -19
  41. data/lib/opal/bbopal.rb +0 -4
  42. data/lib/os/bbos.rb +24 -16
  43. data/lib/os/bbsys.rb +60 -43
  44. data/lib/string/bbstring.rb +165 -66
  45. data/lib/string/cases.rb +37 -29
  46. data/lib/string/fuzzy_matcher.rb +48 -50
  47. data/lib/string/matching.rb +43 -30
  48. data/lib/string/pluralization.rb +156 -0
  49. data/lib/string/regexp.rb +45 -0
  50. data/lib/string/roman.rb +17 -30
  51. data/lib/system/bbsystem.rb +42 -0
  52. data/lib/time/bbtime.rb +79 -58
  53. data/lib/time/cron.rb +174 -132
  54. data/lib/time/task_timer.rb +86 -70
  55. metadata +27 -10
  56. data/lib/gem/bbgem.rb +0 -28
  57. data/lib/hash/hash_path.rb +0 -344
  58. data/lib/hash/hash_path_proc.rb +0 -256
  59. data/lib/hash/path_hash.rb +0 -81
  60. data/lib/object/attr.rb +0 -182
  61. data/lib/object/hooks.rb +0 -69
  62. data/lib/object/lazy_class.rb +0 -73
@@ -1,107 +1,123 @@
1
1
  module BBLib
2
-
3
- class TaskTimer < LazyClass
4
- attr_hash :tasks, default: Hash.new
2
+ # Simple timer that can track tasks based on time. Also provides aggregated metrics
3
+ # and history for each task run. Generally useful for benchmarking or logging.
4
+ #
5
+ # @author Brandon Black
6
+ # @attr [Hash] tasks The information on all running tasks and history of all tasks up to the retention.
7
+ # @attr [Integer] retention The number of runs to collect per task before truncation.
8
+ class TaskTimer
9
+ include Effortless
10
+ attr_hash :tasks, default: {}, serialize: false
5
11
  attr_int_between -1, nil, :retention, default: 100
6
12
 
7
- def time task = :default, type = :current
8
- return nil unless @tasks.keys.include? task
9
- numbers = @tasks[task][:history].map{ |v| v[:time] }
13
+ # Returns an aggregated metric for a given type.
14
+ #
15
+ # @param [Symbol] task The key value of the task to retrieve
16
+ # @param [Symbol] type The metric to return.
17
+ # Options are :avg, :min, :max, :first, :last, :sum, :all and :count.
18
+ # @return [Float, Integer, Array] Returns either the aggregation (Numeric) or an Array in the case of :all.
19
+ def time(task = :default, type = :current)
20
+ return nil unless tasks.keys.include?(task)
21
+ numbers = tasks[task][:history].map { |v| v[:time] }
10
22
  case type
11
23
  when :current
12
- return nil unless @tasks[task][:current]
13
- return Time.now.to_f - @tasks[task][:current]
14
- when :min
15
- return numbers.min
16
- when :max
17
- return numbers.max
24
+ return nil unless tasks[task][:current]
25
+ Time.now.to_f - tasks[task][:current]
26
+ when :min, :max, :first, :last
27
+ numbers.send(type)
18
28
  when :avg
19
- return numbers.inject{ |sum, n| sum + n }.to_f / numbers.size
29
+ numbers.size.zero? ? nil : numbers.inject { |sum, n| sum + n }.to_f / numbers.size
20
30
  when :sum
21
- return numbers.inject{ |sum, n| sum + n }
31
+ numbers.inject { |sum, n| sum + n }
22
32
  when :all
23
- return numbers
24
- when :first
25
- return numbers.first
26
- when :last
27
- return numbers.last
33
+ numbers
28
34
  when :count
29
- return numbers.size
35
+ numbers.size
30
36
  end
31
37
  end
32
38
 
33
- def clear task
34
- return nil unless @tasks.keys.include?(task)
39
+ # Removes all history for a given task
40
+ #
41
+ # @param [Symbol] task The name of the task to clear history from.
42
+ # @return [NilClass] Returns nil
43
+ def clear(task = :default)
44
+ return nil unless tasks.keys.include?(task)
35
45
  stop task
36
- @tasks[task][:history].clear
46
+ tasks[task][:history].clear
37
47
  end
38
48
 
39
- def start task = :default
40
- if !@tasks.keys.include?(task) then @tasks[task] = {history: [], current: nil} end
41
- if @tasks[task][:current] then stop task end
42
- @tasks[task][:current] = Time.now.to_f
43
- return 0
49
+ # Start a new timer for the referenced task. If a timer is already running for that task it will be stopped first.
50
+ #
51
+ # @param [Symbol] task The name of the task to start.
52
+ # @return [Integer] Returns 0
53
+ def start(task = :default)
54
+ tasks[task] = { history: [], current: nil } unless tasks.keys.include?(task)
55
+ stop task if tasks[task][:current]
56
+ tasks[task][:current] = Time.now.to_f
57
+ 0
44
58
  end
45
59
 
46
- def stop task = :default
47
- return nil unless @tasks.keys.include?(task) && active?(task)
48
- time_taken = Time.now.to_f - @tasks[task][:current].to_f
49
- @tasks[task][:history] << {start: @tasks[task][:current], stop: Time.now.to_f, time: time_taken}
50
- @tasks[task][:current] = nil
51
- if @retention && @tasks[task][:history].size > @retention then @tasks[task][:history].shift end
60
+ # Stop the referenced timer.
61
+ #
62
+ # @param [Symbol] task The name of the task to stop.
63
+ # @return [Float, NilClass] The amount of time the task had been running or nil if no matching task was found.
64
+ def stop(task = :default)
65
+ return nil unless tasks.keys.include?(task) && active?(task)
66
+ time_taken = Time.now.to_f - tasks[task][:current].to_f
67
+ tasks[task][:history] << { start: tasks[task][:current], stop: Time.now.to_f, time: time_taken }
68
+ tasks[task][:current] = nil
69
+ if retention && tasks[task][:history].size > retention then tasks[task][:history].shift end
52
70
  time_taken
53
71
  end
54
72
 
55
- def restart task = :default
73
+ def restart(task = :default)
56
74
  start(task) unless stop(task).nil?
57
75
  end
58
76
 
59
- def active? task
60
- return false unless @tasks.keys.include? task
61
- !@tasks[task][:current].nil?
77
+ def active?(task = :default)
78
+ return false unless tasks.keys.include?(task)
79
+ !tasks[task][:current].nil?
62
80
  end
63
81
 
64
- def stats task, pretty: false
65
- return nil unless @tasks.include?(task)
66
- stats = "#{task}" + "\n" + '-'*30 + "\n"
67
- TIMER_TYPES.each do |k,v|
82
+ def stats(task = :default, pretty: false)
83
+ return nil unless tasks.include?(task)
84
+ TIMER_TYPES.map do |k, _v|
68
85
  next if STATS_IGNORE.include?(k)
69
- stats+= k.to_s.capitalize.ljust(10) + "#{self.send(k, task, pretty:pretty)}\n"
70
- end
71
- stats
86
+ [k, send(k, task, pretty: pretty)]
87
+ end.compact.to_h
72
88
  end
73
89
 
74
- def method_missing *args, **named
75
- temp = args.first.to_sym
76
- pretty = named.delete :pretty
77
- type, task = TIMER_TYPES.keys.find{ |k| k == temp || TIMER_TYPES[k].include?(temp) }, args[1] ||= :default
90
+ def method_missing(*args, **named)
91
+ temp = args.first.to_sym
92
+ type = TIMER_TYPES.keys.find { |k| k == temp || TIMER_TYPES[k].include?(temp) }
78
93
  return super unless type
79
- t = time task, type
80
- pretty && type != :count && t ? (t.is_a?(Array) ? t.map{|m| m.to_duration} : t.to_duration) : t
94
+ t = time(args[1] || :default, type)
95
+ return t if type == :count || !named[:pretty]
96
+ t.is_a?(Array) ? t.map(&:to_duration) : t.to_duration
81
97
  end
82
98
 
83
- private
99
+ def respond_to_missing?(method, include_private = false)
100
+ TIMER_TYPES.keys.find { |k| k == method || TIMER_TYPES[k].include?(method) } || super
101
+ end
84
102
 
85
- STATS_IGNORE = [:current, :all]
103
+ TIMER_TYPES = {
104
+ current: [],
105
+ count: [:total],
106
+ first: [:initial],
107
+ last: [:latest],
108
+ min: [:minimum, :smallest],
109
+ max: [:maximum, :largest],
110
+ avg: [:average, :av],
111
+ sum: [],
112
+ all: [:times]
113
+ }.freeze
86
114
 
87
- TIMER_TYPES = {
88
- current: [],
89
- count: [:total],
90
- first: [:initial],
91
- last: [:latest],
92
- min: [:minimum, :smallest],
93
- max: [:maximum, :largest],
94
- avg: [:average, :av],
95
- sum: [],
96
- all: [:times]
97
- }
115
+ protected
98
116
 
99
- def lazy_init *args
100
- if args.first.is_a?(Symbol)
101
- start(args.first)
102
- end
103
- end
117
+ STATS_IGNORE = [:all].freeze
104
118
 
119
+ def simple_init(*args)
120
+ start(args.first) if args.first.is_a?(Symbol)
121
+ end
105
122
  end
106
-
107
123
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bblib
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brandon Black
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-09-18 00:00:00.000000000 Z
11
+ date: 2018-03-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -87,17 +87,31 @@ files:
87
87
  - lib/array/bbarray.rb
88
88
  - lib/bblib.rb
89
89
  - lib/bblib/version.rb
90
+ - lib/class/effortless.rb
91
+ - lib/error/abstract.rb
90
92
  - lib/file/bbfile.rb
91
- - lib/gem/bbgem.rb
92
93
  - lib/hash/bbhash.rb
93
- - lib/hash/hash_path.rb
94
- - lib/hash/hash_path_proc.rb
95
- - lib/hash/path_hash.rb
94
+ - lib/hash/hash_struct.rb
95
+ - lib/hash/tree_hash.rb
96
+ - lib/hash_path/hash_path.rb
97
+ - lib/hash_path/part.rb
98
+ - lib/hash_path/path_hash.rb
99
+ - lib/hash_path/proc.rb
100
+ - lib/hash_path/processors.rb
101
+ - lib/html/bbhtml.rb
102
+ - lib/html/builder.rb
103
+ - lib/html/tag.rb
104
+ - lib/logging/bblogging.rb
105
+ - lib/mixins/attrs.rb
106
+ - lib/mixins/bbmixins.rb
107
+ - lib/mixins/bridge.rb
108
+ - lib/mixins/family_tree.rb
109
+ - lib/mixins/hooks.rb
110
+ - lib/mixins/logger.rb
111
+ - lib/mixins/serializer.rb
112
+ - lib/mixins/simple_init.rb
96
113
  - lib/number/bbnumber.rb
97
- - lib/object/attr.rb
98
114
  - lib/object/bbobject.rb
99
- - lib/object/hooks.rb
100
- - lib/object/lazy_class.rb
101
115
  - lib/opal/bbopal.rb
102
116
  - lib/os/bbos.rb
103
117
  - lib/os/bbsys.rb
@@ -105,7 +119,10 @@ files:
105
119
  - lib/string/cases.rb
106
120
  - lib/string/fuzzy_matcher.rb
107
121
  - lib/string/matching.rb
122
+ - lib/string/pluralization.rb
123
+ - lib/string/regexp.rb
108
124
  - lib/string/roman.rb
125
+ - lib/system/bbsystem.rb
109
126
  - lib/time/bbtime.rb
110
127
  - lib/time/cron.rb
111
128
  - lib/time/task_timer.rb
@@ -129,7 +146,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
129
146
  version: '0'
130
147
  requirements: []
131
148
  rubyforge_project:
132
- rubygems_version: 2.4.5.1
149
+ rubygems_version: 2.7.4
133
150
  signing_key:
134
151
  specification_version: 4
135
152
  summary: A library containing many reusable, basic functions.
@@ -1,28 +0,0 @@
1
-
2
- if defined? Gem
3
-
4
- module BBLib
5
-
6
- def self.gem_list
7
- Gem::Specification.map(&:name).uniq
8
- end
9
-
10
- def self.gem_installed? name
11
- BBLib.gem_list.include? name
12
- end
13
-
14
- end
15
-
16
- # Convenience method that will try to download and install a gem before requiring it
17
- # only if the gem is not already installed
18
- def require_gem gem, name = nil
19
- name = gem if name.nil?
20
- if !BBLib.gem_installed? name
21
- return false unless Gem.install gem
22
- end
23
- require name
24
- end
25
-
26
-
27
-
28
- end
@@ -1,344 +0,0 @@
1
- require_relative 'hash_path_proc'
2
-
3
- module BBLib
4
-
5
- def self.hash_path hash, *paths, multi_path: false, multi_join: false
6
- if multi_path || multi_join
7
- results = paths.map{ |path| BBLib.hash_path(hash, path)}
8
- results = (0..results.max_by{ |m| m.size }.size - 1).map{ |i| results.map{ |r| r[i] } } if multi_join
9
- return results
10
- end
11
- path = split_path(*paths)
12
- matches, recursive = [hash], false
13
- until path.empty? || matches.empty?
14
- current = path.shift.to_s
15
- current = current[0..-2] + '.' + path.shift.to_s if current.end_with?("\\")
16
- if current.strip == ''
17
- recursive = true
18
- next
19
- end
20
- key, formula = BBLib.analyze_hash_path(current)
21
- matches = matches.map do |match|
22
- if recursive
23
- match.dive(key.to_sym, key)
24
- elsif key == '*'
25
- match.is_a?(Hash) ? match.values : (match.is_a?(Array) ? match : nil)
26
- elsif match.is_a?(Hash)
27
- key.is_a?(Regexp) ? match.map{ |k,v| k.to_s =~ key ? v : nil } : [(BBLib::in_opal? ? nil : match[key.to_sym]), match[key]]
28
- elsif match.is_a?(Array) && (key.is_a?(Fixnum) || key.is_a?(Range))
29
- key.is_a?(Range) ? match[key] : [match[key]]
30
- else
31
- nil
32
- end
33
- end.flatten(1).reject{ |m| m.nil? }
34
- matches = BBLib.analyze_hash_path_formula(formula, matches)
35
- recursive = false
36
- end
37
- matches
38
- end
39
-
40
- def self.hash_path_keys hash
41
- hash.squish.keys
42
- end
43
-
44
- def self.hash_path_key_for hash, value
45
- hash.squish.find_all{ |k,v| value.is_a?(Regexp) ? v =~ value : v == value }.to_h.keys
46
- end
47
-
48
- def self.hash_path_set hash, *paths, symbols: true, bridge: true
49
- paths = paths.find{ |a| a.is_a?(Hash) }
50
- paths.each do |path, value|
51
- parts = split_path(path)
52
- matches = BBLib.hash_path(hash, *parts[0..-2])
53
- matches.each do |match|
54
- key, formula = BBLib.analyze_hash_path(parts.last)
55
- key = match.include?(key.to_sym) || (symbols && !match.include?(key) ) ? key.to_sym : key
56
- if match.is_a?(Hash)
57
- match[key] = value
58
- elsif match.is_a?(Array) && key.is_a?(Fixnum)
59
- match[key] = value
60
- end
61
- end
62
- hash.bridge(path, value:value, symbols:symbols) if matches.empty? && bridge
63
- end
64
- hash
65
- end
66
-
67
- def self.hash_path_copy hash, *paths, symbols: true, array: false, overwrite: true, skip_nil: true
68
- paths = paths.find{ |a| a.is_a?(Hash) }
69
- paths.each do |from, to|
70
- value = BBLib.hash_path(hash, from)
71
- value = value.first unless array
72
- hash.bridge(to, value: value, symbols:symbols, overwrite: overwrite) unless value.nil? && skip_nil
73
- end
74
- hash
75
- end
76
-
77
- def self.hash_path_copy_to from, to, *paths, symbols: true, array: false, overwrite: true, skip_nil: true
78
- paths = paths.find{ |a| a.is_a?(Hash) }
79
- paths.each do |p_from, p_to|
80
- value = BBLib.hash_path(from, p_from)
81
- value = value.first unless array
82
- to.bridge(p_to, value:value, symbols:symbols, overwrite: overwrite) unless value.nil? && skip_nil
83
- end
84
- to
85
- end
86
-
87
- def self.hash_path_delete hash, *paths
88
- deleted = Array.new
89
- paths.each do |path|
90
- parts = split_path(path)
91
- BBLib.hash_path(hash, *parts[0..-2]).each do |match|
92
- key, formula = BBLib.analyze_hash_path(parts.last)
93
- if match.is_a?(Hash)
94
- deleted << match.delete(key) << match.delete(key.to_sym)
95
- elsif match.is_a?(Array) && key.is_a?(Fixnum)
96
- deleted << match.delete_at(key)
97
- end
98
- end
99
- end
100
- deleted.flatten.reject{ |v| v.nil? }
101
- end
102
-
103
- def self.hash_path_move hash, *paths
104
- BBLib.hash_path_copy hash, *paths
105
- BBLib.hash_path_delete hash, *paths.find{|pt| pt.is_a?(Hash) }.keys
106
- hash
107
- end
108
-
109
- def self.hash_path_move_to from, to, *paths
110
- BBLib.hash_path_copy_to from, to, *paths
111
- BBLib.hash_path_delete from, *paths.find{|pt| pt.is_a?(Hash) }.keys
112
- to
113
- end
114
-
115
- protected
116
-
117
- def self.split_path *paths
118
- paths.map{|pth| pth.to_s.gsub('..', '. .').scan(/(?:[\(|\[].*?[\)|\]]|[^\.])+/)}.flatten
119
- end
120
-
121
- def self.analyze_hash_path path
122
- return '', nil if path == '' || path.nil?
123
- key = path.scan(/^.*^[^\(]*/i).first.to_s
124
- if key =~ /^\[\d+\]$/
125
- key = key[1..-2].to_i
126
- elsif key =~ /\[\-?\d+\.\s?\.{1,2}\-?\d+\]/
127
- bounds = key.scan(/\-?\d+/).map{|x| x.to_i}
128
- key = key =~ /\.\s?\.{2}/ ? (bounds.first...bounds.last) : (bounds.first..bounds.last)
129
- elsif key =~ /\/.*[\/|\/i]$/
130
- if key.end_with?('i')
131
- key = /#{key[1..-3]}/i
132
- else
133
- key = /#{key[1..-2]}/
134
- end
135
- end
136
- formula = path.scan(/\(.*\)/).first
137
- return key, formula
138
- end
139
-
140
- def self.analyze_hash_path_formula formula, hashes
141
- return hashes unless formula
142
- hashes.map do |p|
143
- begin
144
- if eval(p.is_a?(Hash) ? formula.gsub('$', "(#{p})") : formula.gsub('$', p.to_s))
145
- p
146
- else
147
- nil
148
- end
149
- rescue StandardError, Exception => e
150
- # Do nothing, the formula failed and we reject the value as a false
151
- end
152
- end.reject{ |x| x.nil? }
153
- end
154
-
155
- def self.hash_path_nav obj, path = '', delimiter = '.', &block
156
- case [obj.class]
157
- when [Hash]
158
- obj.each{ |k,v| hash_path_nav(v, "#{path.nil? ? k.to_s : [path, k].join(delimiter)}", delimiter, &block) }
159
- when [Array]
160
- index = 0
161
- obj.each{ |o| hash_path_nav(o, "#{path.nil? ? "[#{index}]" : [path, "[#{index}]" ].join(delimiter)}", delimiter, &block) ; index+=1 }
162
- else
163
- yield path, obj
164
- end
165
- end
166
-
167
- end
168
-
169
-
170
-
171
- class Hash
172
-
173
- def hash_path *path
174
- BBLib.hash_path self, *path
175
- end
176
-
177
- def hash_path_set *paths
178
- BBLib.hash_path_set self, *paths
179
- end
180
-
181
- def hash_path_copy *paths
182
- BBLib.hash_path_copy self, *paths
183
- end
184
-
185
- def hash_path_copy_to to, *paths
186
- BBLib.hash_path_copy_to self, to, *paths
187
- end
188
-
189
- def hash_path_delete *paths
190
- BBLib.hash_path_delete self, *paths
191
- end
192
-
193
- def hash_path_move *paths
194
- BBLib.hash_path_move self, *paths
195
- end
196
-
197
- def hash_path_move_to to, *paths
198
- BBLib.hash_path_move_to self, to, *paths
199
- end
200
-
201
- def hash_paths
202
- BBLib.hash_path_keys self
203
- end
204
-
205
- def hash_path_for value
206
- BBLib.hash_path_key_for self, value
207
- end
208
-
209
- alias_method :hpath, :hash_path
210
- alias_method :hpath_set, :hash_path_set
211
- alias_method :hpath_move, :hash_path_move
212
- alias_method :hpath_move_to, :hash_path_move_to
213
- alias_method :hpath_delete, :hash_path_delete
214
- alias_method :hpath_copy, :hash_path_copy
215
- alias_method :hpath_copy_to, :hash_path_copy_to
216
- alias_method :hpaths, :hash_paths
217
- alias_method :hpath_for, :hash_path_for
218
-
219
- # Returns all matching values with a specific key (or Array of keys) recursively within a Hash (including nested Arrays)
220
- def dive *keys
221
- matches = Array.new
222
- self.each do |k, v|
223
- if keys.any?{ |a| (a.is_a?(Regexp) ? a =~ k : a == k ) } then matches << v end
224
- if v.respond_to? :dive
225
- matches+= v.dive(*keys)
226
- end
227
- end
228
- matches
229
- end
230
-
231
- # Turns nested values' keys into delimiter separated paths
232
- def squish delimiter: '.'
233
- sh = Hash.new
234
- BBLib.hash_path_nav(self.dup, nil, delimiter){ |k, v| sh[k] = v }
235
- sh
236
- end
237
-
238
- # Expands keys in a hash using a delimiter. Opposite of squish.
239
- def expand **args
240
- eh = Hash.new
241
- self.dup.each do |k,v|
242
- eh.bridge k, args.merge({value:v})
243
- end
244
- return eh
245
- end
246
-
247
- # Add a hash path to a hash
248
- def bridge *path, value:nil, delimiter: '.', symbols: true, overwrite: false
249
- path = path.msplit(delimiter).flatten
250
- hash, part, bail, last = self, nil, false, nil
251
- while !path.empty? && !bail
252
- part = path.shift
253
- if part =~ /\A\[\d+\]\z/
254
- part = part[1..-2].to_i
255
- else
256
- part = part.to_sym if symbols
257
- end
258
- if (hash.is_a?(Hash) && hash.include?(part) || hash.is_a?(Array) && hash.size > part.to_i) && !overwrite
259
- bail = true if !hash[part].is_a?(Hash) && !hash[part].is_a?(Array)
260
- hash = hash[part] unless bail
261
- else
262
- hash[part] = path.first =~ /\A\[\d+\]\z/ ? Array.new : Hash.new
263
- hash = hash[part] unless bail || path.empty?
264
- end
265
- end
266
- hash[part] = value unless bail
267
- self
268
- end
269
-
270
- end
271
-
272
- class Array
273
-
274
- def hash_path *path
275
- BBLib.hash_path self, *path
276
- end
277
-
278
- def hash_path_set *paths
279
- BBLib.hash_path_set self, *paths
280
- end
281
-
282
- def hash_path_copy *paths
283
- BBLib.hash_path_copy self, *paths
284
- end
285
-
286
- def hash_path_copy_to to, *paths
287
- BBLib.hash_path_copy_to self, to, *paths
288
- end
289
-
290
- def hash_path_delete *paths
291
- BBLib.hash_path_delete self, *paths
292
- end
293
-
294
- def hash_path_move *paths
295
- BBLib.hash_path_move self, *paths
296
- end
297
-
298
- def hash_path_move_to to, *paths
299
- BBLib.hash_path_move_to self, to, *paths
300
- end
301
-
302
- def hash_paths
303
- BBLib.hash_path_keys self
304
- end
305
-
306
- def hash_path_for value
307
- BBLib.hash_path_key_for self, value
308
- end
309
-
310
- alias_method :hpath, :hash_path
311
- alias_method :hpath_set, :hash_path_set
312
- alias_method :hpath_move, :hash_path_move
313
- alias_method :hpath_move_to, :hash_path_move_to
314
- alias_method :hpath_delete, :hash_path_delete
315
- alias_method :hpath_copy, :hash_path_copy
316
- alias_method :hpath_copy_to, :hash_path_copy_to
317
- alias_method :hpaths, :hash_paths
318
- alias_method :hpath_for, :hash_path_for
319
-
320
- def dive *keys
321
- matches = []
322
- self.each do |i|
323
- matches+= i.dive(*keys) if i.respond_to?(:dive)
324
- end
325
- matches
326
- end
327
-
328
- # Turns nested values' keys into delimiter separated paths
329
- def squish delimiter: '.'
330
- sh = Hash.new
331
- BBLib.hash_path_nav(self.dup, nil, delimiter){ |k, v| sh[k] = v }
332
- sh
333
- end
334
-
335
- # Expands keys in a hash using a delimiter. Opposite of squish.
336
- def expand **args
337
- eh = Hash.new
338
- self.dup.each do |k,v|
339
- eh.bridge k, args.merge({value:v})
340
- end
341
- return eh
342
- end
343
-
344
- end