bblib 0.1.1 → 0.2.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f7b1504ef918ed9ec7c63df50cf267714f4fffed
4
- data.tar.gz: b9627fa1e26c9932a4c0e8301134560a90e2caab
3
+ metadata.gz: 46227889fa2341b852f07f10dd28892a0770b759
4
+ data.tar.gz: f7e8f2dae0ba5148cb77d0ec7086c87806dce3c7
5
5
  SHA512:
6
- metadata.gz: ad8d53750aa9b2842b63cccf814be382468f426f7360e41ff97ca37790a18790110e1f4cff275685982cf2e46b50957e1d8140c2a008eef8c32f695ca5cf11f4
7
- data.tar.gz: e1841e600e35928460e7320800f39858e7e5d2cc2d2920e7d1afa4cbf9628160d1d49df9e2c43c2a385bf11d83de83824189039f98f020d91df6a63c1a882e36
6
+ metadata.gz: 45f56290f00c86f5ead9f13746fe76104d87698bfd8b99a89abc2fc1660b4dc4137fb0bb85d50ed9dc8bfcfb0e987b1bef83e41e40c9599eb2205b2809a0cc7a
7
+ data.tar.gz: 50f0e4937c9857b0f66c14b748a74fec84bd835da790e01c64774005e7d9f9f24a58e0e96f2204cfcaf65a690e1ae5ce0d3eec40c0b76e9a25623f1824f6fd60
data/.gitignore CHANGED
@@ -7,4 +7,4 @@
7
7
  /pkg/
8
8
  /spec/reports/
9
9
  /tmp/
10
- /.gem
10
+ *.gem
@@ -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
@@ -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 "math/bbmath"
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
 
@@ -1,3 +1,3 @@
1
1
  module BBLib
2
- VERSION = "0.1.1"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -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
- def self.string_to_file path, str, mkpath = true
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?\.?\d+[[:space:]]*#{e}s?(?=\W|\d|\z)/i)
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
@@ -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
- def keys_to_sym!
21
- replace self.keys_to_sym
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
@@ -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
@@ -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.scan(/\d+/).map{ |d| convert ? d.to_i : d }
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.scan(/\d+\.?\d+|\d+/).map{ |f| convert ? f.to_f : f }
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
- BBLib.extract_floats str, convert:convert
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
@@ -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
- secs = 0.0
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?\.?\d+[[:space:]]*#{e}(?=\W|\d|\z)/i)
10
+ numbers = str.downcase.scan(/(?=\w|\D|\A)\d*\.?\d+[[:space:]]*#{e}(?=\W|\d|\z)/i)
11
11
  numbers.each do |n|
12
- secs+= n.to_i * v[:mult]
12
+ msecs+= n.to_f * v[:mult]
13
13
  end
14
14
  end
15
15
  end
16
- secs / (TIME_EXPS[output][:mult].to_f rescue 1)
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 > 1
29
- expression << "#{div.floor}#{v[:styles][style]}#{div.floor > 1 && style != :short ? "s" : nil}"
30
- n-= div.floor * v[:mult]
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
- milli: {
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: ' millisecond', medium: ' milli', short: 'ms'},
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: 1,
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: 60,
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: 3600,
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: 86400,
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: 604800,
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: 2592000,
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: 31536000,
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.1.1
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-01-12 00:00:00.000000000 Z
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/math/bbmath.rb
78
- - lib/net/bbnet.rb
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.8
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.
@@ -1 +0,0 @@
1
- # TO BE WRITTEN