bblib 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -1
- data/lib/array/bbarray.rb +15 -0
- data/lib/bblib.rb +5 -1
- data/lib/bblib/version.rb +1 -1
- data/lib/file/bbfile.rb +6 -5
- data/lib/hash/bbhash.rb +24 -4
- data/lib/hash/hash_path.rb +391 -0
- data/lib/hash/hash_path_proc.rb +163 -0
- data/lib/{math/bbmath.rb → number/bbnumber.rb} +0 -0
- data/lib/object/bbobject.rb +28 -0
- data/lib/string/bbstring.rb +36 -9
- data/lib/time/bbtime.rb +55 -19
- data/lib/time/task_timer.rb +101 -0
- metadata +9 -5
- data/lib/net/bbnet.rb +0 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 46227889fa2341b852f07f10dd28892a0770b759
|
4
|
+
data.tar.gz: f7e8f2dae0ba5148cb77d0ec7086c87806dce3c7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 45f56290f00c86f5ead9f13746fe76104d87698bfd8b99a89abc2fc1660b4dc4137fb0bb85d50ed9dc8bfcfb0e987b1bef83e41e40c9599eb2205b2809a0cc7a
|
7
|
+
data.tar.gz: 50f0e4937c9857b0f66c14b748a74fec84bd835da790e01c64774005e7d9f9f24a58e0e96f2204cfcaf65a690e1ae5ce0d3eec40c0b76e9a25623f1824f6fd60
|
data/.gitignore
CHANGED
@@ -0,0 +1,15 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
class Array
|
4
|
+
def msplit *delims, keep_empty: false
|
5
|
+
self.map{ |i| i.msplit(delims, keep_empty:keep_empty)}.flatten
|
6
|
+
end
|
7
|
+
|
8
|
+
def keys_to_sym clean: false
|
9
|
+
self.map{ |v| Hash === v || Array === v ? v.keys_to_sym(clean:clean) : v }
|
10
|
+
end
|
11
|
+
|
12
|
+
def keys_to_s clean: false
|
13
|
+
self.map{ |v| Hash === v || Array === v ? v.keys_to_s : v }
|
14
|
+
end
|
15
|
+
end
|
data/lib/bblib.rb
CHANGED
@@ -3,8 +3,12 @@ require_relative "string/bbstring"
|
|
3
3
|
require_relative "file/bbfile"
|
4
4
|
require_relative "time/bbtime"
|
5
5
|
require_relative "hash/bbhash"
|
6
|
-
require_relative "
|
6
|
+
require_relative "number/bbnumber"
|
7
|
+
require_relative "object/bbobject"
|
8
|
+
require_relative "array/bbarray"
|
7
9
|
require 'fileutils'
|
10
|
+
require 'net/http'
|
11
|
+
require 'uri'
|
8
12
|
|
9
13
|
module BBLib
|
10
14
|
|
data/lib/bblib/version.rb
CHANGED
data/lib/file/bbfile.rb
CHANGED
@@ -23,11 +23,12 @@ module BBLib
|
|
23
23
|
end
|
24
24
|
|
25
25
|
# Shorthand method to write a string to dist. By default the path is created if it doesn't exist.
|
26
|
-
|
26
|
+
# Set mode to w to truncate file or leave at a to append.
|
27
|
+
def self.string_to_file path, str, mkpath = true, mode: 'a'
|
27
28
|
if !Dir.exists?(path) && mkpath
|
28
29
|
FileUtils.mkpath File.dirname(path)
|
29
30
|
end
|
30
|
-
File.write(path, str.to_s)
|
31
|
+
File.write(path, str.to_s, mode:mode)
|
31
32
|
end
|
32
33
|
|
33
34
|
# A file size parser for strings. Extracts any known patterns for file sizes.
|
@@ -36,7 +37,7 @@ module BBLib
|
|
36
37
|
bytes = 0.0
|
37
38
|
FILE_SIZES.each do |k, v|
|
38
39
|
v[:exp].each do |e|
|
39
|
-
numbers = str.scan(/(?=\w|\D|\A)\d
|
40
|
+
numbers = str.scan(/(?=\w|\D|\A)\d*\.?\d+[[:space:]]*#{e}s?(?=\W|\d|\z)/i)
|
40
41
|
numbers.each{ |n| bytes+= n.to_f * v[:mult] }
|
41
42
|
end
|
42
43
|
end
|
@@ -64,8 +65,8 @@ class File
|
|
64
65
|
end
|
65
66
|
|
66
67
|
class String
|
67
|
-
def to_file path, mkpath = true
|
68
|
-
BBLib.string_to_file path, self, mkpath
|
68
|
+
def to_file path, mkpath = true, mode: 'a'
|
69
|
+
BBLib.string_to_file path, self, mkpath, mode:mode
|
69
70
|
end
|
70
71
|
|
71
72
|
def file_name with_extension = true
|
data/lib/hash/bbhash.rb
CHANGED
@@ -1,5 +1,9 @@
|
|
1
|
+
require_relative 'hash_path'
|
2
|
+
|
1
3
|
class Hash
|
2
4
|
|
5
|
+
|
6
|
+
|
3
7
|
# Merges with another hash but also merges all nested hashes and arrays/values.
|
4
8
|
# Based on method found @ http://stackoverflow.com/questions/9381553/ruby-merge-nested-hash
|
5
9
|
def deep_merge with, merge_arrays: true, overwrite_vals: true
|
@@ -13,12 +17,23 @@ class Hash
|
|
13
17
|
|
14
18
|
# Converts the keys of the hash as well as any nested hashes to symbols.
|
15
19
|
# Based on method found @ http://stackoverflow.com/questions/800122/best-way-to-convert-strings-to-symbols-in-hash
|
16
|
-
def keys_to_sym
|
17
|
-
self.inject({}){|memo,(k,v)| memo[k.to_sym] = (Hash === v ? v.keys_to_sym : v); memo}
|
20
|
+
def keys_to_sym clean: false
|
21
|
+
self.inject({}){|memo,(k,v)| memo[clean ? k.to_s.to_clean_sym : k.to_s.to_sym] = (Hash === v || Array === v ? v.keys_to_sym(clean:clean) : v); memo}
|
22
|
+
# self.inject({}){|memo,(k,v)| memo[clean ? k.to_s.to_clean_sym : k.to_s.to_sym] = (Hash === v ? v.keys_to_sym : (Array === v ? v.flatten.map{ |a| Hash === a ? a.keys_to_sym : a } : v) ); memo}
|
23
|
+
end
|
24
|
+
|
25
|
+
def keys_to_sym! clean: false
|
26
|
+
replace(self.keys_to_sym clean:clean)
|
18
27
|
end
|
19
28
|
|
20
|
-
|
21
|
-
|
29
|
+
# Converts the keys of the hash as well as any nested hashes to strings.
|
30
|
+
def keys_to_s
|
31
|
+
self.inject({}){|memo,(k,v)| memo[k.to_s] = (Hash === v || Array === v ? v.keys_to_s : v); memo}
|
32
|
+
# self.inject({}){|memo,(k,v)| memo[k.to_s] = (Hash === v ? v.keys_to_s : (Array === v ? v.flatten.map{ |a| Hash === a ? a.keys_to_s : a } : v)); memo}
|
33
|
+
end
|
34
|
+
|
35
|
+
def keys_to_s!
|
36
|
+
replace(self.keys_to_s)
|
22
37
|
end
|
23
38
|
|
24
39
|
# Reverses the order of keys in the Hash
|
@@ -30,4 +45,9 @@ class Hash
|
|
30
45
|
replace self.reverse
|
31
46
|
end
|
32
47
|
|
48
|
+
def unshift hash, value = nil
|
49
|
+
if !hash.is_a? Hash then hash = {hash => value} end
|
50
|
+
replace hash.merge(self).merge(hash)
|
51
|
+
end
|
52
|
+
|
33
53
|
end
|
@@ -0,0 +1,391 @@
|
|
1
|
+
require_relative 'hash_path_proc'
|
2
|
+
|
3
|
+
module BBLib
|
4
|
+
|
5
|
+
def self.hash_path hashes, path, recursive: false, delimiter: '.', symbol_sensitive: false
|
6
|
+
if String === path then path = BBLib.split_and_analyze(path, delimiter) end
|
7
|
+
if !hashes.is_a? Array then hashes = [hashes] end
|
8
|
+
return hashes if path.nil? || path.empty?
|
9
|
+
if path[0][:key] == '' then return BBLib.hash_path(hashes, path[1..-1], recursive: true, symbol_sensitive:symbol_sensitive) end
|
10
|
+
matches, p = Array.new, path.first
|
11
|
+
hashes.each do |hash|
|
12
|
+
if recursive
|
13
|
+
patterns = Regexp === p[:key] ? p[:key] : p[:key].to_s == '*' ? /.*/ : (symbol_sensitive ? p[:key] : [p[:key].to_sym, p[:key].to_s])
|
14
|
+
matches.push hash.dig(patterns).flatten(1)[p[:slice]]
|
15
|
+
else
|
16
|
+
if p[:key].nil?
|
17
|
+
if hash.is_a?(Array) then matches << hash[p[:slice]] end
|
18
|
+
elsif Symbol === p[:key] || String === p[:key]
|
19
|
+
if p[:key].to_s == '*'
|
20
|
+
matches.push hash.values.flatten(1)[p[:slice]]
|
21
|
+
else
|
22
|
+
next unless symbol_sensitive ? hash.include?(p[:key]) : (hash.include?(p[:key].to_sym) || hash.include?(p[:key].to_s) )
|
23
|
+
mat = (symbol_sensitive ? hash[p[:key]] : ( if hash.include?(p[:key].to_sym) then hash[p[:key].to_sym] else hash[p[:key].to_s] end ))
|
24
|
+
matches.push mat.is_a?(Array) ? mat[p[:slice]] : mat
|
25
|
+
end
|
26
|
+
elsif Regexp === p[:key]
|
27
|
+
hash.keys.find_all{ |k| k =~ p[:key] }.each{ |m| matches << hash[m] }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
matches = BBLib.analyze_hash_path_formula p[:formula], matches
|
32
|
+
if path.size > 1 && !matches.empty?
|
33
|
+
# p "MAT #{matches}"
|
34
|
+
# matches.map!{ |m| m.is_a?(Array) ? [m] : m }
|
35
|
+
BBLib.hash_path(matches.reject{ |r| !(r.is_a?(Hash) || r.is_a?(Array)) }, path[1..-1], symbol_sensitive:symbol_sensitive)
|
36
|
+
else
|
37
|
+
return matches.flatten(1)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.hash_path_set hash, *args
|
42
|
+
details = BBLib.hash_path_setup(hash, args)
|
43
|
+
count = 0
|
44
|
+
details[:paths].each do |path, d|
|
45
|
+
d[:hashes].each do |h|
|
46
|
+
count+=1
|
47
|
+
exists = (details[:symbol_sensitive] ? h.include?(d[:last][:key]) : (h.include?(d[:last][:key].to_sym) || h.include?(d[:last][:key].to_s) ))
|
48
|
+
next unless details[:bridge] || exists
|
49
|
+
key = details[:symbol_sensitive] ? d[:last][:key] : (h.include?(d[:last][:key].to_sym) ? d[:last][:key].to_sym : d[:last][:key].to_s)
|
50
|
+
if details[:symbols] then key = key.to_sym elsif !exists then key = d[:last][:key] end
|
51
|
+
if Fixnum === d[:last][:slice]
|
52
|
+
h[key][d[:last][:slice]] = d[:value]
|
53
|
+
else
|
54
|
+
if h.is_a?(Hash)
|
55
|
+
h[key] = d[:value]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
if count == 0 && details[:bridge] then hash.bridge(path, value:d[:value], symbols:details[:symbols]) end
|
60
|
+
end
|
61
|
+
hash
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.hash_path_exists? hash, path, delimiter: '.', symbol_sensitive: false
|
65
|
+
return !BBLib.hash_path(hash, path, delimiter:delimiter, symbol_sensitive:symbol_sensitive).empty?
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.hash_path_move hash, *args
|
69
|
+
BBLib.hash_path_copy hash, args
|
70
|
+
details = BBLib.hash_path_setup(hash, args)
|
71
|
+
opts = Hash.new
|
72
|
+
details.each do |k, v|
|
73
|
+
if HASH_PATH_PARAMS.include?(k) then opts[k] = v end
|
74
|
+
end
|
75
|
+
BBLib.hash_path_delete hash, [details[:paths].keys, opts ].flatten
|
76
|
+
return hash
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.hash_path_move_to from, to, *args
|
80
|
+
BBLib.hash_path_copy_to from, to, args
|
81
|
+
BBLib.hash_path_delete from, args
|
82
|
+
return to
|
83
|
+
end
|
84
|
+
|
85
|
+
def self.hash_path_copy hash, *args
|
86
|
+
details = BBLib.hash_path_setup(hash, args)
|
87
|
+
details[:paths].each do |path, d|
|
88
|
+
d[:hashes].each do |h|
|
89
|
+
if Hash === h || Array === h
|
90
|
+
exist = details[:symbol_sensitive] ? h.include?(d[:last][:key]) : (h.include?(d[:last][:key].to_sym) || h.include?(d[:last][:key].to_s) )
|
91
|
+
next unless exist || details[:bridge]
|
92
|
+
value = details[:symbol_sensitive] ? h[d[:last][:key]] : (if h.include?(d[:last][:key].to_sym) then h[d[:last][:key].to_sym] else h[d[:last][:key].to_s] end )
|
93
|
+
if value
|
94
|
+
BBLib.hash_path_set hash, d[:value] => value, symbols:details[:symbols]
|
95
|
+
end
|
96
|
+
elsif !details[:stop_on_nil]
|
97
|
+
BBLib.hash_path_set hash, d[:value] => nil, symbols:details[:symbols]
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
hash
|
102
|
+
end
|
103
|
+
|
104
|
+
def self.hash_path_copy_to from, to, *args
|
105
|
+
details = BBLib.hash_path_setup(from, args)
|
106
|
+
details[:paths].each do |path, d|
|
107
|
+
value = from.hash_path(path)
|
108
|
+
if !value.empty? || !details[:stop_on_nil]
|
109
|
+
if !details[:arrays].include?(d[:value]) then value = value.first end
|
110
|
+
to = to.bridge(d[:value], value: value, symbols:details[:symbols])
|
111
|
+
end
|
112
|
+
end
|
113
|
+
to
|
114
|
+
end
|
115
|
+
|
116
|
+
def self.hash_path_delete hash, *args
|
117
|
+
args.flatten!
|
118
|
+
details = BBLib.hash_path_setup hash, [args.find{ |f| Hash === f }.to_h.merge(args.find_all{ |a| String === a }.zip([]).to_h)]
|
119
|
+
deleted = []
|
120
|
+
details[:paths].each do |path, d|
|
121
|
+
d[:hashes].each do |h|
|
122
|
+
next unless details[:symbol_sensitive] ? h.include?(d[:last][:key]) : (h.include?(d[:last][:key].to_sym) || h.include?(d[:last][:key].to_s) )
|
123
|
+
if Fixnum === d[:last][:slice]
|
124
|
+
(details[:symbol_sensitive] ? h[d[:last][:key]] : (if h.include?(d[:last][:key].to_sym) then h[d[:last][:key].to_sym] else h[d[:last][:key].to_s] end)).delete_at d[:last][:slice]
|
125
|
+
else
|
126
|
+
if details[:symbol_sensitive]
|
127
|
+
deleted << h.delete(d[:last][:key])
|
128
|
+
else
|
129
|
+
if h.include?(d[:last][:key].to_sym) then deleted << h.delete(d[:last][:key].to_sym) end
|
130
|
+
if h.include?(d[:last][:key].to_s) then deleted << h.delete(d[:last][:key].to_s) end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
return deleted.flatten
|
136
|
+
end
|
137
|
+
|
138
|
+
def self.hash_path_keys hash
|
139
|
+
hash.squish.keys
|
140
|
+
end
|
141
|
+
|
142
|
+
def self.hash_path_key_for hash, value
|
143
|
+
hash.squish.find_all{ |k,v| v == value }.to_h.keys
|
144
|
+
end
|
145
|
+
|
146
|
+
def self.path_nav obj, path = '', delimiter = '.', &block
|
147
|
+
case [obj.class]
|
148
|
+
when [Hash]
|
149
|
+
obj.each{ |k,v| path_nav v, "#{path}#{path.nil? || path.empty? ? nil : delimiter}#{k}", delimiter, &block }
|
150
|
+
when [Array]
|
151
|
+
index = 0
|
152
|
+
obj.each{ |o| path_nav o, "#{path}#{path.end_with?(']') ? delimiter : nil}[#{index}]", delimiter, &block ; index+=1 }
|
153
|
+
else
|
154
|
+
yield path, obj
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
private
|
159
|
+
|
160
|
+
def self.hash_path_analyze path
|
161
|
+
key = path.scan(/\A.*^[^\[\(\{]*/i).first.to_s
|
162
|
+
if key.encap_by?('/')
|
163
|
+
key = eval(key)
|
164
|
+
elsif key.start_with? ':'
|
165
|
+
key = key[1..-1].to_sym
|
166
|
+
end
|
167
|
+
slice = eval(path.scan(/(?<=\[).*?(?=\])/).first.to_s)
|
168
|
+
if !slice.is_a?(Range) && !slice.is_a?(Fixnum) then slice = (0..-1) end
|
169
|
+
if slice.nil? then slice = (0..-1) end
|
170
|
+
formula = path.scan(/(?<=\().*?(?=\))/).first
|
171
|
+
if key.empty? && slice != (0..-1) then key = nil end
|
172
|
+
{key:key, slice:slice, formula:formula}
|
173
|
+
end
|
174
|
+
|
175
|
+
def self.split_hash_path path, delimiter = '.'
|
176
|
+
if path.to_s.start_with?(delimiter) then path = path.to_s.sub(delimiter, '') end
|
177
|
+
paths, stop, open = [], 0, false
|
178
|
+
path.chars.each do |t|
|
179
|
+
if t == '[' then open = true end
|
180
|
+
if t == ']' then open = false end
|
181
|
+
if t == delimiter && !open then paths << path[0..stop].reverse.sub(delimiter,'').reverse; path = path[stop+1..-1]; stop = -1 end
|
182
|
+
stop += 1
|
183
|
+
end
|
184
|
+
paths << path
|
185
|
+
end
|
186
|
+
|
187
|
+
def self.split_and_analyze path, delimiter = '.'
|
188
|
+
split_hash_path(path, delimiter).collect{ |p| hash_path_analyze(p) }
|
189
|
+
end
|
190
|
+
|
191
|
+
def self.analyze_hash_path_formula formula, hashes
|
192
|
+
return hashes unless formula
|
193
|
+
temp = []
|
194
|
+
hashes.flatten.each do |p|
|
195
|
+
begin
|
196
|
+
if eval(formula.gsub('$', p.to_s))
|
197
|
+
temp << p
|
198
|
+
end
|
199
|
+
rescue StandardError, SyntaxError => se
|
200
|
+
# Do nothing, the formula failed and we reject the value as a false
|
201
|
+
end
|
202
|
+
end
|
203
|
+
temp
|
204
|
+
end
|
205
|
+
|
206
|
+
def self.hash_path_setup hash, *args
|
207
|
+
args.flatten!
|
208
|
+
return nil unless args && args[0].class == Hash
|
209
|
+
info = Hash.new
|
210
|
+
info[:paths] = Hash.new
|
211
|
+
map = args[0].dup
|
212
|
+
HASH_PATH_PARAMS.each do |p, h|
|
213
|
+
info[p] = (map.include?(p) ? map.delete(p) : h[:default])
|
214
|
+
end
|
215
|
+
if info[:keys_to_sym] then hash.keys_to_sym! end
|
216
|
+
map.each do |path, value|
|
217
|
+
info[:paths][path] = Hash.new
|
218
|
+
info[:paths][path][:value] = value
|
219
|
+
info[:paths][path][:paths] = BBLib.split_hash_path(path, info[:delimiter])
|
220
|
+
info[:paths][path][:last] = BBLib.hash_path_analyze info[:paths][path][:paths].last
|
221
|
+
info[:paths][path][:hashes] = BBLib.hash_path(hash, info[:paths][path][:paths][0..-2].join(info[:delimiter]), delimiter:info[:delimiter], symbol_sensitive:info[:symbol_sensitive] )
|
222
|
+
end
|
223
|
+
return info
|
224
|
+
end
|
225
|
+
|
226
|
+
HASH_PATH_PARAMS = {
|
227
|
+
delimiter: {default:'.'},
|
228
|
+
bridge: {default:true},
|
229
|
+
symbols: {default:true},
|
230
|
+
symbol_sensitive: {default:false},
|
231
|
+
stop_on_nil: {default:true},
|
232
|
+
arrays: {default:[]},
|
233
|
+
keys_to_sym: {default:true}
|
234
|
+
}
|
235
|
+
|
236
|
+
end
|
237
|
+
|
238
|
+
|
239
|
+
|
240
|
+
class Hash
|
241
|
+
|
242
|
+
def hash_path path, delimiter: '.'
|
243
|
+
BBLib.hash_path self, path, delimiter:delimiter
|
244
|
+
end
|
245
|
+
|
246
|
+
def hash_path_set *args
|
247
|
+
BBLib.hash_path_set self, args
|
248
|
+
end
|
249
|
+
|
250
|
+
def hash_path_copy *args
|
251
|
+
BBLib.hash_path_copy self, args
|
252
|
+
end
|
253
|
+
|
254
|
+
def hash_path_copy_to hash, *args
|
255
|
+
BBLib.hash_path_copy_to self, hash, args
|
256
|
+
end
|
257
|
+
|
258
|
+
def hash_path_move_to hash, *args
|
259
|
+
BBLib.hash_path_move_to self, hash, args
|
260
|
+
end
|
261
|
+
|
262
|
+
def hash_path_move *args
|
263
|
+
BBLib.hash_path_move self, args
|
264
|
+
end
|
265
|
+
|
266
|
+
def hash_path_delete *args
|
267
|
+
BBLib.hash_path_delete self, args
|
268
|
+
end
|
269
|
+
|
270
|
+
def hash_path_keys
|
271
|
+
BBLib.hash_path_keys self
|
272
|
+
end
|
273
|
+
|
274
|
+
def hash_path_exists? path, delimiter: '.', symbol_sensitive: false
|
275
|
+
BBLib.hash_path_exists? self, path, delimiter:delimiter, symbol_sensitive:symbol_sensitive
|
276
|
+
end
|
277
|
+
|
278
|
+
# Returns all matching values with a specific key (or Array of keys) recursively within a Hash (including nested Arrays)
|
279
|
+
def dig keys, search_arrays: true
|
280
|
+
keys = [keys].flatten
|
281
|
+
matches = []
|
282
|
+
self.each do |k, v|
|
283
|
+
if keys.any?{ |a| (a.is_a?(Regexp) ? a =~ k : a == k ) } then matches << v end
|
284
|
+
if v.is_a? Hash
|
285
|
+
matches+= v.dig(keys)
|
286
|
+
elsif v.is_a?(Array) && search_arrays
|
287
|
+
v.flatten.each{ |i| if i.is_a?(Hash) then matches+= i.dig(keys) end }
|
288
|
+
end
|
289
|
+
end
|
290
|
+
matches
|
291
|
+
end
|
292
|
+
|
293
|
+
# Turns nested values' keys into delimiter separated paths
|
294
|
+
def squish delimiter: '.', path_start: nil
|
295
|
+
sh = Hash.new
|
296
|
+
BBLib.path_nav(self.dup, path_start, delimiter){ |k, v| sh[k] = v }
|
297
|
+
sh
|
298
|
+
end
|
299
|
+
|
300
|
+
# Expands keys in a hash using a delimiter. Opposite of squish.
|
301
|
+
def expand delimiter: '.', symbols: false
|
302
|
+
eh = Hash.new
|
303
|
+
self.dup.each do |k,v|
|
304
|
+
eh.bridge k, delimiter: delimiter, value:v, symbols: true
|
305
|
+
end
|
306
|
+
return eh
|
307
|
+
end
|
308
|
+
|
309
|
+
# Builds out a shell of a hash path using a delimited path. Use value to set a value.
|
310
|
+
def bridge path, delimiter: '.', value: nil, symbols: false
|
311
|
+
# Ensure the path is a delimiter string. This supports arrays being passed in
|
312
|
+
path = (path.is_a?(Array) ? path.join(delimiter) : path.to_s)
|
313
|
+
#Generate the paths and set the current variable
|
314
|
+
current, paths = self, BBLib.split_and_analyze(path, delimiter)
|
315
|
+
# If symbols is true, then all keys are turned into symbols
|
316
|
+
if symbols then paths.each{ |p| p[:key] = p[:key].to_s.to_sym } end
|
317
|
+
# Check to see if there is only one path. If there is return either an Array of Hash
|
318
|
+
if paths.size == 1
|
319
|
+
# If the first value is a slice the value is inserted into an empty array
|
320
|
+
if Fixnum === paths.first[:slice]
|
321
|
+
current[paths.first[:key]] = ([].insert paths.first[:slice], value )
|
322
|
+
return self
|
323
|
+
end
|
324
|
+
# If the value does not have a slice, a hash with a single key-value pair is returned
|
325
|
+
current[paths.first[:key]] = value
|
326
|
+
return self
|
327
|
+
end
|
328
|
+
index, count = -1, 0
|
329
|
+
paths.each do |p|
|
330
|
+
count+=1
|
331
|
+
last = paths.size == count
|
332
|
+
if p[:slice].is_a?(Fixnum)
|
333
|
+
index = p[:slice]
|
334
|
+
if paths[count-2] && Fixnum === paths[count-2][:slice]
|
335
|
+
current[paths[count-2][:slice]] = current[paths[count-2][:slice]] ||= Array.new
|
336
|
+
current = current[paths[count-2][:slice]]
|
337
|
+
else
|
338
|
+
if current[p[:key]].nil?
|
339
|
+
current[p[:key]] = []
|
340
|
+
end
|
341
|
+
current = current[p[:key]]
|
342
|
+
end
|
343
|
+
if last then current[index] = value end
|
344
|
+
else
|
345
|
+
if current.is_a?(Hash)
|
346
|
+
if last
|
347
|
+
current[p[:key]] = value
|
348
|
+
else
|
349
|
+
current[p[:key]] = current[p[:key]] ||= Hash.new
|
350
|
+
end
|
351
|
+
current = current[p[:key]]
|
352
|
+
else
|
353
|
+
if last
|
354
|
+
if current[index]
|
355
|
+
if current[index].is_a? Hash
|
356
|
+
current[index].merge({p[:key] => value})
|
357
|
+
else
|
358
|
+
current[index] = ({p[:key] => value})
|
359
|
+
end
|
360
|
+
else
|
361
|
+
current.insert index, {p[:key] => value}
|
362
|
+
end
|
363
|
+
else
|
364
|
+
temp = { p[:key] => {} }
|
365
|
+
if current[index].is_a? Hash
|
366
|
+
current[index].merge!(temp)
|
367
|
+
else
|
368
|
+
current[index] = temp
|
369
|
+
end
|
370
|
+
end
|
371
|
+
if !last then current = temp[p[:key]] end
|
372
|
+
end
|
373
|
+
index = -1
|
374
|
+
end
|
375
|
+
end
|
376
|
+
return self
|
377
|
+
end
|
378
|
+
|
379
|
+
end
|
380
|
+
|
381
|
+
class Array
|
382
|
+
|
383
|
+
def dig keys
|
384
|
+
matches = []
|
385
|
+
self.each do |i|
|
386
|
+
matches << i.dig(keys) if i.is_a?(Hash) || i.is_a?(Array)
|
387
|
+
end
|
388
|
+
matches
|
389
|
+
end
|
390
|
+
|
391
|
+
end
|
@@ -0,0 +1,163 @@
|
|
1
|
+
require 'time'
|
2
|
+
|
3
|
+
class Hash
|
4
|
+
def hash_path_proc action, paths, *args, **params
|
5
|
+
BBLib.hash_path_proc self, action, paths, *args, **params
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
module BBLib
|
10
|
+
|
11
|
+
def self.hash_path_proc hash, action, paths, *args, **params
|
12
|
+
action = HASH_PATH_PROC_TYPES.keys.find{ |k| k == action || HASH_PATH_PROC_TYPES[k][:aliases].include?(action) }
|
13
|
+
return nil unless action
|
14
|
+
paths.to_a.each do |path|
|
15
|
+
value = hash.hash_path(path).first
|
16
|
+
if params.include?(:condition) && params[:condition]
|
17
|
+
begin
|
18
|
+
next unless eval(params[:condition].gsub('$', value.to_s))
|
19
|
+
rescue
|
20
|
+
next
|
21
|
+
end
|
22
|
+
end
|
23
|
+
HashPath.send(action, hash, path, value, *args, **params)
|
24
|
+
end
|
25
|
+
return hash
|
26
|
+
end
|
27
|
+
|
28
|
+
HASH_PATH_PROC_TYPES = {
|
29
|
+
evaluate: { aliases: [:eval, :equation, :equate]},
|
30
|
+
append: { aliases: [:suffix]},
|
31
|
+
prepend: { aliases: [:prefix]},
|
32
|
+
split: { aliases: [:delimit, :delim, :separate, :msplit]},
|
33
|
+
replace: { aliases: [:swap]},
|
34
|
+
extract: { aliases: [:grab, :scan]},
|
35
|
+
extract_first: {aliases: [:grab_first, :scan_first]},
|
36
|
+
extract_last: {aliases: [:grab_last, :scan_last]},
|
37
|
+
parse_date: { aliases: [:date, :parse_time, :time]},
|
38
|
+
parse_duration: { aliases: [:duration]},
|
39
|
+
parse_file_size: { aliases: [:file_size]},
|
40
|
+
to_string: {aliases: [:to_s, :stringify]},
|
41
|
+
downcase: { aliases: [:lower, :lowercase, :to_lower]},
|
42
|
+
upcase: { aliases: [:upper, :uppercase, :to_upper]},
|
43
|
+
# titlecase: { aliases: [:title_case]},
|
44
|
+
roman: { aliases: [:convert_roman, :roman_numeral, :parse_roman]},
|
45
|
+
remove_symbols: { aliases: [:chop_symbols, :drop_symbols]},
|
46
|
+
format_articles: { aliases: [:articles]},
|
47
|
+
reverse: { aliases: [:invert]},
|
48
|
+
delete: { aliases: [:del]},
|
49
|
+
remove: { aliases: [:rem]},
|
50
|
+
custom: {aliases: [:send]}
|
51
|
+
# rename: { aliases: [:rename_key]},
|
52
|
+
# concat: { aliases: [:join, :concat_with]},
|
53
|
+
# reverse_concat: { aliases: [:reverse_join, :reverse_concat_with]}
|
54
|
+
}
|
55
|
+
|
56
|
+
module HashPath
|
57
|
+
|
58
|
+
def self.evaluate hash, path, value, args, params
|
59
|
+
exp = args.to_a.first.to_s.gsub('$', value.to_s)
|
60
|
+
hash.hash_path_set path => eval(exp)
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.append hash, path, value, args, params
|
64
|
+
hash.hash_path_set path => "#{value}#{args}"
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.prepend hash, path, value, args, params
|
68
|
+
hash.hash_path_set path => "#{args}#{value}"
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.split hash, path, value, args, params
|
72
|
+
hash.hash_path_set path => value.msplit(args)
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.replace hash, path, value, args, params
|
76
|
+
value = value.dup.to_s
|
77
|
+
args.each{ |k,v| value.gsub!(k.to_s, v.to_s) }
|
78
|
+
hash.hash_path_set path => value
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.extract hash, path, value, *args, **params
|
82
|
+
slice = (Array === args && args[1].nil? ? (0..-1) : args[1])
|
83
|
+
hash.hash_path_set path => value.scan(args.first)[slice]
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.extract_first hash, path, value, *args, **params
|
87
|
+
extract(hash, path, value, *args + [0])
|
88
|
+
end
|
89
|
+
|
90
|
+
def self.extract_last hash, path, value, *args, **params
|
91
|
+
extract(hash, path, value, *args + [-1])
|
92
|
+
end
|
93
|
+
|
94
|
+
def self.parse_date hash, path, value, *args, **params
|
95
|
+
format = params.include?(:format) ? params[:format] : '%Y-%m-%d %H:%M:%S'
|
96
|
+
formatted = nil
|
97
|
+
args.each do |pattern|
|
98
|
+
next unless formatted.nil?
|
99
|
+
begin
|
100
|
+
formatted = Time.strptime(value.to_s, pattern.to_s).strftime(format)
|
101
|
+
rescue
|
102
|
+
end
|
103
|
+
end
|
104
|
+
begin
|
105
|
+
if formatted.nil? then formatted = Time.parse(value) end
|
106
|
+
rescue
|
107
|
+
end
|
108
|
+
hash.hash_path_set path => formatted
|
109
|
+
end
|
110
|
+
|
111
|
+
def self.parse_duration hash, path, value, args, params
|
112
|
+
hash.hash_path_set path => value.to_s.parse_duration(output: args.empty? ? :sec : args )
|
113
|
+
end
|
114
|
+
|
115
|
+
def self.parse_file_size hash, path, value, args, params
|
116
|
+
hash.hash_path_set path => value.to_s.parse_file_size(output: args.empty? ? :bytes : args )
|
117
|
+
end
|
118
|
+
|
119
|
+
def self.to_string hash, path, value, *args, **params
|
120
|
+
hash.hash_path_set path => value.to_s
|
121
|
+
end
|
122
|
+
|
123
|
+
def self.downcase hash, path, value, *args, **params
|
124
|
+
hash.hash_path_set path => value.to_s.downcase
|
125
|
+
end
|
126
|
+
|
127
|
+
def self.upcase hash, path, value, *args, **params
|
128
|
+
hash.hash_path_set path => value.to_s.upcase
|
129
|
+
end
|
130
|
+
|
131
|
+
def self.roman hash, path, value, *args, **params
|
132
|
+
hash.hash_path_set path => (args[0] == :to ? value.to_s.to_roman : value.to_s.from_roman)
|
133
|
+
end
|
134
|
+
|
135
|
+
def self.remove_symbols hash, path, value, *args, **params
|
136
|
+
hash.hash_path_set path => value.to_s.drop_symbols
|
137
|
+
end
|
138
|
+
|
139
|
+
def self.format_articles hash, path, value, args, **params
|
140
|
+
hash.hash_path_set path => value.to_s.move_articles(args.nil? ? :front : args)
|
141
|
+
end
|
142
|
+
|
143
|
+
def self.reverse hash, path, value, *args, **params
|
144
|
+
hash.hash_path_set path => value.to_s.reverse
|
145
|
+
end
|
146
|
+
|
147
|
+
def self.delete hash, path, value, *args, **params
|
148
|
+
hash.hash_path_delete path
|
149
|
+
end
|
150
|
+
|
151
|
+
def self.remove hash, path, value, *args, **params
|
152
|
+
removed = value.to_s
|
153
|
+
args.each{ |a| removed.gsub!(a, '')}
|
154
|
+
hash.hash_path_set path => removed
|
155
|
+
end
|
156
|
+
|
157
|
+
def self.custom hash, path, value, *args, **params
|
158
|
+
hash.hash_path_set path => value.send(*args)
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|
162
|
+
|
163
|
+
end
|
File without changes
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module BBLib
|
2
|
+
|
3
|
+
def self.to_hash obj
|
4
|
+
return {obj => nil} unless !obj.instance_variables.empty?
|
5
|
+
hash = {}
|
6
|
+
obj.instance_variables.each do |var|
|
7
|
+
value = obj.instance_variable_get(var)
|
8
|
+
if value.is_a? Array
|
9
|
+
hash[var.to_s.delete("@")] = value.map{ |v| v.respond_to?(:obj_to_hash) && !v.instance_variables.empty? ? v.obj_to_hash : v }
|
10
|
+
elsif value.is_a? Hash
|
11
|
+
begin
|
12
|
+
if !hash[var.to_s.delete("@")].is_a?(Hash) then hash[var.to_s.delete("@")] = Hash.new end
|
13
|
+
rescue
|
14
|
+
hash[var.to_s.delete("@")] = Hash.new
|
15
|
+
end
|
16
|
+
value.each do |k, v|
|
17
|
+
hash[var.to_s.delete("@")][k.to_s.delete("@")] = v.respond_to?(:obj_to_hash) && !v.instance_variables.empty? ? v.obj_to_hash : v
|
18
|
+
end
|
19
|
+
elsif value.respond_to?(:obj_to_hash) && !value.instance_variables.empty?
|
20
|
+
hash[var.to_s.delete("@")] = value.obj_to_hash
|
21
|
+
else
|
22
|
+
hash[var.to_s.delete("@")] = value
|
23
|
+
end
|
24
|
+
end
|
25
|
+
return hash
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
data/lib/string/bbstring.rb
CHANGED
@@ -9,6 +9,10 @@ module BBLib
|
|
9
9
|
# General Functions
|
10
10
|
##############################################
|
11
11
|
|
12
|
+
# def self.title_case str
|
13
|
+
# TODO
|
14
|
+
# end
|
15
|
+
|
12
16
|
# Quickly remove any symbols from a string leaving onl alpha-numeric characters and white space.
|
13
17
|
def self.drop_symbols str
|
14
18
|
str.gsub(/[^\w\s\d]|_/, '')
|
@@ -16,17 +20,17 @@ module BBLib
|
|
16
20
|
|
17
21
|
# Extract all integers from a string. Use extract_floats if numbers may contain decimal places.
|
18
22
|
def self.extract_integers str, convert: true
|
19
|
-
str.
|
23
|
+
BBLib.extract_numbers(str, convert:false).reject{ |r| r.include?('.') }.map{ |m| convert ? m.to_i : m }
|
20
24
|
end
|
21
25
|
|
22
26
|
# Extracts all integers or decimals from a string into an array.
|
23
27
|
def self.extract_floats str, convert: true
|
24
|
-
str.
|
28
|
+
BBLib.extract_numbers(str, convert:false).reject{ |r| !r.include?('.') }.map{ |m| convert ? m.to_f : m }
|
25
29
|
end
|
26
30
|
|
27
31
|
# Alias for extract_floats
|
28
32
|
def self.extract_numbers str, convert: true
|
29
|
-
|
33
|
+
str.scan(/\d+\.?\d+|\d+/).map{ |f| convert ? (f.include?('.') ? f.to_f : f.to_i) : f }
|
30
34
|
end
|
31
35
|
|
32
36
|
# Used to move the position of the articles 'the', 'a' and 'an' in strings for normalization.
|
@@ -61,22 +65,22 @@ end
|
|
61
65
|
|
62
66
|
class String
|
63
67
|
# Multi-split. Similar to split, but can be passed an array of delimiters to split on.
|
64
|
-
def msplit delims, keep_empty: false
|
68
|
+
def msplit *delims, keep_empty: false
|
65
69
|
return [self] unless !delims.nil? && !delims.empty?
|
66
70
|
ar = [self]
|
67
|
-
delims.each do |d|
|
71
|
+
[delims].flatten.each do |d|
|
68
72
|
ar.map!{ |a| a.split d }
|
69
73
|
ar.flatten!
|
70
74
|
end
|
71
75
|
keep_empty ? ar : ar.reject{ |l| l.empty? }
|
72
76
|
end
|
73
77
|
|
74
|
-
def move_articles position, capitalize = true
|
75
|
-
BBLib.move_articles self, position, capitalize
|
78
|
+
def move_articles position = :front, capitalize = true
|
79
|
+
BBLib.move_articles self, position, capitalize:capitalize
|
76
80
|
end
|
77
81
|
|
78
|
-
def move_articles! position, capitalize = true
|
79
|
-
replace BBLib.move_articles(self, position, capitalize)
|
82
|
+
def move_articles! position = :front, capitalize = true
|
83
|
+
replace BBLib.move_articles(self, position, capitalize:capitalize)
|
80
84
|
end
|
81
85
|
|
82
86
|
def drop_symbols
|
@@ -91,7 +95,30 @@ class String
|
|
91
95
|
BBLib.extract_integers self, convert:convert
|
92
96
|
end
|
93
97
|
|
98
|
+
def extract_floats convert: true
|
99
|
+
BBLib.extract_floats self, convert:convert
|
100
|
+
end
|
101
|
+
|
94
102
|
def extract_numbers convert: true
|
95
103
|
BBLib.extract_numbers self, convert:convert
|
96
104
|
end
|
105
|
+
|
106
|
+
def to_clean_sym
|
107
|
+
self.strip.downcase.gsub('_', ' ').drop_symbols.gsub(' ', '_').to_sym
|
108
|
+
end
|
109
|
+
|
110
|
+
# Simple method to convert a string into an array containing only itself
|
111
|
+
def to_a
|
112
|
+
return [self]
|
113
|
+
end
|
114
|
+
|
115
|
+
def encap_by? str
|
116
|
+
return self.start_with?(str) && self.end_with?(str)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
class Symbol
|
121
|
+
def to_clean_sym
|
122
|
+
self.to_s.strip.downcase.gsub('_', ' ').drop_symbols.gsub(' ', '_').to_sym
|
123
|
+
end
|
97
124
|
end
|
data/lib/time/bbtime.rb
CHANGED
@@ -1,19 +1,19 @@
|
|
1
|
-
|
1
|
+
require_relative 'task_timer'
|
2
2
|
|
3
3
|
module BBLib
|
4
4
|
|
5
5
|
# Parses known time based patterns out of a string to construct a numeric duration.
|
6
6
|
def self.parse_duration str, output: :sec
|
7
|
-
|
7
|
+
msecs = 0.0
|
8
8
|
TIME_EXPS.each do |k, v|
|
9
9
|
v[:exp].each do |e|
|
10
|
-
numbers = str.downcase.scan(/(?=\w|\D|\A)\d
|
10
|
+
numbers = str.downcase.scan(/(?=\w|\D|\A)\d*\.?\d+[[:space:]]*#{e}(?=\W|\d|\z)/i)
|
11
11
|
numbers.each do |n|
|
12
|
-
|
12
|
+
msecs+= n.to_f * v[:mult]
|
13
13
|
end
|
14
14
|
end
|
15
15
|
end
|
16
|
-
|
16
|
+
msecs / (TIME_EXPS[output][:mult] rescue 1)
|
17
17
|
end
|
18
18
|
|
19
19
|
# Turns a numeric input into a time string.
|
@@ -24,47 +24,83 @@ module BBLib
|
|
24
24
|
n, done = num * TIME_EXPS[input.to_sym][:mult], false
|
25
25
|
TIME_EXPS.reverse.each do |k, v|
|
26
26
|
next unless !done
|
27
|
+
if k == stop then done = true end
|
27
28
|
div = n / v[:mult]
|
28
|
-
if div
|
29
|
-
|
30
|
-
|
29
|
+
if div >= 1
|
30
|
+
val = (done ? div.round : div.floor)
|
31
|
+
expression << "#{val}#{v[:styles][style]}#{val > 1 && style != :short ? "s" : nil}"
|
32
|
+
n-= val.to_f * v[:mult]
|
31
33
|
end
|
32
|
-
if k == stop then done = true end
|
33
34
|
end
|
34
35
|
expression.join ' '
|
35
36
|
end
|
36
37
|
|
37
38
|
TIME_EXPS = {
|
38
|
-
|
39
|
+
yocto: {
|
40
|
+
mult: 0.000000000000000000001,
|
41
|
+
styles: {full: ' yoctosecond', medium: ' yocto', short: 'ys'},
|
42
|
+
exp: ['yoctosecond', 'yocto', 'yoctoseconds', 'yoctos', 'ys']
|
43
|
+
},
|
44
|
+
zepto: {
|
45
|
+
mult: 0.000000000000000001,
|
46
|
+
styles: {full: ' zeptosecond', medium: ' zepto', short: 'zs'},
|
47
|
+
exp: ['zeptosecond', 'zepto', 'zeptoseconds', 'zeptos', 'zs']
|
48
|
+
},
|
49
|
+
atto: {
|
50
|
+
mult: 0.000000000000001,
|
51
|
+
styles: {full: ' attosecond', medium: ' atto', short: 'as'},
|
52
|
+
exp: ['attoseconds', 'atto', 'attoseconds', 'attos', 'as']
|
53
|
+
},
|
54
|
+
femto: {
|
55
|
+
mult: 0.000000000001,
|
56
|
+
styles: {full: ' femtosecond', medium: ' fempto', short: 'fs'},
|
57
|
+
exp: ['femtosecond', 'fempto', 'femtoseconds', 'femptos', 'fs']
|
58
|
+
},
|
59
|
+
pico: {
|
60
|
+
mult: 0.000000001,
|
61
|
+
styles: {full: ' picosecond', medium: ' pico', short: 'ps'},
|
62
|
+
exp: ['picosecond', 'pico', 'picoseconds', 'picos', 'ps']
|
63
|
+
},
|
64
|
+
nano: {
|
65
|
+
mult: 0.000001,
|
66
|
+
styles: {full: ' nanosecond', medium: ' nano', short: 'ns'},
|
67
|
+
exp: ['nanosecond', 'nano', 'nanoseconds', 'nanos', 'ns']
|
68
|
+
},
|
69
|
+
micro: {
|
39
70
|
mult: 0.001,
|
40
|
-
styles: {full: '
|
71
|
+
styles: {full: ' microsecond', medium: ' micro', short: 'μs'},
|
72
|
+
exp: ['microsecond', 'micro', 'microseconds', 'micros', 'μs']
|
73
|
+
},
|
74
|
+
milli: {
|
75
|
+
mult: 1,
|
76
|
+
styles: {full: ' millisecond', medium: ' mil', short: 'ms'},
|
41
77
|
exp: ['ms', 'mil', 'mils', 'milli', 'millis', 'millisecond', 'milliseconds', 'milsec', 'milsecs', 'msec', 'msecs', 'msecond', 'mseconds']},
|
42
78
|
sec: {
|
43
|
-
mult:
|
79
|
+
mult: 1000,
|
44
80
|
styles: {full: ' second', medium: ' sec', short: 's'},
|
45
81
|
exp: ['s', 'sec', 'secs', 'second', 'seconds']},
|
46
82
|
min: {
|
47
|
-
mult:
|
83
|
+
mult: 60000,
|
48
84
|
styles: {full: ' minute', medium: ' min', short: 'm'},
|
49
85
|
exp: ['m', 'mn', 'mns', 'min', 'mins', 'minute', 'minutes']},
|
50
86
|
hour: {
|
51
|
-
mult:
|
87
|
+
mult: 3600000,
|
52
88
|
styles: {full: ' hour', medium: ' hr', short: 'h'},
|
53
89
|
exp: ['h', 'hr', 'hrs', 'hour', 'hours']},
|
54
90
|
day: {
|
55
|
-
mult:
|
91
|
+
mult: 86400000,
|
56
92
|
styles: {full: ' day', medium: ' day', short: 'd'},
|
57
|
-
exp: ['d', 'day' 'days']},
|
93
|
+
exp: ['d', 'day', 'days']},
|
58
94
|
week: {
|
59
|
-
mult:
|
95
|
+
mult: 604800000,
|
60
96
|
styles: {full: ' week', medium: ' wk', short: 'w'},
|
61
97
|
exp: ['w', 'wk', 'wks', 'week', 'weeks']},
|
62
98
|
month: {
|
63
|
-
mult:
|
99
|
+
mult: 2592000000,
|
64
100
|
styles: {full: ' month', medium: ' mo', short: 'mo'},
|
65
101
|
exp: ['mo', 'mon', 'mons', 'month', 'months', 'mnth', 'mnths', 'mth', 'mths']},
|
66
102
|
year: {
|
67
|
-
mult:
|
103
|
+
mult: 31536000000,
|
68
104
|
styles: {full: ' year', medium: ' yr', short: 'y'},
|
69
105
|
exp: ['y', 'yr', 'yrs', 'year', 'years']}
|
70
106
|
}
|
@@ -0,0 +1,101 @@
|
|
1
|
+
module BBLib
|
2
|
+
|
3
|
+
class TaskTimer
|
4
|
+
attr_reader :tasks, :save, :retention
|
5
|
+
|
6
|
+
def initialize task:nil, retention:100
|
7
|
+
@tasks = {}
|
8
|
+
self.retention = retention
|
9
|
+
if task then start task end
|
10
|
+
end
|
11
|
+
|
12
|
+
def retention= num
|
13
|
+
@retention = num.nil? ? nil : BBLib.keep_between(num, -1, nil)
|
14
|
+
end
|
15
|
+
|
16
|
+
def time task = :default, type = :current
|
17
|
+
return nil unless @tasks.keys.include? task
|
18
|
+
numbers = @tasks[task][:history].map{ |v| v[:time] }
|
19
|
+
case type
|
20
|
+
when :current
|
21
|
+
return nil unless @tasks[task][:current]
|
22
|
+
return Time.now.to_f - @tasks[task][:current]
|
23
|
+
when :min
|
24
|
+
return numbers.min
|
25
|
+
when :max
|
26
|
+
return numbers.max
|
27
|
+
when :avg
|
28
|
+
return numbers.inject{ |sum, n| sum + n }.to_f / numbers.size
|
29
|
+
when :sum
|
30
|
+
return numbers.inject{ |sum, n| sum + n }
|
31
|
+
when :all
|
32
|
+
return numbers
|
33
|
+
when :first
|
34
|
+
return numbers.first
|
35
|
+
when :last
|
36
|
+
return numbers.last
|
37
|
+
when :count
|
38
|
+
return numbers.size
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def clear task
|
43
|
+
return nil unless @tasks.keys.include?(task)
|
44
|
+
stop task
|
45
|
+
@tasks[task][:history].clear
|
46
|
+
end
|
47
|
+
|
48
|
+
def start task = :default
|
49
|
+
if !@tasks.keys.include?(task) then @tasks[task] = {history: [], current: nil} end
|
50
|
+
if @tasks[task][:current] then stop task end
|
51
|
+
@tasks[task][:current] = Time.now.to_f
|
52
|
+
return 0
|
53
|
+
end
|
54
|
+
|
55
|
+
def stop task = :default
|
56
|
+
return nil unless @tasks.keys.include?(task) && active?(task)
|
57
|
+
time_taken = Time.now.to_f - @tasks[task][:current].to_f
|
58
|
+
@tasks[task][:history] << {start: @tasks[task][:current], stop: Time.now.to_f, time: time_taken}
|
59
|
+
@tasks[task][:current] = nil
|
60
|
+
if @retention && @tasks[task][:history].size > @retention then @tasks[task][:history].shift end
|
61
|
+
time_taken
|
62
|
+
end
|
63
|
+
|
64
|
+
def restart task = :default
|
65
|
+
start(task) unless stop(task).nil?
|
66
|
+
end
|
67
|
+
|
68
|
+
def save= save
|
69
|
+
@save = save
|
70
|
+
end
|
71
|
+
|
72
|
+
def active? task
|
73
|
+
return false unless @tasks.keys.include? task
|
74
|
+
!@tasks[task][:current].nil?
|
75
|
+
end
|
76
|
+
|
77
|
+
def method_missing *args
|
78
|
+
temp = args.first.to_s.sub('p_','').to_sym
|
79
|
+
type, task = TIMER_TYPES.keys.find{ |k| k == temp || TIMER_TYPES[k].include?(temp) }, args[1] ||= :default
|
80
|
+
raise NoMethodError unless type
|
81
|
+
t = time task, type
|
82
|
+
args.first.to_s.start_with?('p_') && type != :count ? t.to_duration : t
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
|
87
|
+
TIMER_TYPES = {
|
88
|
+
current: [],
|
89
|
+
avg: [:average, :av],
|
90
|
+
all: [:times],
|
91
|
+
max: [:maximum, :largest],
|
92
|
+
min: [:minimum, :smallest],
|
93
|
+
sum: [],
|
94
|
+
last: [:latest],
|
95
|
+
first: [:initial],
|
96
|
+
count: [:total]
|
97
|
+
}
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
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.2.0
|
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-
|
11
|
+
date: 2016-02-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -70,17 +70,21 @@ files:
|
|
70
70
|
- bblib.gemspec
|
71
71
|
- bin/console
|
72
72
|
- bin/setup
|
73
|
+
- lib/array/bbarray.rb
|
73
74
|
- lib/bblib.rb
|
74
75
|
- lib/bblib/version.rb
|
75
76
|
- lib/file/bbfile.rb
|
76
77
|
- lib/hash/bbhash.rb
|
77
|
-
- lib/
|
78
|
-
- lib/
|
78
|
+
- lib/hash/hash_path.rb
|
79
|
+
- lib/hash/hash_path_proc.rb
|
80
|
+
- lib/number/bbnumber.rb
|
81
|
+
- lib/object/bbobject.rb
|
79
82
|
- lib/string/bbstring.rb
|
80
83
|
- lib/string/fuzzy_matcher.rb
|
81
84
|
- lib/string/matching.rb
|
82
85
|
- lib/string/roman.rb
|
83
86
|
- lib/time/bbtime.rb
|
87
|
+
- lib/time/task_timer.rb
|
84
88
|
homepage: https://github.com/bblack16/bblib-ruby
|
85
89
|
licenses:
|
86
90
|
- MIT
|
@@ -101,7 +105,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
101
105
|
version: '0'
|
102
106
|
requirements: []
|
103
107
|
rubyforge_project:
|
104
|
-
rubygems_version: 2.4.
|
108
|
+
rubygems_version: 2.4.5.1
|
105
109
|
signing_key:
|
106
110
|
specification_version: 4
|
107
111
|
summary: A library containing many reusable, basic functions.
|
data/lib/net/bbnet.rb
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
# TO BE WRITTEN
|