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.
- 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/time/task_timer.rb
CHANGED
@@ -1,107 +1,123 @@
|
|
1
1
|
module BBLib
|
2
|
-
|
3
|
-
|
4
|
-
|
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
|
-
|
8
|
-
|
9
|
-
|
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
|
13
|
-
|
14
|
-
when :min
|
15
|
-
|
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
|
-
|
29
|
+
numbers.size.zero? ? nil : numbers.inject { |sum, n| sum + n }.to_f / numbers.size
|
20
30
|
when :sum
|
21
|
-
|
31
|
+
numbers.inject { |sum, n| sum + n }
|
22
32
|
when :all
|
23
|
-
|
24
|
-
when :first
|
25
|
-
return numbers.first
|
26
|
-
when :last
|
27
|
-
return numbers.last
|
33
|
+
numbers
|
28
34
|
when :count
|
29
|
-
|
35
|
+
numbers.size
|
30
36
|
end
|
31
37
|
end
|
32
38
|
|
33
|
-
|
34
|
-
|
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
|
-
|
46
|
+
tasks[task][:history].clear
|
37
47
|
end
|
38
48
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
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
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
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
|
73
|
+
def restart(task = :default)
|
56
74
|
start(task) unless stop(task).nil?
|
57
75
|
end
|
58
76
|
|
59
|
-
def active?
|
60
|
-
return false unless
|
61
|
-
|
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
|
65
|
-
return nil unless
|
66
|
-
|
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
|
-
|
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
|
75
|
-
temp
|
76
|
-
|
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
|
80
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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.
|
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:
|
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/
|
94
|
-
- lib/hash/
|
95
|
-
- lib/
|
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
|
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.
|
data/lib/gem/bbgem.rb
DELETED
@@ -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
|
data/lib/hash/hash_path.rb
DELETED
@@ -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
|