ruby-progress 1.1.9 → 1.2.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/CHANGELOG.md +63 -122
- data/DEMO_SCRIPTS.md +162 -0
- data/Gemfile.lock +1 -1
- data/README.md +201 -62
- data/Rakefile +7 -0
- data/bin/fill +10 -0
- data/bin/prg +50 -1009
- data/demo_screencast.rb +296 -0
- data/experimental_terminal.rb +7 -0
- data/lib/ruby-progress/cli/fill_options.rb +193 -0
- data/lib/ruby-progress/cli/ripple_cli.rb +150 -0
- data/lib/ruby-progress/cli/ripple_options.rb +148 -0
- data/lib/ruby-progress/cli/twirl_cli.rb +173 -0
- data/lib/ruby-progress/cli/twirl_options.rb +136 -0
- data/lib/ruby-progress/cli/twirl_runner.rb +130 -0
- data/lib/ruby-progress/cli/twirl_spinner.rb +78 -0
- data/lib/ruby-progress/cli/worm_cli.rb +75 -0
- data/lib/ruby-progress/cli/worm_options.rb +156 -0
- data/lib/ruby-progress/cli/worm_runner.rb +260 -0
- data/lib/ruby-progress/fill.rb +211 -0
- data/lib/ruby-progress/fill_cli.rb +219 -0
- data/lib/ruby-progress/ripple.rb +4 -2
- data/lib/ruby-progress/utils.rb +32 -2
- data/lib/ruby-progress/version.rb +8 -4
- data/lib/ruby-progress/worm.rb +43 -178
- data/lib/ruby-progress.rb +1 -0
- data/quick_demo.rb +134 -0
- data/readme_demo.rb +128 -0
- data/ruby-progress.gemspec +40 -0
- data/scripts/run_matrix_mise.fish +41 -0
- data/test_daemon_interruption.rb +2 -0
- data/test_daemon_orphan.rb +1 -0
- metadata +21 -1
data/demo_screencast.rb
ADDED
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Ruby Progress Gem Demo Screencast Script
|
|
5
|
+
# ========================================
|
|
6
|
+
#
|
|
7
|
+
# This script creates a comprehensive demonstration of the ruby-progress gem
|
|
8
|
+
# showing all major features across the three subcommands: ripple, worm, and twirl.
|
|
9
|
+
#
|
|
10
|
+
# Usage: ruby demo_screencast.rb
|
|
11
|
+
#
|
|
12
|
+
# The script includes pauses between demonstrations to allow for narration
|
|
13
|
+
# and clear visual separation of different features.
|
|
14
|
+
|
|
15
|
+
require 'io/console'
|
|
16
|
+
|
|
17
|
+
# Demo runner that exercises the major features of the ruby-progress gem
|
|
18
|
+
# used by the documentation and screencast recordings.
|
|
19
|
+
class ProgressDemo
|
|
20
|
+
def initialize
|
|
21
|
+
@gem_path = File.expand_path('bin/prg', __dir__)
|
|
22
|
+
@lib_path = File.expand_path('lib', __dir__)
|
|
23
|
+
|
|
24
|
+
# Colors for terminal output
|
|
25
|
+
@colors = {
|
|
26
|
+
header: "\e[1;36m", # Bright cyan
|
|
27
|
+
command: "\e[1;33m", # Bright yellow
|
|
28
|
+
description: "\e[0;32m", # Green
|
|
29
|
+
reset: "\e[0m", # Reset
|
|
30
|
+
dim: "\e[2m" # Dim
|
|
31
|
+
}
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def run
|
|
35
|
+
clear_screen
|
|
36
|
+
show_title
|
|
37
|
+
|
|
38
|
+
# Introduction
|
|
39
|
+
show_section_header('Ruby Progress Gem Demo')
|
|
40
|
+
show_description('Demonstrating terminal progress indicators with style!')
|
|
41
|
+
pause_for_narration(3)
|
|
42
|
+
|
|
43
|
+
# Basic examples for each command
|
|
44
|
+
demo_basic_commands
|
|
45
|
+
|
|
46
|
+
# Style demonstrations
|
|
47
|
+
demo_styles
|
|
48
|
+
|
|
49
|
+
# Advanced options
|
|
50
|
+
demo_advanced_options
|
|
51
|
+
|
|
52
|
+
# New v1.2.0 features
|
|
53
|
+
demo_new_features
|
|
54
|
+
|
|
55
|
+
# Finale
|
|
56
|
+
show_finale
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
|
|
61
|
+
def demo_basic_commands
|
|
62
|
+
show_section_header('Basic Commands Overview')
|
|
63
|
+
|
|
64
|
+
# Ripple - basic expanding circle animation
|
|
65
|
+
show_demo_header('Ripple', 'Expanding circle animation for tasks with unknown duration')
|
|
66
|
+
run_command("#{ruby_cmd} ripple --command 'sleep 4' --success 'Download complete!' --checkmark")
|
|
67
|
+
pause_between_demos
|
|
68
|
+
|
|
69
|
+
# Worm - progress bar animation
|
|
70
|
+
show_demo_header('Worm', 'Animated progress bar for visual feedback')
|
|
71
|
+
run_command("#{ruby_cmd} worm --length 10 --command 'sleep 5' --success 'Processing finished!' --checkmark")
|
|
72
|
+
pause_between_demos
|
|
73
|
+
|
|
74
|
+
# Twirl - spinning indicator
|
|
75
|
+
show_demo_header('Twirl', 'Classic spinning indicator for quick tasks')
|
|
76
|
+
run_command("#{ruby_cmd} twirl --command 'sleep 3' --success 'Task completed!' --checkmark")
|
|
77
|
+
pause_between_demos
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def demo_styles
|
|
81
|
+
show_section_header('Style Variations')
|
|
82
|
+
|
|
83
|
+
# Ripple styles
|
|
84
|
+
show_demo_header('Ripple Styles', 'Different visual patterns')
|
|
85
|
+
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'")
|
|
91
|
+
pause_between_demos
|
|
92
|
+
|
|
93
|
+
# Worm styles
|
|
94
|
+
show_demo_header('Worm Styles', 'Various progress bar animations')
|
|
95
|
+
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(2)
|
|
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)
|
|
102
|
+
|
|
103
|
+
show_command_info('Blocks worm style')
|
|
104
|
+
run_command("#{ruby_cmd} worm --length 10 --style blocks --command 'sleep 4' --success 'Block worm'")
|
|
105
|
+
pause_between_demos
|
|
106
|
+
|
|
107
|
+
# Twirl styles
|
|
108
|
+
show_demo_header('Twirl Styles', 'Different spinning patterns')
|
|
109
|
+
show_command_info('Classic spinner')
|
|
110
|
+
run_command("#{ruby_cmd} twirl --style classic --command 'sleep 3' --success 'Classic spin'")
|
|
111
|
+
pause_between_demos(2)
|
|
112
|
+
|
|
113
|
+
show_command_info('Dots spinner')
|
|
114
|
+
run_command("#{ruby_cmd} twirl --style dots --command 'sleep 3' --success 'Dotty!'")
|
|
115
|
+
pause_between_demos(2)
|
|
116
|
+
|
|
117
|
+
show_command_info('Arrow spinner')
|
|
118
|
+
run_command("#{ruby_cmd} twirl --style arrow --command 'sleep 3' --success 'Arrow spin'")
|
|
119
|
+
pause_between_demos
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def demo_advanced_options
|
|
123
|
+
show_section_header('Advanced Options')
|
|
124
|
+
|
|
125
|
+
# Error handling
|
|
126
|
+
show_demo_header('Error Handling', 'Graceful failure with custom messages')
|
|
127
|
+
show_command_info('Simulating a failed task')
|
|
128
|
+
run_command("#{ruby_cmd} worm --length 10 --command 'sleep 2 && exit 1' --error 'Something went wrong!' --fail-icon")
|
|
129
|
+
pause_between_demos
|
|
130
|
+
|
|
131
|
+
# Custom colors (if supported)
|
|
132
|
+
show_demo_header('Success Messages', 'Custom completion messages')
|
|
133
|
+
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 '✓'")
|
|
139
|
+
pause_between_demos
|
|
140
|
+
|
|
141
|
+
# No completion message
|
|
142
|
+
show_demo_header('Silent Completion', 'Progress without completion messages')
|
|
143
|
+
show_command_info('Silent completion (no message)')
|
|
144
|
+
run_command("#{ruby_cmd} worm --length 10 --command 'sleep 3'")
|
|
145
|
+
pause_between_demos
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def demo_new_features
|
|
149
|
+
show_section_header('New in v1.2.0 - Enhanced Features')
|
|
150
|
+
|
|
151
|
+
# Universal --ends flag
|
|
152
|
+
show_demo_header('Universal --ends Flag', 'Add decorative start/end characters')
|
|
153
|
+
show_command_info("Ripple with square brackets: --ends '[]'")
|
|
154
|
+
run_command("#{ruby_cmd} ripple --ends '[]' --command 'sleep 4' --success 'Framed ripple!'")
|
|
155
|
+
pause_between_demos(2)
|
|
156
|
+
|
|
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(2)
|
|
160
|
+
|
|
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
|
|
164
|
+
|
|
165
|
+
# Worm direction control
|
|
166
|
+
show_demo_header('Worm Direction Control', 'Forward-only vs bidirectional movement')
|
|
167
|
+
show_command_info('Bidirectional worm (default back-and-forth)')
|
|
168
|
+
run_command("#{ruby_cmd} worm --length 10 --direction bidirectional --command 'sleep 5' --success 'Back and forth!'")
|
|
169
|
+
pause_between_demos(2)
|
|
170
|
+
|
|
171
|
+
show_command_info('Forward-only worm (resets at end)')
|
|
172
|
+
run_command("#{ruby_cmd} worm --length 10 --direction forward --command 'sleep 5' --success 'Always forward!'")
|
|
173
|
+
pause_between_demos
|
|
174
|
+
|
|
175
|
+
# 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(2)
|
|
180
|
+
|
|
181
|
+
show_command_info('Unicode custom style: --style custom=▫▪■')
|
|
182
|
+
run_command("#{ruby_cmd} worm --length 10 --style custom=▫▪■ --command 'sleep 4' --success 'Custom Unicode!'")
|
|
183
|
+
pause_between_demos(2)
|
|
184
|
+
|
|
185
|
+
show_command_info('Emoji custom style: --style custom=🟦🟨🟥')
|
|
186
|
+
run_command("#{ruby_cmd} worm --length 10 --style custom=🟦🟨🟥 --command 'sleep 4' --success 'Custom emoji!'")
|
|
187
|
+
pause_between_demos
|
|
188
|
+
|
|
189
|
+
# 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)
|
|
196
|
+
pause_between_demos
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def show_finale
|
|
200
|
+
show_section_header('Demo Complete!')
|
|
201
|
+
show_description('Ruby Progress Gem v1.2.0 - Making terminal progress beautiful! 🚀')
|
|
202
|
+
puts
|
|
203
|
+
show_description('Key features demonstrated:')
|
|
204
|
+
puts "#{@colors[:description]} • Three animation types: ripple, worm, twirl#{@colors[:reset]}"
|
|
205
|
+
puts "#{@colors[:description]} • Multiple built-in styles#{@colors[:reset]}"
|
|
206
|
+
puts "#{@colors[:description]} • Universal --ends flag for decoration#{@colors[:reset]}"
|
|
207
|
+
puts "#{@colors[:description]} • Worm direction control#{@colors[:reset]}"
|
|
208
|
+
puts "#{@colors[:description]} • Custom user-defined styles#{@colors[:reset]}"
|
|
209
|
+
puts "#{@colors[:description]} • Success/error handling#{@colors[:reset]}"
|
|
210
|
+
puts "#{@colors[:description]} • Multi-byte character support (Unicode/emoji)#{@colors[:reset]}"
|
|
211
|
+
puts
|
|
212
|
+
show_description('Install: gem install ruby-progress')
|
|
213
|
+
show_description('GitHub: https://github.com/ttscoff/ruby-progress')
|
|
214
|
+
puts
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
# Utility methods
|
|
218
|
+
|
|
219
|
+
def ruby_cmd
|
|
220
|
+
"ruby -I #{@lib_path} #{@gem_path}"
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def clear_screen
|
|
224
|
+
system('clear') || system('cls')
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
def show_title
|
|
228
|
+
puts
|
|
229
|
+
puts "#{@colors[:header]}#{'=' * 60}#{@colors[:reset]}"
|
|
230
|
+
puts "#{@colors[:header]} RUBY PROGRESS GEM - DEMO SCREENCAST#{@colors[:reset]}"
|
|
231
|
+
puts "#{@colors[:header]} Version 1.2.0 Feature Demonstration#{@colors[:reset]}"
|
|
232
|
+
puts "#{@colors[:header]}#{'=' * 60}#{@colors[:reset]}"
|
|
233
|
+
puts
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
def show_section_header(title)
|
|
237
|
+
puts
|
|
238
|
+
puts "#{@colors[:header]}#{'-' * 50}#{@colors[:reset]}"
|
|
239
|
+
puts "#{@colors[:header]} #{title}#{@colors[:reset]}"
|
|
240
|
+
puts "#{@colors[:header]}#{'-' * 50}#{@colors[:reset]}"
|
|
241
|
+
puts
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
def show_demo_header(title, description)
|
|
245
|
+
puts
|
|
246
|
+
puts "#{@colors[:header]}### #{title}#{@colors[:reset]}"
|
|
247
|
+
puts "#{@colors[:description]}#{description}#{@colors[:reset]}"
|
|
248
|
+
puts
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
def show_command_info(description)
|
|
252
|
+
puts "#{@colors[:dim]}#{description}#{@colors[:reset]}"
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
def show_description(text)
|
|
256
|
+
puts "#{@colors[:description]}#{text}#{@colors[:reset]}"
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
def run_command(cmd)
|
|
260
|
+
puts "#{@colors[:command]}$ #{cmd}#{@colors[:reset]}"
|
|
261
|
+
puts
|
|
262
|
+
system(cmd)
|
|
263
|
+
puts
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
def pause_for_narration(seconds = 2)
|
|
267
|
+
puts "#{@colors[:dim]}[Pausing #{seconds}s for narration...]#{@colors[:reset]}"
|
|
268
|
+
sleep(seconds)
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
def pause_between_demos(seconds = 3)
|
|
272
|
+
puts "#{@colors[:dim]}[Pausing #{seconds}s between demos...]#{@colors[:reset]}"
|
|
273
|
+
sleep(seconds)
|
|
274
|
+
end
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
# Script execution
|
|
278
|
+
if __FILE__ == $PROGRAM_NAME
|
|
279
|
+
demo = ProgressDemo.new
|
|
280
|
+
|
|
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
|
|
289
|
+
|
|
290
|
+
begin
|
|
291
|
+
demo.run
|
|
292
|
+
rescue Interrupt
|
|
293
|
+
puts "\n\nDemo interrupted. Thanks for watching!"
|
|
294
|
+
exit(0)
|
|
295
|
+
end
|
|
296
|
+
end
|
data/experimental_terminal.rb
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
# Experimental terminal line protection for daemon mode
|
|
2
4
|
# This demonstrates potential solutions for the daemon output interruption problem
|
|
3
5
|
|
|
4
6
|
module RubyProgress
|
|
7
|
+
# SmartTerminal: experimental helpers for safer terminal cursor/line operations
|
|
8
|
+
# used by daemon-mode demos and scripts where output can interleave with animations.
|
|
5
9
|
module SmartTerminal
|
|
6
10
|
# Save current cursor position
|
|
7
11
|
def self.save_cursor_position
|
|
@@ -16,6 +20,8 @@ module RubyProgress
|
|
|
16
20
|
end
|
|
17
21
|
|
|
18
22
|
# Get current cursor position (requires terminal interaction)
|
|
23
|
+
# rubocop:disable Naming/AccessorMethodName
|
|
24
|
+
# Intentionally named get_cursor_position because it performs an I/O request
|
|
19
25
|
def self.get_cursor_position
|
|
20
26
|
# This is complex and requires reading from stdin
|
|
21
27
|
# which may not work reliably in daemon mode
|
|
@@ -24,6 +30,7 @@ module RubyProgress
|
|
|
24
30
|
# Would need to read response: "\e[{row};{col}R"
|
|
25
31
|
# But this requires terminal input capability
|
|
26
32
|
end
|
|
33
|
+
# rubocop:enable Naming/AccessorMethodName
|
|
27
34
|
|
|
28
35
|
# Alternative: Use absolute positioning
|
|
29
36
|
def self.position_cursor_absolute(row, col)
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'optparse'
|
|
4
|
+
|
|
5
|
+
module RubyProgress
|
|
6
|
+
module FillCLI
|
|
7
|
+
# Option parsing extracted to reduce module length in FillCLI
|
|
8
|
+
module Options
|
|
9
|
+
def self.parse_cli_options
|
|
10
|
+
options = {
|
|
11
|
+
style: :blocks,
|
|
12
|
+
length: 20,
|
|
13
|
+
speed: :medium,
|
|
14
|
+
percent: nil,
|
|
15
|
+
advance: false,
|
|
16
|
+
complete: false,
|
|
17
|
+
cancel: false,
|
|
18
|
+
success_message: nil,
|
|
19
|
+
error_message: nil,
|
|
20
|
+
checkmark: false,
|
|
21
|
+
daemon: false,
|
|
22
|
+
pid_file: nil,
|
|
23
|
+
stop: false,
|
|
24
|
+
status: false,
|
|
25
|
+
current: false,
|
|
26
|
+
report: false
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
begin
|
|
30
|
+
OptionParser.new do |opts|
|
|
31
|
+
opts.banner = 'Usage: prg fill [options]'
|
|
32
|
+
opts.separator ''
|
|
33
|
+
opts.separator 'Progress Bar Options:'
|
|
34
|
+
|
|
35
|
+
opts.on('-l', '--length LENGTH', Integer, 'Progress bar length (default: 20)') do |length|
|
|
36
|
+
options[:length] = length
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
opts.on('-s', '--style STYLE', 'Progress bar style (blocks, classic, dots, etc. or custom=XY)') do |style|
|
|
40
|
+
options[:style] = style
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
opts.on('--ends CHARS', 'Start/end characters (even number of chars, split in half)') do |chars|
|
|
44
|
+
options[:ends] = chars
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
opts.separator ''
|
|
48
|
+
opts.separator 'Progress Control:'
|
|
49
|
+
|
|
50
|
+
opts.on('-p', '--percent PERCENT', Float, 'Set progress to percentage (0-100)') do |percent|
|
|
51
|
+
options[:percent] = percent
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
opts.on('--advance', 'Advance progress by one step') do
|
|
55
|
+
options[:advance] = true
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
opts.on('--complete', 'Complete the progress bar') do
|
|
59
|
+
options[:complete] = true
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
opts.on('--cancel', 'Cancel the progress bar') do
|
|
63
|
+
options[:cancel] = true
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
opts.on('--current', 'Show current progress percentage (0-100 float)') do
|
|
67
|
+
options[:current] = true
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
opts.on('--report', 'Show detailed progress report') do
|
|
71
|
+
options[:report] = true
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
opts.separator ''
|
|
75
|
+
opts.separator 'Auto-advance Mode:'
|
|
76
|
+
|
|
77
|
+
opts.on('--speed SPEED', 'Auto-advance speed (fast/medium/slow or numeric)') do |speed|
|
|
78
|
+
options[:speed] = case speed.downcase
|
|
79
|
+
when /^f/ then :fast
|
|
80
|
+
when /^m/ then :medium
|
|
81
|
+
when /^s/ then :slow
|
|
82
|
+
else speed.to_f.positive? ? speed.to_f : :medium
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
opts.separator ''
|
|
87
|
+
opts.separator 'Messages:'
|
|
88
|
+
|
|
89
|
+
opts.on('--success MESSAGE', 'Success message to display on completion') do |msg|
|
|
90
|
+
options[:success_message] = msg
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
opts.on('--error MESSAGE', 'Error message to display on cancellation') do |msg|
|
|
94
|
+
options[:error_message] = msg
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
opts.on('--checkmark', 'Show checkmarks (✅ success, 🛑 failure)') do
|
|
98
|
+
options[:checkmark] = true
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
opts.separator ''
|
|
102
|
+
opts.separator 'Daemon Mode:'
|
|
103
|
+
|
|
104
|
+
opts.on('--daemon', 'Run in background daemon mode') do
|
|
105
|
+
options[:daemon] = true
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
opts.on('--pid-file FILE', 'PID file location (default: /tmp/ruby-progress/fill.pid)') do |file|
|
|
109
|
+
options[:pid_file] = file
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
opts.on('--stop', 'Stop daemon') do
|
|
113
|
+
options[:stop] = true
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
opts.on('--status', 'Show daemon status') do
|
|
117
|
+
options[:status] = true
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
opts.separator ''
|
|
121
|
+
opts.separator 'General:'
|
|
122
|
+
|
|
123
|
+
opts.on('--show-styles', 'Show available fill styles with visual previews') do
|
|
124
|
+
options[:show_styles] = true
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
opts.on('-v', '--version', 'Show version') do
|
|
128
|
+
options[:version] = true
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
opts.on('-h', '--help', 'Show this help') do
|
|
132
|
+
options[:help] = true
|
|
133
|
+
end
|
|
134
|
+
end.parse!
|
|
135
|
+
rescue OptionParser::InvalidOption => e
|
|
136
|
+
warn "Invalid option: #{e.args.first}"
|
|
137
|
+
warn ''
|
|
138
|
+
warn 'Usage: prg fill [options]'
|
|
139
|
+
warn "Run 'prg fill --help' for more information."
|
|
140
|
+
exit 1
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
options
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def self.help_text
|
|
147
|
+
opts = OptionParser.new
|
|
148
|
+
|
|
149
|
+
opts.banner = 'Usage: prg fill [options]'
|
|
150
|
+
opts.separator ''
|
|
151
|
+
opts.separator 'Progress Bar Options:'
|
|
152
|
+
|
|
153
|
+
opts.on('-l', '--length LENGTH', Integer, 'Progress bar length (default: 20)')
|
|
154
|
+
opts.on('-s', '--style STYLE', 'Progress bar style (blocks, classic, dots, etc. or custom=XY)')
|
|
155
|
+
opts.on('--ends CHARS', 'Start/end characters (even number of chars, split in half)')
|
|
156
|
+
|
|
157
|
+
opts.separator ''
|
|
158
|
+
opts.separator 'Progress Control:'
|
|
159
|
+
opts.on('-p', '--percent PERCENT', Float, 'Set progress to percentage (0-100)')
|
|
160
|
+
opts.on('--advance', 'Advance progress by one step')
|
|
161
|
+
opts.on('--complete', 'Complete the progress bar')
|
|
162
|
+
opts.on('--cancel', 'Cancel the progress bar')
|
|
163
|
+
opts.on('--current', 'Show current progress percentage (0-100 float)')
|
|
164
|
+
opts.on('--report', 'Show detailed progress report')
|
|
165
|
+
|
|
166
|
+
opts.separator ''
|
|
167
|
+
opts.separator 'Auto-advance Mode:'
|
|
168
|
+
opts.on('--speed SPEED', 'Auto-advance speed (fast/medium/slow or numeric)')
|
|
169
|
+
|
|
170
|
+
opts.separator ''
|
|
171
|
+
opts.separator 'Messages:'
|
|
172
|
+
opts.on('--success MESSAGE', 'Success message to display on completion')
|
|
173
|
+
opts.on('--error MESSAGE', 'Error message to display on cancellation')
|
|
174
|
+
opts.on('--checkmark', 'Show checkmarks (✅ success, 🛑 failure)')
|
|
175
|
+
|
|
176
|
+
opts.separator ''
|
|
177
|
+
opts.separator 'Daemon Mode:'
|
|
178
|
+
opts.on('--daemon', 'Run in background daemon mode')
|
|
179
|
+
opts.on('--pid-file FILE', 'PID file location (default: /tmp/ruby-progress/fill.pid)')
|
|
180
|
+
opts.on('--stop', 'Stop daemon')
|
|
181
|
+
opts.on('--status', 'Show daemon status')
|
|
182
|
+
|
|
183
|
+
opts.separator ''
|
|
184
|
+
opts.separator 'General:'
|
|
185
|
+
opts.on('--show-styles', 'Show available fill styles with visual previews')
|
|
186
|
+
opts.on('-v', '--version', 'Show version')
|
|
187
|
+
opts.on('-h', '--help', 'Show this help')
|
|
188
|
+
|
|
189
|
+
opts.to_s
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
end
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'fileutils'
|
|
4
|
+
require_relative 'ripple_options'
|
|
5
|
+
|
|
6
|
+
# Enhanced Ripple CLI with unified flags (extracted from bin/prg)
|
|
7
|
+
module RippleCLI
|
|
8
|
+
def self.run
|
|
9
|
+
trap('INT') do
|
|
10
|
+
RubyProgress::Utils.show_cursor
|
|
11
|
+
exit
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
options = RippleCLI::Options.parse_cli_options
|
|
15
|
+
|
|
16
|
+
# Daemon/status/stop handling (process these without requiring text)
|
|
17
|
+
if options[:status]
|
|
18
|
+
pid_file = options[:pid_file] || RubyProgress::Daemon.default_pid_file
|
|
19
|
+
RubyProgress::Daemon.show_status(pid_file)
|
|
20
|
+
exit
|
|
21
|
+
elsif options[:stop]
|
|
22
|
+
pid_file = options[:pid_file] || RubyProgress::Daemon.default_pid_file
|
|
23
|
+
stop_msg = options[:stop_error] || options[:stop_success]
|
|
24
|
+
is_error = !options[:stop_error].nil?
|
|
25
|
+
RubyProgress::Daemon.stop_daemon_by_pid_file(
|
|
26
|
+
pid_file,
|
|
27
|
+
message: stop_msg,
|
|
28
|
+
checkmark: options[:stop_checkmark],
|
|
29
|
+
error: is_error
|
|
30
|
+
)
|
|
31
|
+
exit
|
|
32
|
+
elsif options[:daemon]
|
|
33
|
+
# For daemon mode, detach so shell has no tracked job
|
|
34
|
+
PrgCLI.daemonize
|
|
35
|
+
|
|
36
|
+
# For daemon mode, default message if none provided
|
|
37
|
+
text = options[:message] || ARGV.join(' ')
|
|
38
|
+
text = 'Processing' if text.nil? || text.empty?
|
|
39
|
+
run_daemon_mode(text, options)
|
|
40
|
+
else
|
|
41
|
+
# Non-daemon path requires text
|
|
42
|
+
text = options[:message] || ARGV.join(' ')
|
|
43
|
+
if text.empty?
|
|
44
|
+
puts 'Error: Please provide text to animate via argument or --message flag'
|
|
45
|
+
puts "Example: prg ripple 'Loading...' or prg ripple --message 'Loading...'"
|
|
46
|
+
exit 1
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Convert styles array to individual flags for backward compatibility
|
|
50
|
+
options[:rainbow] = options[:styles].include?(:rainbow)
|
|
51
|
+
options[:inverse] = options[:styles].include?(:inverse)
|
|
52
|
+
options[:caps] = options[:styles].include?(:caps)
|
|
53
|
+
|
|
54
|
+
if options[:command]
|
|
55
|
+
run_with_command(text, options)
|
|
56
|
+
else
|
|
57
|
+
run_indefinitely(text, options)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def self.run_with_command(text, options)
|
|
63
|
+
captured_output = nil
|
|
64
|
+
RubyProgress::Ripple.progress(text, options) do
|
|
65
|
+
captured_output = `#{options[:command]} 2>&1`
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
success = $CHILD_STATUS.success?
|
|
69
|
+
|
|
70
|
+
puts captured_output if options[:output] == :stdout
|
|
71
|
+
if options[:success_message] || options[:complete_checkmark]
|
|
72
|
+
message = success ? options[:success_message] : options[:fail_message] || options[:success_message]
|
|
73
|
+
RubyProgress::Ripple.complete(text, message, options[:complete_checkmark], success)
|
|
74
|
+
end
|
|
75
|
+
exit success ? 0 : 1
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def self.run_indefinitely(text, options)
|
|
79
|
+
rippler = RubyProgress::Ripple.new(text, options)
|
|
80
|
+
RubyProgress::Utils.hide_cursor
|
|
81
|
+
begin
|
|
82
|
+
loop { rippler.advance }
|
|
83
|
+
ensure
|
|
84
|
+
RubyProgress::Utils.show_cursor
|
|
85
|
+
RubyProgress::Ripple.complete(text, options[:success_message], options[:complete_checkmark], true)
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def self.run_daemon_mode(text, options)
|
|
90
|
+
pid_file = options[:pid_file] || RubyProgress::Daemon.default_pid_file
|
|
91
|
+
FileUtils.mkdir_p(File.dirname(pid_file))
|
|
92
|
+
File.write(pid_file, Process.pid.to_s)
|
|
93
|
+
|
|
94
|
+
begin
|
|
95
|
+
# For Ripple, re-use the existing animation loop via a simple loop
|
|
96
|
+
RubyProgress::Utils.hide_cursor
|
|
97
|
+
rippler = RubyProgress::Ripple.new(text, options)
|
|
98
|
+
stop_requested = false
|
|
99
|
+
|
|
100
|
+
Signal.trap('INT') { stop_requested = true }
|
|
101
|
+
Signal.trap('USR1') { stop_requested = true }
|
|
102
|
+
Signal.trap('TERM') { stop_requested = true }
|
|
103
|
+
Signal.trap('HUP') { stop_requested = true }
|
|
104
|
+
|
|
105
|
+
rippler.advance until stop_requested
|
|
106
|
+
ensure
|
|
107
|
+
RubyProgress::Utils.clear_line
|
|
108
|
+
RubyProgress::Utils.show_cursor
|
|
109
|
+
|
|
110
|
+
# If a control message file exists, output its message with optional checkmark
|
|
111
|
+
cmf = RubyProgress::Daemon.control_message_file(pid_file)
|
|
112
|
+
if File.exist?(cmf)
|
|
113
|
+
begin
|
|
114
|
+
data = JSON.parse(File.read(cmf))
|
|
115
|
+
message = data['message']
|
|
116
|
+
check = if data.key?('checkmark')
|
|
117
|
+
data['checkmark'] ? true : false
|
|
118
|
+
else
|
|
119
|
+
false
|
|
120
|
+
end
|
|
121
|
+
success_val = if data.key?('success')
|
|
122
|
+
data['success'] ? true : false
|
|
123
|
+
else
|
|
124
|
+
true
|
|
125
|
+
end
|
|
126
|
+
if message
|
|
127
|
+
RubyProgress::Utils.display_completion(
|
|
128
|
+
message,
|
|
129
|
+
success: success_val,
|
|
130
|
+
show_checkmark: check,
|
|
131
|
+
output_stream: :stdout
|
|
132
|
+
)
|
|
133
|
+
end
|
|
134
|
+
rescue StandardError
|
|
135
|
+
# ignore
|
|
136
|
+
ensure
|
|
137
|
+
begin
|
|
138
|
+
File.delete(cmf)
|
|
139
|
+
rescue StandardError
|
|
140
|
+
nil
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
FileUtils.rm_f(pid_file)
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Options parsing moved to ripple_options.rb
|
|
150
|
+
end
|