vete 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +3 -0
- data/LICENSE +21 -0
- data/README.md +11 -0
- data/lib/vete.rb +182 -0
- data/test/example.rb +13 -0
- data/test/vete.gif +0 -0
- data/vete.gemspec +14 -0
- metadata +49 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 5c68c30f1f9ce2177fb7c5a83ac8fa9f402b387203e2e1de2321a04ec3effa0c
|
4
|
+
data.tar.gz: 246eed50879a2fd260d1f0b18aab85d3ffe953cd6980d9bb5cca1311a789b202
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 55b4b6dd945930575cf37a814ce1c9d7b7dbfbab6e8a306887841d48dd49331f6b302cfbac0d1741e409fd6bac57153fa52d5bbabe5fab4110f736f3e7660136
|
7
|
+
data.tar.gz: 992cd2007a936f4f7ef53b0741011c916aa3b90802a8794c31a48577fb551fadb50adbcb98e6521d0f5e2716a29e74ca650b5b0dd01c0b023585ecabf89696bc
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2023 Steve Shreeve
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
# vete
|
2
|
+
|
3
|
+
Ruby CLI to spawn processes to get work done
|
4
|
+
|
5
|
+
The phrase "¡véte!" in Spanish means, basically, "Get out!". This tool helps to clear out work in a hurry, using a simple approach of spawning a set number of concurrent processes to handle each job. Jobs are defined as files in a directory, so there is no need for a database or any other complexity.
|
6
|
+
|
7
|
+
### Example
|
8
|
+
|
9
|
+
Running the `test/example.rb` script with 10 workers:
|
10
|
+
|
11
|
+
![Example](https://raw.githubusercontent.com/shreeve/vete/main/test/vete.gif)
|
data/lib/vete.rb
ADDED
@@ -0,0 +1,182 @@
|
|
1
|
+
# ============================================================================
|
2
|
+
# vete - Ruby CLI to spawn processes to get work done
|
3
|
+
#
|
4
|
+
# Author: Steve Shreeve (steve.shreeve@gmail.com)
|
5
|
+
# Date: Mar 21, 2023
|
6
|
+
# ============================================================================
|
7
|
+
|
8
|
+
STDOUT.sync = true
|
9
|
+
|
10
|
+
# ==[ Command line ]==========================================================
|
11
|
+
|
12
|
+
require "fileutils"
|
13
|
+
require "optparse"
|
14
|
+
require "thread"
|
15
|
+
|
16
|
+
trap("INT" ) { print clear + go; abort "\n" }
|
17
|
+
|
18
|
+
OptionParser.new.instance_eval do
|
19
|
+
@version = "0.1.0"
|
20
|
+
@banner = "usage: #{program_name} [options]"
|
21
|
+
|
22
|
+
on "-b", "--bar <width>" , "Progress bar width, in characters", Integer
|
23
|
+
on "-c", "--char <character>" , "Character to use for progress bar", String
|
24
|
+
on "-r", "--reset" , "Remove directory used for job processing and quit"
|
25
|
+
on "-h", "--help" , "Show help and command usage" do Kernel.abort to_s; end
|
26
|
+
on "-v", "--version" , "Show version number" do Kernel.abort "#{program_name} #{@version}"; end
|
27
|
+
on "-w", "--workers <count>" , "Set the number of workers (default is 1)", Integer
|
28
|
+
|
29
|
+
self
|
30
|
+
end.parse!(into: opts={}) rescue abort($!.message)
|
31
|
+
|
32
|
+
# populate CLI options
|
33
|
+
@bar = opts[:bar ] || 20
|
34
|
+
@chr = opts[:char ] || "•"; @chr = @chr[0]
|
35
|
+
@rmf = opts[:reset ]
|
36
|
+
@wrk = opts[:workers] || 1
|
37
|
+
|
38
|
+
# define job directories
|
39
|
+
@vete = File.expand_path(".vete")
|
40
|
+
@todo = File.join(@vete, "todo")
|
41
|
+
@live = File.join(@vete, "live")
|
42
|
+
@done = File.join(@vete, "done")
|
43
|
+
@bomb = File.join(@vete, "bomb")
|
44
|
+
|
45
|
+
if @rmf
|
46
|
+
FileUtils.rm_rf @vete
|
47
|
+
exit
|
48
|
+
end
|
49
|
+
|
50
|
+
def move(path, dest)
|
51
|
+
dest = File.join(dest, File.basename(path))
|
52
|
+
FileUtils.mv(path, dest)
|
53
|
+
end
|
54
|
+
|
55
|
+
# ==[ Drawing ]===============================================================
|
56
|
+
|
57
|
+
# https://www.cse.psu.edu/~kxc104/class/cmpen472/16f/hw/hw8/vt100ansi.htm
|
58
|
+
|
59
|
+
def clear ; "\e[2J" ; end
|
60
|
+
def cursor(on) ; print on ? "\e[?25h": "\e[?25l"; end
|
61
|
+
def go(r=1,c=1); "\e[#{r};#{c}H" ; end
|
62
|
+
def go!(...) ; print go(...) ; end
|
63
|
+
|
64
|
+
def fg(rgb=nil); rgb ? "\e[38;2;#{hx(rgb)}m" : "\e[39m"; end
|
65
|
+
def bg(rgb=nil); rgb ? "\e[48;2;#{hx(rgb)}m" : "\e[49m"; end
|
66
|
+
def hx(str=nil); str =~ /\A#?(?:(\h\h)(\h\h)(\h\h)|(\h)(\h)(\h))\z/ or return
|
67
|
+
r, g, b = $1 ? [$1, $2, $3] : [$4*2, $5*2, $6*2]
|
68
|
+
[r.hex, g.hex, b.hex] * ";"
|
69
|
+
end
|
70
|
+
|
71
|
+
def draw(rows, done=0, live=0, bomb=0, jobs=0, info=nil)
|
72
|
+
|
73
|
+
# outer box
|
74
|
+
unless info
|
75
|
+
print [
|
76
|
+
clear,
|
77
|
+
go(2 + rows, @len + 3) + "└" + "─" * (@bar + 2) + "┘\n",
|
78
|
+
go(1 , @len + 3) + "┌" + "─" * (@bar + 2) + "┐\n",
|
79
|
+
].join
|
80
|
+
rows.times {|i| print " %*d │ %*s │\n" % [@len, i + 1, @bar, ""] }
|
81
|
+
return
|
82
|
+
end
|
83
|
+
|
84
|
+
# worker bars
|
85
|
+
dpct = done.to_f / jobs
|
86
|
+
lpct = live.to_f / jobs
|
87
|
+
most = info.values.max
|
88
|
+
info.each do |slot, this|
|
89
|
+
tpct = this.to_f / most
|
90
|
+
cols = dpct * tpct * @bar
|
91
|
+
print go(slot + 1, @len + 5) + bg("5383ec") + @chr * cols
|
92
|
+
end
|
93
|
+
|
94
|
+
# summary bar
|
95
|
+
gcol = dpct * @bar
|
96
|
+
ycol = lpct * @bar
|
97
|
+
print [
|
98
|
+
go(rows + 3, @len + 5),
|
99
|
+
fg("fff"),
|
100
|
+
bg("58a65c") + @chr * ( gcol ) , # green (done)
|
101
|
+
bg("f1bf42") + @chr * ( ycol) , # yellow (live) <= Add live
|
102
|
+
bg("d85140") + " " * (@bar - gcol - ycol).ceil, # red (left)
|
103
|
+
go(rows + 3, @len + 5 + @bar + 3) + " %.1f%% done " % [dpct * 100],
|
104
|
+
bomb == 0 ? nil : (bg + " " + bg("f1bf42") + " #{bomb} bombed "),
|
105
|
+
].join
|
106
|
+
|
107
|
+
# clear colors
|
108
|
+
print fg + bg
|
109
|
+
end
|
110
|
+
|
111
|
+
# ==[ Simulate job creation, add helpers so vete makes this easy ]============
|
112
|
+
|
113
|
+
FileUtils.rm_rf @vete
|
114
|
+
FileUtils.mkdir_p @todo
|
115
|
+
FileUtils.mkdir_p @live
|
116
|
+
FileUtils.mkdir_p @done
|
117
|
+
FileUtils.mkdir_p @bomb
|
118
|
+
|
119
|
+
1.upto(100) {|i| FileUtils.touch(File.join(@todo, i.to_s)) }
|
120
|
+
|
121
|
+
# ==[ Configure workers ]=====================================================
|
122
|
+
|
123
|
+
@len = @wrk.to_s.size
|
124
|
+
@mtx = Mutex.new
|
125
|
+
@que = Thread::Queue.new; @wrk.times {|slot| @que << (slot + 1) }
|
126
|
+
|
127
|
+
begin
|
128
|
+
list = Dir[File.join(@todo, "*")]
|
129
|
+
jobs = list.size
|
130
|
+
info = Hash.new(0)
|
131
|
+
|
132
|
+
setup
|
133
|
+
|
134
|
+
cursor(false)
|
135
|
+
draw(@wrk)
|
136
|
+
|
137
|
+
time = Time.now
|
138
|
+
done = 0
|
139
|
+
live = 0
|
140
|
+
bomb = 0
|
141
|
+
Thread.new do
|
142
|
+
list.each do |path|
|
143
|
+
slot = @que.pop
|
144
|
+
@mtx.synchronize {
|
145
|
+
live += 1
|
146
|
+
}
|
147
|
+
show = "Working on task " + File.basename(path)
|
148
|
+
print go(slot + 1, @len + 5 + @bar + 3) + show
|
149
|
+
if chld = fork # parent
|
150
|
+
Thread.new do
|
151
|
+
okay = Process.waitpid2(chld)[1] == 0
|
152
|
+
move(path, okay ? @done : @bomb)
|
153
|
+
@que.push(slot)
|
154
|
+
@mtx.synchronize {
|
155
|
+
done += 1
|
156
|
+
live -= 1
|
157
|
+
bomb += 1 unless okay
|
158
|
+
info[slot] += 1
|
159
|
+
}
|
160
|
+
end
|
161
|
+
draw(@wrk, done, live, bomb, jobs, info.dup)
|
162
|
+
else
|
163
|
+
perform(slot, path)
|
164
|
+
exit
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end.join
|
168
|
+
draw(@wrk, done, live, bomb, jobs, info)
|
169
|
+
secs = Time.now.to_f - time.to_f
|
170
|
+
|
171
|
+
# summary
|
172
|
+
print [
|
173
|
+
go(@wrk + 5, 1),
|
174
|
+
"%.2f secs" % secs,
|
175
|
+
" for #{jobs} jobs",
|
176
|
+
" by #{@wrk} workers",
|
177
|
+
" @ %.2f jobs/sec" % [jobs / secs]
|
178
|
+
].join + "\n\n"
|
179
|
+
|
180
|
+
ensure
|
181
|
+
cursor(true)
|
182
|
+
end
|
data/test/example.rb
ADDED
data/test/vete.gif
ADDED
Binary file
|
data/vete.gemspec
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = "vete"
|
5
|
+
s.version = `grep -m 1 '^\s*@version' lib/vete.rb | cut -f 2 -d '"'`
|
6
|
+
s.author = "Steve Shreeve"
|
7
|
+
s.email = "steve.shreeve@gmail.com"
|
8
|
+
s.summary =
|
9
|
+
s.description = "Ruby CLI to spawn processes to get work done"
|
10
|
+
s.homepage = "https://github.com/shreeve/vete"
|
11
|
+
s.license = "MIT"
|
12
|
+
s.files = `git ls-files`.split("\n") - %w[.gitignore]
|
13
|
+
# s.executables = `cd bin && git ls-files .`.split("\n")
|
14
|
+
end
|
metadata
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: vete
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Steve Shreeve
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-03-21 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: Ruby CLI to spawn processes to get work done
|
14
|
+
email: steve.shreeve@gmail.com
|
15
|
+
executables: []
|
16
|
+
extensions: []
|
17
|
+
extra_rdoc_files: []
|
18
|
+
files:
|
19
|
+
- Gemfile
|
20
|
+
- LICENSE
|
21
|
+
- README.md
|
22
|
+
- lib/vete.rb
|
23
|
+
- test/example.rb
|
24
|
+
- test/vete.gif
|
25
|
+
- vete.gemspec
|
26
|
+
homepage: https://github.com/shreeve/vete
|
27
|
+
licenses:
|
28
|
+
- MIT
|
29
|
+
metadata: {}
|
30
|
+
post_install_message:
|
31
|
+
rdoc_options: []
|
32
|
+
require_paths:
|
33
|
+
- lib
|
34
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
35
|
+
requirements:
|
36
|
+
- - ">="
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: '0'
|
39
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
requirements: []
|
45
|
+
rubygems_version: 3.4.8
|
46
|
+
signing_key:
|
47
|
+
specification_version: 4
|
48
|
+
summary: Ruby CLI to spawn processes to get work done
|
49
|
+
test_files: []
|