ruby-progress 1.3.1 → 1.3.4
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/.rubocop_todo.yml +32 -45
- data/CHANGELOG.md +67 -96
- data/Gemfile +2 -0
- data/Gemfile.lock +7 -1
- data/README.md +48 -0
- data/Rakefile +0 -3
- data/bin/prg +15 -0
- data/demo_screencast.rb +223 -71
- data/lib/ruby-progress/cli/fill_options.rb +57 -1
- data/lib/ruby-progress/cli/job_cli.rb +70 -10
- data/lib/ruby-progress/cli/ripple_cli.rb +34 -11
- data/lib/ruby-progress/cli/ripple_options.rb +16 -0
- data/lib/ruby-progress/cli/twirl_options.rb +22 -0
- data/lib/ruby-progress/cli/twirl_runner.rb +13 -7
- data/lib/ruby-progress/cli/twirl_spinner.rb +18 -2
- data/lib/ruby-progress/cli/worm_cli.rb +14 -5
- data/lib/ruby-progress/cli/worm_options.rb +16 -0
- data/lib/ruby-progress/cli/worm_runner.rb +12 -9
- data/lib/ruby-progress/fill.rb +5 -3
- data/lib/ruby-progress/fill_cli.rb +174 -51
- data/lib/ruby-progress/output_capture.rb +169 -37
- data/lib/ruby-progress/ripple.rb +3 -2
- data/lib/ruby-progress/utils.rb +47 -26
- data/lib/ruby-progress/version.rb +5 -5
- data/lib/ruby-progress/worm.rb +16 -68
- data/screencast +2497 -0
- data/screencast.svg +1 -0
- metadata +31 -2
- data/ruby-progress.gemspec +0 -40
data/demo_screencast.rb
CHANGED
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
# and clear visual separation of different features.
|
|
14
14
|
|
|
15
15
|
require 'io/console'
|
|
16
|
+
require 'shellwords'
|
|
17
|
+
require 'tmpdir'
|
|
16
18
|
|
|
17
19
|
# Demo runner that exercises the major features of the ruby-progress gem
|
|
18
20
|
# used by the documentation and screencast recordings.
|
|
@@ -26,6 +28,11 @@ class ProgressDemo
|
|
|
26
28
|
header: "\e[1;36m", # Bright cyan
|
|
27
29
|
command: "\e[1;33m", # Bright yellow
|
|
28
30
|
description: "\e[0;32m", # Green
|
|
31
|
+
exec: "\e[1;33m", # executable (bright yellow)
|
|
32
|
+
tool: "\e[1;35m", # prg/tool (bright magenta)
|
|
33
|
+
flag: "\e[1;36m", # flags (bright cyan)
|
|
34
|
+
value: "\e[1;37m", # flag values (bright white)
|
|
35
|
+
prompt: "\e[2m", # prompt (dim)
|
|
29
36
|
reset: "\e[0m", # Reset
|
|
30
37
|
dim: "\e[2m" # Dim
|
|
31
38
|
}
|
|
@@ -33,12 +40,13 @@ class ProgressDemo
|
|
|
33
40
|
|
|
34
41
|
def run
|
|
35
42
|
clear_screen
|
|
36
|
-
|
|
43
|
+
|
|
44
|
+
# show_title
|
|
37
45
|
|
|
38
46
|
# Introduction
|
|
39
|
-
show_section_header('Ruby Progress Gem Demo')
|
|
40
|
-
show_description('Demonstrating terminal progress indicators with style!')
|
|
41
|
-
pause_for_narration(3)
|
|
47
|
+
# show_section_header('Ruby Progress Gem Demo')
|
|
48
|
+
# show_description('Demonstrating terminal progress indicators with style!')
|
|
49
|
+
# pause_for_narration(3)
|
|
42
50
|
|
|
43
51
|
# Basic examples for each command
|
|
44
52
|
demo_basic_commands
|
|
@@ -63,17 +71,17 @@ class ProgressDemo
|
|
|
63
71
|
|
|
64
72
|
# Ripple - basic expanding circle animation
|
|
65
73
|
show_demo_header('Ripple', 'Expanding circle animation for tasks with unknown duration')
|
|
66
|
-
run_command("#{ruby_cmd} ripple --command 'sleep
|
|
74
|
+
run_command("#{ruby_cmd} ripple --command 'sleep 2' --success 'Download complete!' --checkmark PROCESSING")
|
|
67
75
|
pause_between_demos
|
|
68
76
|
|
|
69
77
|
# Worm - progress bar animation
|
|
70
78
|
show_demo_header('Worm', 'Animated progress bar for visual feedback')
|
|
71
|
-
run_command("#{ruby_cmd} worm --length
|
|
79
|
+
run_command("#{ruby_cmd} worm --length 4 --command 'sleep 5' --success 'Processing finished!' --checkmark --message 'Loading'")
|
|
72
80
|
pause_between_demos
|
|
73
81
|
|
|
74
82
|
# Twirl - spinning indicator
|
|
75
83
|
show_demo_header('Twirl', 'Classic spinning indicator for quick tasks')
|
|
76
|
-
run_command("#{ruby_cmd} twirl --command 'sleep
|
|
84
|
+
run_command("#{ruby_cmd} twirl --command 'sleep 2' --success 'Task completed!' --checkmark")
|
|
77
85
|
pause_between_demos
|
|
78
86
|
end
|
|
79
87
|
|
|
@@ -83,39 +91,35 @@ class ProgressDemo
|
|
|
83
91
|
# Ripple styles
|
|
84
92
|
show_demo_header('Ripple Styles', 'Different visual patterns')
|
|
85
93
|
show_command_info('Default ripple style')
|
|
86
|
-
run_command("#{ruby_cmd} ripple --command 'sleep 3' --success 'Default style'")
|
|
87
|
-
pause_between_demos(2)
|
|
88
|
-
|
|
89
|
-
show_command_info('Pulse style')
|
|
90
|
-
run_command("#{ruby_cmd} ripple --style pulse --command 'sleep 3' --success 'Pulse style'")
|
|
94
|
+
run_command("#{ruby_cmd} ripple --command 'sleep 3' --success 'Default style' 'Rippling Progress Default Style'")
|
|
91
95
|
pause_between_demos
|
|
92
96
|
|
|
97
|
+
# show_command_info('Pulse style')
|
|
98
|
+
# run_command("#{ruby_cmd} ripple --style pulse --command 'sleep 3' --success 'Pulse style' 'Rippling Progress'")
|
|
99
|
+
# pause_between_demos
|
|
100
|
+
|
|
93
101
|
# Worm styles
|
|
94
102
|
show_demo_header('Worm Styles', 'Various progress bar animations')
|
|
95
103
|
show_command_info('Classic worm style')
|
|
96
|
-
run_command("#{ruby_cmd} worm --length 10 --style classic --command 'sleep 4' --success 'Classic worm'")
|
|
97
|
-
pause_between_demos
|
|
98
|
-
|
|
99
|
-
show_command_info('Emoji worm style')
|
|
100
|
-
run_command("#{ruby_cmd} worm --length 10 --style emoji --command 'sleep 4' --success 'Emoji worm! 🎉'")
|
|
101
|
-
pause_between_demos(2)
|
|
104
|
+
run_command("#{ruby_cmd} worm --length 10 --style classic --command 'sleep 4' --success 'Classic worm' --message 'Classic'")
|
|
105
|
+
pause_between_demos
|
|
102
106
|
|
|
103
107
|
show_command_info('Blocks worm style')
|
|
104
|
-
run_command("#{ruby_cmd} worm --length 10 --style blocks --command 'sleep 4' --success 'Block worm'")
|
|
108
|
+
run_command("#{ruby_cmd} worm --length 10 --style blocks --command 'sleep 4' --success 'Block worm' --message 'Blocks'")
|
|
105
109
|
pause_between_demos
|
|
106
110
|
|
|
107
111
|
# Twirl styles
|
|
108
112
|
show_demo_header('Twirl Styles', 'Different spinning patterns')
|
|
109
113
|
show_command_info('Classic spinner')
|
|
110
|
-
run_command("#{ruby_cmd} twirl --style classic --command 'sleep 3' --success 'Classic spin'")
|
|
111
|
-
pause_between_demos
|
|
114
|
+
run_command("#{ruby_cmd} twirl --style classic --command 'sleep 3' --success 'Classic spin' --message 'Loading'")
|
|
115
|
+
pause_between_demos
|
|
112
116
|
|
|
113
117
|
show_command_info('Dots spinner')
|
|
114
118
|
run_command("#{ruby_cmd} twirl --style dots --command 'sleep 3' --success 'Dotty!'")
|
|
115
|
-
pause_between_demos
|
|
119
|
+
pause_between_demos
|
|
116
120
|
|
|
117
121
|
show_command_info('Arrow spinner')
|
|
118
|
-
run_command("#{ruby_cmd} twirl --style arrow --command 'sleep
|
|
122
|
+
run_command("#{ruby_cmd} twirl --style arrow --command 'sleep 2' --success 'Arrow spin' --message 'Loading'")
|
|
119
123
|
pause_between_demos
|
|
120
124
|
end
|
|
121
125
|
|
|
@@ -125,80 +129,170 @@ class ProgressDemo
|
|
|
125
129
|
# Error handling
|
|
126
130
|
show_demo_header('Error Handling', 'Graceful failure with custom messages')
|
|
127
131
|
show_command_info('Simulating a failed task')
|
|
128
|
-
run_command("#{ruby_cmd} worm --length 10 --command 'sleep
|
|
132
|
+
run_command("#{ruby_cmd} worm --length 10 --command 'sleep 3 && exit 1' --error 'Error message!' --checkmark")
|
|
129
133
|
pause_between_demos
|
|
130
134
|
|
|
131
135
|
# Custom colors (if supported)
|
|
132
136
|
show_demo_header('Success Messages', 'Custom completion messages')
|
|
133
137
|
show_command_info('Custom success message with checkmark')
|
|
134
|
-
run_command("#{ruby_cmd} ripple --command 'sleep 3' --success 'Data synchronized successfully' --checkmark")
|
|
135
|
-
pause_between_demos(2)
|
|
136
|
-
|
|
137
|
-
show_command_info('Different success icon')
|
|
138
|
-
run_command("#{ruby_cmd} twirl --command 'sleep 3' --success 'Backup completed' --icon '✓'")
|
|
138
|
+
run_command("#{ruby_cmd} ripple --command 'sleep 3' --success 'Data synchronized successfully' --checkmark --message 'Syncing data...'")
|
|
139
139
|
pause_between_demos
|
|
140
140
|
|
|
141
|
+
# show_command_info('Different success icon')
|
|
142
|
+
# run_command("#{ruby_cmd} twirl --command 'sleep 3' --success 'Backup completed' --success-icon '✓' --checkmark")
|
|
143
|
+
# pause_between_demos
|
|
144
|
+
|
|
141
145
|
# No completion message
|
|
142
146
|
show_demo_header('Silent Completion', 'Progress without completion messages')
|
|
143
147
|
show_command_info('Silent completion (no message)')
|
|
144
|
-
run_command("#{ruby_cmd} worm --length 10 --command 'sleep
|
|
148
|
+
run_command("#{ruby_cmd} worm --length 10 --command 'sleep 2'")
|
|
145
149
|
pause_between_demos
|
|
146
150
|
end
|
|
147
151
|
|
|
148
152
|
def demo_new_features
|
|
149
|
-
show_section_header('New in v1.2.0 - Enhanced Features')
|
|
153
|
+
# show_section_header('New in v1.2.0 - Enhanced Features')
|
|
150
154
|
|
|
151
155
|
# Universal --ends flag
|
|
152
156
|
show_demo_header('Universal --ends Flag', 'Add decorative start/end characters')
|
|
153
157
|
show_command_info("Ripple with square brackets: --ends '[]'")
|
|
154
|
-
run_command("#{ruby_cmd} ripple --ends '[]' --command 'sleep
|
|
155
|
-
pause_between_demos
|
|
158
|
+
run_command("#{ruby_cmd} ripple --ends '[]' --command 'sleep 3' --success 'Framed ripple!' 'With a frame'")
|
|
159
|
+
pause_between_demos
|
|
156
160
|
|
|
157
|
-
show_command_info("Worm with angle brackets: --ends '<<>>'")
|
|
158
|
-
run_command("#{ruby_cmd} worm --length 10 --ends '<<>>' --command 'sleep 4' --success 'Angled worm!'")
|
|
159
|
-
pause_between_demos
|
|
161
|
+
# show_command_info("Worm with angle brackets: --ends '<<>>'")
|
|
162
|
+
# run_command("#{ruby_cmd} worm --length 10 --ends '<<>>' --command 'sleep 4' --success 'Angled worm!'")
|
|
163
|
+
# pause_between_demos
|
|
160
164
|
|
|
161
|
-
show_command_info("Twirl with emoji decoration: --ends '🎯🎪'")
|
|
162
|
-
run_command("#{ruby_cmd} twirl --ends '🎯🎪' --command 'sleep 3' --success 'Emoji decorated!'")
|
|
163
|
-
pause_between_demos
|
|
165
|
+
# show_command_info("Twirl with emoji decoration: --ends '🎯🎪'")
|
|
166
|
+
# run_command("#{ruby_cmd} twirl --ends '🎯🎪' --command 'sleep 3' --success 'Emoji decorated!'")
|
|
167
|
+
# pause_between_demos
|
|
164
168
|
|
|
165
169
|
# Worm direction control
|
|
166
170
|
show_demo_header('Worm Direction Control', 'Forward-only vs bidirectional movement')
|
|
167
171
|
show_command_info('Bidirectional worm (default back-and-forth)')
|
|
168
|
-
run_command("#{ruby_cmd} worm --length 10 --direction bidirectional --command 'sleep
|
|
169
|
-
pause_between_demos
|
|
172
|
+
run_command("#{ruby_cmd} worm --length 10 --direction bidirectional --command 'sleep 3' --speed fast --success 'Both ways'")
|
|
173
|
+
pause_between_demos
|
|
170
174
|
|
|
171
175
|
show_command_info('Forward-only worm (resets at end)')
|
|
172
|
-
run_command("#{ruby_cmd} worm --length
|
|
176
|
+
run_command("#{ruby_cmd} worm --length 5 --direction forward --command 'sleep 4' --speed fast --success 'Forward only!'")
|
|
173
177
|
pause_between_demos
|
|
174
178
|
|
|
175
179
|
# Custom worm styles
|
|
176
|
-
show_demo_header('Custom Worm Styles', 'User-defined 3-character patterns')
|
|
177
|
-
show_command_info('ASCII custom style: --style custom=_-=')
|
|
178
|
-
run_command("#{ruby_cmd} worm --length 10 --style custom=_-= --command 'sleep 4' --success 'Custom ASCII!'")
|
|
179
|
-
pause_between_demos
|
|
180
|
+
# show_demo_header('Custom Worm Styles', 'User-defined 3-character patterns')
|
|
181
|
+
# show_command_info('ASCII custom style: --style custom=_-=')
|
|
182
|
+
# run_command("#{ruby_cmd} worm --length 10 --style custom=_-= --command 'sleep 4' --success 'Custom ASCII!'")
|
|
183
|
+
# pause_between_demos
|
|
180
184
|
|
|
181
185
|
show_command_info('Unicode custom style: --style custom=▫▪■')
|
|
182
|
-
run_command("#{ruby_cmd} worm --length 10 --style custom=▫▪■ --command 'sleep
|
|
183
|
-
pause_between_demos
|
|
186
|
+
run_command("#{ruby_cmd} worm --length 10 --style custom=▫▪■ --command 'sleep 3' --speed fast --success 'Custom Unicode!'")
|
|
187
|
+
pause_between_demos
|
|
184
188
|
|
|
185
189
|
show_command_info('Emoji custom style: --style custom=🟦🟨🟥')
|
|
186
|
-
run_command("#{ruby_cmd} worm --length 10 --style custom=🟦🟨🟥 --command 'sleep
|
|
190
|
+
run_command("#{ruby_cmd} worm --length 10 --style custom=🟦🟨🟥 --command 'sleep 3' --speed fast --success 'Custom emoji!'")
|
|
187
191
|
pause_between_demos
|
|
188
192
|
|
|
189
193
|
# Combining features
|
|
190
|
-
show_demo_header('Feature Combinations', 'Mixing multiple options together')
|
|
191
|
-
show_command_info('Custom style + direction + ends: the full package!')
|
|
192
|
-
# Split the long command string to avoid RuboCop line-length issues while preserving behavior
|
|
193
|
-
part1 = "#{ruby_cmd} worm --length 10 --style custom=.🟡* --direction forward "
|
|
194
|
-
part2 = "--ends '【】' --command 'sleep 5' --success 'Ultimate combo!' --checkmark"
|
|
195
|
-
run_command(part1 + part2)
|
|
194
|
+
# show_demo_header('Feature Combinations', 'Mixing multiple options together')
|
|
195
|
+
# show_command_info('Custom style + direction + ends: the full package!')
|
|
196
|
+
# # Split the long command string to avoid RuboCop line-length issues while preserving behavior
|
|
197
|
+
# part1 = "#{ruby_cmd} worm --length 10 --style custom=.🟡* --direction forward "
|
|
198
|
+
# part2 = "--ends '【】' --command 'sleep 5' --success 'Ultimate combo!' --checkmark"
|
|
199
|
+
# run_command(part1 + part2)
|
|
200
|
+
# pause_between_demos
|
|
201
|
+
|
|
202
|
+
# v1.3.x additions: output capture and job queue demo snippets
|
|
203
|
+
# show_section_header('New in v1.3.x - Output Capture & Job Queue')
|
|
204
|
+
show_demo_header('Worm --command (output capture)', 'Run a command and reserve terminal rows for output while preserving animation')
|
|
205
|
+
show_command_info('Capture command stdout/stderr into reserved rows:')
|
|
206
|
+
# ensure a trailing space so flags aren't squashed onto the command string
|
|
207
|
+
part_a = %(#{ruby_cmd} worm --style blocks --length 10 --speed fast --command "ruby -e 'for i in 1..3 do; puts \\"line:\#{i}\\"; sleep 1; end'" )
|
|
208
|
+
part_b = "--stdout --success 'Captured!' --checkmark"
|
|
209
|
+
run_command(part_a + part_b)
|
|
210
|
+
pause_between_demos
|
|
211
|
+
|
|
212
|
+
show_command_info('Capture command stdout/stderr and display live:')
|
|
213
|
+
part_a = %(#{ruby_cmd} worm --style blocks --length 10 --speed fast --command "ruby -e 'for i in 1..5 do; puts \\"line:\#{i}\\"; sleep 0.5; end'" )
|
|
214
|
+
part_b = "--output-lines 3 --output-position top --stdout-live --success 'Captured live!' --checkmark"
|
|
215
|
+
run_command(part_a + part_b)
|
|
196
216
|
pause_between_demos
|
|
217
|
+
|
|
218
|
+
# show_demo_header('prg job send (enqueue a job)', 'Send a job to a running daemon using the file-based job queue')
|
|
219
|
+
# show_command_info('Example: create a job payload and atomically enqueue it for the daemon')
|
|
220
|
+
# show_command_info('Use the bundled helper to enqueue control/action jobs:')
|
|
221
|
+
# puts "#{@colors[:command]}$ prg job send --daemon-name demo --advance#{@colors[:reset]}"
|
|
222
|
+
# puts "#{@colors[:command]}$ prg job send --daemon-name demo --percent 42#{@colors[:reset]}"
|
|
223
|
+
# puts
|
|
224
|
+
# show_command_info('Or enqueue a shell command:')
|
|
225
|
+
|
|
226
|
+
# # For the demo we run the worker in the foreground so you can see the
|
|
227
|
+
# # live animation and completion message inline. Daemon mode (started with
|
|
228
|
+
# # `--daemon` or `--daemon-as`) detaches to the background and processes
|
|
229
|
+
# # jobs via the file-based queue; `prg job send` targets a background
|
|
230
|
+
# # daemon and prints the job result JSON, but won't show the daemon's
|
|
231
|
+
# # animation in the foreground.
|
|
232
|
+
# show_command_info('Start a named fill daemon that processes percent/action jobs')
|
|
233
|
+
# # Start the fill daemon in non-detaching background mode so animation remains visible
|
|
234
|
+
# run_command("#{ruby_cmd} fill --daemon-as demo --no-detach --output-lines 3 --output-position top --success 'Captured!' --checkmark")
|
|
235
|
+
# pause_between_demos(1)
|
|
236
|
+
|
|
237
|
+
# show_command_info('We will enqueue several percent actions via a small shell script')
|
|
238
|
+
# demo_script = <<~BASH
|
|
239
|
+
# #!/usr/bin/env bash
|
|
240
|
+
# set -eu
|
|
241
|
+
# echo "Sending percent updates to demo daemon (atomic mktemp+mv writes)"
|
|
242
|
+
|
|
243
|
+
# job_dir="/tmp/ruby-progress/demo.jobs"
|
|
244
|
+
# mkdir -p "$job_dir"
|
|
245
|
+
|
|
246
|
+
# enqueue_percent() {
|
|
247
|
+
# percent=$1
|
|
248
|
+
# # Build JSON payload
|
|
249
|
+
# id=$(uuidgen 2>/dev/null || echo "job-$(date +%s%N)")
|
|
250
|
+
# tmp=$(mktemp "$job_dir/${id}.json.tmp.XXXXXX")
|
|
251
|
+
# printf '%s\n' '{"id":"'"${id}"'","action":"percent","value":'"${percent}"'}' > "$tmp"
|
|
252
|
+
# mv "$tmp" "$job_dir/${id}.json"
|
|
253
|
+
|
|
254
|
+
# # Wait for the daemon to process the job (poll for .processing.result)
|
|
255
|
+
# result_path="$job_dir/${id}.json.processing.result"
|
|
256
|
+
# start=$(date +%s)
|
|
257
|
+
# timeout=10
|
|
258
|
+
# while [ ! -f "$result_path" ]; do
|
|
259
|
+
# sleep 0.1
|
|
260
|
+
# now=$(date +%s)
|
|
261
|
+
# if [ $((now - start)) -gt $timeout ]; then
|
|
262
|
+
# echo "Timed out waiting for result for job ${id}" >&2
|
|
263
|
+
# return 2
|
|
264
|
+
# fi
|
|
265
|
+
# done
|
|
266
|
+
# cat "$result_path"
|
|
267
|
+
# }
|
|
268
|
+
|
|
269
|
+
# enqueue_percent 10
|
|
270
|
+
# sleep 1
|
|
271
|
+
# enqueue_percent 40
|
|
272
|
+
# sleep 1
|
|
273
|
+
# enqueue_percent 70
|
|
274
|
+
# sleep 1
|
|
275
|
+
# enqueue_percent 100
|
|
276
|
+
|
|
277
|
+
# # After updates, stop the daemon cleanly (call local bin/prg to avoid global conflicts)
|
|
278
|
+
# #{ruby_cmd} fill --stop-id demo --stop-success 'Demo daemon stopped'
|
|
279
|
+
# BASH
|
|
280
|
+
|
|
281
|
+
# # Show the simulated script contents
|
|
282
|
+
# puts "#{@colors[:command]}$ cat demo_percent_updates.sh#{@colors[:reset]}"
|
|
283
|
+
# puts demo_script
|
|
284
|
+
|
|
285
|
+
# # Write and execute the script (run in a subshell so output doesn't interleave too badly)
|
|
286
|
+
# script_path = File.join(Dir.tmpdir, "demo_percent_updates_#{Time.now.to_i}.sh")
|
|
287
|
+
# File.write(script_path, demo_script)
|
|
288
|
+
# File.chmod(0o755, script_path)
|
|
289
|
+
# run_command("bash #{Shellwords.escape(script_path)}")
|
|
290
|
+
# pause_between_demos
|
|
197
291
|
end
|
|
198
292
|
|
|
199
293
|
def show_finale
|
|
200
294
|
show_section_header('Demo Complete!')
|
|
201
|
-
show_description('Ruby Progress Gem v1.2
|
|
295
|
+
show_description('Ruby Progress Gem v1.3.2 - Making terminal progress beautiful! 🚀')
|
|
202
296
|
puts
|
|
203
297
|
show_description('Key features demonstrated:')
|
|
204
298
|
puts "#{@colors[:description]} • Three animation types: ripple, worm, twirl#{@colors[:reset]}"
|
|
@@ -217,18 +311,20 @@ class ProgressDemo
|
|
|
217
311
|
# Utility methods
|
|
218
312
|
|
|
219
313
|
def ruby_cmd
|
|
220
|
-
"ruby -I #{@lib_path} #{@gem_path}"
|
|
314
|
+
# "ruby -I #{@lib_path} #{@gem_path}"
|
|
315
|
+
'bin/prg'
|
|
221
316
|
end
|
|
222
317
|
|
|
223
318
|
def clear_screen
|
|
224
319
|
system('clear') || system('cls')
|
|
320
|
+
system('tput cup 15 0')
|
|
225
321
|
end
|
|
226
322
|
|
|
227
323
|
def show_title
|
|
228
324
|
puts
|
|
229
325
|
puts "#{@colors[:header]}#{'=' * 60}#{@colors[:reset]}"
|
|
230
326
|
puts "#{@colors[:header]} RUBY PROGRESS GEM - DEMO SCREENCAST#{@colors[:reset]}"
|
|
231
|
-
puts "#{@colors[:header]} Version 1.2
|
|
327
|
+
puts "#{@colors[:header]} Version 1.3.2 Feature Demonstration#{@colors[:reset]}"
|
|
232
328
|
puts "#{@colors[:header]}#{'=' * 60}#{@colors[:reset]}"
|
|
233
329
|
puts
|
|
234
330
|
end
|
|
@@ -257,19 +353,75 @@ class ProgressDemo
|
|
|
257
353
|
end
|
|
258
354
|
|
|
259
355
|
def run_command(cmd)
|
|
260
|
-
|
|
356
|
+
# Type the command with syntax highlighting
|
|
357
|
+
type_command("$ #{cmd.sub(%r{^bin/}, '')}")
|
|
261
358
|
puts
|
|
262
359
|
system(cmd)
|
|
263
360
|
puts
|
|
264
361
|
end
|
|
265
362
|
|
|
363
|
+
# Type out a shell command with simple token-based syntax highlighting.
|
|
364
|
+
# - executable (first token) uses @colors[:exec]
|
|
365
|
+
# - flags (tokens starting with '-') use @colors[:flag]
|
|
366
|
+
# - values use @colors[:value]
|
|
367
|
+
# The function prints one character at a time to simulate typing.
|
|
368
|
+
def type_command(line, speed: 0.02)
|
|
369
|
+
tokens = line.scan(/'[^']*'|"[^"]*"|\S+/)
|
|
370
|
+
pos = 0
|
|
371
|
+
|
|
372
|
+
# Determine the executable token position (skip leading prompt '$' tokens)
|
|
373
|
+
exec_pos = tokens.find_index { |t| !t.start_with?('$') }
|
|
374
|
+
subcmd_pos = exec_pos ? exec_pos + 1 : nil
|
|
375
|
+
|
|
376
|
+
tokens.each_with_index do |token, idx|
|
|
377
|
+
# Find token in original line starting at pos (to preserve spacing)
|
|
378
|
+
start_idx = line.index(token, pos) || pos
|
|
379
|
+
|
|
380
|
+
# print any intermediate whitespace
|
|
381
|
+
inter = line[pos...start_idx]
|
|
382
|
+
$stdout.print(inter) if inter && !inter.empty?
|
|
383
|
+
|
|
384
|
+
# Default color for tokens is value
|
|
385
|
+
color = @colors[:value]
|
|
386
|
+
|
|
387
|
+
if token.start_with?('$')
|
|
388
|
+
color = @colors[:prompt]
|
|
389
|
+
elsif idx == exec_pos && token == 'prg'
|
|
390
|
+
# Highlight the bundled tool name in magenta
|
|
391
|
+
color = @colors[:tool]
|
|
392
|
+
elsif idx == exec_pos
|
|
393
|
+
# Executable token (e.g. ruby or other) - keep exec color
|
|
394
|
+
color = @colors[:exec]
|
|
395
|
+
elsif idx == subcmd_pos
|
|
396
|
+
# Subcommand (ripple/twirl/worm/fill etc.) highlighted in yellow
|
|
397
|
+
color = @colors[:command]
|
|
398
|
+
elsif token.start_with?('-')
|
|
399
|
+
color = @colors[:flag]
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
# Print the token one char at a time
|
|
403
|
+
token.each_char do |ch|
|
|
404
|
+
$stdout.print("#{color}#{ch}#{@colors[:reset]}")
|
|
405
|
+
$stdout.flush
|
|
406
|
+
sleep(speed)
|
|
407
|
+
end
|
|
408
|
+
|
|
409
|
+
pos = start_idx + token.length
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
# Print any trailing whitespace/newline
|
|
413
|
+
trailing = line[pos..-1]
|
|
414
|
+
$stdout.print(trailing) if trailing
|
|
415
|
+
$stdout.flush
|
|
416
|
+
end
|
|
417
|
+
|
|
266
418
|
def pause_for_narration(seconds = 2)
|
|
267
|
-
puts "#{@colors[:dim]}[Pausing #{seconds}s for narration...]#{@colors[:reset]}"
|
|
268
|
-
sleep(seconds)
|
|
419
|
+
# puts "#{@colors[:dim]}[Pausing #{seconds}s for narration...]#{@colors[:reset]}"
|
|
420
|
+
# sleep(seconds)
|
|
269
421
|
end
|
|
270
422
|
|
|
271
|
-
def pause_between_demos(seconds =
|
|
272
|
-
puts "#{@colors[:dim]}[Pausing #{seconds}s between demos...]#{@colors[:reset]}"
|
|
423
|
+
def pause_between_demos(seconds = 1)
|
|
424
|
+
# puts "#{@colors[:dim]}[Pausing #{seconds}s between demos...]#{@colors[:reset]}"
|
|
273
425
|
sleep(seconds)
|
|
274
426
|
end
|
|
275
427
|
end
|
|
@@ -278,14 +430,14 @@ end
|
|
|
278
430
|
if __FILE__ == $PROGRAM_NAME
|
|
279
431
|
demo = ProgressDemo.new
|
|
280
432
|
|
|
281
|
-
puts 'Ruby Progress Gem Demo Screencast'
|
|
282
|
-
puts '================================='
|
|
283
|
-
puts
|
|
284
|
-
puts 'This script will demonstrate various features of the ruby-progress gem.'
|
|
285
|
-
puts 'Press ENTER to start the demo, or Ctrl+C to exit.'
|
|
286
|
-
puts
|
|
287
|
-
print 'Ready? '
|
|
288
|
-
gets
|
|
433
|
+
# puts 'Ruby Progress Gem Demo Screencast'
|
|
434
|
+
# puts '================================='
|
|
435
|
+
# puts
|
|
436
|
+
# puts 'This script will demonstrate various features of the ruby-progress gem.'
|
|
437
|
+
# puts 'Press ENTER to start the demo, or Ctrl+C to exit.'
|
|
438
|
+
# puts
|
|
439
|
+
# print 'Ready? '
|
|
440
|
+
# gets
|
|
289
441
|
|
|
290
442
|
begin
|
|
291
443
|
demo.run
|
|
@@ -5,7 +5,9 @@ require 'optparse'
|
|
|
5
5
|
module RubyProgress
|
|
6
6
|
module FillCLI
|
|
7
7
|
# Option parsing extracted to reduce module length in FillCLI
|
|
8
|
+
# rubocop:disable Metrics/AbcSize, Metrics/BlockLength
|
|
8
9
|
module Options
|
|
10
|
+
# rubocop :disable Metrics/MethodLength
|
|
9
11
|
def self.parse_cli_options
|
|
10
12
|
options = {
|
|
11
13
|
style: :blocks,
|
|
@@ -60,6 +62,10 @@ module RubyProgress
|
|
|
60
62
|
options[:output_lines] = n
|
|
61
63
|
end
|
|
62
64
|
|
|
65
|
+
opts.on('--stdout-live', 'Stream captured output to STDOUT as it arrives (non-blocking)') do
|
|
66
|
+
options[:stdout_live] = true
|
|
67
|
+
end
|
|
68
|
+
|
|
63
69
|
opts.separator ''
|
|
64
70
|
opts.separator 'Progress Control:'
|
|
65
71
|
|
|
@@ -106,6 +112,14 @@ module RubyProgress
|
|
|
106
112
|
options[:success_message] = msg
|
|
107
113
|
end
|
|
108
114
|
|
|
115
|
+
opts.on('--success-icon ICON', 'Custom success icon to show with completion messages') do |ic|
|
|
116
|
+
options[:success_icon] = ic
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
opts.on('--error-icon ICON', 'Custom error icon to show with failure messages') do |ic|
|
|
120
|
+
options[:error_icon] = ic
|
|
121
|
+
end
|
|
122
|
+
|
|
109
123
|
opts.on('--error MESSAGE', 'Error message to display on cancellation') do |msg|
|
|
110
124
|
options[:error_message] = msg
|
|
111
125
|
end
|
|
@@ -121,6 +135,21 @@ module RubyProgress
|
|
|
121
135
|
options[:daemon] = true
|
|
122
136
|
end
|
|
123
137
|
|
|
138
|
+
opts.on('--daemon-as NAME', 'Run in daemon mode with custom name (creates /tmp/ruby-progress/NAME.pid)') do |name|
|
|
139
|
+
options[:daemon] = true
|
|
140
|
+
options[:daemon_name] = name
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Accept --daemon-name as alias for --daemon-as
|
|
144
|
+
opts.on('--daemon-name NAME', 'Alias for --daemon-as (compat)') do |name|
|
|
145
|
+
options[:daemon] = true
|
|
146
|
+
options[:daemon_name] = name
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
opts.on('--no-detach', 'When used with --daemon/--daemon-as: run background child but do not fully detach from the terminal') do
|
|
150
|
+
options[:no_detach] = true
|
|
151
|
+
end
|
|
152
|
+
|
|
124
153
|
opts.on('--pid-file FILE', 'PID file location (default: /tmp/ruby-progress/fill.pid)') do |file|
|
|
125
154
|
options[:pid_file] = file
|
|
126
155
|
end
|
|
@@ -129,10 +158,36 @@ module RubyProgress
|
|
|
129
158
|
options[:stop] = true
|
|
130
159
|
end
|
|
131
160
|
|
|
161
|
+
opts.on('--stop-id NAME', 'Stop daemon by name (implies --stop)') do |name|
|
|
162
|
+
# Backwards-compatible shorthand used in demos: set stop flag
|
|
163
|
+
options[:stop] = true
|
|
164
|
+
# Normalize to canonical keys used by FillCLI (daemon_name/status_name)
|
|
165
|
+
options[:stop_name] = name
|
|
166
|
+
options[:daemon_name] = name
|
|
167
|
+
options[:status_name] = name
|
|
168
|
+
end
|
|
169
|
+
|
|
132
170
|
opts.on('--status', 'Show daemon status') do
|
|
133
171
|
options[:status] = true
|
|
134
172
|
end
|
|
135
173
|
|
|
174
|
+
opts.on('--status-id NAME', 'Show daemon status by name') do |name|
|
|
175
|
+
options[:status] = true
|
|
176
|
+
# Normalize to canonical key
|
|
177
|
+
options[:status_name] = name
|
|
178
|
+
options[:daemon_name] = name
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
opts.on('--stop-success MESSAGE', 'Stop daemon with success message (implies --stop)') do |msg|
|
|
182
|
+
options[:stop] = true
|
|
183
|
+
options[:stop_success] = msg
|
|
184
|
+
end
|
|
185
|
+
opts.on('--stop-error MESSAGE', 'Stop daemon with error message (implies --stop)') do |msg|
|
|
186
|
+
options[:stop] = true
|
|
187
|
+
options[:stop_error] = msg
|
|
188
|
+
end
|
|
189
|
+
opts.on('--stop-checkmark', 'When stopping, include a success checkmark') { options[:stop_checkmark] = true }
|
|
190
|
+
|
|
136
191
|
opts.separator ''
|
|
137
192
|
opts.separator 'General:'
|
|
138
193
|
|
|
@@ -155,9 +210,9 @@ module RubyProgress
|
|
|
155
210
|
warn "Run 'prg fill --help' for more information."
|
|
156
211
|
exit 1
|
|
157
212
|
end
|
|
158
|
-
|
|
159
213
|
options
|
|
160
214
|
end
|
|
215
|
+
# rubocop :enable Metrics/MethodLength
|
|
161
216
|
|
|
162
217
|
def self.help_text
|
|
163
218
|
opts = OptionParser.new
|
|
@@ -211,5 +266,6 @@ module RubyProgress
|
|
|
211
266
|
opts.to_s
|
|
212
267
|
end
|
|
213
268
|
end
|
|
269
|
+
# rubocop:enable Metrics/AbcSize, Metrics/BlockLength
|
|
214
270
|
end
|
|
215
271
|
end
|
|
@@ -28,12 +28,43 @@ module JobCLI
|
|
|
28
28
|
options = { wait: false }
|
|
29
29
|
opt = OptionParser.new do |o|
|
|
30
30
|
o.banner = 'Usage: prg job send [options]'
|
|
31
|
-
o.on('--pid-file PATH', 'Path to daemon pid file')
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
o.on('--
|
|
35
|
-
|
|
36
|
-
|
|
31
|
+
o.on('--pid-file PATH', 'Path to daemon pid file') do |v|
|
|
32
|
+
options[:pid_file] = v
|
|
33
|
+
end
|
|
34
|
+
o.on('--daemon-name NAME', 'Daemon name (maps to /tmp/ruby-progress/NAME.pid)') do |v|
|
|
35
|
+
options[:daemon_name] = v
|
|
36
|
+
end
|
|
37
|
+
o.on('--command CMD', 'Command to run') do |v|
|
|
38
|
+
options[:command] = v
|
|
39
|
+
end
|
|
40
|
+
o.on('--stdin', 'Read command from stdin (overrides --command)') do
|
|
41
|
+
options[:stdin] = true
|
|
42
|
+
end
|
|
43
|
+
o.on('--advance', 'Send an advance action (no value)') do
|
|
44
|
+
options[:action] = 'advance'
|
|
45
|
+
end
|
|
46
|
+
o.on('--percent N', Integer, 'Send a percent action with value N') do |v|
|
|
47
|
+
options[:action] = 'percent'
|
|
48
|
+
options[:value] = v
|
|
49
|
+
end
|
|
50
|
+
o.on('--complete', 'Send a complete action (no value)') do
|
|
51
|
+
options[:action] = 'complete'
|
|
52
|
+
end
|
|
53
|
+
o.on('--cancel', 'Send a cancel action (no value)') do
|
|
54
|
+
options[:action] = 'cancel'
|
|
55
|
+
end
|
|
56
|
+
o.on('--action ACTION', 'Send a custom action name') do |v|
|
|
57
|
+
options[:action] = v
|
|
58
|
+
end
|
|
59
|
+
o.on('--value VAL', 'Value for the action (string or number)') do |v|
|
|
60
|
+
options[:value] = v
|
|
61
|
+
end
|
|
62
|
+
o.on('--wait', 'Wait for result file and print it') do
|
|
63
|
+
options[:wait] = true
|
|
64
|
+
end
|
|
65
|
+
o.on('--timeout SECONDS', Integer, 'Timeout seconds for wait') do |v|
|
|
66
|
+
options[:timeout] = v
|
|
67
|
+
end
|
|
37
68
|
end
|
|
38
69
|
|
|
39
70
|
rest = opt.parse(argv)
|
|
@@ -63,16 +94,25 @@ module JobCLI
|
|
|
63
94
|
opts[:command]
|
|
64
95
|
end
|
|
65
96
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
97
|
+
is_action = !opts[:action].nil? && opts[:action] != false
|
|
98
|
+
|
|
99
|
+
if is_action
|
|
100
|
+
if cmd && !cmd.strip.empty?
|
|
101
|
+
warn 'Cannot specify both --command/--stdin and an action flag'
|
|
102
|
+
exit 1
|
|
103
|
+
end
|
|
104
|
+
else
|
|
105
|
+
unless cmd && !cmd.strip.empty?
|
|
106
|
+
warn 'No command specified. Use --command, --stdin, or pass an action flag.'
|
|
107
|
+
exit 1
|
|
108
|
+
end
|
|
69
109
|
end
|
|
70
110
|
|
|
71
111
|
job_id = SecureRandom.uuid
|
|
72
112
|
tmp = File.join(job_dir, "#{job_id}.json.tmp")
|
|
73
113
|
final = File.join(job_dir, "#{job_id}.json")
|
|
74
114
|
|
|
75
|
-
payload =
|
|
115
|
+
payload = build_payload(opts, job_id, cmd)
|
|
76
116
|
|
|
77
117
|
File.write(tmp, JSON.dump(payload))
|
|
78
118
|
FileUtils.mv(tmp, final)
|
|
@@ -96,4 +136,24 @@ module JobCLI
|
|
|
96
136
|
puts job_id
|
|
97
137
|
end
|
|
98
138
|
end
|
|
139
|
+
|
|
140
|
+
# Build the JSON payload for a job based on parsed options.
|
|
141
|
+
def self.build_payload(opts, job_id, cmd)
|
|
142
|
+
payload = { 'id' => job_id }
|
|
143
|
+
|
|
144
|
+
is_action = !opts[:action].nil? && opts[:action] != false
|
|
145
|
+
|
|
146
|
+
if is_action
|
|
147
|
+
payload['action'] = opts[:action]
|
|
148
|
+
if opts.key?(:value)
|
|
149
|
+
val = opts[:value]
|
|
150
|
+
payload['value'] = val.to_i if val.is_a?(String) && val =~ /^\d+$/
|
|
151
|
+
payload['value'] ||= val
|
|
152
|
+
end
|
|
153
|
+
else
|
|
154
|
+
payload['command'] = cmd
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
payload
|
|
158
|
+
end
|
|
99
159
|
end
|