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 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