ruby-progress 1.3.6 → 1.3.7

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '028f7d94178db61fa5e6fc4353b977b1279504486e83dd540ea63fa9bc7bd6ee'
4
- data.tar.gz: 135e0f649c449fccb49de440e1b4ff33782bb4d1e44e340d818a2c989628d098
3
+ metadata.gz: ed28989b5200a0ba018f976c1b7b90b8077916959633a5e2ea576de25bb6ddb5
4
+ data.tar.gz: f5c0ddea618bad931125e8f3195162e3cae7405c583fe27da5759d0febf30fa1
5
5
  SHA512:
6
- metadata.gz: 721f3ca96efffadf06b20079ad931f9f47c1580506c7fb4e174323353fafed08f855594561fa77b4af2cbc99ba1d23347662a4363363b5364f41908a936b8cf1
7
- data.tar.gz: 262fc8c721794e2f9a13474d00619577c918b70c6af55b30cad2e92d7382af36d3b2d743e3cc3ff0b2159c6498b40ae40f8a057e224f75a12bc385045f23df4e
6
+ metadata.gz: 6722478a39177ac8c8618035abbd0b155afc275eb53120c7c89ff9f7a0c2194a02313efb5cf19ec59205d5f8002435edbef41c7cf5590f7e5f30ad00fa10f02d
7
+ data.tar.gz: 861feef40116e9c4a5f1c18e2455032a286c9c38fd7bccca19d24c1438b2554be90db4b5c820421b87301b4f21a3527a9bb17095c35140cf4ddddd0d8551539a
data/CHANGELOG.md CHANGED
@@ -5,6 +5,22 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.3.7] - 2025-10-22
9
+
10
+ ### Fixed
11
+
12
+ - PTY command spawning now executes via the user's login shell to support shell aliases/functions and ensure PATH customizations are honored.
13
+ - Uses `ENV['SHELL']` when available (fallback `/bin/sh`).
14
+ - `bash`/`zsh`/`sh`: `-lc` is used; `fish`: `-l -c` is used.
15
+ - Resolves `Errno::ENOENT` when running commands like `-c 'r fast'` under `--stdout-live`.
16
+
17
+ ### Changed
18
+
19
+ - Refactored output capture internals:
20
+ - Introduced `RubyProgress::ShellExec.build_shell_argv` for shell argv construction.
21
+ - Extracted terminal reservation to `RubyProgress::OutputUI.reserve_space`.
22
+ - Reduced method complexity and addressed linter warnings without behavior changes.
23
+
8
24
  ## [1.3.6] - 2025-10-15
9
25
 
10
26
  ### Fixed
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- ruby-progress (1.3.6)
4
+ ruby-progress (1.3.7)
5
5
  tty-cursor (~> 0.7)
6
6
  tty-screen (~> 0.8)
7
7
 
data/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
  [![Ruby](https://img.shields.io/badge/ruby-%3E%3D%202.5.0-ruby.svg)](https://www.ruby-lang.org/)
7
7
  <!-- [![Coverage Status](https://img.shields.io/badge/coverage-31%25-yellow.svg)](#) -->
8
8
 
9
- This repository contains three different Ruby progress indicator projects: **Ripple**, **Worm**, and **Twirl**. All provide animated terminal progress indicators with different visual styles and features.
9
+ This repository contains a collection of Ruby progress indicator projects: **Ripple**, **Worm**, **Twirl**, and **Fill**. All provide animated terminal progress indicators with different visual styles and features.
10
10
 
11
11
  ## Table of Contents
12
12
 
@@ -8,6 +8,9 @@ module RubyProgress
8
8
  # rubocop:disable Metrics/AbcSize
9
9
  module Options
10
10
  # rubocop :disable Metrics/MethodLength
11
+ # Parse CLI options for the fill subcommand.
12
+ #
13
+ # @return [Hash] parsed options keyed by symbols
11
14
  def self.parse_cli_options
12
15
  options = {
13
16
  style: :blocks,
@@ -6,6 +6,9 @@ require 'json'
6
6
  module RippleCLI
7
7
  # Option parsing extracted to its own file to reduce module size of RippleCLI.
8
8
  module Options
9
+ # Parse CLI options for the ripple subcommand.
10
+ #
11
+ # @return [Hash] parsed options keyed by symbols
9
12
  def self.parse_cli_options
10
13
  options = {
11
14
  speed: :medium,
@@ -7,6 +7,16 @@ require_relative 'twirl_runner'
7
7
 
8
8
  # Twirl CLI (extracted from bin/prg)
9
9
  module TwirlCLI
10
+ # CLI dispatcher for the Twirl spinner indicator. Parses options and
11
+ # dispatches to runtime helpers in TwirlRunner or controls daemons via
12
+ # RubyProgress::Daemon.
13
+ #
14
+ # Public methods:
15
+ # - .run
16
+ # - .resolve_pid_file
17
+ # - .parse_cli_options
18
+ #
19
+ # @return [void]
10
20
  def self.run
11
21
  trap('INT') do
12
22
  RubyProgress::Utils.show_cursor
@@ -43,6 +53,11 @@ module TwirlCLI
43
53
  end
44
54
 
45
55
  # runtime methods moved to TwirlRunner
56
+ # Resolve the pid file path used for named/unnamed daemons.
57
+ #
58
+ # @param options [Hash]
59
+ # @param name_key [Symbol]
60
+ # @return [String]
46
61
  def self.resolve_pid_file(options, name_key)
47
62
  return options[:pid_file] if options[:pid_file]
48
63
 
@@ -9,6 +9,9 @@ module TwirlCLI
9
9
  # Keeps the CLI option definitions extracted from the main dispatcher
10
10
  # so the `TwirlCLI` module stays small and focused on dispatching.
11
11
  module Options
12
+ # Parse CLI options for the twirl subcommand.
13
+ #
14
+ # @return [Hash] parsed options keyed by symbols
12
15
  def self.parse_cli_options
13
16
  options = {
14
17
  output_position: :above,
@@ -12,6 +12,12 @@ require_relative '../output_capture'
12
12
  # indefinitely, or launching in daemon mode) and were extracted to
13
13
  # reduce module size and improve testability.
14
14
  module TwirlRunner
15
+ # Runtime helper: run the provided command while showing a twirl spinner.
16
+ # Captures output via RubyProgress::OutputCapture when appropriate and
17
+ # prints final completion messages according to options.
18
+ #
19
+ # @param options [Hash] CLI options parsed from TwirlCLI::Options
20
+ # @return [void] exits with appropriate status code (0 success, 1 failure)
15
21
  def self.run_with_command(options)
16
22
  message = options[:message]
17
23
  captured_output = nil
@@ -72,6 +78,10 @@ module TwirlRunner
72
78
  exit success ? 0 : 1
73
79
  end
74
80
 
81
+ # Run the spinner indefinitely until interrupted (SIGINT).
82
+ #
83
+ # @param options [Hash] CLI options used to configure the spinner
84
+ # @return [void] exits 130 on interrupt
75
85
  def self.run_indefinitely(options)
76
86
  message = options[:message]
77
87
  spinner = TwirlSpinner.new(message, options)
@@ -96,6 +106,11 @@ module TwirlRunner
96
106
  end
97
107
  end
98
108
 
109
+ # Run the spinner in daemon mode. Writes a pid file and listens for
110
+ # control messages via the daemon control message file.
111
+ #
112
+ # @param options [Hash]
113
+ # @return [void]
99
114
  def self.run_daemon_mode(options)
100
115
  pid_file = resolve_pid_file(options, :daemon_name)
101
116
  FileUtils.mkdir_p(File.dirname(pid_file))
@@ -150,6 +165,11 @@ module TwirlRunner
150
165
  end
151
166
  end
152
167
 
168
+ # Resolve pid file helper used by run_daemon_mode and CLI.
169
+ #
170
+ # @param options [Hash]
171
+ # @param name_key [Symbol]
172
+ # @return [String]
153
173
  def self.resolve_pid_file(options, name_key)
154
174
  return options[:pid_file] if options[:pid_file]
155
175
 
@@ -6,6 +6,25 @@ require_relative 'worm_options'
6
6
 
7
7
  # Enhanced Worm CLI (extracted from bin/prg)
8
8
  module WormCLI
9
+ # CLI dispatcher for the Worm indicator.
10
+ #
11
+ # Responsibilities:
12
+ # - parse CLI options (delegates to WormCLI::Options)
13
+ # - handle daemonization, status, and stop commands via RubyProgress::Daemon
14
+ # - launch the appropriate runtime mode (command-run, indefinite, daemon)
15
+ #
16
+ # Public methods:
17
+ # - .run
18
+ # - .resolve_pid_file
19
+ # - .run_daemon_mode
20
+
21
+ # Determine the pid file path from options. If options specify a custom
22
+ # :pid_file, return it. If a named daemon key is present, use
23
+ # /tmp/ruby-progress/<name>.pid. Otherwise fall back to the default.
24
+ #
25
+ # @param options [Hash] parsed CLI options
26
+ # @param name_key [Symbol] key used for named daemons (default :daemon_name)
27
+ # @return [String] path to the pid file
9
28
  def self.resolve_pid_file(options, name_key = :daemon_name)
10
29
  return options[:pid_file] if options[:pid_file]
11
30
 
@@ -14,6 +33,10 @@ module WormCLI
14
33
  RubyProgress::Daemon.default_pid_file
15
34
  end
16
35
 
36
+ # Entrypoint for the Worm CLI. Parses options and dispatches to status,
37
+ # stop, daemon, or runtime modes. Ensures the cursor is restored on Ctrl+C.
38
+ #
39
+ # @return [void] exits with appropriate exit codes for status/stop modes.
17
40
  def self.run
18
41
  trap('INT') do
19
42
  RubyProgress::Utils.show_cursor
@@ -53,6 +76,11 @@ module WormCLI
53
76
  end
54
77
  end
55
78
 
79
+ # Launch the worm indicator in daemon mode writing a pid file and
80
+ # monitoring for control messages. Ensures pid file is removed on exit.
81
+ #
82
+ # @param options [Hash] parsed CLI options used to configure the Worm instance
83
+ # @return [void]
56
84
  def self.run_daemon_mode(options)
57
85
  pid_file = resolve_pid_file(options, :daemon_name)
58
86
  FileUtils.mkdir_p(File.dirname(pid_file))
@@ -8,6 +8,9 @@ module WormCLI
8
8
  # Keeps the CLI option definitions for `prg worm` extracted from
9
9
  # the main dispatcher to keep the CLI module small and focused.
10
10
  module Options
11
+ # Parse CLI options for the worm subcommand.
12
+ #
13
+ # @return [Hash] parsed options keyed by symbols
11
14
  def self.parse_cli_options
12
15
  options = {
13
16
  output_position: :above,
@@ -9,14 +9,39 @@ module RubyProgress
9
9
  module Daemon
10
10
  module_function
11
11
 
12
+ # Return the default PID file path used by ruby-progress daemons.
13
+ #
14
+ # @return [String] The filesystem path to the default PID file.
15
+ # @example
16
+ # RubyProgress::Daemon.default_pid_file
17
+ # #=> "/tmp/ruby-progress/progress.pid"
12
18
  def default_pid_file
13
19
  '/tmp/ruby-progress/progress.pid'
14
20
  end
15
21
 
22
+ # Return the path for the control message file corresponding to a PID file.
23
+ # The control message file is used to pass JSON-encoded messages to a running
24
+ # daemon (for example, success/failure metadata or a final message).
25
+ #
26
+ # @param pid_file [String] the path to the pid file
27
+ # @return [String] the control-message file path (pid_file + ".msg")
28
+ # @example
29
+ # RubyProgress::Daemon.control_message_file('/tmp/foo.pid')
30
+ # #=> '/tmp/foo.pid.msg'
16
31
  def control_message_file(pid_file)
17
32
  "#{pid_file}.msg"
18
33
  end
19
34
 
35
+ # Print the daemon status for the given PID file and exit with an appropriate
36
+ # exit code. If the PID file exists and the process is running this prints
37
+ # a message and exits 0. If the PID file exists but the process is not
38
+ # running this prints a warning and exits 1. If the PID file does not
39
+ # exist this prints that the daemon is not running and exits 1.
40
+ #
41
+ # @param pid_file [String] path to the pid file
42
+ # @return [void]
43
+ # @example
44
+ # RubyProgress::Daemon.show_status('/tmp/ruby-progress/progress.pid')
20
45
  def show_status(pid_file)
21
46
  if File.exist?(pid_file)
22
47
  pid = File.read(pid_file).strip
@@ -29,6 +54,23 @@ module RubyProgress
29
54
  end
30
55
  end
31
56
 
57
+ # Stop a running daemon by reading its PID from the given pid file. Optionally
58
+ # write a small JSON control message (containing :checkmark and :message keys)
59
+ # to the corresponding control message file before signalling the process.
60
+ # The method attempts to send SIGUSR1 to the running process, sleeps briefly
61
+ # to allow the process to handle the signal, and then removes the pid file.
62
+ #
63
+ # This helper prints user-friendly errors and exits with non-zero status when
64
+ # the pid file is missing, the process cannot be found, or permission is
65
+ # denied when sending the signal.
66
+ #
67
+ # @param pid_file [String] path to the pid file
68
+ # @param message [String, nil] optional message to send to the daemon via the control file
69
+ # @param checkmark [Boolean] whether to include a checkmark flag in the control payload
70
+ # @param error [Boolean] whether this stop represents an error (affects success flag in payload)
71
+ # @return [void]
72
+ # @example
73
+ # RubyProgress::Daemon.stop_daemon_by_pid_file('/tmp/ruby-progress/progress.pid', message: 'Stopping', checkmark: true)
32
74
  def stop_daemon_by_pid_file(pid_file, message: nil, checkmark: false, error: false)
33
75
  unless File.exist?(pid_file)
34
76
  puts "PID file #{pid_file} not found"
@@ -3,6 +3,16 @@
3
3
  module RubyProgress
4
4
  # Determinate progress bar with customizable fill styles
5
5
  class Fill
6
+ # A simple determinate progress bar that can be advanced programmatically
7
+ # or used with the block-based convenience method {Fill.progress}.
8
+ #
9
+ # Public API (instance): #advance, #percent=, #completed?, #render, #complete, #cancel
10
+ # Public API (class): .progress, .parse_custom_style
11
+ #
12
+ # @example
13
+ # Fill.progress(length: 10) do |bar|
14
+ # 10.times { bar.advance; sleep 0.1 }
15
+ # end
6
16
  # Built-in fill styles with empty and full characters
7
17
  FILL_STYLES = {
8
18
  blocks: { empty: '▱', full: '▰' },
@@ -19,6 +29,14 @@ module RubyProgress
19
29
  attr_reader :length, :style, :current_progress, :start_chars, :end_chars
20
30
  attr_accessor :success_message, :error_message
21
31
 
32
+ # Initialize a progress bar instance.
33
+ #
34
+ # @param options [Hash] configuration values
35
+ # @option options [Integer] :length number of cells in the bar (default 20)
36
+ # @option options [Symbol,String,Hash] :style style name, symbol, or custom hash
37
+ # @option options [String] :success success message shown on completion
38
+ # @option options [String] :error error message shown on cancel/exception
39
+ # @return [void]
22
40
  def initialize(options = {})
23
41
  @length = options[:length] || 20
24
42
  @style = parse_style(options[:style] || :blocks)
@@ -36,7 +54,11 @@ module RubyProgress
36
54
  end
37
55
  end
38
56
 
39
- # Advance the progress bar by one step or specified increment
57
+ # Advance the progress bar by one step or specified increment.
58
+ #
59
+ # @param increment [Integer] amount to increase progress by (default 1)
60
+ # @param percent [Numeric,nil] optional percent to set the bar to (0-100)
61
+ # @return [Boolean] true if the bar is complete after advancing
40
62
  def advance(increment: 1, percent: nil)
41
63
  @current_progress = if percent
42
64
  [@length * percent / 100.0, @length].min.round
@@ -48,7 +70,10 @@ module RubyProgress
48
70
  completed?
49
71
  end
50
72
 
51
- # Set progress to specific percentage (0-100)
73
+ # Set progress to specific percentage (0-100).
74
+ #
75
+ # @param percent [Numeric] percentage 0..100 to set the bar to
76
+ # @return [Boolean] true if the bar is complete after setting percent
52
77
  def percent=(percent)
53
78
  percent = percent.clamp(0, 100) # Clamp between 0-100
54
79
  @current_progress = (@length * percent / 100.0).round
@@ -56,22 +81,30 @@ module RubyProgress
56
81
  completed?
57
82
  end
58
83
 
59
- # Check if progress bar is complete
84
+ # Check if progress bar is complete.
85
+ #
86
+ # @return [Boolean]
60
87
  def completed?
61
88
  @current_progress >= @length
62
89
  end
63
90
 
64
- # Get current progress as percentage
91
+ # Get current progress as percentage.
92
+ #
93
+ # @return [Float] percentage (0.0-100.0) rounded to 1 decimal place
65
94
  def percent
66
95
  (@current_progress.to_f / @length * 100).round(1)
67
96
  end
68
97
 
69
- # Get current progress as float (0.0-100.0) - for scripting
98
+ # Get current progress as float (0.0-100.0) - for scripting.
99
+ #
100
+ # @return [Float]
70
101
  def current
71
102
  (@current_progress.to_f / @length * 100).round(1)
72
103
  end
73
104
 
74
- # Get detailed progress status information
105
+ # Get detailed progress status information.
106
+ #
107
+ # @return [Hash] structured status information about the bar
75
108
  def report
76
109
  {
77
110
  progress: [@current_progress, @length],
@@ -81,7 +114,9 @@ module RubyProgress
81
114
  }
82
115
  end
83
116
 
84
- # Render the current progress bar to stderr
117
+ # Render the current progress bar to stderr.
118
+ #
119
+ # @return [void]
85
120
  def render
86
121
  # First redraw captured output (if any) so it appears above/below the bar
87
122
  @output_capture&.redraw($stderr)
@@ -94,7 +129,11 @@ module RubyProgress
94
129
  $stderr.flush
95
130
  end
96
131
 
97
- # Complete the progress bar and show success message
132
+ # Complete the progress bar and show success message.
133
+ #
134
+ # @param message [String,nil] optional override message
135
+ # @param icons [Hash] optional icons map passed to Utils.display_completion
136
+ # @return [void]
98
137
  def complete(message = nil, icons: {})
99
138
  @current_progress = @length
100
139
  render
@@ -113,7 +152,10 @@ module RubyProgress
113
152
  end
114
153
  end
115
154
 
116
- # Cancel the progress bar and show error message
155
+ # Cancel the progress bar and show error message.
156
+ #
157
+ # @param message [String,nil] optional override message
158
+ # @return [void]
117
159
  def cancel(message = nil)
118
160
  $stderr.print "\r\e[2K" # Clear the progress bar
119
161
  $stderr.flush
@@ -12,6 +12,10 @@ module RubyProgress
12
12
  # rubocop:disable Metrics/ClassLength
13
13
  module FillCLI
14
14
  class << self
15
+ # Entrypoint for the `prg fill` CLI. Parses options and dispatches to
16
+ # the matching behavior (auto-advance, command-run, daemon, report).
17
+ #
18
+ # @return [void]
15
19
  def run
16
20
  trap('INT') do
17
21
  Utils.show_cursor
@@ -13,11 +13,64 @@ rescue LoadError
13
13
  end
14
14
 
15
15
  module RubyProgress
16
+ # Shell execution helpers for spawning commands within a PTY
17
+ module ShellExec
18
+ module_function
19
+
20
+ # Build argv for invoking the user's shell with a command string.
21
+ # Uses login shells so aliases/functions and environment are available.
22
+ #
23
+ # @param shell [String] path to shell executable
24
+ # @param command [String] command to execute
25
+ # @return [Array<String>] argv (excluding the shell path for convenience)
26
+ def build_shell_argv(shell, command)
27
+ shell_name = File.basename(shell)
28
+ case shell_name
29
+ when 'fish'
30
+ ['-l', '-c', command]
31
+ else
32
+ ['-lc', command]
33
+ end
34
+ end
35
+ end
36
+
37
+ # Output helpers for reserving terminal space
38
+ module OutputUI
39
+ module_function
40
+
41
+ def reserve_space(io, position, lines)
42
+ return unless io.tty?
43
+
44
+ if position == :above
45
+ io.print "\e[#{lines}L"
46
+ else
47
+ io.print("\n" * lines)
48
+ io.print "\e[#{lines}A"
49
+ end
50
+
51
+ io.flush
52
+ end
53
+ end
54
+
16
55
  # PTY-based live output capture that reserves a small terminal area
17
56
  # for printing captured output while the animation draws elsewhere.
18
57
  class OutputCapture
19
58
  attr_reader :exit_status
20
59
 
60
+ # Create a new OutputCapture instance.
61
+ #
62
+ # @param command [String] the shell command to spawn and capture via PTY
63
+ # @param lines [Integer] number of reserved lines to keep for captured output (minimum 1)
64
+ # @param position [Symbol,String] :above/:below (or :top/:bottom) to place the reserved area
65
+ # @param log_path [String,nil] optional path to append raw captured output
66
+ # @param stream [Boolean] when true, redraw captured output live into the terminal area
67
+ # @param debug [Boolean,nil] enable debug logging when true; nil will consult ENV['RUBY_PROGRESS_DEBUG']
68
+ # @return [OutputCapture]
69
+ # @example
70
+ # oc = RubyProgress::OutputCapture.new(command: 'bundle exec rspec', lines: 4, position: :below)
71
+ # oc.start
72
+ # oc.wait
73
+ # oc.flush_to($stdout)
21
74
  def initialize(command:, lines: 3, position: :above, log_path: nil, stream: false, debug: nil)
22
75
  @command = command
23
76
  # Coerce lines into a positive Integer
@@ -63,30 +116,49 @@ module RubyProgress
63
116
  end
64
117
 
65
118
  # Start capturing the child process. Returns self.
119
+ #
120
+ # This spawns the configured command in a PTY and begins a background
121
+ # reader thread which buffers the most recent lines. When +stream+ is true
122
+ # the captured lines are redrawn into the terminal area reserved by
123
+ # {#reserve_space}.
66
124
  def start
67
- reserve_space($stderr) if @stream
125
+ OutputUI.reserve_space($stderr, @position, @lines) if @stream
68
126
  @reader_thread = Thread.new { spawn_and_read }
69
127
  self
70
128
  end
71
129
 
130
+ # Signal the reader thread to stop and wait for it to finish.
131
+ # @return [void]
72
132
  def stop
73
133
  @stop = true
74
134
  @reader_thread&.join
75
135
  end
76
136
 
137
+ # Wait for the background reader thread to finish and return control to
138
+ # the caller. This is a simple join wrapper used by callers that need to
139
+ # block until the captured command completes.
140
+ #
141
+ # @return [Thread, nil] the joined thread or nil if not started
77
142
  def wait
78
143
  @reader_thread&.join
79
144
  end
80
145
 
146
+ # Return a snapshot of the currently buffered lines.
147
+ # @return [Array<String>]
81
148
  def lines
82
149
  @buf_mutex.synchronize { @buffer.dup }
83
150
  end
84
151
 
152
+ # Return true when the background reader thread is alive.
153
+ # @return [Boolean]
85
154
  def alive?
86
155
  @reader_thread&.alive? || false
87
156
  end
88
157
 
89
158
  # Redraw the reserved area using the current buffered lines.
159
+ #
160
+ # @param io [IO] the IO stream to draw into (defaults to $stderr)
161
+ # @return [void]
90
162
  def redraw(io = $stderr)
91
163
  buf = lines
92
164
  debug_log("redraw called; buffer=#{buf.size}; lines=#{@lines}; position=#{@position}")
@@ -154,6 +226,9 @@ module RubyProgress
154
226
  # Flush the buffered lines to the given IO (defaults to STDOUT).
155
227
  # This is used when capturing non-live output: capture silently during
156
228
  # the run and emit all captured output at the end.
229
+ #
230
+ # @param io [IO] the IO to write captured lines to (defaults to STDOUT)
231
+ # @return [void]
157
232
  def flush_to(io = $stdout)
158
233
  buf = lines
159
234
  return if buf.empty?
@@ -171,16 +246,35 @@ module RubyProgress
171
246
  private
172
247
 
173
248
  def spawn_and_read
174
- PTY.spawn(@command) do |reader, _writer, pid|
249
+ # Run the command through the user's login shell so that shell aliases,
250
+ # functions, and PATH modifications are available.
251
+ shell = ENV['SHELL'] || '/bin/sh'
252
+ argv = ShellExec.build_shell_argv(shell, @command)
253
+
254
+ debug_log("spawning via shell=#{shell} argv=#{argv.inspect} raw_cmd=#{@command.inspect}")
255
+
256
+ PTY.spawn(shell, *argv) do |reader, _writer, pid|
175
257
  @child_pid = pid
176
258
  debug_log("spawned pid=#{pid} cmd=#{@command}")
177
259
 
178
260
  until reader.eof? || @stop
179
- ready = if reader.respond_to?(:wait_readable)
180
- reader.wait_readable(0.1)
181
- else
182
- IO.select([reader], nil, nil, 0.1)
183
- end
261
+ # Prefer IO#wait_readable when available (avoids Fiber scheduler issues).
262
+ # If not available, attempt to use an underlying IO via #to_io, otherwise
263
+ # fall back to a short sleep to avoid calling deprecated/select APIs.
264
+ io = if reader.respond_to?(:wait_readable)
265
+ reader
266
+ elsif reader.respond_to?(:to_io)
267
+ reader.to_io
268
+ end
269
+
270
+ # Prefer IO#wait_readable when available (use safe navigation).
271
+ if io.respond_to?(:wait_readable)
272
+ ready = io.wait_readable(0.1)
273
+ else
274
+ # Best-effort fallback: sleep briefly and continue reading loop.
275
+ sleep 0.1
276
+ ready = true
277
+ end
184
278
  next unless ready
185
279
 
186
280
  chunk = reader.read_nonblock(4096, exception: false)
@@ -249,25 +343,6 @@ module RubyProgress
249
343
  end
250
344
  end
251
345
 
252
- def reserve_space(io = $stderr)
253
- return unless io.tty?
254
-
255
- debug_log("reserve_space called; position=#{@position.inspect}; lines=#{@lines}")
256
-
257
- if @position == :above
258
- # Insert lines above current cursor using CSI n L
259
- io.print "\e[#{@lines}L"
260
- debug_log("reserve_space: inserted #{@lines} lines for :above")
261
- else
262
- # Print newlines then move cursor back up so animation stays above
263
- io.print("\n" * @lines)
264
- io.print "\e[#{@lines}A"
265
- debug_log("reserve_space: printed #{@lines} newlines and moved up #{@lines} for :below")
266
- end
267
-
268
- io.flush
269
- rescue StandardError => e
270
- debug_log("reserve_space error: #{e.class}: #{e.message}")
271
- end
346
+ # reserve_space moved to RubyProgress::OutputUI
272
347
  end
273
348
  end
@@ -56,6 +56,11 @@ module RubyProgress
56
56
 
57
57
  # String extensions for color support
58
58
  module StringExtensions
59
+ # Extensions that add colorization helpers to String instances.
60
+ # Methods are dynamically defined from the COLORS map (e.g. #red, #green).
61
+ #
62
+ # Example:
63
+ # "hello".red #=> "\e[31mhello\e[0m"
59
64
  COLORS.each do |color_name, color_code|
60
65
  define_method(color_name) do
61
66
  "#{color_code}#{self}#{COLORS['reset']}"
@@ -63,6 +68,11 @@ module RubyProgress
63
68
  end
64
69
 
65
70
  def rainbow(index = 0)
71
+ # Apply a per-character rainbow by cycling through the COLORS map.
72
+ #
73
+ # @param index [Integer] an optional offset to shift the colors
74
+ # @return [String] colorized string where each character is wrapped
75
+ # in a terminal color escape sequence
66
76
  chars = self.chars
67
77
  colored_chars = chars.map.with_index do |char, idx|
68
78
  color = COLORS.values[(idx + index) % COLORS.size]
@@ -72,6 +82,11 @@ module RubyProgress
72
82
  end
73
83
 
74
84
  def normalize_type
85
+ # Attempt to guess a spinner indicator type from this string's
86
+ # characters. Returns a symbol matching one of INDICATORS keys or
87
+ # :classic as fallback.
88
+ #
89
+ # @return [Symbol]
75
90
  spinner_type = :classic
76
91
  INDICATORS.each do |spinner, _v|
77
92
  spinner_type = spinner if spinner =~ /^#{chars.join('.*?')}/i
@@ -82,9 +97,21 @@ module RubyProgress
82
97
 
83
98
  # Text ripple animation class
84
99
  class Ripple
100
+ # Public API:
101
+ # - instance: #advance, #printout
102
+ # - class: .progress (block-based helper), .complete
85
103
  attr_accessor :index, :string, :speed, :format, :inverse, :rainbow, :spinner, :spinner_position, :caps
86
104
 
87
105
  def initialize(string, options = {})
106
+ # Create a new Ripple animation for the given string.
107
+ #
108
+ # @param string [String] the text to animate
109
+ # @param options [Hash] configuration options (speed, format, rainbow, etc.)
110
+ # @option options [Symbol] :speed (:medium) animation speed :fast/:medium/:slow
111
+ # @option options [Symbol] :format (:bidirectional) :bidirectional/:forward_only
112
+ # @option options [Boolean] :rainbow (false) colorize characters individually
113
+ # @option options [Boolean] :spinner (false) use a spinner glyph instead of ripple
114
+ # @return [void]
88
115
  defaults = {
89
116
  speed: :medium,
90
117
  format: :bidirectional,
@@ -109,6 +136,10 @@ module RubyProgress
109
136
  end
110
137
 
111
138
  def printout
139
+ # Render a single frame of the ripple to stderr, preserving any
140
+ # reserved output area managed by OutputCapture.
141
+ #
142
+ # @return [void]
112
143
  letters = @string.dup.chars
113
144
  i = @index
114
145
  if @spinner
@@ -154,7 +185,19 @@ module RubyProgress
154
185
  RubyProgress::Utils.show_cursor
155
186
  end
156
187
 
188
+ # Show the cursor in the terminal. Delegates to Utils.
189
+ #
190
+ # @return [void]
191
+
157
192
  def self.complete(string, message, checkmark, success, icons: {})
193
+ # Display a final completion message for the ripple indicator.
194
+ #
195
+ # @param string [String] fallback message
196
+ # @param message [String,nil] explicit message to show
197
+ # @param checkmark [Boolean] whether to show a checkmark
198
+ # @param success [Boolean] whether the result is success
199
+ # @param icons [Hash] optional icons mapping
200
+ # @return [void]
158
201
  display_message = message || (checkmark ? string : nil)
159
202
  return unless display_message
160
203
 
@@ -197,6 +240,15 @@ module RubyProgress
197
240
  end
198
241
 
199
242
  def self.progress(string, options = {})
243
+ # Block-style helper which runs the ripple animation while the
244
+ # provided block executes. The cursor is hidden for the duration and
245
+ # restored afterwards. This method attempts to return a sensible
246
+ # value depending on the :output option (see code).
247
+ #
248
+ # @param string [String] the label text to animate
249
+ # @param options [Hash] configuration options forwarded to Ripple.new
250
+ # @yield the work to perform while the animation runs
251
+ # @return [Object,nil] returns block result or boolean depending on :output
200
252
  Signal.trap('INT') do
201
253
  Thread.current.kill
202
254
  nil
@@ -1,17 +1,37 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RubyProgress
4
- # Universal terminal utilities shared between progress indicators
4
+ # Universal terminal utilities shared between progress indicators.
5
+ #
6
+ # This module provides common functionality for terminal manipulation,
7
+ # cursor control, and output formatting used across all progress indicator types.
5
8
  module Utils
6
- # Terminal cursor control
9
+ # Hides the terminal cursor.
10
+ #
11
+ # @return [void]
12
+ # @example
13
+ # RubyProgress::Utils.hide_cursor
7
14
  def self.hide_cursor
8
15
  $stderr.print "\e[?25l"
9
16
  end
10
17
 
18
+ # Shows the terminal cursor.
19
+ #
20
+ # @return [void]
21
+ # @example
22
+ # RubyProgress::Utils.show_cursor
11
23
  def self.show_cursor
12
24
  $stderr.print "\e[?25h"
13
25
  end
14
26
 
27
+ # Clears the current terminal line.
28
+ #
29
+ # @param output_stream [Symbol, IO] Stream to clear (:stdout, :stderr, or IO object)
30
+ # @return [void]
31
+ # @example Clear to stderr (default)
32
+ # RubyProgress::Utils.clear_line
33
+ # @example Clear to stdout
34
+ # RubyProgress::Utils.clear_line(:stdout)
15
35
  def self.clear_line(output_stream = :stderr)
16
36
  io = case output_stream
17
37
  when :stdout
@@ -26,18 +46,40 @@ module RubyProgress
26
46
  io.print "\r\e[K"
27
47
  end
28
48
 
29
- # Enhanced line clearing for daemon mode that handles output interruption
49
+ # Enhanced line clearing for daemon mode that handles output interruption.
50
+ #
51
+ # Clears the current line and the line above it, useful when daemon
52
+ # output has been interrupted by other command output.
53
+ #
54
+ # @return [void]
55
+ # @example
56
+ # RubyProgress::Utils.clear_line_aggressive
30
57
  def self.clear_line_aggressive
31
58
  $stderr.print "\r\e[2K" # Clear entire current line
32
59
  $stderr.print "\e[1A\e[2K" # Move up one line and clear it too
33
60
  $stderr.print "\r" # Return to start of line
34
61
  end
35
62
 
36
- # Universal completion message display
63
+ # Displays a completion message with optional icons and formatting.
64
+ #
65
+ # Universal completion message display that handles success/error states,
66
+ # custom icons, and output stream selection.
67
+ #
37
68
  # @param message [String] The message to display
38
- # @param success [Boolean] Whether this represents success or failure
69
+ # @param success [Boolean] Whether this represents success (true) or failure (false)
39
70
  # @param show_checkmark [Boolean] Whether to show checkmark/X symbols
40
- # @param output_stream [Symbol] Where to output (:stdout, :stderr, :warn)
71
+ # @param output_stream [Symbol, IO] Where to output (:stdout, :stderr, :warn, or IO object)
72
+ # @param icons [Hash] Custom icons hash with :success and :error keys
73
+ # @option icons [String] :success Custom success icon (overrides default ✅)
74
+ # @option icons [String] :error Custom error icon (overrides default 🛑)
75
+ # @return [void]
76
+ # @example Basic success message
77
+ # RubyProgress::Utils.display_completion("Done!", success: true, show_checkmark: true)
78
+ # @example With custom icons
79
+ # RubyProgress::Utils.display_completion("Build complete",
80
+ # success: true,
81
+ # show_checkmark: true,
82
+ # icons: { success: '🚀', error: '💥' })
41
83
  def self.display_completion(message, success: true, show_checkmark: false, output_stream: :warn, icons: {})
42
84
  return unless message
43
85
 
@@ -87,16 +129,42 @@ module RubyProgress
87
129
  end
88
130
  end
89
131
 
90
- # Clear current line and display completion message
91
- # Convenience method that combines line clearing with message display
132
+ # Clears the current line and displays a completion message.
133
+ #
134
+ # Convenience method that combines line clearing with message display.
135
+ # Note: When output_stream is :warn, line clearing is already included
136
+ # in display_completion.
137
+ #
138
+ # @param message [String] The message to display
139
+ # @param success [Boolean] Whether this represents success (true) or failure (false)
140
+ # @param show_checkmark [Boolean] Whether to show checkmark/X symbols
141
+ # @param output_stream [Symbol, IO] Where to output (:stdout, :stderr, :warn, or IO object)
142
+ # @param icons [Hash] Custom icons hash with :success and :error keys
143
+ # @return [void]
144
+ # @see display_completion
145
+ # @example
146
+ # RubyProgress::Utils.complete_with_clear("Task complete", success: true, show_checkmark: true)
92
147
  def self.complete_with_clear(message, success: true, show_checkmark: false, output_stream: :warn, icons: {})
93
148
  clear_line(output_stream) if output_stream != :warn # warn already includes clear in display_completion
94
149
  display_completion(message, success: success, show_checkmark: show_checkmark, output_stream: output_stream, icons: icons)
95
150
  end
96
151
 
97
- # Parse start/end characters for animation wrapping
98
- # @param ends_string [String] Even-length string to split in half for start/end chars
152
+ # Parses start/end characters for animation wrapping.
153
+ #
154
+ # Takes an even-length string and splits it in half to create start
155
+ # and end decorative characters for progress indicators. Handles
156
+ # multi-byte characters correctly.
157
+ #
158
+ # @param ends_string [String, nil] Even-length string to split in half
99
159
  # @return [Array<String>] Array with [start_chars, end_chars]
160
+ # @example Basic decoration
161
+ # RubyProgress::Utils.parse_ends("[]") # => ["[", "]"]
162
+ # @example Multi-character decoration
163
+ # RubyProgress::Utils.parse_ends("<<>>") # => ["<<", ">>"]
164
+ # @example Emoji decoration
165
+ # RubyProgress::Utils.parse_ends("🎯🎪") # => ["🎯", "🎪"]
166
+ # @example Empty or nil input
167
+ # RubyProgress::Utils.parse_ends(nil) # => ["", ""]
100
168
  def self.parse_ends(ends_string)
101
169
  return ['', ''] unless ends_string && !ends_string.empty?
102
170
 
@@ -110,7 +178,20 @@ module RubyProgress
110
178
  [start_chars, end_chars]
111
179
  end
112
180
 
113
- # Validate ends string: must be non-empty and even-length (handles multi-byte chars)
181
+ # Validates an ends string for proper format.
182
+ #
183
+ # Checks that the string is non-empty and has an even number of
184
+ # characters (handles multi-byte characters correctly).
185
+ #
186
+ # @param ends_string [String, nil] String to validate
187
+ # @return [Boolean] true if valid, false otherwise
188
+ # @example Valid strings
189
+ # RubyProgress::Utils.ends_valid?("[]") # => true
190
+ # RubyProgress::Utils.ends_valid?("🎯🎪") # => true
191
+ # @example Invalid strings
192
+ # RubyProgress::Utils.ends_valid?("abc") # => false (odd length)
193
+ # RubyProgress::Utils.ends_valid?("") # => false (empty)
194
+ # RubyProgress::Utils.ends_valid?(nil) # => false (nil)
114
195
  def self.ends_valid?(ends_string)
115
196
  return false unless ends_string && !ends_string.empty?
116
197
 
@@ -2,11 +2,18 @@
2
2
 
3
3
  module RubyProgress
4
4
  # Main gem version
5
- VERSION = '1.3.6'
5
+ # Main gem version
6
+ # @return [String]
7
+ VERSION = '1.3.7'
6
8
 
7
9
  # Component-specific versions (patch bumps)
10
+ # Component-specific versions (patch bumps)
11
+ # @return [String]
8
12
  WORM_VERSION = '1.1.6'
13
+ # @return [String]
9
14
  TWIRL_VERSION = '1.1.6'
15
+ # @return [String]
10
16
  RIPPLE_VERSION = '1.1.6'
17
+ # @return [String]
11
18
  FILL_VERSION = '1.0.6'
12
19
  end
@@ -9,6 +9,16 @@ require_relative 'cli/worm_runner'
9
9
  module RubyProgress
10
10
  # Animated progress indicator with ripple effect using Unicode combining characters
11
11
  class Worm
12
+ # Worm indicator renders a small ripple/wave of characters. Use this
13
+ # class directly to control a running indicator, or use the CLI helpers
14
+ # which wrap this behavior for daemonization and command execution.
15
+ #
16
+ # Public instance methods (selected): #generate_dots
17
+ # Public module mixins provide the main animation loop via WormRunner.
18
+ #
19
+ # @example
20
+ # w = RubyProgress::Worm.new(length: 5, message: 'loading')
21
+ # w.send(:generate_dots, 2, 1) # => "..o.."
12
22
  # Ripple effect styles
13
23
  RIPPLE_STYLES = {
14
24
  'circles' => {
@@ -56,6 +66,13 @@ module RubyProgress
56
66
  }.freeze
57
67
 
58
68
  def initialize(options = {})
69
+ # Create a new Worm indicator instance.
70
+ #
71
+ # @param options [Hash] configuration options
72
+ # @option options [Integer] :length number of characters in the ripple (default 3)
73
+ # @option options [String] :message optional label text
74
+ # @option options [String,Symbol] :style ripple style name or custom spec
75
+ # @return [void]
59
76
  @length = options[:length] || 3
60
77
  @message = options[:message]
61
78
  @speed = parse_speed(options[:speed] || 'medium')
@@ -154,7 +171,7 @@ module RubyProgress
154
171
  input_chars.all? do |char|
155
172
  idx = key_chars.index(char)
156
173
  if idx
157
- key_chars = key_chars[idx + 1..-1] # Remove matched chars and continue
174
+ key_chars = key_chars[(idx + 1)..-1] # Remove matched chars and continue
158
175
  true
159
176
  else
160
177
  false
@@ -207,6 +224,11 @@ module RubyProgress
207
224
  # @output_capture&.redraw) will be overridden.
208
225
 
209
226
  def generate_dots(ripple_position, direction)
227
+ # Generate the string representing the ripple at a given position.
228
+ #
229
+ # @param ripple_position [Integer] index of the ripple peak
230
+ # @param direction [Integer] -1 for left-moving, +1 for right-moving
231
+ # @return [String] composed characters for current frame
210
232
  dots = Array.new(@length) { @style[:baseline] }
211
233
 
212
234
  # Apply ripple effect
data/project-page.md ADDED
@@ -0,0 +1,162 @@
1
+ # Ruby Progress Indicators
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/ruby-progress.svg)](https://badge.fury.io/rb/ruby-progress)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+ [![Ruby](https://img.shields.io/badge/ruby-%3E%3D%202.5.0-ruby.svg)](https://www.ruby-lang.org/)
6
+
7
+ A collection of Ruby progress indicator projects: **Ripple**, **Worm**,
8
+ **Twirl**, and **Fill**. All provide animated terminal progress indicators
9
+ with different visual styles and features.
10
+
11
+ ## Quick Start
12
+
13
+ Install the gem:
14
+
15
+ {% iterm "gem install ruby-progress" %}
16
+
17
+ Use the unified interface:
18
+
19
+ {% iterm "prg worm --message 'Processing data' --style blocks --checkmark" %}
20
+
21
+ {% iterm "prg ripple 'Loading...' --style rainbow --speed fast" %}
22
+
23
+ {% iterm "prg twirl --message 'Working...' --style dots --speed fast" %}
24
+
25
+ ## Unified Interface
26
+
27
+ The gem provides a unified `prg` command that supports all progress
28
+ indicators through subcommands. Run commands with progress animation:
29
+
30
+ {% iterm "prg worm --command 'sleep 5' --success 'Completed!' --error 'Failed!' --checkmark" %}
31
+
32
+ {% iterm "prg ripple 'Building...' --command 'make build' --success 'Build complete!' --stdout" %}
33
+
34
+ {% iterm "prg twirl --command 'npm install' --message 'Installing packages' --style arc" %}
35
+
36
+ [See the README for more CLI examples](https://github.com/ttscoff/ruby-progress/blob/main/README.md#ripple-cli-examples)
37
+
38
+ ## Ripple
39
+
40
+ Sophisticated text animation library that creates ripple effects across
41
+ text strings in the terminal. Supports various animation modes including
42
+ bidirectional movement and rainbow colors.
43
+
44
+ **Key Features:**
45
+
46
+ - Text ripple animations with customizable speed and direction
47
+ - Style system supporting rainbow colors and inverse highlighting
48
+ - Multiple animation formats: forward-only, bidirectional
49
+ - Command execution with animated progress display
50
+
51
+ Basic text animation:
52
+
53
+ {% iterm "prg ripple 'Loading...'" %}
54
+
55
+ With style options:
56
+
57
+ {% iterm "prg ripple 'Processing Data' --speed fast --style rainbow --direction bidirectional" %}
58
+
59
+ Run a command with progress animation:
60
+
61
+ {% iterm "prg ripple 'Installing packages' --command 'sleep 5' --success 'Installation complete!' --checkmark" %}
62
+
63
+ [See the README for more Ripple CLI examples](https://github.com/ttscoff/ruby-progress/blob/main/README.md#ripple-cli-examples)
64
+
65
+ ## Twirl
66
+
67
+ Lightweight spinner animation system providing over 35 different spinner
68
+ styles for terminal progress indication. Perfect for showing indefinite
69
+ progress during command execution.
70
+
71
+ **Key Features:**
72
+
73
+ - 35+ spinner styles including dots, arrows, blocks, and geometric patterns
74
+ - Flexible speed control (1-10 scale or named speeds)
75
+ - Command execution with animated progress display
76
+ - Daemon mode for background progress indication
77
+
78
+ Basic spinner animation:
79
+
80
+ {% iterm "prg twirl --message 'Processing...' --style dots" %}
81
+
82
+ With command execution:
83
+
84
+ {% iterm "prg twirl --command 'npm install' --message 'Installing' --style arc" %}
85
+
86
+ Different spinner styles:
87
+
88
+ {% iterm "prg twirl --message 'Working' --style arrows --speed fast" %}
89
+
90
+ [See the README for more Twirl CLI examples](https://github.com/ttscoff/ruby-progress/blob/main/README.md#twirl-usage)
91
+
92
+ ## Worm
93
+
94
+ Clean, Unicode-based progress indicator that creates a ripple effect using
95
+ combining characters. Designed for running commands with visual progress
96
+ feedback.
97
+
98
+ **Key Features:**
99
+
100
+ - Ripple wave animation using Unicode characters
101
+ - Multiple visual styles (circles, blocks, geometric)
102
+ - Configurable speed and customizable length
103
+ - Command execution with progress indication
104
+ - Custom styles with 3-character patterns
105
+
106
+ Run indefinitely without a command:
107
+
108
+ {% iterm "prg worm --message 'Loading...' --speed fast --style circles" %}
109
+
110
+ Run a command with progress animation:
111
+
112
+ {% iterm "prg worm --command 'sleep 5' --message 'Installing' --success 'Done!'" %}
113
+
114
+ Custom animations with 3-character patterns:
115
+
116
+ {% iterm "prg worm --message 'Custom style' --style 'custom=_-=' --command 'sleep 2'" %}
117
+
118
+ {% iterm "prg worm --message 'Emoji worm!' --style 'custom=🟦🟨🟥' --success 'Complete!'" %}
119
+
120
+ [See the README for more Worm CLI examples](https://github.com/ttscoff/ruby-progress/blob/main/README.md#worm-usage)
121
+
122
+ ## Background Mode
123
+
124
+ All progress indicators support daemon mode for background tasks:
125
+
126
+ Start a background indicator:
127
+
128
+ {% iterm "prg worm --daemon-as mytask --message 'Background processing'" %}
129
+
130
+ Stop it later with a message:
131
+
132
+ {% iterm "prg job stop --daemon-name mytask --message 'Task complete!' --checkmark" %}
133
+
134
+ [See the README for more background mode examples](https://github.com/ttscoff/ruby-progress/blob/main/README.md#example-background-mode-demo)
135
+
136
+ ## Installation
137
+
138
+ As a gem (recommended):
139
+
140
+ {% iterm "gem install ruby-progress" %}
141
+
142
+ From source:
143
+
144
+ {% iterm "git clone https://github.com/ttscoff/ruby-progress.git" %}
145
+
146
+ {% iterm "cd ruby-progress" %}
147
+
148
+ {% iterm "bundle install" %}
149
+
150
+ {% iterm "bundle exec rake build" %}
151
+
152
+ {% iterm "gem install pkg/ruby-progress-*.gem" %}
153
+
154
+ [See the README for more installation options](https://github.com/ttscoff/ruby-progress/blob/main/README.md#installation)
155
+
156
+ ## Requirements
157
+
158
+ - Ruby 2.7 or higher
159
+ - Terminal with Unicode support (for Worm)
160
+ - ANSI color support (for Ripple rainbow effects)
161
+
162
+ [See the README for complete documentation](https://github.com/ttscoff/ruby-progress/blob/main/README.md)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-progress
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.6
4
+ version: 1.3.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brett Terpstra
@@ -140,6 +140,7 @@ files:
140
140
  - lib/ruby-progress/utils.rb
141
141
  - lib/ruby-progress/version.rb
142
142
  - lib/ruby-progress/worm.rb
143
+ - project-page.md
143
144
  - quick_demo.rb
144
145
  - readme_demo.rb
145
146
  - ruby-progress.gemspec