vete 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
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
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ def setup
4
+ @time = Time.now
5
+ end
6
+
7
+ def perform(slot, task)
8
+ sleep rand
9
+ secs = Time.now - @time
10
+ exit 4 if rand < 0.02
11
+ end
12
+
13
+ require_relative "../lib/vete"
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: []