ruby-progress 1.2.0 ā 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 +59 -169
- data/DEMO_SCRIPTS.md +162 -0
- data/Gemfile.lock +1 -1
- data/README.md +116 -53
- data/Rakefile +7 -0
- data/bin/fill +10 -0
- data/bin/prg +50 -1033
- 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/utils.rb +16 -2
- data/lib/ruby-progress/version.rb +8 -4
- data/lib/ruby-progress/worm.rb +2 -177
- 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
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'optparse'
|
|
4
|
+
require 'fileutils'
|
|
5
|
+
require_relative 'cli/fill_options'
|
|
6
|
+
|
|
7
|
+
module RubyProgress
|
|
8
|
+
# CLI module for Fill command
|
|
9
|
+
module FillCLI
|
|
10
|
+
class << self
|
|
11
|
+
def run
|
|
12
|
+
trap('INT') do
|
|
13
|
+
Utils.show_cursor
|
|
14
|
+
exit
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
options = RubyProgress::FillCLI::Options.parse_cli_options
|
|
18
|
+
|
|
19
|
+
# Handle basic output flags first
|
|
20
|
+
if options[:help]
|
|
21
|
+
puts RubyProgress::FillCLI::Options.help_text
|
|
22
|
+
exit
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
if options[:version]
|
|
26
|
+
puts "Fill version #{RubyProgress::FILL_VERSION}"
|
|
27
|
+
exit
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
if options[:show_styles]
|
|
31
|
+
show_fill_styles
|
|
32
|
+
exit
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Handle daemon control first
|
|
36
|
+
if options[:status] || options[:stop]
|
|
37
|
+
pid_file = options[:pid_file] || '/tmp/ruby-progress/fill.pid'
|
|
38
|
+
if options[:status]
|
|
39
|
+
Daemon.show_status(pid_file)
|
|
40
|
+
else
|
|
41
|
+
Daemon.stop_daemon_by_pid_file(pid_file)
|
|
42
|
+
end
|
|
43
|
+
exit
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Parse style option
|
|
47
|
+
parsed_style = parse_fill_style(options[:style])
|
|
48
|
+
|
|
49
|
+
if options[:daemon]
|
|
50
|
+
run_daemon_mode(options, parsed_style)
|
|
51
|
+
elsif options[:current]
|
|
52
|
+
show_current_percentage(options, parsed_style)
|
|
53
|
+
elsif options[:report]
|
|
54
|
+
show_progress_report(options, parsed_style)
|
|
55
|
+
elsif options[:advance] || options[:complete] || options[:cancel]
|
|
56
|
+
handle_progress_commands(options, parsed_style)
|
|
57
|
+
else
|
|
58
|
+
run_auto_advance_mode(options, parsed_style)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
|
|
64
|
+
def parse_fill_style(style_option)
|
|
65
|
+
case style_option
|
|
66
|
+
when String
|
|
67
|
+
if style_option.start_with?('custom=')
|
|
68
|
+
Fill.parse_custom_style(style_option)
|
|
69
|
+
else
|
|
70
|
+
style_option.to_sym
|
|
71
|
+
end
|
|
72
|
+
else
|
|
73
|
+
style_option
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def run_daemon_mode(options, parsed_style)
|
|
78
|
+
# For daemon mode, detach the process
|
|
79
|
+
PrgCLI.daemonize
|
|
80
|
+
|
|
81
|
+
pid_file = options[:pid_file] || '/tmp/ruby-progress/fill.pid'
|
|
82
|
+
FileUtils.mkdir_p(File.dirname(pid_file))
|
|
83
|
+
File.write(pid_file, Process.pid.to_s)
|
|
84
|
+
|
|
85
|
+
# Create the fill bar and show initial empty state
|
|
86
|
+
fill_options = {
|
|
87
|
+
style: parsed_style,
|
|
88
|
+
length: options[:length],
|
|
89
|
+
ends: options[:ends],
|
|
90
|
+
success: options[:success_message],
|
|
91
|
+
error: options[:error_message]
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
fill_bar = Fill.new(fill_options)
|
|
95
|
+
Fill.hide_cursor
|
|
96
|
+
|
|
97
|
+
begin
|
|
98
|
+
fill_bar.render # Show initial empty bar
|
|
99
|
+
|
|
100
|
+
# Set up signal handlers for daemon control
|
|
101
|
+
stop_requested = false
|
|
102
|
+
Signal.trap('INT') { stop_requested = true }
|
|
103
|
+
Signal.trap('USR1') { stop_requested = true }
|
|
104
|
+
Signal.trap('TERM') { stop_requested = true }
|
|
105
|
+
|
|
106
|
+
# Keep daemon alive until stop requested
|
|
107
|
+
sleep(0.1) until stop_requested
|
|
108
|
+
ensure
|
|
109
|
+
Fill.show_cursor
|
|
110
|
+
FileUtils.rm_f(pid_file)
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def show_current_percentage(options, _parsed_style)
|
|
115
|
+
# Just output the percentage for scripting (default to 50% for demonstration)
|
|
116
|
+
percentage = options[:percent] || 50
|
|
117
|
+
puts percentage.to_f
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def show_progress_report(options, parsed_style)
|
|
121
|
+
# Create a fill bar to demonstrate current progress
|
|
122
|
+
fill_options = {
|
|
123
|
+
style: parsed_style,
|
|
124
|
+
length: options[:length],
|
|
125
|
+
ends: options[:ends]
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
fill_bar = Fill.new(fill_options)
|
|
129
|
+
|
|
130
|
+
# Set percentage (default to 50% for demonstration)
|
|
131
|
+
fill_bar.percent = options[:percent] || 50
|
|
132
|
+
|
|
133
|
+
# Get detailed report
|
|
134
|
+
report = fill_bar.report
|
|
135
|
+
|
|
136
|
+
# Display the current progress bar and detailed status
|
|
137
|
+
fill_bar.render
|
|
138
|
+
puts "\nProgress Report:"
|
|
139
|
+
puts " Progress: #{report[:progress][0]}/#{report[:progress][1]}"
|
|
140
|
+
puts " Percent: #{report[:percent]}%"
|
|
141
|
+
puts " Completed: #{report[:completed] ? 'Yes' : 'No'}"
|
|
142
|
+
puts " Style: #{report[:style]}"
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def handle_progress_commands(_options, _parsed_style)
|
|
146
|
+
# For progress commands, we assume there's a daemon running
|
|
147
|
+
# This is a simplified version - in a real implementation,
|
|
148
|
+
# we'd need IPC to communicate with the daemon
|
|
149
|
+
warn 'Progress commands require daemon mode implementation'
|
|
150
|
+
warn "Run 'prg fill --daemon' first, then use progress commands"
|
|
151
|
+
exit 1
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def run_auto_advance_mode(options, parsed_style)
|
|
155
|
+
fill_options = {
|
|
156
|
+
style: parsed_style,
|
|
157
|
+
length: options[:length],
|
|
158
|
+
ends: options[:ends],
|
|
159
|
+
success: options[:success_message],
|
|
160
|
+
error: options[:error_message]
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
fill_bar = Fill.new(fill_options)
|
|
164
|
+
Fill.hide_cursor
|
|
165
|
+
|
|
166
|
+
begin
|
|
167
|
+
if options[:percent]
|
|
168
|
+
# Set to specific percentage
|
|
169
|
+
fill_bar.percent = options[:percent]
|
|
170
|
+
fill_bar.render
|
|
171
|
+
unless fill_bar.completed?
|
|
172
|
+
# For non-complete percentages, show the result briefly
|
|
173
|
+
sleep(0.1)
|
|
174
|
+
end
|
|
175
|
+
else
|
|
176
|
+
# Auto-advance mode
|
|
177
|
+
sleep_time = case options[:speed]
|
|
178
|
+
when :fast then 0.1
|
|
179
|
+
when :medium, nil then 0.2
|
|
180
|
+
when :slow then 0.5
|
|
181
|
+
when Numeric then 1.0 / options[:speed]
|
|
182
|
+
else 0.3
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
fill_bar.render
|
|
186
|
+
(1..options[:length]).each do
|
|
187
|
+
sleep(sleep_time)
|
|
188
|
+
fill_bar.advance
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
fill_bar.complete
|
|
192
|
+
rescue Interrupt
|
|
193
|
+
fill_bar.cancel
|
|
194
|
+
ensure
|
|
195
|
+
Fill.show_cursor
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def show_fill_styles
|
|
200
|
+
puts "\nAvailable Fill Styles:"
|
|
201
|
+
puts '=' * 50
|
|
202
|
+
|
|
203
|
+
Fill::FILL_STYLES.each do |name, style|
|
|
204
|
+
print "#{name.to_s.ljust(12)} : "
|
|
205
|
+
|
|
206
|
+
# Show a sample progress bar
|
|
207
|
+
filled = style[:full] * 6
|
|
208
|
+
empty = style[:empty] * 4
|
|
209
|
+
puts "[#{filled}#{empty}] (60%)"
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
puts "\nCustom Style:"
|
|
213
|
+
puts "#{'custom=XY'.ljust(12)} : Specify X=empty, Y=full characters"
|
|
214
|
+
puts ' Example: --style custom=.# ā [######....] (60%)'
|
|
215
|
+
puts
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
end
|
data/lib/ruby-progress/utils.rb
CHANGED
|
@@ -49,9 +49,15 @@ module RubyProgress
|
|
|
49
49
|
when :stderr
|
|
50
50
|
warn formatted_message
|
|
51
51
|
when :warn
|
|
52
|
-
|
|
52
|
+
# Ensure we're at the beginning of a fresh line, clear it, then display message
|
|
53
|
+
$stderr.print "\r\e[2K"
|
|
54
|
+
$stderr.flush
|
|
55
|
+
warn formatted_message
|
|
53
56
|
else
|
|
54
|
-
|
|
57
|
+
# Ensure we're at the beginning of a fresh line, clear it, then display message
|
|
58
|
+
$stderr.print "\r\e[2K"
|
|
59
|
+
$stderr.flush
|
|
60
|
+
warn formatted_message
|
|
55
61
|
end
|
|
56
62
|
end
|
|
57
63
|
|
|
@@ -77,5 +83,13 @@ module RubyProgress
|
|
|
77
83
|
|
|
78
84
|
[start_chars, end_chars]
|
|
79
85
|
end
|
|
86
|
+
|
|
87
|
+
# Validate ends string: must be non-empty and even-length (handles multi-byte chars)
|
|
88
|
+
def self.ends_valid?(ends_string)
|
|
89
|
+
return false unless ends_string && !ends_string.empty?
|
|
90
|
+
|
|
91
|
+
chars = ends_string.each_char.to_a
|
|
92
|
+
!chars.empty? && (chars.length % 2).zero?
|
|
93
|
+
end
|
|
80
94
|
end
|
|
81
95
|
end
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module RubyProgress
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
# Main gem version
|
|
5
|
+
VERSION = '1.2.4'
|
|
6
|
+
|
|
7
|
+
# Component-specific versions
|
|
8
|
+
WORM_VERSION = '1.1.2'
|
|
9
|
+
TWIRL_VERSION = '1.1.2'
|
|
10
|
+
RIPPLE_VERSION = '1.1.2'
|
|
11
|
+
FILL_VERSION = '1.0.1'
|
|
8
12
|
end
|
data/lib/ruby-progress/worm.rb
CHANGED
|
@@ -4,6 +4,7 @@ require 'optparse'
|
|
|
4
4
|
require 'open3'
|
|
5
5
|
require 'json'
|
|
6
6
|
require_relative 'utils'
|
|
7
|
+
require_relative 'cli/worm_runner'
|
|
7
8
|
|
|
8
9
|
module RubyProgress
|
|
9
10
|
# Animated progress indicator with ripple effect using Unicode combining characters
|
|
@@ -68,183 +69,7 @@ module RubyProgress
|
|
|
68
69
|
@start_chars, @end_chars = RubyProgress::Utils.parse_ends(options[:ends])
|
|
69
70
|
@running = false
|
|
70
71
|
end
|
|
71
|
-
|
|
72
|
-
def animate(message: nil, success: nil, error: nil, &block)
|
|
73
|
-
@message = message if message
|
|
74
|
-
@success_text = success if success
|
|
75
|
-
@error_text = error if error
|
|
76
|
-
@running = true
|
|
77
|
-
|
|
78
|
-
# Set up interrupt handler to ensure cursor is restored
|
|
79
|
-
original_int_handler = Signal.trap('INT') do
|
|
80
|
-
@running = false
|
|
81
|
-
RubyProgress::Utils.clear_line
|
|
82
|
-
RubyProgress::Utils.show_cursor
|
|
83
|
-
exit 130
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
# Hide cursor
|
|
87
|
-
RubyProgress::Utils.hide_cursor
|
|
88
|
-
|
|
89
|
-
animation_thread = Thread.new { animation_loop }
|
|
90
|
-
|
|
91
|
-
begin
|
|
92
|
-
if block_given?
|
|
93
|
-
result = yield
|
|
94
|
-
@running = false
|
|
95
|
-
animation_thread.join
|
|
96
|
-
display_completion_message(@success_text, true)
|
|
97
|
-
result
|
|
98
|
-
else
|
|
99
|
-
animation_thread.join
|
|
100
|
-
end
|
|
101
|
-
rescue StandardError => e
|
|
102
|
-
@running = false
|
|
103
|
-
animation_thread.join
|
|
104
|
-
display_completion_message(@error_text || "Error: #{e.message}", false)
|
|
105
|
-
nil # Return nil instead of re-raising when used as a progress indicator
|
|
106
|
-
ensure
|
|
107
|
-
# Always clear animation line and restore cursor
|
|
108
|
-
$stderr.print "\r\e[2K"
|
|
109
|
-
RubyProgress::Utils.show_cursor
|
|
110
|
-
Signal.trap('INT', original_int_handler) if original_int_handler
|
|
111
|
-
end
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
def run_with_command
|
|
115
|
-
return unless @command
|
|
116
|
-
|
|
117
|
-
exit_code = 0
|
|
118
|
-
stdout_content = nil
|
|
119
|
-
|
|
120
|
-
begin
|
|
121
|
-
stdout_content = animate do
|
|
122
|
-
# Use popen3 instead of capture3 for better signal handling
|
|
123
|
-
Open3.popen3(@command) do |stdin, stdout, stderr, wait_thr|
|
|
124
|
-
stdin.close
|
|
125
|
-
captured_stdout = stdout.read
|
|
126
|
-
stderr_content = stderr.read
|
|
127
|
-
exit_code = wait_thr.value.exitstatus
|
|
128
|
-
|
|
129
|
-
unless wait_thr.value.success?
|
|
130
|
-
error_msg = @error_text || "Command failed with exit code #{exit_code}"
|
|
131
|
-
error_msg += ": #{stderr_content.strip}" if stderr_content && !stderr_content.empty?
|
|
132
|
-
raise StandardError, error_msg
|
|
133
|
-
end
|
|
134
|
-
captured_stdout
|
|
135
|
-
end
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
# Output to stdout if --stdout flag is set
|
|
139
|
-
puts stdout_content if @output_stdout && stdout_content
|
|
140
|
-
rescue StandardError => e
|
|
141
|
-
# animate method handles error display, just exit with proper code
|
|
142
|
-
exit exit_code.nonzero? || 1
|
|
143
|
-
rescue Interrupt
|
|
144
|
-
exit 130
|
|
145
|
-
end
|
|
146
|
-
end
|
|
147
|
-
|
|
148
|
-
def run_indefinitely
|
|
149
|
-
# Set up interrupt handler to ensure cursor is restored
|
|
150
|
-
original_int_handler = Signal.trap('INT') do
|
|
151
|
-
@running = false
|
|
152
|
-
RubyProgress::Utils.clear_line
|
|
153
|
-
RubyProgress::Utils.show_cursor
|
|
154
|
-
exit 130
|
|
155
|
-
end
|
|
156
|
-
|
|
157
|
-
@running = true
|
|
158
|
-
RubyProgress::Utils.hide_cursor
|
|
159
|
-
|
|
160
|
-
begin
|
|
161
|
-
animation_loop
|
|
162
|
-
ensure
|
|
163
|
-
RubyProgress::Utils.show_cursor
|
|
164
|
-
Signal.trap('INT', original_int_handler) if original_int_handler
|
|
165
|
-
end
|
|
166
|
-
end
|
|
167
|
-
|
|
168
|
-
def stop
|
|
169
|
-
@running = false
|
|
170
|
-
end
|
|
171
|
-
|
|
172
|
-
def run_daemon_mode(success_message: nil, show_checkmark: false, control_message_file: nil)
|
|
173
|
-
@running = true
|
|
174
|
-
stop_requested = false
|
|
175
|
-
|
|
176
|
-
# Set up signal handlers
|
|
177
|
-
original_int_handler = Signal.trap('INT') { stop_requested = true }
|
|
178
|
-
Signal.trap('USR1') { stop_requested = true }
|
|
179
|
-
Signal.trap('TERM') { stop_requested = true }
|
|
180
|
-
Signal.trap('HUP') { stop_requested = true }
|
|
181
|
-
|
|
182
|
-
RubyProgress::Utils.hide_cursor
|
|
183
|
-
|
|
184
|
-
begin
|
|
185
|
-
animation_loop_daemon_mode(stop_requested_proc: -> { stop_requested })
|
|
186
|
-
ensure
|
|
187
|
-
RubyProgress::Utils.clear_line
|
|
188
|
-
RubyProgress::Utils.show_cursor
|
|
189
|
-
|
|
190
|
-
# Display stop-time completion message, preferring control file if provided
|
|
191
|
-
final_message = success_message
|
|
192
|
-
final_checkmark = show_checkmark
|
|
193
|
-
final_success = true
|
|
194
|
-
if control_message_file && File.exist?(control_message_file)
|
|
195
|
-
begin
|
|
196
|
-
data = JSON.parse(File.read(control_message_file))
|
|
197
|
-
final_message = data['message'] if data['message']
|
|
198
|
-
final_checkmark = !!data['checkmark'] if data.key?('checkmark')
|
|
199
|
-
final_success = !!data['success'] if data.key?('success')
|
|
200
|
-
rescue StandardError
|
|
201
|
-
# ignore parse errors, fallback to provided message
|
|
202
|
-
ensure
|
|
203
|
-
begin
|
|
204
|
-
File.delete(control_message_file)
|
|
205
|
-
rescue StandardError
|
|
206
|
-
nil
|
|
207
|
-
end
|
|
208
|
-
end
|
|
209
|
-
end
|
|
210
|
-
|
|
211
|
-
if final_message
|
|
212
|
-
RubyProgress::Utils.display_completion(
|
|
213
|
-
final_message,
|
|
214
|
-
success: final_success,
|
|
215
|
-
show_checkmark: final_checkmark,
|
|
216
|
-
output_stream: :stdout
|
|
217
|
-
)
|
|
218
|
-
end
|
|
219
|
-
|
|
220
|
-
Signal.trap('INT', original_int_handler) if original_int_handler
|
|
221
|
-
end
|
|
222
|
-
end
|
|
223
|
-
|
|
224
|
-
def animation_loop_step
|
|
225
|
-
return unless @running
|
|
226
|
-
|
|
227
|
-
@position ||= 0
|
|
228
|
-
@direction ||= 1
|
|
229
|
-
|
|
230
|
-
message_part = @message && !@message.empty? ? "#{@message} " : ''
|
|
231
|
-
$stderr.print "\r\e[2K#{@start_chars}#{message_part}#{generate_dots(@position, @direction)}#{@end_chars}"
|
|
232
|
-
$stderr.flush
|
|
233
|
-
|
|
234
|
-
sleep @speed
|
|
235
|
-
|
|
236
|
-
# Update position and direction
|
|
237
|
-
@position += @direction
|
|
238
|
-
if @position >= @length - 1
|
|
239
|
-
if @direction_mode == :forward_only
|
|
240
|
-
@position = 0
|
|
241
|
-
else
|
|
242
|
-
@direction = -1
|
|
243
|
-
end
|
|
244
|
-
elsif @position <= 0
|
|
245
|
-
@direction = 1
|
|
246
|
-
end
|
|
247
|
-
end
|
|
72
|
+
include WormRunner
|
|
248
73
|
|
|
249
74
|
private
|
|
250
75
|
|
data/lib/ruby-progress.rb
CHANGED
|
@@ -4,6 +4,7 @@ require_relative 'ruby-progress/version'
|
|
|
4
4
|
require_relative 'ruby-progress/utils'
|
|
5
5
|
require_relative 'ruby-progress/ripple'
|
|
6
6
|
require_relative 'ruby-progress/worm'
|
|
7
|
+
require_relative 'ruby-progress/fill'
|
|
7
8
|
require_relative 'ruby-progress/daemon'
|
|
8
9
|
|
|
9
10
|
module RubyProgress
|
data/quick_demo.rb
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Short demo script for interactive/manual testing of ruby-progress features.
|
|
5
|
+
# Focuses on a compact set of examples used during development and quick checks.
|
|
6
|
+
##
|
|
7
|
+
#
|
|
8
|
+
# A shorter, more focused demo script for quick testing and demonstrations.
|
|
9
|
+
# This version focuses on the most impressive features without long pauses.
|
|
10
|
+
# QuickDemo: small, focused demo runner used for development and CI smoke tests.
|
|
11
|
+
class QuickDemo
|
|
12
|
+
def initialize
|
|
13
|
+
@gem_path = File.expand_path('bin/prg', __dir__)
|
|
14
|
+
@lib_path = File.expand_path('lib', __dir__)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def run
|
|
18
|
+
puts "\nš Ruby Progress Gem v1.2.2 - Quick Demo\n"
|
|
19
|
+
puts "=========================================\n\n"
|
|
20
|
+
|
|
21
|
+
# Showcase the most impressive features
|
|
22
|
+
showcase_ends_feature
|
|
23
|
+
showcase_custom_styles
|
|
24
|
+
showcase_direction_control
|
|
25
|
+
showcase_error_handling
|
|
26
|
+
|
|
27
|
+
puts "\n⨠Demo complete! Install with: gem install ruby-progress"
|
|
28
|
+
puts "š GitHub: https://github.com/ttscoff/ruby-progress\n\n"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def showcase_ends_feature
|
|
34
|
+
puts 'šÆ Universal --ends Flag (NEW in v1.2.0)'
|
|
35
|
+
puts '-' * 40
|
|
36
|
+
|
|
37
|
+
puts 'Ripple with brackets:'
|
|
38
|
+
run_cmd("ripple 'Loading...' --ends '[]' --command 'sleep 3' --success 'Bracketed!' --checkmark")
|
|
39
|
+
|
|
40
|
+
puts "\nWorm with angles:"
|
|
41
|
+
run_cmd("worm --length 10 --ends '<<>>' --command 'sleep 3' --success 'Angled!' --checkmark")
|
|
42
|
+
|
|
43
|
+
puts "\nTwirl with emojis:"
|
|
44
|
+
run_cmd("twirl --ends 'ā„ļøš' --command 'sleep 2' --success 'Decorated!'")
|
|
45
|
+
|
|
46
|
+
puts "\n"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def showcase_custom_styles
|
|
50
|
+
puts 'šØ Custom Worm Styles (NEW in v1.2.0)'
|
|
51
|
+
puts '-' * 40
|
|
52
|
+
|
|
53
|
+
puts 'ASCII custom pattern:'
|
|
54
|
+
run_cmd("worm --length 10 --style custom=_-= --command 'sleep 3' --success 'Custom ASCII!'")
|
|
55
|
+
|
|
56
|
+
puts "\nEmoji custom pattern:"
|
|
57
|
+
run_cmd("worm --length 10 --style custom=š¦šØš„ --command 'sleep 3' --success 'Custom emoji!'")
|
|
58
|
+
|
|
59
|
+
puts "\n"
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def showcase_direction_control
|
|
63
|
+
puts 'š Direction Control (NEW in v1.2.0)'
|
|
64
|
+
puts '-' * 40
|
|
65
|
+
|
|
66
|
+
puts 'Forward-only worm:'
|
|
67
|
+
run_cmd("worm --length 10 --direction forward --command 'sleep 4' --success 'Always forward!'")
|
|
68
|
+
|
|
69
|
+
puts "\nCombined features:"
|
|
70
|
+
run_cmd("worm --length 10 --style custom=.*š” --direction forward --ends 'ćć' --command 'sleep 4' --success 'Ultimate combo!'")
|
|
71
|
+
|
|
72
|
+
puts "\n"
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def showcase_error_handling
|
|
76
|
+
puts 'ā Error Handling'
|
|
77
|
+
puts '-' * 40
|
|
78
|
+
|
|
79
|
+
puts 'Graceful failure:'
|
|
80
|
+
run_cmd("ripple 'Processing...' --command 'sleep 2 && exit 1' --error 'Task failed!' --checkmark")
|
|
81
|
+
|
|
82
|
+
puts "\n"
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def print_command(args)
|
|
86
|
+
# ANSI color codes
|
|
87
|
+
cyan = "\e[36m"
|
|
88
|
+
bright_red = "\e[91m"
|
|
89
|
+
bright_white = "\e[97m"
|
|
90
|
+
bright_green = "\e[92m"
|
|
91
|
+
reset = "\e[0m"
|
|
92
|
+
|
|
93
|
+
# Start with the base command
|
|
94
|
+
colored_cmd = "$ #{cyan}prg#{reset}"
|
|
95
|
+
|
|
96
|
+
# Use regex to colorize the command
|
|
97
|
+
# First capture the subcommand (ripple, worm, twirl)
|
|
98
|
+
colored_args = args.gsub(/^(ripple|worm|twirl)/, "#{bright_red}\\1#{reset}")
|
|
99
|
+
|
|
100
|
+
# Colorize flags (--flag-name) in bright white and their values in bright green
|
|
101
|
+
colored_args = colored_args.gsub(/(--[\w-]+)(\s+|=)('[^']*'|"[^"]*"|\S+)/) do |_match|
|
|
102
|
+
flag = ::Regexp.last_match(1)
|
|
103
|
+
separator = ::Regexp.last_match(2)
|
|
104
|
+
value = ::Regexp.last_match(3)
|
|
105
|
+
"#{bright_white}#{flag}#{reset}#{separator}#{bright_green}#{value}#{reset}"
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Colorize short flags (-f) in bright white and their values in bright green
|
|
109
|
+
colored_args = colored_args.gsub(/(-[a-zA-Z])(\s+)('[^']*'|"[^"]*"|\S+)/) do |_match|
|
|
110
|
+
flag = ::Regexp.last_match(1)
|
|
111
|
+
separator = ::Regexp.last_match(2)
|
|
112
|
+
value = ::Regexp.last_match(3)
|
|
113
|
+
"#{bright_white}#{flag}#{reset}#{separator}#{bright_green}#{value}#{reset}"
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
puts "#{colored_cmd} #{colored_args}"
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def run_cmd(args)
|
|
120
|
+
full_cmd = "ruby -I #{@lib_path} #{@gem_path} #{args}"
|
|
121
|
+
print_command(args)
|
|
122
|
+
system(full_cmd)
|
|
123
|
+
puts
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Quick execution
|
|
128
|
+
if __FILE__ == $PROGRAM_NAME
|
|
129
|
+
begin
|
|
130
|
+
QuickDemo.new.run
|
|
131
|
+
rescue Interrupt
|
|
132
|
+
puts "\n\nDemo stopped. Thanks!"
|
|
133
|
+
end
|
|
134
|
+
end
|