bblib 0.2.2 → 0.3.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 +4 -4
- data/README.md +178 -20
- data/bblib.gemspec +1 -0
- data/lib/array/bbarray.rb +22 -13
- data/lib/bblib.rb +27 -17
- data/lib/bblib/version.rb +1 -1
- data/lib/file/bbfile.rb +20 -31
- data/lib/gem/bbgem.rb +28 -0
- data/lib/hash/bbhash.rb +26 -13
- data/lib/hash/hash_path.rb +214 -304
- data/lib/hash/hash_path_proc.rb +82 -82
- data/lib/hash/path_hash.rb +81 -0
- data/lib/object/attr.rb +182 -0
- data/lib/object/bbobject.rb +16 -0
- data/lib/object/hooks.rb +69 -0
- data/lib/object/lazy_class.rb +73 -0
- data/lib/opal/bbopal.rb +16 -0
- data/lib/os/bbos.rb +93 -0
- data/lib/os/bbsys.rb +238 -0
- data/lib/string/bbstring.rb +36 -6
- data/lib/string/cases.rb +1 -1
- data/lib/string/fuzzy_matcher.rb +14 -19
- data/lib/string/matching.rb +1 -1
- data/lib/string/roman.rb +5 -6
- data/lib/time/cron.rb +27 -27
- data/lib/time/task_timer.rb +34 -28
- metadata +24 -2
data/lib/object/bbobject.rb
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
require_relative 'attr'
|
2
|
+
require_relative 'hooks'
|
3
|
+
require_relative 'simple_serialize'
|
4
|
+
|
1
5
|
module BBLib
|
2
6
|
|
3
7
|
def self.to_hash obj
|
@@ -25,4 +29,16 @@ module BBLib
|
|
25
29
|
return hash
|
26
30
|
end
|
27
31
|
|
32
|
+
def self.named_args *args
|
33
|
+
args.last.is_a?(Hash) && args.last.keys.all?{|k|k.is_a?(Symbol)} ? args.last : Hash.new
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.named_args! *args
|
37
|
+
if args.last.is_a?(Hash) && args.last.keys.all?{|k|k.is_a?(Symbol)}
|
38
|
+
args.delete_at(-1)
|
39
|
+
else
|
40
|
+
Hash.new
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
28
44
|
end
|
data/lib/object/hooks.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
module BBLib::Hooks
|
2
|
+
|
3
|
+
def method_added name
|
4
|
+
before_hooks_for(name).each do |hook|
|
5
|
+
next if before_hooked_methods[hook] && before_hooked_methods[hook].include?(name)
|
6
|
+
add_before_hook(name, hook)
|
7
|
+
end
|
8
|
+
after_hooks_for(name).each do |hook|
|
9
|
+
next if after_hooked_methods[hook] && after_hooked_methods[hook].include?(name)
|
10
|
+
add_after_hook(name, hook)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def before hook, *methods
|
15
|
+
methods.each{ |m| before_hooks[hook] = methods }
|
16
|
+
end
|
17
|
+
|
18
|
+
def before_hooks
|
19
|
+
@before_hooks ||= {}
|
20
|
+
end
|
21
|
+
|
22
|
+
def before_hooked_methods
|
23
|
+
@before_hooked_methods ||= {}
|
24
|
+
end
|
25
|
+
|
26
|
+
def before_hooks_for name
|
27
|
+
before_hooks.map{ |n, m| m.include?(name)? n : nil }.reject(&:nil?)
|
28
|
+
end
|
29
|
+
|
30
|
+
def add_before_hook method, hook
|
31
|
+
before_hooked_methods[hook] = Array.new unless before_hooked_methods[hook]
|
32
|
+
before_hooked_methods[hook] += [method]
|
33
|
+
original = instance_method(method)
|
34
|
+
define_method(method) do |*args, &block|
|
35
|
+
# puts "ARGS: #{args}"
|
36
|
+
method(hook).call
|
37
|
+
original.bind(self).call(*args, &block)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def after hook, *methods
|
42
|
+
methods.each{ |m| after_hooks[hook] = methods }
|
43
|
+
end
|
44
|
+
|
45
|
+
def after_hooks
|
46
|
+
@after_hooks ||= {}
|
47
|
+
end
|
48
|
+
|
49
|
+
def after_hooked_methods
|
50
|
+
@after_hooked_methods ||= {}
|
51
|
+
end
|
52
|
+
|
53
|
+
def after_hooks_for name
|
54
|
+
after_hooks.map{ |n, m| m.include?(name) ? n : nil }.reject(&:nil?)
|
55
|
+
end
|
56
|
+
|
57
|
+
def add_after_hook method, hook
|
58
|
+
after_hooked_methods[hook] = Array.new unless after_hooked_methods[hook]
|
59
|
+
after_hooked_methods[hook] += [method]
|
60
|
+
original = instance_method(method)
|
61
|
+
|
62
|
+
define_method(method) do |*args, &block|
|
63
|
+
rtr = original.bind(self).call(*args, &block)
|
64
|
+
method(hook).call
|
65
|
+
rtr
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
|
2
|
+
module BBLib
|
3
|
+
|
4
|
+
class LazyClass
|
5
|
+
extend Hooks
|
6
|
+
extend Attr
|
7
|
+
attr_reader :_serialize_fields
|
8
|
+
|
9
|
+
def initialize *args
|
10
|
+
_pre_setup
|
11
|
+
lazy_setup
|
12
|
+
_lazy_init(*args)
|
13
|
+
end
|
14
|
+
|
15
|
+
def serialize
|
16
|
+
_serialize_fields.map do |name, h|
|
17
|
+
value = send(h[:method])
|
18
|
+
if !h[:always] && value == h[:ignore]
|
19
|
+
nil
|
20
|
+
else
|
21
|
+
[ name, value ]
|
22
|
+
end
|
23
|
+
end.reject(&:nil?).to_h
|
24
|
+
end
|
25
|
+
|
26
|
+
protected
|
27
|
+
|
28
|
+
def lazy_setup
|
29
|
+
# Instantiate necessary variables here
|
30
|
+
end
|
31
|
+
|
32
|
+
def _lazy_init *args
|
33
|
+
BBLib::named_args(*args).each do |k,v|
|
34
|
+
if self.respond_to?("#{k}=".to_sym)
|
35
|
+
send("#{k}=".to_sym, v)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
lazy_init *args
|
39
|
+
custom_lazy_init BBLib::named_args(*args), *args
|
40
|
+
end
|
41
|
+
|
42
|
+
def _pre_setup
|
43
|
+
self.methods.each do |m|
|
44
|
+
if m.to_s.start_with?('__reset_')
|
45
|
+
send(m) rescue nil
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def lazy_init *args
|
51
|
+
# Define custom initialization here...
|
52
|
+
end
|
53
|
+
|
54
|
+
def custom_lazy_init *args
|
55
|
+
# Left in for legacy support...don't use this!
|
56
|
+
end
|
57
|
+
|
58
|
+
def serialize_method name, method = nil, ignore: nil, always: false
|
59
|
+
return if method == :serialize || name == :serialize && method.nil?
|
60
|
+
_serialize_fields[name.to_sym] = {
|
61
|
+
method: (method.nil? ? name.to_sym : method.to_sym),
|
62
|
+
ignore: ignore,
|
63
|
+
always: always
|
64
|
+
}
|
65
|
+
end
|
66
|
+
|
67
|
+
def _serialize_fields
|
68
|
+
@_serialize_fields ||= Hash.new
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
data/lib/opal/bbopal.rb
ADDED
data/lib/os/bbos.rb
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
require_relative 'bbsys'
|
2
|
+
|
3
|
+
module BBLib
|
4
|
+
|
5
|
+
module OS
|
6
|
+
|
7
|
+
def self.os
|
8
|
+
return :windows if windows?
|
9
|
+
return :mac if mac?
|
10
|
+
return :linux if linux?
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.windows?
|
14
|
+
builds = ['mingw', 'mswin', 'cygwin', 'bccwin']
|
15
|
+
!(/#{builds.join('|')}/i =~ RUBY_PLATFORM).nil?
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.linux?
|
19
|
+
!windows? && !mac?
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.unix?
|
23
|
+
!windows?
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.mac?
|
27
|
+
builds = ['darwin']
|
28
|
+
!(/#{builds.join('|')}/i =~ RUBY_PLATFORM).nil?
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.os_info
|
32
|
+
if windows?
|
33
|
+
data = `wmic os get manufacturer,name,organization,osarchitecture,version /format:list`
|
34
|
+
data = data.split("\n").reject{ |r| r.strip == '' }.map do |m|
|
35
|
+
spl = m.split('=')
|
36
|
+
[spl.first.to_clean_sym.downcase, spl[1..-1].join('=')]
|
37
|
+
end.to_h
|
38
|
+
data[:name] = data[:name].split('|').first
|
39
|
+
data[:osarchitecture] = data[:osarchitecture].extract_integers.first
|
40
|
+
data.hpath_move( 'osarchitecture' => 'bits' )
|
41
|
+
data[:host] = `hostname`.strip
|
42
|
+
data[:os] = os
|
43
|
+
data
|
44
|
+
else
|
45
|
+
release = {}
|
46
|
+
begin
|
47
|
+
# First attempt to get release info uses lsb_release
|
48
|
+
release = `lsb_release -a`.split("\n").map do |l|
|
49
|
+
spl = l.split(':')
|
50
|
+
[
|
51
|
+
spl.first.downcase.to_clean_sym,
|
52
|
+
spl[1..-1].join(':').strip
|
53
|
+
]
|
54
|
+
end.to_h
|
55
|
+
release.hpath_move('description' => 'name', 'release' => 'name', 'distributor_id' => 'manufacturer')
|
56
|
+
rescue
|
57
|
+
# Try finding the release file and parsing it instead of lsb_release
|
58
|
+
begin
|
59
|
+
release = `cat /etc/*release`
|
60
|
+
.split("\n")
|
61
|
+
.reject{ |l| !(l.include?(':') || l.include?('=')) }
|
62
|
+
.map{|l| l.msplit('=',':') }
|
63
|
+
.map{ |a| [a.first.downcase.to_clean_sym, a[1..-1].join(':').uncapsulate] }
|
64
|
+
.to_h
|
65
|
+
rescue
|
66
|
+
# Both attempts failed
|
67
|
+
end
|
68
|
+
end
|
69
|
+
{
|
70
|
+
release: `uname -r`.strip,
|
71
|
+
bits: `uname -r` =~ /x86_64/i ? 64 : 32,
|
72
|
+
host: `uname -n`.strip,
|
73
|
+
os: os
|
74
|
+
}.merge(release)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# The following is Windows specific code
|
79
|
+
if windows?
|
80
|
+
|
81
|
+
def self.parse_wmic cmd
|
82
|
+
`#{cmd} /format:list`
|
83
|
+
.split("\n\n\n").reject(&:empty?)
|
84
|
+
.map{ |l| l.split("\n\n")
|
85
|
+
.map{ |l| spl = l.split('='); [spl.first.strip.downcase.to_clean_sym, spl[1..-1].join('=').strip ] }.to_h
|
86
|
+
}.reject(&:empty?)
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
data/lib/os/bbsys.rb
ADDED
@@ -0,0 +1,238 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
|
4
|
+
module BBLib
|
5
|
+
module OS
|
6
|
+
|
7
|
+
def self.cpu_usages
|
8
|
+
if windows?
|
9
|
+
{
|
10
|
+
total: `wmic cpu get loadpercentage /format:value`.extract_numbers.first.to_f
|
11
|
+
}
|
12
|
+
elsif linux? || mac?
|
13
|
+
system_stats[:cpu]
|
14
|
+
else
|
15
|
+
nil
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.uptime
|
20
|
+
if windows?
|
21
|
+
uptime = `net statistics server`.split("\n").find{|l| l.start_with?('Statistics since ')}.split(/since /i).last.strip
|
22
|
+
Time.now - Time.strptime(uptime, '%m/%d/%Y %l:%M:%S %p')
|
23
|
+
else
|
24
|
+
`cat /proc/uptime`.extract_numbers.first
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.cpu_used_p
|
29
|
+
cpu_usages[:total]
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.cpu_free_p
|
33
|
+
100 - cpu_used
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.mem_total
|
37
|
+
if windows?
|
38
|
+
`wmic computersystem get TotalPhysicalMemory`.extract_numbers.first / 1024.0
|
39
|
+
elsif linux?
|
40
|
+
system_stats.hpath('memory.total')
|
41
|
+
else
|
42
|
+
nil
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.mem_used
|
47
|
+
mem_total.to_f - mem_free.to_f
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.mem_used_p
|
51
|
+
(mem_used.to_f / mem_total.to_f) * 100.0
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.mem_free
|
55
|
+
if windows?
|
56
|
+
`wmic os get freephysicalmemory /format:value`.extract_numbers.first
|
57
|
+
elsif linux?
|
58
|
+
system_stats.hpath('memory.free')
|
59
|
+
else
|
60
|
+
nil
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.mem_free_p
|
65
|
+
(mem_free.to_f / mem_total.to_f) * 100.0
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.system_stats
|
69
|
+
if windows?
|
70
|
+
memfree = mem_free
|
71
|
+
memtotal = mem_total
|
72
|
+
memused = memtotal - memfree
|
73
|
+
{
|
74
|
+
cpu: cpu_usages,
|
75
|
+
memory: {
|
76
|
+
free: memfree,
|
77
|
+
used: memused,
|
78
|
+
total: memtotal,
|
79
|
+
free_p: (memfree / memtotal.to_f) * 100,
|
80
|
+
used_p: (memused / memtotal.to_f) * 100
|
81
|
+
},
|
82
|
+
uptime: uptime
|
83
|
+
}
|
84
|
+
else
|
85
|
+
stats = `top -b -n2 -d 0.1`.split("\n")
|
86
|
+
cpu = stats.find_all{|l| l =~ /\A\%?Cpu\(s\)/i }.last.extract_numbers
|
87
|
+
loads = stats.find_all{|l| l =~ / load average\: /i }.last.scan(/load average:.*/i).first.extract_numbers
|
88
|
+
mem = stats.find_all{|l| l =~ /KiB Mem|Mem\:/i }.last.extract_numbers
|
89
|
+
time = `cat /proc/uptime`.extract_numbers
|
90
|
+
{
|
91
|
+
cpu: {
|
92
|
+
user: cpu[0],
|
93
|
+
system: cpu[1],
|
94
|
+
nice: cpu[2],
|
95
|
+
total: cpu[0..2].inject(0){ |sum, v| sum += v.to_f },
|
96
|
+
idle: cpu[3],
|
97
|
+
wait: cpu[4],
|
98
|
+
hardware_interrupts: cpu[5],
|
99
|
+
software_interrupts: cpu[6],
|
100
|
+
hypervisor: cpu[7]
|
101
|
+
},
|
102
|
+
uptime: time[0],
|
103
|
+
uptime_idle: time[1],
|
104
|
+
memory: {
|
105
|
+
free: mem[1],
|
106
|
+
used: mem[2],
|
107
|
+
total: mem[0],
|
108
|
+
cache: mem[3],
|
109
|
+
free_p: (mem[1] / mem[0].to_f) * 100,
|
110
|
+
used_p: (mem[2] / mem[0].to_f) * 100
|
111
|
+
},
|
112
|
+
load_average: {
|
113
|
+
1 => loads[0],
|
114
|
+
5 => loads[1],
|
115
|
+
15 => loads[2]
|
116
|
+
}
|
117
|
+
}
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def self.processes
|
122
|
+
if windows?
|
123
|
+
tasks = `tasklist /v`
|
124
|
+
cpu = `wmic path win32_perfformatteddata_perfproc_process get PercentProcessorTime,percentusertime,IDProcess /format:list`
|
125
|
+
cpu = cpu.split("\n\n\n\n").reject(&:empty?)
|
126
|
+
.map{ |l| l.scan(/\d+/).map(&:to_i)}
|
127
|
+
.map{ |n|[ n[0], {cpu: n[1], user: n[2] }]}.to_h
|
128
|
+
lines = tasks.split("\n")[3..-1].map{ |l| l.split(/\s{2,}/) }
|
129
|
+
mem = mem_total
|
130
|
+
cmds = `wmic process get processid,commandline /format:csv`.split("\n")[1..-1].reject{ |r| r.strip == ''}.map{ |l| l.split(',')[1..-1] }.map{ |l| [l.last.to_i, l[0..-2].join(',')]}.to_h
|
131
|
+
lines.map do |l|
|
132
|
+
pid = l[1].extract_numbers.first
|
133
|
+
{
|
134
|
+
name: l[0],
|
135
|
+
pid: pid,
|
136
|
+
user: l[4],
|
137
|
+
mem: (((l[3].gsub(',', '').extract_numbers.first / mem_total) * 100) rescue nil),
|
138
|
+
cpu: (cpu[pid][:cpu] rescue nil),
|
139
|
+
cmd: cmds[pid]
|
140
|
+
}
|
141
|
+
end
|
142
|
+
else
|
143
|
+
t = `ps -e -o comm,pid,ruser,%cpu,%mem,cmd`
|
144
|
+
lines = t.split("\n")[1..-1].map{ |l| l.split(/\s+/) }
|
145
|
+
lines.map{ |l| l.size == 6 ? l : [l[0], l[1], l[2], l[3], l[4], l[5..-1].join(' ')] }
|
146
|
+
lines.map do |l|
|
147
|
+
{
|
148
|
+
name: l[0],
|
149
|
+
pid: l[1].to_i,
|
150
|
+
user: l[2],
|
151
|
+
cpu: l[3].to_f,
|
152
|
+
mem: l[4].to_f,
|
153
|
+
cmd: l[5]
|
154
|
+
}
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def self.filesystems
|
160
|
+
if windows?
|
161
|
+
types = {
|
162
|
+
0 => 'Unknown',
|
163
|
+
1 => 'No Root Directory',
|
164
|
+
2 => 'Removable Disk',
|
165
|
+
3 => 'Local Disk',
|
166
|
+
4 => 'Network Drive',
|
167
|
+
5 => 'Compact Disc',
|
168
|
+
6 => 'RAM Disk'
|
169
|
+
}
|
170
|
+
parse_wmic('wmic logicaldisk get name,description,filesystem,freespace,size,volumename,volumeserialnumber,providername,drivetype')
|
171
|
+
.map do |v|
|
172
|
+
v.hpath_move(
|
173
|
+
'freespace' => 'free',
|
174
|
+
'providername' => 'provider',
|
175
|
+
'volumename' => 'volume_name',
|
176
|
+
'volumeserialnumber' => 'serial_number',
|
177
|
+
'filesystem' => 'filesystem_type',
|
178
|
+
'name' => 'disk'
|
179
|
+
)
|
180
|
+
dt = v.delete(:drivetype).to_i
|
181
|
+
v[:type] = types[dt]
|
182
|
+
if (2..4) === dt
|
183
|
+
v[:free] = v[:free].to_i
|
184
|
+
v[:size] = v[:size].to_i
|
185
|
+
v[:used] = v[:size] - v[:free]
|
186
|
+
v[:free_p] = (v[:free] / v[:size].to_f) * 100
|
187
|
+
v[:used_p] = (v[:used] / v[:size].to_f) * 100
|
188
|
+
end
|
189
|
+
v
|
190
|
+
end
|
191
|
+
else
|
192
|
+
`df -aTB 1`
|
193
|
+
.split("\n")[1..-1]
|
194
|
+
.map{ |l| l.split(/\s{2,}|(?<=\d)\s|(?<=%)\s|(?<=\-)\s|(?<=\w)\s+(?=\w+\s+\d)/)}
|
195
|
+
.map do |i|
|
196
|
+
{
|
197
|
+
filesystem: i[0],
|
198
|
+
type: i[1],
|
199
|
+
size: i[2].to_i,
|
200
|
+
used: i[3].to_i,
|
201
|
+
available: i[4].to_i,
|
202
|
+
used_p: i[5].extract_integers.first.to_f,
|
203
|
+
mount: i[6],
|
204
|
+
}
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
|
210
|
+
# A mostly platform agnostic call to get root volumes
|
211
|
+
def self.root_volumes
|
212
|
+
if BBLib.windows?
|
213
|
+
begin # For windows
|
214
|
+
`wmic logicaldisk get name`.split("\n").map{ |m| m.strip }[1..-1].reject{ |r| r == '' }
|
215
|
+
rescue
|
216
|
+
begin # Windows attempt 2
|
217
|
+
`fsutil fsinfo drives`.scan(/(?<=\s)\w\:/)
|
218
|
+
rescue
|
219
|
+
nil
|
220
|
+
end
|
221
|
+
end
|
222
|
+
else
|
223
|
+
begin
|
224
|
+
`ls /`.split("\n").map{ |m| m.strip }.reject{ |r| r == '' }
|
225
|
+
rescue # All attempts failed
|
226
|
+
nil
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
# Windows only method to get the volume labels of disk drives
|
232
|
+
def self.root_volume_labels
|
233
|
+
return nil unless BBLib.windows?
|
234
|
+
`wmic logicaldisk get caption,volumename`.split("\n")[1..-1].map{ |m| [m.split(" ").first.to_s.strip, m.split(" ")[1..-1].to_a.join(' ').strip] }.reject{ |o,t| o == '' }.to_h
|
235
|
+
end
|
236
|
+
|
237
|
+
end
|
238
|
+
end
|