Showing preview only (233K chars total). Download the full file or copy to clipboard to get everything.
Repository: piotrmurach/tty-progressbar
Branch: master
Commit: 9cd539e4b2ab
Files: 126
Total size: 204.0 KB
Directory structure:
gitextract_x1cqiz4d/
├── .editorconfig
├── .github/
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE/
│ │ ├── BUG_REPORT.md
│ │ ├── FEATURE_REQUEST.md
│ │ └── config.yml
│ ├── PULL_REQUEST_TEMPLATE.md
│ └── workflows/
│ └── ci.yml
├── .gitignore
├── .rspec
├── .rubocop.yml
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── Gemfile
├── LICENSE.txt
├── README.md
├── Rakefile
├── appveyor.yml
├── examples/
│ ├── color.rb
│ ├── failure.rb
│ ├── indeterminate.rb
│ ├── iterator.rb
│ ├── lazy.rb
│ ├── log.rb
│ ├── multi/
│ │ ├── formats.rb
│ │ ├── main_bar.rb
│ │ ├── resume.rb
│ │ ├── simple.rb
│ │ ├── single.rb
│ │ └── width.rb
│ ├── pause.rb
│ ├── simple.rb
│ ├── slow_process.rb
│ ├── speed.rb
│ ├── threaded.rb
│ ├── tokens.rb
│ ├── unicode.rb
│ └── unicode_unknown.rb
├── lib/
│ ├── tty/
│ │ ├── progressbar/
│ │ │ ├── configuration.rb
│ │ │ ├── converter.rb
│ │ │ ├── formats.rb
│ │ │ ├── formatter/
│ │ │ │ ├── bar.rb
│ │ │ │ ├── byte_rate.rb
│ │ │ │ ├── current.rb
│ │ │ │ ├── current_byte.rb
│ │ │ │ ├── elapsed.rb
│ │ │ │ ├── estimated.rb
│ │ │ │ ├── estimated_time.rb
│ │ │ │ ├── mean_byte.rb
│ │ │ │ ├── mean_rate.rb
│ │ │ │ ├── percent.rb
│ │ │ │ ├── rate.rb
│ │ │ │ ├── total.rb
│ │ │ │ └── total_byte.rb
│ │ │ ├── formatter.rb
│ │ │ ├── formatters.rb
│ │ │ ├── meter.rb
│ │ │ ├── multi.rb
│ │ │ ├── pipeline.rb
│ │ │ ├── timer.rb
│ │ │ └── version.rb
│ │ └── progressbar.rb
│ └── tty-progressbar.rb
├── spec/
│ ├── perf/
│ │ └── render_spec.rb
│ ├── spec_helper.rb
│ ├── support/
│ │ └── output_io.rb
│ └── unit/
│ ├── advance_spec.rb
│ ├── bar_format_spec.rb
│ ├── clear_spec.rb
│ ├── complete_spec.rb
│ ├── configure_spec.rb
│ ├── converter/
│ │ ├── to_bytes_spec.rb
│ │ ├── to_seconds_spec.rb
│ │ └── to_time_spec.rb
│ ├── custom_formatter_spec.rb
│ ├── custom_token_spec.rb
│ ├── events_spec.rb
│ ├── finish_spec.rb
│ ├── formatter/
│ │ ├── bar_spec.rb
│ │ ├── byte_rate_spec.rb
│ │ ├── current_byte_spec.rb
│ │ ├── current_spec.rb
│ │ ├── elapsed_spec.rb
│ │ ├── estimated_spec.rb
│ │ ├── estimated_time_spec.rb
│ │ ├── mean_byte_spec.rb
│ │ ├── mean_rate_spec.rb
│ │ ├── percent_spec.rb
│ │ ├── rate_spec.rb
│ │ ├── total_byte_spec.rb
│ │ └── total_spec.rb
│ ├── frequency_spec.rb
│ ├── head_spec.rb
│ ├── hide_cursor_spec.rb
│ ├── indeterminate_spec.rb
│ ├── inspect_spec.rb
│ ├── iterate_spec.rb
│ ├── log_spec.rb
│ ├── meter_spec.rb
│ ├── multi/
│ │ ├── advance_spec.rb
│ │ ├── events_spec.rb
│ │ ├── finish_spec.rb
│ │ ├── line_inset_spec.rb
│ │ ├── pause_spec.rb
│ │ ├── register_spec.rb
│ │ ├── reset_spec.rb
│ │ ├── resume_spec.rb
│ │ ├── stop_spec.rb
│ │ └── width_spec.rb
│ ├── new_spec.rb
│ ├── pause_spec.rb
│ ├── pipeline_spec.rb
│ ├── ratio_spec.rb
│ ├── render_spec.rb
│ ├── reset_spec.rb
│ ├── resize_spec.rb
│ ├── resume_spec.rb
│ ├── set_current_spec.rb
│ ├── start_spec.rb
│ ├── stop_spec.rb
│ ├── timer_spec.rb
│ ├── update_spec.rb
│ └── width_spec.rb
├── tasks/
│ ├── console.rake
│ ├── coverage.rake
│ └── spec.rake
└── tty-progressbar.gemspec
================================================
FILE CONTENTS
================================================
================================================
FILE: .editorconfig
================================================
root = true
[*.rb]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
================================================
FILE: .github/FUNDING.yml
================================================
github: piotrmurach
================================================
FILE: .github/ISSUE_TEMPLATE/BUG_REPORT.md
================================================
---
name: Bug report
about: Report something not working correctly or as expected
title: ''
labels: bug
assignees: ''
---
### Describe the problem
A brief description of the issue.
### Steps to reproduce the problem
```
Your code here to reproduce the issue
```
### Actual behaviour
What happened? This could be a description, log output, error raised etc.
### Expected behaviour
What did you expect to happen?
### Describe your environment
* OS version:
* Ruby version:
* TTY::ProgressBar version:
================================================
FILE: .github/ISSUE_TEMPLATE/FEATURE_REQUEST.md
================================================
---
name: Feature request
about: Suggest new functionality
title: ''
labels: enhancement
assignees: ''
---
### Describe the problem
A brief description of the problem you're trying to solve.
### How would the new feature work?
A short explanation of the new feature.
```
Example code that shows possible usage
```
### Drawbacks
Can you see any potential drawbacks?
================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: false
contact_links:
- name: TTY Community Discussions
url: https://github.com/piotrmurach/tty/discussions
about: Suggest ideas, ask and answer questions
================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
### Describe the change
What does this Pull Request do?
### Why are we doing this?
Any related context as to why is this is a desirable change.
### Benefits
How will the library improve?
### Drawbacks
Possible drawbacks applying this change.
### Requirements
<!--- Put an X between brackets on each line if you have done the item: -->
- [ ] Tests written & passing locally?
- [ ] Code style checked?
- [ ] Rebased with `master` branch?
- [ ] Documentation updated?
- [ ] Changelog updated?
================================================
FILE: .github/workflows/ci.yml
================================================
---
name: CI
on:
push:
branches:
- master
paths-ignore:
- "examples/**"
- "*.md"
pull_request:
branches:
- master
paths-ignore:
- "examples/**"
- "*.md"
jobs:
tests:
name: Ruby ${{ matrix.ruby }}
runs-on: ${{ matrix.os || 'ubuntu-latest' }}
strategy:
fail-fast: false
matrix:
ruby:
- "2.0"
- "2.1"
- "2.3"
- "2.4"
- "2.5"
- "2.6"
- "3.0"
- "3.1"
- "3.2"
- "3.3"
- "3.4"
- ruby-head
- jruby-9.3
- jruby-9.4
- jruby-head
- truffleruby-head
include:
- ruby: "2.2"
os: ubuntu-20.04
- ruby: "2.7"
coverage: true
- ruby: jruby-9.2
os: ubuntu-20.04
env:
COVERAGE: ${{ matrix.coverage }}
COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }}
continue-on-error: ${{ endsWith(matrix.ruby, 'head') }}
steps:
- uses: actions/checkout@v4
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby }}
bundler-cache: true
- name: Run tests
run: bundle exec rake ci
================================================
FILE: .gitignore
================================================
/.bundle/
/.yardoc
/Gemfile.lock
/_yardoc/
/coverage/
/doc/
/pkg/
/spec/reports/
/tmp/
*.bundle
*.so
*.o
*.a
mkmf.log
================================================
FILE: .rspec
================================================
--color
--require spec_helper
--warnings
================================================
FILE: .rubocop.yml
================================================
AllCops:
NewCops: enable
TargetRubyVersion: 2.0
Gemspec/DevelopmentDependencies:
Enabled: false
Layout/FirstArrayElementIndentation:
Enabled: false
Layout/LineLength:
Max: 82
Exclude:
- "**/*.gemspec"
Lint/AssignmentInCondition:
Enabled: false
Metrics/AbcSize:
Max: 30
Metrics/BlockLength:
Exclude:
- "spec/**/*"
- "**/*.gemspec"
Metrics/ClassLength:
Max: 1500
Metrics/CyclomaticComplexity:
Enabled: false
Metrics/MethodLength:
Max: 20
Naming/BinaryOperatorParameterName:
Enabled: false
Style/AccessorGrouping:
Enabled: false
Style/AsciiComments:
Enabled: false
Style/Lambda:
Enabled: false
Style/LambdaCall:
EnforcedStyle: braces
Style/FormatStringToken:
Enabled: false
Style/StringLiterals:
EnforcedStyle: double_quotes
Style/TrivialAccessors:
Enabled: false
# { ... } for multi-line blocks is okay
Style/BlockDelimiters:
Enabled: false
Style/CommentedKeyword:
Enabled: false
================================================
FILE: CHANGELOG.md
================================================
# Change log
## [v0.18.3] - 2024-11-10
### Fixed
* Fix setting the current progress for an indeterminate bar by Alex Watt
(@alexcwatt)
## [v0.18.2] - 2021-03-08
### Fixed
* Fix calculating total in MultiBar with indeterminate children by Tim Tilberg(@ttilberg)
## [v0.18.1] - 2021-01-25
### Fixed
* Fix :eta and :eta_time format tokens display when progress isn't started
## [v0.18.0] - 2021-01-20
### Added
* Add #resume to allow stopped or paused bar to continue progressing
* Add :clear_head option to remove head when progress is done
* Add #configure to allow runtime configuration
* Add Multi#done? to check if all bar are stopped or finished
* Add indeterminate progress support when no total is given
* Add :bar_format option to allow selecting preconfigured bar displays
* Add :eta_time format token to display the estimated time of day at completion
* Add measurement of the total elapsed time that ignores stopped time intervals
* Add #pause to prevent bar from continuing progression and suspend time measurements
* Add Multi#pause to allow suspending progression of all registered bars at once
* Add Multi#resume to start again all registered bars that are stopped or paused
* Add Timer class to handle the total elapsed time measurements
### Changed
* Change Multi#stopped? to check that all bars are stopped
* Change gemspec to load version directly and remove test artifacts
* Change to update strings-ansi and tty-screen dependencies
* Change Pipeline to inject progress bar instance only once
* Change :elapsed and :eta to show days after running for 24 hours
* Change to ensure complete, incomplete and unknown option cannot be an empty string
* Change to allow setting total to nil via accessor
* Change gemspec to allow version 2.0 of unicode-display_width dependency
* Change #stop to show hidden cursor after render similar to #finish
### Fixed
* Fix MultiBar top bar to allow resuming progress when stopped/done (@d4be4st)
* Fix MultiBar to only set width when top bar present
## [v0.17.0] - 2019-05-31
### Changed
* Change gemspec to load files directly without git
* Change to update tty-cursor and tty-screen dependencies
## [v0.16.0] - 2018-08-27
### Added
* Add strings-ansi dependency
### Changed
* Change tty-cursor dependency version
### Fixed
* Fix to handle ANSI codes in bar formatting to allow correct size calculation
## [v0.15.1] - 2018-07-19
### Fixed
* Fix to always restore hidden cursor by Eric Hodel(@drbrain)
## [v0.15.0] - 2018-06-24
### Added
* Add #format= for overriding formatting string
* Add #display_columns for determining display width of multibyte characters
* Add :inset option to bar configuration options
* Add ability to configure width for multi bar with top level bar
* Add unicode-display_width dependency
### Changed
* Change #update to only set configuration if actually present
* Change bar formatter to handle multibyte characters
### Fixed
* Fix to stop reseting multibar state when registered bar reset by Eric Hodel(@drbrain)
* Fix rendered bar to pad formatted output when it gets shorter by Eric Hodel(@drbrain)
* Fix multi bar to advance in steps matching each bar advance progress
* Fix multi bar rendering for widths exceeding screen columns count
## [v0.14.0] - 2018-01-17
### Changed
* Change to only output to a console and stop output to a file, pipe etc...
* Change #iterate to accept enumerators as collection type by Victor Shepelev(@zverok)
### Fixed
* Fix #iterate to take into account progress value in total steps calculation
## [v0.13.0] - 2017-10-29
### Changed
* Change tty-screen dependency version
* Change gemspec to require Ruby >= 2.0.0
* Remove encoding comments
## [v0.12.2] - 2017-09-15
### Changed
* Change to automatically start & update top level progress bar
when registered bars advance
## [v0.12.1] - 2017-09-09
### Added
* Add rspec to gem development dependencies
### Changed
* Change line clearing to rely on tty-cursor
### Fixed
* Fix multi bar finishing before registered progress bars
## [v0.12.0] - 2017-09-03
### Added
* Add :head option to allow changing bar head progression character
* Add thread safety to allow sharing progress between multiple threads
* Add #update to allow changing bar configuration options
* Add #stop to stop bar in current position and terminate any further progress
* Add #iterate to progress over a collection
* Add validation to check if bar formatting string is provided
* Add ability to listen for completion events such as :done, :progress and :stopped
* Add TTY::ProgressBar::Multi for creating parallel multiple progress bars
### Changed
* Change to stop mutating strings
* Change #reset to stop drawing and use for initialization
### Fixed
* Fix configuration to add interval option
## [v0.11.0] - 2017-04-04
### Added
* Add :decimals, :separator, :unit_separator to Converter#to_bytes
* Add ability to Converter#to_bytes to calculate higher sizes TB, PB & EB
### Changed
* Change files loading
* Change Converter to be a module
### Fixed
* Fix :byte_rate token to correctly format bytes
## [v0.10.1] - 2016-12-26
### Fixed
* Fix redefinition of Configuration#total=
## [v0.10.0] - 2016-06-25
### Fixed
* Fix Meter#sample to accurately calculate rate and mean_rate by Sylvain Joyeux
## [v0.9.0] - 2016-04-09
### Fixed
* Fix #resize to stop raising error when finished
### Changed
* Remove #register_signals and leave the choice on how exit and resize are handled to developer
## [v0.8.1] - 2016-02-27
### Added
* Add progress bar #inspect
### Fixed
* Fix the progressbar resizing call, help from @squarism
## [v0.8.0] - 2016-02-07
### Changed
* Update tty-screen dependency
## [v0.7.0] - 2015-09-20
* Update tty-screen dependency
## [v0.6.0] - 2015-06-27
### Added
* Add ability to add custom tokens
### Changed
* Internal cleanup of parameters for formatters and pipeline
* Fix ratio to avoid division by zero by @sleewoo issue #9
## [v0.5.1] - 2015-05-31
* Update tty-screen dependency with bug fixes
## [v0.5.0] - 2015-01-01
### Added
* Add ability to reset progress
* Add start method for manually setting the timer
* Add meter to measure speed rate
* Add to_seconds converter
* Add :rate, :mean_rate, :byte_rate & :mean_byte formatters
### Changed
* Fix bug with finish not rendering the bar full
## [v0.4.0] - 2014-12-25
### Added
* Add :total_byte, :current_byte formatters by @vincentjames501
* Add current= method for updating progress to a given value by @vincentjames501
* Add ratio= method for updating progress ratio
## [v0.3.0] - 2014-12-21
### Added
* Add tty-screen dependency for terminal size detection
* Add to_bytes converter
* Add formatter for managing formats pipeline
* Add block configuration
### Changed
* Catch INT signal and cleanly end progress
* Change to add matching condition to formatter
## [v0.2.0] - 2014-11-09
### Added
* Add estimated time formatter.
* Add frequency option to limit repainting of progress.
* Add log method for printing out during progress rendering.
* Add complete? for checking progress bar state
### Changed
* Fix bug with hide_cursor option
* Increase test coverage
## [v0.1.0] - 2014-11-01
* Initial implementation and release
[v0.18.3]: https://github.com/piotrmurach/tty-progressbar/compare/v0.18.2...v0.18.3
[v0.18.2]: https://github.com/piotrmurach/tty-progressbar/compare/v0.18.1...v0.18.2
[v0.18.1]: https://github.com/piotrmurach/tty-progressbar/compare/v0.18.0...v0.18.1
[v0.18.0]: https://github.com/piotrmurach/tty-progressbar/compare/v0.17.0...v0.18.0
[v0.17.0]: https://github.com/piotrmurach/tty-progressbar/compare/v0.16.0...v0.17.0
[v0.16.0]: https://github.com/piotrmurach/tty-progressbar/compare/v0.15.1...v0.16.0
[v0.15.1]: https://github.com/piotrmurach/tty-progressbar/compare/v0.15.0...v0.15.1
[v0.15.0]: https://github.com/piotrmurach/tty-progressbar/compare/v0.14.0...v0.15.0
[v0.14.0]: https://github.com/piotrmurach/tty-progressbar/compare/v0.13.0...v0.14.0
[v0.13.0]: https://github.com/piotrmurach/tty-progressbar/compare/v0.12.2...v0.13.0
[v0.12.2]: https://github.com/piotrmurach/tty-progressbar/compare/v0.12.1...v0.12.2
[v0.12.1]: https://github.com/piotrmurach/tty-progressbar/compare/v0.12.0...v0.12.1
[v0.12.0]: https://github.com/piotrmurach/tty-progressbar/compare/v0.11.0...v0.12.0
[v0.11.0]: https://github.com/piotrmurach/tty-progressbar/compare/v0.10.1...v0.11.0
[v0.10.1]: https://github.com/piotrmurach/tty-progressbar/compare/v0.10.0...v0.10.1
[v0.10.0]: https://github.com/piotrmurach/tty-progressbar/compare/v0.9.0...v0.10.0
[v0.9.0]: https://github.com/piotrmurach/tty-progressbar/compare/v0.8.2...v0.9.0
[v0.8.2]: https://github.com/piotrmurach/tty-progressbar/compare/v0.8.1...v0.8.2
[v0.8.1]: https://github.com/piotrmurach/tty-progressbar/compare/v0.8.0...v0.8.1
[v0.8.0]: https://github.com/piotrmurach/tty-progressbar/compare/v0.7.0...v0.8.0
[v0.7.0]: https://github.com/piotrmurach/tty-progressbar/compare/v0.6.0...v0.7.0
[v0.6.0]: https://github.com/piotrmurach/tty-progressbar/compare/v0.5.1...v0.6.0
[v0.5.1]: https://github.com/piotrmurach/tty-progressbar/compare/v0.5.0...v0.5.1
[v0.5.0]: https://github.com/piotrmurach/tty-progressbar/compare/v0.4.0...v0.5.0
[v0.4.0]: https://github.com/piotrmurach/tty-progressbar/compare/v0.3.0...v0.4.0
[v0.3.0]: https://github.com/piotrmurach/tty-progressbar/compare/v0.2.0...v0.3.0
[v0.2.0]: https://github.com/piotrmurach/tty-progressbar/compare/v0.1.0...v0.2.0
[v0.1.0]: https://github.com/piotrmurach/tty-progressbar/compare/v0.1.0
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, caste, color, religion, or sexual
identity and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the overall
community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or advances of
any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email address,
without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
piotr@piotrmurach.com.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series of
actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or permanent
ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within the
community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.1, available at
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
Community Impact Guidelines were inspired by
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
For answers to common questions about this code of conduct, see the FAQ at
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
[https://www.contributor-covenant.org/translations][translations].
[homepage]: https://www.contributor-covenant.org
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
[Mozilla CoC]: https://github.com/mozilla/diversity
[FAQ]: https://www.contributor-covenant.org/faq
[translations]: https://www.contributor-covenant.org/translations
================================================
FILE: Gemfile
================================================
# frozen_string_literal: true
source "https://rubygems.org"
gemspec
gem "json", "2.4.1" if RUBY_VERSION == "2.0.0"
gem "pastel", "~> 0.8"
gem "yardstick", "~> 0.9.9"
if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.1.0")
gem "rspec-benchmark", "~> 0.6"
end
if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.7.0")
gem "coveralls_reborn", "~> 0.28.0"
gem "simplecov", "~> 0.22.0"
end
================================================
FILE: LICENSE.txt
================================================
Copyright (c) 2014 Piotr Murach (piotrmurach.com)
MIT License
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
================================================
FILE: README.md
================================================
<div align="center">
<a href="https://ttytoolkit.org"><img width="130" src="https://github.com/piotrmurach/tty/raw/master/images/tty.png" alt="TTY Toolkit logo"/></a>
</div>
# TTY::ProgressBar
[][gem]
[][gh_actions_ci]
[][appveyor]
[][codeclimate]
[][coverage]
[gem]: https://badge.fury.io/rb/tty-progressbar
[gh_actions_ci]: https://github.com/piotrmurach/tty-progressbar/actions/workflows/ci.yml
[appveyor]: https://ci.appveyor.com/project/piotrmurach/tty-progressbar
[codeclimate]: https://codeclimate.com/github/piotrmurach/tty-progressbar/maintainability
[coverage]: https://coveralls.io/github/piotrmurach/tty-progressbar
> A flexible and extensible progress bar for terminal applications.
**TTY::ProgressBar** provides independent progress bar component for [TTY](https://github.com/piotrmurach/tty) toolkit.
## Features
* **Customisable.** Choose from many [configuration](#3-configuration) options to get the behaviour you want.
* **Flexible.** Describe bar [format](#4-formatting) and pick from many predefined [tokens](#41-tokens) and [bar styles](#37-bar_format).
* **Extensible.** Define [custom tokens](#42-custom-formatters) to fit your needs.
* **Powerful.** Display [multi](#6-ttyprogressbarmulti-api) progress bars in parallel.
* Show an unbounded operation with [indeterminate](#31-total) progress.
* [Pause](#210-pause) and [resume](#212-resume) progress at any time.
* Include [Unicode](#44-unicode) characters in progress bar.
* Works on all ECMA-48 compatible terminals.
## Installation
Add this line to your application's Gemfile:
```ruby
gem "tty-progressbar"
```
And then execute:
```
$ bundle
```
Or install it yourself as:
```
$ gem install tty-progressbar
```
## Contents
* [1. Usage](#1-usage)
* [2. TTY::ProgressBar::API](#2-ttyprogressbar-api)
* [2.1 advance](#21-advance)
* [2.2 iterate](#22-iterate)
* [2.3 current=](#23-current)
* [2.4 ratio=](#24-ratio)
* [2.5 width=](#25-width)
* [2.6 start](#26-start)
* [2.7 update](#27-update)
* [2.8 finish](#28-finish)
* [2.9 stop](#29-stop)
* [2.10 pause](#210-pause)
* [2.11 reset](#211-reset)
* [2.12 resume](#212-resume)
* [2.13 complete?](#213-complete)
* [2.14 paused?](#214-paused)
* [2.15 stopped?](#215-stopped)
* [2.16 indeterminate?](#216-indeterminate)
* [2.17 resize](#217-resize)
* [2.18 on](#218-on)
* [3. Configuration](#3-configuration)
* [3.1 :total](#31-total)
* [3.1 :width](#32-width)
* [3.3 :complete](#33-complete)
* [3.4 :incomplete](#34-incomplete)
* [3.5 :head](#35-head)
* [3.6 :unknown](#36-unknown)
* [3.7 :bar_format](#37-bar_format)
* [3.8 :output](#38-output)
* [3.9 :frequency](#39-frequency)
* [3.10 :interval](#310-interval)
* [3.11 :hide_cursor](#311-hide_cursor)
* [3.12 :clear](#312-clear)
* [3.13 :clear_head](#313-clear_head)
* [4. Formatting](#4-formatting)
* [4.1 Tokens](#41-tokens)
* [4.2 Custom Formatters](#42-custom-formatters)
* [4.3 Custom Tokens](#43-custom-tokens)
* [4.4 Unicode](#44-unicode)
* [5. Logging](#5-logging)
* [6. TTY::ProgressBar::Multi API](#6-ttyprogressbarmulti-api)
* [6.1 new](#61-new)
* [6.2 register](#62-register)
* [6.3 advance](#63-advance)
* [6.4 start](#64-start)
* [6.5 finish](#65-finish)
* [6.6 stop](#66-stop)
* [6.7 pause](#67-pause)
* [6.8 resume](#68-resume)
* [6.9 complete?](#69-complete)
* [6.10 paused?](#610-paused)
* [6.11 stopped?](#611-stopped)
* [6.12 on](#612-on)
* [6.13 :style](#613-style)
* [7. Examples](#7-examples)
* [7.1 Colors](#71-colors)
* [7.2 Speed](#72-speed)
## 1. Usage
**TTY::ProgressBar** requires only a format string with `:bar` [token](#41-tokens) and total number of steps to completion:
```ruby
bar = TTY::ProgressBar.new("downloading [:bar]", total: 30)
```
Once initialized, use [advance](#21-advance) method to indicated progress:
```ruby
30.times do
sleep(0.1)
bar.advance # by default increases by 1
end
```
This would produce the following animation in your terminal:
```ruby
# downloading [======================= ]
```
You can further change a progress bar behaviour and display by changing [configuration](#3-configuration) options and using many predefined [tokens](#41-tokens) and [bar formats](#37-bar_format).
When you don't know the total yet, you can set it to `nil` to switch to [indeterminate](#31-total) progress:
```ruby
# downloading [ <=> ]
```
Use [TTY::ProgressBar::Multi](#6-ttyprogressbarmulti-api) to display multiple parallel progress bars.
Declare a top level bar and then register child bars:
```ruby
bars = TTY::ProgressBar::Multi.new("main [:bar] :percent")
bar1 = bars.register("one [:bar] :percent", total: 15)
bar2 = bars.register("two [:bar] :percent", total: 15)
```
Then progress the child bars in parallel:
```ruby
bars.start # starts all registered bars timers
th1 = Thread.new { 15.times { sleep(0.1); bar1.advance } }
th2 = Thread.new { 15.times { sleep(0.1); bar2.advance } }
[th1, th2].each { |t| t.join }
```
A possible terminal output may look like this:
```ruby
# ┌ main [=============== ] 50%
# ├── one [===== ] 34%
# └── two [========== ] 67%
```
## 2. TTY::ProgressBar API
### 2.1 advance
Once you have **TTY::ProgressBar** instance, you can progress the display by calling `advance` method. By default, it will increase by `1` but you can pass any number of steps, for instance, a number of bytes for a downloaded file:
```ruby
bar.advance(1024)
```
You can also pass negative steps if you wish to backtrack the progress:
```ruby
bar.advance(-1)
```
*Note:* If a progress bar has already finished then any negative steps will not set it back to desired value.
### 2.2 iterate
To simplify progressing over an enumerable you can use `iterate` which as a first argument accepts an `Enumerable` and as a second the amount to progress the bar with.
First, create a progress bar without a total which will be automatically updated for you once iteration starts:
```ruby
bar = TTY::ProgressBar.new("[:bar]")
```
Then, either directly iterate over a collection by yielding values to a block:
```ruby
bar.iterate(30.times) { |v| ... }
```
Or return an `Enumerator`:
```ruby
progress = bar.iterate(30.times)
# => #<Enumerator: #<Enumerator::Generator:0x...:each>
```
By default, progress bar is advanced by `1` but you can change it by passing second argument:
```ruby
bar.iterate(30.times, 5)
```
One particularly useful application of `iterate` are Ruby infamous [lazy enumerators](https://ruby-doc.org/core-2.5.0/Enumerator/Lazy.html), or slowly advancing enumerations, representing complex processes.
For example, an `Enumerator` that downloads content from a remote server chunk at a time:
```ruby
downloader = Enumerator.new do |y|
start = 0
loop do
yield(download_from_server(start, CHUNK_SIZE))
raise StopIteration if download_finished?
start += CHUNK_SIZE
end
end
```
Would be used with progress bar with the total size matching the content size like so:
```ruby
bar = TTY::ProgressBar.new("[:bar]", total: content_size)
# you need to provide the total for the iterate to avoid calling enumerator.count
response = bar.iterate(downloader, CHUNK_SIZE).to_a.join
```
This would result in progress bar advancing after each chunk up until all content has been downloaded, returning the result of the download in `response` variable.
Please run [slow_process example](examples/slow_process.rb) to see this in action.
### 2.3 current=
A progress doesn't have to start from zero. You can set it to a given value using `current=` method:
```ruby
bar.current = 50
```
*Note:* If a progress bar has already finished then setting current value will not have any effect.
### 2.4 ratio=
In order to update overall completion of a progress bar as an exact percentage use the `ratio=` method. The method accepts values between `0` and `1` inclusive. For example, a ratio of `0.5` will attempt to set the progress bar halfway:
```ruby
bar.ratio = 0.5
```
### 2.5 width=
You can set how many terminal columns will the `:bar` actually span excluding any other tokens and/or text.
For example, if you need the bar to be always 20 columns wide do:
```ruby
bar.width = 20
```
Or with configuration options:
```ruby
bar = TTY::ProgressBar.new("[:bar]", width: 20)
```
### 2.6 start
By default the timer for internal time estimation is started automatically when the `advance` method is called. However, if you require control on when the progression timer is started use `start` call:
```ruby
bar.start # => sets timer and draws initial progress bar
```
### 2.7 update
Once a progress bar has been started, you can change its configuration option(s) by calling `update`:
```ruby
bar.update(complete: "+", frequency: 10)
```
### 2.8 finish
In order to immediately stop and finish progress of a bar call `finish`. This will finish drawing the progress by advancing it to 100% and returning to a new line.
```ruby
bar.finish
```
### 2.9 stop
In order to immediately stop a bar in the current position and thus prevent any further progress use `stop`:
```ruby
bar.stop
```
### 2.10 pause
A running progress bar can be paused at the current position using `pause` method:
```ruby
bar.pause
```
A paused progress bar will stop accumulating any time measurements like elapsed time. It also won't return to a new line, so a progress animation can be smoothly resumed.
### 2.11 reset
In order to reset currently running or finished progress bar to its original configuration and initial position use `reset` like so:
```ruby
bar.reset
```
After resetting a progress bar, if you wish to draw and start a bar and its timers use `start` call.
### 2.12 resume
When a bar is stopped or paused, you can continue its progression using the `resume` method.
```ruby
bar.resume
```
A resumed progression will continue accumulating the total elapsed time without including time intervals for pausing or stopping.
### 2.13 complete?
During progression you can check whether a bar is finished or not by calling `complete?`. The bar will only return `true` if the progression finished successfully, otherwise `false` will be returned.
```ruby
bar.complete? # => false
```
### 2.14 paused?
To check whether a progress bar is paused or not use `paused?`:
```ruby
bar.paused? # => true
```
### 2.15 stopped?
To check whether a progress bar is stopped or not use `stopped?`:
```ruby
bar.stopped? # => true
```
### 2.16 indeterminate?
You can make a progress bar indeterminate by setting `:total` to `nil`. In this state, a progress bar animation is displayed to show unbounded task. You can check whether the progress bar is indeterminate with the `indeterminate?` method:
```ruby
bar.indeterminate? # => false
```
### 2.17 resize
If you want to change a progress bar's current width, use `resize` and pass in a new desired length. However, if you don't provide any width the `resize` will use terminal current width as its base for scaling.
```ruby
bar.resize # determine terminal width and scale accordingly
bar.resize(50) # will resize bar proportionately from this point onwards
```
To handle automatic resizing you can trap `:WINCH` signal:
```ruby
trap(:WINCH) { bar.resize }
```
### 2.18 on
A progress bar fires events when it is progressing, paused, stopped or finished. You can register to listen for these events using the `on` message.
Every time an `advance` is called the `:progress` event gets fired which you can listen for inside a block. A first yielded argument is the actual amount of progress:
```ruby
bar.on(:progress) { |amount| ... }
```
When a progress bar finishes and completes then the `:done` event is fired. You can listen for this event:
```ruby
bar.on(:done) { ... }
```
Alternatively, when a progress bar gets stopped the `:stopped` event is fired. You can listen for this event:
```ruby
bar.on(:stopped) { ... }
```
Anytime a progress bar is paused the `:paused` event will be fired. To listen for this event do:
```ruby
bar.on(:paused) { ... }
```
## 3. Configuration
There are number of configuration options that can be provided:
* [:total](#31-total) - the total number of steps to completion.
* [:width](#32-width) - the number of terminal columns for displaying a bar excluding other tokens. Defaults to total steps.
* [:complete](#33-complete) - the completion character, by default `=`.
* [:incomplete](#34-incomplete) - the incomplete character, by default single space.
* [:head](#35-head) - the head character, by default `=`.
* [:unknown](#36-unknown) - the character(s) used to show indeterminate progress, defaults to `<=>`.
* [:bar_format](#37-bar_format) - the predefined bar format, by default `:classic`.
* [:output](#38-output) - the output stream defaulting to `stderr`.
* [:frequency](#39-frequency) - used to throttle the output, by default `0`.
* [:interval](#310-interval) - the time interval used to measure rate, by default `1 sec`.
* [:hide_cursor](#311-hide_cursor) - whether to hide the console cursor or not, defaults to `false`.
* [:clear](#312-clear) - whether to clear the finished bar or not, defaults to `false`.
* [:clear_head](#313-clear_head) - whether to clear the head character when the progress is done or not, defaults to `false`.
All the above options can be passed in as hash options or block parameters:
```ruby
bar = TTY::ProgressBar.new("[:bar]") do |config|
config.total = 30
config.frequency = 10
config.clear = true
end
```
The progress bar's configuration can also be changed at runtime with `configure`:
```ruby
bar.configure do |config|
config.total = 100 # takes precedence over the original value
config.frequency = 20
end
```
Or with the [update](#27-update) method:
```ruby
bar.update(total: 100, frequency: 20)
```
### 3.1 :total
The `:total` option determines the final value at which the progress bar fills up and stops.
```ruby
TTY::ProgressBar.new("[:bar]", total: 30)
```
Setting `:total` to `nil` or leaving it out will cause the progress bar to switch to indeterminate mode. Instead of showing completeness for a task, it will render animation like `<=>` that moves left and right:
```ruby
# [ <=> ]
```
The indeterminate mode is useful to show time-consuming and unbounded task.
Run [examples/indeterminate](https://github.com/piotrmurach/tty-progressbar/blob/master/examples/indeterminate.rb) to see indeterminate progress animation in action.
### 3.2 :width
The progress bar width defaults to the total value and is capped at the maximum terminal width minus all the labels. If you want to enforce the bar to have a specific length use the `:width` option:
```ruby
TTY::ProgressBar.new("[:bar]", width: 30)
```
### 3.3 :complete
By default, the `=` character is used to mark progression but this can be changed with `:complete` option:
```ruby
TTY::ProgressBar.new("[:bar]", complete: "x")
```
Then the output could look like this:
```ruby
# [xxxxxxxx ]
```
### 3.4 :incomplete
By default no characters are shown to mark the remaining progress in the `:classic` bar format. Other [bar styles](#37-bar_format) often have incomplete character. You can change this with `:incomplete` option:
```ruby
TTY::ProgressBar.new("[:bar]", incomplete: "_")
```
A possible output may look like this:
```ruby
# [======_________]
```
### 3.5 :head
If you prefer for the animated bar to display a specific character for a head of progression then use `:head` option:
```ruby
TTY::ProgressBar.new("[:bar]", head: ">")
```
This could result in output like this:
```ruby
# [=======> ]
```
### 3.6 :unknown
By default, a progress bar shows indeterminate progress using `<=>` characters:
```ruby
# [ <=> ]
```
Other [bar formats](#37-bar_format) use different characters.
You can change this with the `:unknown` option:
```ruby
TTY::ProgressBar.new("[:bar]", unknown: "<?>")
```
This may result in the following output:
```ruby
# [ <?> ]
````
### 3.7 :bar_format
There are number of preconfigured bar formats you can choose from.
| Name | Determinate | Indeterminate |
|:-----------|:-------------|:--------------|
| `:arrow` | `▸▸▸▸▸▹▹▹▹▹` | `◂▸` |
| `:asterisk`| `✱✱✱✱✱✳✳✳✳✳` | `✳✱✳` |
| `:blade` | `▰▰▰▰▰▱▱▱▱▱` | `▱▰▱` |
| `:block` | `█████░░░░░` | `█` |
| `:box` | `■■■■■□□□□□` | `□■□` |
| `:bracket` | `❭❭❭❭❭❭❭❭❭❭` | `❬=❭` |
| `:burger` | `≡≡≡≡≡≡≡≡≡≡` | `<≡>` |
| `:button` | `⦿⦿⦿⦿⦿⦾⦾⦾⦾⦾` | `⦾⦿⦾` |
| `:chevron` | `››››››››››` | `‹=›` |
| `:circle` | `●●●●●○○○○○` | `○●○` |
| `:classic` | `==========` | `<=>` |
| `:crate` | `▣▣▣▣▣⬚⬚⬚⬚⬚` | `⬚▣⬚` |
| `:diamond` | `♦♦♦♦♦♢♢♢♢♢` | `♢♦♢` |
| `:dot` | `・・・・・・・・・・` | `・・・` |
| `:heart` | `♥♥♥♥♥♡♡♡♡♡` | `♡♥♡` |
| `:rectangle` | `▮▮▮▮▮▯▯▯▯▯` | `▯▮▯` |
| `:square` | `▪▪▪▪▪▫▫▫▫▫` | `▫▪▫` |
| `:star` | `★★★★★☆☆☆☆☆` | `☆★☆` |
| `:track` | `▬▬▬▬▬═════` | `═▬═` |
| `:tread` | `❱❱❱❱❱❱❱❱❱❱` | `❰=❱` |
| `:triangle`| `▶▶▶▶▶▷▷▷▷▷` | `◀▶` |
| `:wave` | `~~~~~_____` | `<~>` |
For example, you can specify `:box` format with the `:bar_format` option:
```ruby
TTY::ProgressBar.new("[:bar]", bar_format: :box)
```
This will result in output like this:
```ruby
# [■■■■■□□□□□□□□□□]
```
You can overwrite `:complete`, `:incomplete`, `:head` and `:unknown` characters:
```ruby
TTY::ProgressBar.new("[:bar]", bar_format: :box, incomplete: " ", unknown: "?")
```
This will display the following when total is given:
```ruby
# [■■■■■ ]
```
And for the unknown progress the `?` character will move from left to right:
```ruby
# [ ? ]
```
### 3.8 :output
A progress bar only outputs to a console. When the output is, for example, redirected to a file or a pipe, the progress bar doesn't get printed. This is so, for example, your error logs do not overflow with progress bar output.
You can change where console output is streamed with `:output` option:
```ruby
bar = TTY::ProgressBar.new(output: $stdout)
```
The output stream defaults to `stderr`.
### 3.9 :frequency
Each time the `advance` is called it causes the progress bar to repaint. In cases when there is a huge number of updates per second, you may need to limit the rendering process by using the `frequency` option.
The `frequency` option accepts `integer` representing number of `Hz` units, for instance, frequency of 2 will mean that the progress will be updated maximum 2 times per second.
```ruby
TTY::ProgressBar.new("[:bar]", total: 30, frequency: 10) # 10 Hz
```
### 3.10 :interval
Every time `advance` method is called, a time sample is taken for speed measurement. By default, all the samples are grouped in second intervals to provide a rate of speed. You can change this by passing the `interval` option.
The `interval` option is an `integer` that represents the number of seconds, for example, interval of `60` would mean that speed is measured per 1 minute.
```ruby
TTY::ProgressBar.new(":rate/minute", total: 100, interval: 60) # 1 minute
TTY::ProgressBar.new(":rate/hour", total: 100, interval: 3600) # 1 hour
```
### 3.11 :hide_cursor
By default the cursor is visible during progress bar rendering. If you wish to hide it, you can do so with the `:hide_cursor` option.
Please note that hiding cursor changes user's terminal and you need to ensure that the cursor is made visible after your code finishes. This means also handling premature interrupt signals and other unpredictable events.
One solution is to wrap your progress rendering inside the `begin` and `ensure` like so:
```ruby
progress = TTY::ProgressBar.new("[:bar]", hide_cursor: true)
begin
# logic to advance progress bar
ensure
progress.stop # or progress.finish
# both methods will ensure that cursor is made visible again
end
```
### 3.12 :clear
By default, when a progress bar finishes it returns to a new line leaving the last progress output behind.
If you prefer to erase a progress bar when it is finished use `:clear` option:
```ruby
TTY::ProgressBar.new("[:bar]", clear: true)
```
### 3.13 :clear_head
When a progress bar finishes and its animation includes [:head](#35-head) character, the character will remain in the output:
```ruby
# [=============>]
```
To replace a head character when a progress bar is finished use `:clear_head` option:
```ruby
TTY::ProgressBar.new("[:bar]", clear_head: true)
```
This will result in the following output:
```ruby
# [==============]
```
## 4. Formatting
Every **TTY::ProgressBar** instance requires a format string, which apart from regular characters accepts special tokens to display dynamic information. For instance, a format to measure download progress could be:
```ruby
"downloading [:bar] :elapsed :percent"
```
### 4.1 Tokens
These are the tokens that are currently supported:
* `:bar` the progress bar
* `:current` the current progress number
* `:current_byte` the current progress in bytes
* `:total` the total progress number
* `:total_byte` the total progress in bytes
* `:percent` the completion percentage
* `:elapsed` the elapsed time in seconds
* `:eta` the estimated time to completion in seconds
* `:eta_time` the estimated time of day at completion
* `:rate` the current rate of progression per second
* `:byte_rate` the current rate of progression in bytes per second
* `:mean_rate` the averaged rate of progression per second
* `:mean_byte` the averaged rate of progression in bytes per second
In the indeterminate mode, the progress bar displays `-` for tokens that cannot be calculated like `:total`, `:total_byte`, `:percent` and `:eta`. The following format:
```ruby
"[:bar] :current/:total :total_byte :percent ET::elapsed ETA::eta :rate/s"
```
Will result in:
```ruby
# [ <=> ] 23/- -B -% ET: 1s ETA:--s 18.01/s
```
### 4.2 Custom Formatters
If the provided tokens do not meet your needs, you can write your own formatter and instrument formatting pipeline to use a formatter you prefer. This option is preferred if you are going to rely on progress bar internal data such as `rate`, `current` etc. which will all be available on the passed in progress bar instance.
For example, let's say you want to add `:time` token. First, start by creating a custom formatter class called `TimeFormatter` that will dynamically update `:time` token in the formatted string. In order for the `TimeFormatter` to recognise the `:time` token, you'll need to include the `TTY::ProgressBar::Formatter` module with a regular expression matching the token like so:
```ruby
class TimeFormatter
include TTY::ProgressBar::Formatter[/:time/i]
...
end
```
Next, add `call` method that will substitute the matched token with an actual value. For example, to see the time elapsed since the start do:
```ruby
class TimeFormatter
include TTY::ProgressBar::Formatter[/:time/i]
def call(value) # specify how display string is formatted
# access current progress bar instance to read start time
elapsed = (Time.now - progress.start_time).to_s
value.gsub(matcher, elapsed) # replace :time token with a value
end
end
```
Notice that you have access to all the configuration options inside the formatter by simply invoking them on the `progress` instance.
Create **TTY::ProgressBar** instance using the new token:
```ruby
bar = TTY::ProgressBar.new(":time", total: 30)
```
Then add `TimeFormatter` to the pipeline like so:
```ruby
bar.use TimeFormatter
```
Then invoke progression:
```ruby
bar.advance
```
### 4.3 Custom Tokens
You can define custom tokens by passing pairs `name: value` to `advance` method in order to dynamically update formatted bar. This option is useful for lightweight content replacement such as titles that doesn't depend on the internal data of a progress bar. For example:
```ruby
bar = TTY::ProgressBar.new("(:current) :title", total: 4)
bar.advance(title: "Hello Piotr!")
bar.advance(3, title: "Bye Piotr!")
```
This will output:
```ruby
# (1) Hello Piotr!
# (4) Bye Piotr!
```
### 4.4 Unicode
The format string as well as [:complete](#33-complete), [:head](#35-head), [:incomplete](#34-incomplete) and [:unknown](#36-unknown) configuration options can contain Unicode characters that aren't monospaced.
For example, you can specify complete bar progression character to be Unicode non-monospaced:
```ruby
bar = TTY::ProgressBar.new("Unicode [:bar]", total: 30, complete: "あ")
```
Advancing above progress bar to completion will fit `あ` characters in 30 terminal columns:
```ruby
# Unicode [あああああああああああああああ]
```
Similarly, the formatted string can include Unicode characters:
```ruby
bar = TTY::ProgressBar.new("あめかんむり[:bar]", total: 20)
```
A finished progress bar will also fit within allowed width:
```ruby
# あめかんむり[== ]
```
## 5. Logging
If you want to print messages out to terminal along with the progress bar use the `log` method. The messages will appear above the progress bar and will continue scrolling up as more are logged out.
```ruby
bar.log("Piotrrrrr")
bar.advance
```
This could result in the following output:
```ruby
# Piotrrrrr
# downloading [======================= ]
```
## 6. TTY::ProgressBar::Multi API
### 6.1 new
The multi progress bar can be created in two ways. If you simply want to group multiple progress bars together, you can create multi bar without a format string like so:
```ruby
TTY::ProgressBar::Multi.new
```
However, if you want a top level multibar that tracks progress of all the registered progress bars then you need to provide a formatted string:
```ruby
TTY::ProgressBar::Multi.new("main [:bar] :percent")
```
### 6.2 register
To create a `TTY::ProgressBar` under the multibar use `register` like so:
```ruby
multibar = TTY::ProgressBar::Multi.new
bar = multibar.register("[:bar]", total: 30)
```
The `register` call returns the newly created progress bar that can be changed using all the available [progress bar API](#2-ttyprogressbar-api) methods.
*Note:* Remember to specify total value for each registered progress bar, either when sending `register` message or when using `update` to dynamically assign the total value.
### 6.3 advance
Once multi progress bar has been created you can advance each registered progress bar individually, either by executing them one after the other synchronously or by placing them in separate threads thus progressing each bar asynchronously. The multi bar handles synchronization and display of all bars as they continue their respective rendering.
For example, to display two bars asynchronously, first register them with the multi bar:
```ruby
bar1 = multibar.register("one [:bar]", total: 20)
bar2 = multibar.register("two [:bar]", total: 30)
```
Next place the progress behaviour in separate process or thread:
```ruby
th1 = Thread.new { 20.times { expensive_work(); bar1.advance } }
th2 = Thread.new { 30.times { expensive_work(); bar2.advance } }
```
Finally, wait for the threads to finish:
```ruby
[th1, th2].each { |t| t.join }
```
### 6.4 start
By default the top level multi bar will be rendered as the first bar and have its timer started when one of the registered bars advances. However, if you wish to start timers and draw the top level multi bar do:
```ruby
multibar.start # => sets timer and draws top level multi progress bar
```
### 6.5 finish
In order to finish all progress bars call `finish`. This will finish the top level progress bar, if it exists, and any registered progress bar still in progress.
```ruby
multibar.finish
```
### 6.6 stop
Use `stop` to terminate immediately all progress bars registered with the multibar.
```ruby
multibar.stop
```
### 6.7 pause
All running progress bars can be paused at their current positions using the `pause` method:
```ruby
multibar.pause
````
### 6.8 resume
When one or more registered progress bar is stopped or paused, they can be resumed all at once using the `resume` method:
```ruby
multibar.resume
```
### 6.9 complete?
To check if all registered progress bars have been successfully finished use `complete?`
```ruby
multibar.complete? # => true
```
### 6.10 paused?
To check whether all progress bars are paused or not use `paused?`:
```ruby
multibar.paused? # => true
```
### 6.11 stopped?
To check whether all progress bars are stopped or not use `stopped?`:
```ruby
multibar.stopped? # => true
```
### 6.12 on
Similar to `TTY::ProgressBar` the multi bar fires events when it is progressing, stopped or finished. You can register to listen for events using the `on` message.
Every time any of the registered progress bars progresses the `:progress` event is fired which you can listen for:
```ruby
multibar.on(:progress) { ... }
```
When all the registered progress bars finish and complete then the `:done` event is fired. You can listen for this event:
```ruby
multibar.on(:done) { ... }
```
When any of the progress bars gets stopped the `:stopped` event is fired. You can listen for this event:
```ruby
multibar.on(:stopped) { ... }
```
Anytime a registered progress bar pauses, a `:paused` event will be fired. To listen for this event do:
```ruby
multibar.on(:paused) { ... }
```
### 6.13 :style
In addition to all [configuration options](#3-configuration) you can style multi progress bar:
```ruby
TTY::ProgressBar::Multi.new("[:bar]", style: {
top: ". ",
middle: "|-> ",
bottom: "|__ "
})
```
## 7. Examples
This section demonstrates some of the possible uses for the **TTY::ProgressBar**, for more please see examples folder in the source directory.
### 7.1 Colors
Creating a progress bar that displays in color is as simple as coloring the `:complete` and `:incomplete` character options. In order to help with coloring you can use [pastel](https://github.com/piotrmurach/pastel) library like so:
```ruby
require "pastel"
pastel = Pastel.new
green = pastel.on_green(" ")
red = pastel.on_red(" ")
```
And then pass in the colored strings as options to **TTY::ProgressBar**:
```ruby
bar = TTY::ProgressBar.new("|:bar|",
total: 30,
complete: green,
incomplete: red
)
```
To see how a progress bar is reported in terminal you can do:
```ruby
30.times do
sleep(0.1)
bar.advance
end
```
### 7.2 Speed
Commonly a progress bar is utilized to measure download speed per second. This can be done like so:
```ruby
TTY::ProgressBar.new("[:bar] :byte_rate/s") do |config|
config.total = 300000
config.interval = 1 # => 1 sec
end
```
This will result in output similar to:
```ruby
# downloading [======================= ] 4.12MB/s
```
## Contributing
1. Fork it ( https://github.com/piotrmurach/tty-progressbar/fork )
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Add some feature'`)
4. Push to the branch (`git push origin my-new-feature`)
5. Create a new Pull Request
This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](https://contributor-covenant.org) code of conduct.
## Code of Conduct
Everyone interacting in the TTY::ProgressBar project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/piotrmurach/tty-progressbar/blob/master/CODE_OF_CONDUCT.md).
## Copyright
Copyright (c) 2014 Piotr Murach. See LICENSE for further details.
================================================
FILE: Rakefile
================================================
require "bundler/gem_tasks"
FileList["tasks/**/*.rake"].each(&method(:import))
desc "Run all specs"
task ci: %w[ spec ]
task default: :spec
================================================
FILE: appveyor.yml
================================================
---
skip_commits:
files:
- "examples/**"
- "*.md"
install:
- SET PATH=C:\Ruby%ruby_version%\bin;%PATH%
- gem install bundler -v '< 2.0'
- bundle install
before_test:
- ruby -v
- gem -v
- bundle -v
build: off
test_script:
- bundle exec rake ci
environment:
matrix:
- ruby_version: "200"
- ruby_version: "200-x64"
- ruby_version: "21"
- ruby_version: "21-x64"
- ruby_version: "22"
- ruby_version: "22-x64"
- ruby_version: "23"
- ruby_version: "23-x64"
- ruby_version: "24"
- ruby_version: "24-x64"
- ruby_version: "25"
- ruby_version: "25-x64"
- ruby_version: "26"
- ruby_version: "26-x64"
================================================
FILE: examples/color.rb
================================================
# frozen_string_literal: true
require "pastel"
require_relative "../lib/tty-progressbar"
pastel = Pastel.new
green = pastel.on_green(" ")
red = pastel.on_red(" ")
bar = TTY::ProgressBar.new("|:bar|", total: 30, complete: green, incomplete: red)
30.times do
sleep(0.1)
bar.advance
end
================================================
FILE: examples/failure.rb
================================================
# frozen_string_literal: true
require_relative "../lib/tty-progressbar"
bar = TTY::ProgressBar.new("downloading [:bar] :percent", head: ">", total: 30)
30.times do |i|
if i == 15
bar.update(head: "x")
bar.stop
break
end
sleep(0.1)
bar.advance
end
================================================
FILE: examples/indeterminate.rb
================================================
# frozen_string_literal: true
require_relative "../lib/tty-progressbar"
bar = TTY::ProgressBar.new("downloading [:bar] :current/:total :current_byte " \
":total_byte :percent ET::elapsed ETA::eta " \
":rate/s :mean_rate/s :byte_rate/s :mean_byte/s",
width: 40)
170.times do |i|
sleep(0.05)
bar.advance
bar.update(total: 170) if i == 69
end
================================================
FILE: examples/iterator.rb
================================================
# frozen_string_literal: true
require_relative "../lib/tty-progressbar"
bar = TTY::ProgressBar.new("[:bar]", total: 30)
bar.iterate(30.times) { sleep(0.1) }
================================================
FILE: examples/lazy.rb
================================================
# frozen_string_literal: true
require_relative "../lib/tty-progressbar"
bar = TTY::ProgressBar.new("[:bar] :current", total: 10, width: 20)
range = 1..Float::INFINITY
bar.iterate(range.lazy.take(10)) { sleep(0.1) }
================================================
FILE: examples/log.rb
================================================
# frozen_string_literal: true
require_relative "../lib/tty-progressbar"
format = "[:bar] :percent :elapsed :mean_rate/s ETA :eta :eta_time"
bar = TTY::ProgressBar.new(format, total: 10)
10.times do |i|
bar.log("[#{i}] Task")
sleep(0.2)
bar.advance
end
================================================
FILE: examples/multi/formats.rb
================================================
# frozen_string_literal: true
require_relative "../../lib/tty-progressbar"
bars = []
multi_bar = TTY::ProgressBar::Multi.new(width: 50)
TTY::ProgressBar::Formats::FORMATS.each_key do |format|
bars << multi_bar.register("%10s |:bar|" % [format], hide_cursor: true,
total: 50, bar_format: format)
end
begin
50.times do
bars.each do |bar|
sleep(0.002)
bar.advance
end
end
ensure
multi_bar.stop
end
================================================
FILE: examples/multi/main_bar.rb
================================================
# frozen_string_literal: true
require_relative "../../lib/tty-progressbar"
bars = TTY::ProgressBar::Multi.new("main [:bar] :percent")
bar1 = bars.register "foo [:bar] :percent", total: 15
bar2 = bars.register "bar [:bar] :percent", total: 15
bar3 = bars.register "baz [:bar] :percent", total: 45
th1 = Thread.new { 15.times { sleep(0.1); bar1.advance } }
th2 = Thread.new { 15.times { sleep(0.1); bar2.advance } }
th3 = Thread.new { 45.times { sleep(0.1); bar3.advance } }
[th1, th2, th3].each(&:join)
================================================
FILE: examples/multi/resume.rb
================================================
# frozen_string_literal: true
require_relative "../../lib/tty-progressbar"
bars = TTY::ProgressBar::Multi.new("main [:bar] (:current/:total)")
bar1 = bars.register "foo [:bar] :percent", total: 10
10.times { bar1.advance; sleep(0.1) }
bar2 = bars.register "bar [:bar] :percent", total: 15
15.times { bar2.advance; sleep(0.1) }
================================================
FILE: examples/multi/simple.rb
================================================
# frozen_string_literal: true
require_relative "../../lib/tty-progressbar"
bars = TTY::ProgressBar::Multi.new
bar1 = bars.register "foo [:bar] :percent", total: 20
bar2 = bars.register "bar [:bar] :percent", total: 30
bar3 = bars.register "baz [:bar] :percent", total: 10
th1 = Thread.new { 20.times { sleep(0.2); bar1.advance } }
th2 = Thread.new { 30.times { sleep(0.1); bar2.advance } }
th3 = Thread.new { 10.times { sleep(0.3); bar3.advance } }
[th1, th2, th3].each(&:join)
================================================
FILE: examples/multi/single.rb
================================================
# frozen_string_literal: true
require_relative "../../lib/tty-progressbar"
bars = TTY::ProgressBar::Multi.new("main [:bar] :percent")
foo_bar = bars.register "foo [:bar] :percent", total: 30
bar_bar = bars.register "bar [:bar] :percent", total: 30
baz_bar = bars.register "baz [:bar] :percent", total: 30
30.times do |i|
foo_bar.advance
bar_bar.advance(2) if i.even?
baz_bar.advance(3) if (i % 3).zero?
sleep(0.1)
end
================================================
FILE: examples/multi/width.rb
================================================
# frozen_string_literal: true
require_relative "../../lib/tty-progressbar"
bars = TTY::ProgressBar::Multi.new("main [:bar] :percent")
bar1 = bars.register "foo [:bar] :percent", total: 150
bar2 = bars.register "bar [:bar] :percent", total: 250
bar3 = bars.register "baz [:bar] :percent", total: 100
th1 = Thread.new { 15.times { sleep(0.1); bar1.advance(10) } }
th2 = Thread.new { 50.times { sleep(0.1); bar2.advance(5)} }
th3 = Thread.new { 50.times { sleep(0.1); bar3.advance(5) } }
[th1, th2, th3].each(&:join)
================================================
FILE: examples/pause.rb
================================================
# frozen_string_literal: true
require_relative "../lib/tty-progressbar"
bar = TTY::ProgressBar.new("[:bar] :current/:total :current_byte/:total_byte " \
":rate/s :mean_rate/s ET::elapsed ETA::eta", total: 40)
20.times { sleep(0.1); bar.advance }
bar.pause
sleep(1)
bar.resume
20.times { sleep(0.1); bar.advance }
================================================
FILE: examples/simple.rb
================================================
# frozen_string_literal: true
require_relative "../lib/tty-progressbar"
bar = TTY::ProgressBar.new("downloading [:bar] :elapsed :percent", total: 30)
30.times do
sleep(0.1)
bar.advance
end
================================================
FILE: examples/slow_process.rb
================================================
# frozen_string_literal: true
require_relative "../lib/tty-progressbar"
CONTENT_SIZE = 2048
CHUNK_SIZE = 255
# Dummy "long responding server"
def download_from_server(offset, limit)
sleep(0.1)
"<chunk #{offset}..#{offset + limit}>"
end
def download_finished?(position)
position >= CONTENT_SIZE
end
downloader = Enumerator.new do |y|
start = 0
loop do
y.yield(download_from_server(start, CHUNK_SIZE))
start += CHUNK_SIZE
raise StopIteration if download_finished?(start)
end
end
bar = TTY::ProgressBar.new("[:bar] :current_byte/:total_byte", total: CONTENT_SIZE)
response = bar.iterate(downloader, CHUNK_SIZE).to_a.join
puts response
================================================
FILE: examples/speed.rb
================================================
# frozen_string_literal: true
require_relative "../lib/tty-progressbar"
bar = TTY::ProgressBar.new "downloading [:bar] :rate/s :mean_rate/s" do |conf|
conf.total = 100
conf.interval = 1
end
30.times do
sleep(0.1)
bar.advance(Random.rand(10))
end
================================================
FILE: examples/threaded.rb
================================================
# frozen_string_literal: true
require_relative "../lib/tty-progressbar"
threads = []
bar = TTY::ProgressBar.new("[:bar] :percent", total: 30)
threads << Thread.new do
15.times { sleep(0.1); bar.update(complete: "-", head: "-"); bar.advance; }
end
threads << Thread.new do
15.times { sleep(0.1); bar.update(complete: "+", head: "+"); bar.advance; }
end
threads.map(&:join)
================================================
FILE: examples/tokens.rb
================================================
# frozen_string_literal: true
require_relative "../lib/tty-progressbar"
files = [
"file1.txt", "file2.txt", "file3.txt", "file4.txt", "file5.txt",
"file6.txt", "file7.txt", "file8.txt", "file9.txt", "file10.txt"
]
bar = TTY::ProgressBar.new("downloading :file :percent", total: files.size)
10.times do |num|
sleep(0.1)
bar.advance(file: files[num])
end
================================================
FILE: examples/unicode.rb
================================================
# frozen_string_literal: true
require_relative "../lib/tty-progressbar"
bar = TTY::ProgressBar.new("Unicode [:bar]", total: 30,
head: ">", complete: "本", incomplete: "〜")
30.times do
sleep(0.1)
bar.advance
end
================================================
FILE: examples/unicode_unknown.rb
================================================
# frozen_string_literal: true
require_relative "../lib/tty-progressbar"
bar = TTY::ProgressBar.new("Unicode [:bar]", total: nil,
head: ">", complete: "本", incomplete: "〜",
unknown: "<本>", width: 31)
60.times { sleep(0.05); bar.advance }
bar.update(total: 100)
40.times { sleep(0.1); bar.advance }
================================================
FILE: lib/tty/progressbar/configuration.rb
================================================
# frozen_string_literal: true
require_relative "formats"
module TTY
class ProgressBar
class Configuration
include TTY::ProgressBar::Formats
# The total number of steps to completion
# @api public
attr_reader :total
# The maximum width for the progress bar except all formatting tokens
# @api public
attr_accessor :width
# The complete character in progress animation
# @api public
attr_reader :complete
# The incomplete character in progress animation
# @api public
attr_reader :incomplete
# The head character, defaults to complete
# @api public
attr_accessor :head
# The unknown character for indeterminate progress animation
# @api public
attr_reader :unknown
# The amount of indentation before a progress animation
# @api private
attr_accessor :inset
# The preconfigured bar format name, defaults to :classic
# @api public
attr_accessor :bar_format
# The object that responds to print call, defaults to stderr
# @api public
attr_accessor :output
# The frequency with which to display a progress bar per second
# @api public
attr_accessor :frequency
# The time interval for sampling of speed measurement, defaults to 1 second
# @api public
attr_accessor :interval
# Whether or not to hide the cursor, defaults to false
# @api public
attr_accessor :hide_cursor
# Whether or not to clear the progress line, defaults to false
# @api public
attr_accessor :clear
# Whether or not to replace head character with complete, defaults to false
# @api public
attr_accessor :clear_head
def initialize(options)
self.total = options[:total] if options[:total]
@width = options.fetch(:width) { total }
@bar_format = options.fetch(:bar_format, :classic)
self.incomplete = options.fetch(:incomplete) { fetch_char(@bar_format, :incomplete) }
self.complete = options.fetch(:complete) { fetch_char(@bar_format, :complete) }
self.unknown = options.fetch(:unknown) { fetch_char(@bar_format, :unknown) }
@head = options.fetch(:head) { @complete || "=" }
@clear_head = options.fetch(:clear_head, false)
@hide_cursor = options.fetch(:hide_cursor, false)
@clear = options.fetch(:clear, false)
@output = options.fetch(:output) { $stderr }
@frequency = options.fetch(:frequency, 0) # 0Hz
@interval = options.fetch(:interval, 1) # 1 sec
@inset = options.fetch(:inset, 0)
end
# Set complete character(s)
#
# @param [String] value
#
# @api public
def complete=(value)
raise_if_empty(:complete, value)
@complete = value
end
# Set incomplete character(s)
#
# @param [String] value
#
# @api public
def incomplete=(value)
raise_if_empty(:incomplete, value)
@incomplete = value
end
# Set unknown character(s)
#
# @param [String] value
#
# @api public
def unknown=(value)
raise_if_empty(:unknown, value)
@unknown = value
end
# Set total and adjust width if unset
#
# @param [Integer,nil] value
#
# @api public
def total=(value)
@total = value
self.width = value if width.nil?
end
private
# Find bar char by type name and property
#
# @param [Symbol] name
# @param [Symbol] property
#
# @api private
def fetch_char(name, property)
if FORMATS.key?(name)
FORMATS[name][property]
else
raise ArgumentError, "unsupported bar format: #{name.inspect}. " \
"Available formats are: " \
"#{FORMATS.keys.sort.map(&:inspect).join(', ')}"
end
end
# Check whether a parameter's value is empty or not
#
# @raise [ArgumentError]
#
# @api private
def raise_if_empty(name, value)
return value unless value.to_s.empty?
raise ArgumentError, "cannot provide an empty string for #{name.inspect}"
end
end # Configuration
end # ProgressBar
end # TTY
================================================
FILE: lib/tty/progressbar/converter.rb
================================================
# frozen_string_literal: true
module TTY
class ProgressBar
# Responsible for converting values to different formats
#
# @api public
module Converter
HOURSECONDS = 3600
# Convert seconds to time notation
#
# @param [Numeric] seconds
# the seconds to convert to time
#
# @api public
def to_time(seconds)
days = (seconds / (24 * HOURSECONDS).to_f).floor
seconds -= days * 24 * HOURSECONDS
hours = (seconds / HOURSECONDS.to_f).floor
seconds -= hours * HOURSECONDS
minutes = (seconds / 60).floor
seconds -= minutes * 60
if days > 0 # over 24 hours switch to days
format("%dd%2dh%2dm", days, hours, minutes)
elsif hours > 0
format("%2dh%2dm", hours, minutes)
elsif minutes > 0
format("%2dm%2ds", minutes, seconds)
else
format("%2ds", seconds)
end
end
module_function :to_time
# Convert seconds to set precision
#
# @param [Numeric] seconds
# the seconds to convert
#
# @return [String]
# the formatted result
#
# @api public
def to_seconds(seconds, precision: nil)
precision ||= (seconds < 1 && !seconds.zero?) ? 5 : 2
format "%5.#{precision}f", seconds
end
module_function :to_seconds
BYTE_UNITS = %w[b kb mb gb tb pb eb].freeze
# Convert value to bytes
#
# @param [Numeric] value
# the value to convert to bytes
# @param [Integer] decimals
# the number of decimals parts
# @param [String] separator
# the separator to use for thousands in a number
# @param [String] unit_separator
# the separtor to use between number and unit
#
# @return [String]
#
# @api public
def to_bytes(value, decimals: 2, separator: ".", unit_separator: "")
base = 1024
pattern = "%.#{decimals}f"
unit = BYTE_UNITS.find.with_index { |_, i| value < base**(i + 1) }
if value < base
formatted_value = value.to_i.to_s
else
value_to_size = value / (base**BYTE_UNITS.index(unit)).to_f
formatted_value = format(pattern, value_to_size)
end
formatted_value.gsub(/\./, separator) + unit_separator + unit.to_s.upcase
end
module_function :to_bytes
end # Converter
end # ProgressBar
end # TTY
================================================
FILE: lib/tty/progressbar/formats.rb
================================================
# frozen_string_literal: true
module TTY
class ProgressBar
module Formats
FORMATS = {
arrow: { # ▸▸▸▸▸▹▹▹▹▹
complete: "▸",
incomplete: "▹",
unknown: "◂▸"
},
asterisk: { # ✱✱✱✱✱✳✳✳✳✳
complete: "✱",
incomplete: "✳",
unknown: "✳✱✳"
},
blade: { # ▰▰▰▰▰▱▱▱▱▱
complete: "▰",
incomplete: "▱",
unknown: "▱▰▱"
},
block: { # █████░░░░░
complete: "█",
incomplete: "░",
unknown: "█"
},
box: { # ■■■■■□□□□□
complete: "■",
incomplete: "□",
unknown: "□■□"
},
bracket: { # ❭❭❭❭❭❭❭❭❭❭
complete: "❭",
incomplete: " ",
unknown: "❬=❭"
},
burger: { # ≡≡≡≡≡≡≡≡≡≡
complete: "≡",
incomplete: " ",
unknown: "<≡>"
},
button: { # ⦿⦿⦿⦿⦿⦾⦾⦾⦾⦾
complete: "⦿",
incomplete: "⦾",
unknown: "⦾⦿⦾"
},
chevron: { # ››››››››››
complete: "›",
incomplete: " ",
unknown: "‹=›"
},
circle: { # ●●●●●○○○○○
complete: "●",
incomplete: "○",
unknown: "○●○"
},
classic: { # ==========
complete: "=",
incomplete: " ",
unknown: "<=>"
},
crate: { # ▣▣▣▣▣⬚⬚⬚⬚⬚
complete: "▣",
incomplete: "⬚",
unknown: "⬚▣⬚"
},
diamond: { # ♦♦♦♦♦♢♢♢♢♢
complete: "♦",
incomplete: "♢",
unknown: "♢♦♢"
},
dot: { # ・・・・・・・・・・
complete: "・",
incomplete: " ",
unknown: "・・・"
},
heart: { # ♥♥♥♥♥♡♡♡♡♡
complete: "♥",
incomplete: "♡",
unknown: "♡♥♡"
},
rectangle: { # ▮▮▮▮▮▯▯▯▯▯
complete: "▮",
incomplete: "▯",
unknown: "▯▮▯"
},
square: { # ▪▪▪▪▪▫▫▫▫▫
complete: "▪",
incomplete: "▫",
unknown: "▫▪▫"
},
star: { # ★★★★★☆☆☆☆☆
complete: "★",
incomplete: "☆",
unknown: "☆★☆"
},
track: { # ▬▬▬▬▬═════
complete: "▬",
incomplete: "═",
unknown: "═▬═"
},
tread: { # ❱❱❱❱❱❱❱❱❱❱
complete: "❱",
incomplete: " ",
unknown: "❰=❱"
},
triangle: { # ▶▶▶▶▶▷▷▷▷▷
complete: "▶",
incomplete: "▷",
unknown: "◀▶"
},
wave: { # ~~~~~_____
complete: "~",
incomplete: "_",
unknown: "<~>"
}
}.freeze
end # Formats
end # ProgressBar
end # TTY
================================================
FILE: lib/tty/progressbar/formatter/bar.rb
================================================
# frozen_string_literal: true
require_relative "../formatter"
module TTY
class ProgressBar
# Used by {Pipeline} to format bar
#
# @api private
class BarFormatter
include TTY::ProgressBar::Formatter[/:bar/i.freeze]
# Format :bar token
#
# @param [String] value
# the value being formatted
#
# @api public
def call(value)
without_bar = value.gsub(/:bar/, "")
available_space = [0, ProgressBar.max_columns -
ProgressBar.display_columns(without_bar) -
@progress.inset].max
width = [@progress.width.to_i, available_space].min
# When we don't know the total progress, use either user
# defined width or rely on terminal width detection
if @progress.indeterminate?
width = available_space if width.zero?
format_indeterminate(value, width)
else
format_determinate(value, width)
end
end
private
# @api private
def format_indeterminate(value, width)
buffer = []
possible_width = width
unknown_char_length = ProgressBar.display_columns(@progress.unknown)
complete_char_length = ProgressBar.display_columns(@progress.complete)
incomplete_char_length = ProgressBar.display_columns(@progress.incomplete)
head_char_length = ProgressBar.display_columns(@progress.head)
possible_width -= unknown_char_length
max_char_length = [complete_char_length, incomplete_char_length,
head_char_length].max
# figure out how many unicode chars would fit normally
# when the bar has total to prevent resizing
possible_width = (possible_width / max_char_length) * max_char_length
complete = (possible_width * @progress.ratio).round
incomplete = possible_width - complete
buffer << " " * complete
buffer << @progress.unknown
buffer << " " * incomplete
value.gsub(matcher, buffer.join)
end
# @api private
def format_determinate(value, width)
complete_bar_length = (width * @progress.ratio).round
complete_char_length = ProgressBar.display_columns(@progress.complete)
incomplete_char_length = ProgressBar.display_columns(@progress.incomplete)
head_char_length = ProgressBar.display_columns(@progress.head)
# division by char length only when unicode chars are used
# otherwise it has no effect on regular ascii chars
complete_items = [
complete_bar_length / complete_char_length,
# or see how many incomplete (unicode) items fit
(complete_bar_length / incomplete_char_length) * incomplete_char_length
].min
complete_width = complete_items * complete_char_length
incomplete_width = width - complete_width
incomplete_items = [
incomplete_width / incomplete_char_length,
# or see how many complete (unicode) items fit
(incomplete_width / complete_char_length) * complete_char_length
].min
complete = Array.new(complete_items, @progress.complete)
incomplete = Array.new(incomplete_items, @progress.incomplete)
if complete_items > 0 && head_char_length > 0 &&
(incomplete_items > 0 || incomplete_items.zero? && !@progress.clear_head)
# see how many head chars per complete char
times = (head_char_length / complete_char_length.to_f).round
if complete_items < times # not enough complete chars to fit
incomplete.pop(times - complete_items)
end
complete.pop(times)
extra_space = " " * (times * complete_char_length - head_char_length)
complete << "#{@progress.head}#{extra_space}"
end
value.gsub(matcher, "#{complete.join}#{incomplete.join}")
end
end # BarFormatter
end # ProgressBar
end # TTY
================================================
FILE: lib/tty/progressbar/formatter/byte_rate.rb
================================================
# frozen_string_literal: true
require_relative "../converter"
require_relative "../formatter"
module TTY
class ProgressBar
# Used by {Pipeline} to format :byte_rate token
#
# @api private
class ByteRateFormatter
include TTY::ProgressBar::Formatter[/:byte_rate/i.freeze]
# Format :byte_rate token
#
# @param [String] value
# the value to format
#
# @api public
def call(value)
formatted = Converter.to_bytes(@progress.rate)
value.gsub(matcher, formatted)
end
end # ByteRateFormatter
end # ProgressBar
end # TTY
================================================
FILE: lib/tty/progressbar/formatter/current.rb
================================================
# frozen_string_literal: true
module TTY
class ProgressBar
# Used by {Pipeline} to format :current token
#
# @api private
class CurrentFormatter
include TTY::ProgressBar::Formatter[/:current\b/i.freeze]
# Format :current token
#
# @param [String] value
# the value to format
#
# @api public
def call(value)
value.gsub(matcher, @progress.current.to_s)
end
end # CurrentFormatter
end # ProgressBar
end # TTY
================================================
FILE: lib/tty/progressbar/formatter/current_byte.rb
================================================
# frozen_string_literal: true
require_relative "../converter"
require_relative "../formatter"
module TTY
class ProgressBar
# Used by {Pipeline} to format :byte and :current_byte tokens
#
# @api private
class ByteFormatter
include TTY::ProgressBar::Formatter[/(:current_byte|:byte)\b/i.freeze]
# Format :current_byte token
#
# @param [String] value
# the value to format
#
# @api public
def call(value)
bytes = Converter.to_bytes(@progress.current)
value.gsub(matcher, bytes)
end
end # ByteFormatter
end # ProgressBar
end # TTY
================================================
FILE: lib/tty/progressbar/formatter/elapsed.rb
================================================
# frozen_string_literal: true
require_relative "../converter"
require_relative "../formatter"
module TTY
class ProgressBar
# Used by {Pipeline} to format :elapsed token
#
# @api private
class ElapsedFormatter
include TTY::ProgressBar::Formatter[/:elapsed/.freeze]
# Format :elapsed token
#
# @param [String] value
# the value to format
#
# @api public
def call(value)
value.gsub(matcher, Converter.to_time(@progress.elapsed_time))
end
end # ElapsedFormatter
end # ProgressBar
end # TTY
================================================
FILE: lib/tty/progressbar/formatter/estimated.rb
================================================
# frozen_string_literal: true
require_relative "../converter"
require_relative "../formatter"
module TTY
class ProgressBar
# Used by {Pipeline} to format :eta token
#
# @api private
class EstimatedFormatter
include TTY::ProgressBar::Formatter[/:eta/.freeze]
# Format :eta token
#
# @param [String] value
# the value to format
#
# @api public
def call(value)
if @progress.indeterminate? ||
(@progress.elapsed_time.zero? && @progress.ratio.zero?)
return value.gsub(matcher, "--s")
end
elapsed = @progress.elapsed_time
estimated = @progress.ratio.zero? ? 0.0 : (elapsed / @progress.ratio).to_f
estimated -= elapsed
estimated = 0.0 if estimated < 0
value.gsub(matcher, Converter.to_time(estimated))
end
end # EstimatedFormatter
end # ProgressBar
end # TTY
================================================
FILE: lib/tty/progressbar/formatter/estimated_time.rb
================================================
# frozen_string_literal: true
module TTY
class ProgressBar
# Used by {Pipeline} to format :eta_time token
#
# @api private
class EstimatedTimeFormatter
include TTY::ProgressBar::Formatter[/:eta_time/.freeze]
# Format :eta_time token
#
# @param [String] value
# the value to format
#
# @api public
def call(value)
if @progress.indeterminate? ||
(@progress.elapsed_time.zero? && @progress.ratio.zero?)
return value.gsub(matcher, "--:--:--")
end
elapsed = @progress.elapsed_time
estimated = @progress.ratio.zero? ? 0.0 : (elapsed / @progress.ratio).to_f
estimated -= elapsed
estimated = 0.0 if estimated < 0
time_format = if estimated >= 86_400 # longer than a day
"%Y-%m-%d %H:%M:%S"
else
"%H:%M:%S"
end
completion_time = Time.now + estimated
eta_time = completion_time.strftime(time_format)
value.gsub(matcher, eta_time)
end
end # EstimatedTimeFormatter
end # ProgressBar
end # TTY
================================================
FILE: lib/tty/progressbar/formatter/mean_byte.rb
================================================
# frozen_string_literal: true
require_relative "../converter"
require_relative "../formatter"
module TTY
class ProgressBar
# Used by {Pipeline} to format :mean_byte token
#
# @api private
class MeanByteFormatter
include TTY::ProgressBar::Formatter[/:mean_byte/i.freeze]
# Format :mean_byte token
#
# @param [String] value
# the value being formatted
#
# @api public
def call(value)
formatted = Converter.to_bytes(@progress.mean_rate)
value.gsub(matcher, formatted)
end
end # MeanByteFormatter
end # ProgressBar
end # TTY
================================================
FILE: lib/tty/progressbar/formatter/mean_rate.rb
================================================
# frozen_string_literal: true
require_relative "../converter"
require_relative "../formatter"
module TTY
class ProgressBar
# Used by {Pipeline} to format :mean_rate token
#
# @api private
class MeanRateFormatter
include TTY::ProgressBar::Formatter[/:mean_rate/i.freeze]
# Format :mean_rate token
#
# @param [String] value
# the value being formatted
#
# @api public
def call(value)
formatted = Converter.to_seconds(@progress.mean_rate)
value.gsub(matcher, formatted)
end
end # MeanRateFormatter
end # ProgressBar
end # TTY
================================================
FILE: lib/tty/progressbar/formatter/percent.rb
================================================
# frozen_string_literal: true
require_relative "../formatter"
module TTY
class ProgressBar
# Used by {Pipeline} to format :percent token
#
# @api private
class PercentFormatter
include TTY::ProgressBar::Formatter[/:percent\b/.freeze]
# Format :percent token
#
# @param [String] value
# the value to format
#
# @api public
def call(value)
percent = @progress.width == 0 ? 100 : (@progress.ratio * 100).to_i
display = @progress.indeterminate? ? "-" : percent.to_s
value.gsub(matcher, "#{display}%")
end
end # PercentFormatter
end # ProgressBar
end # TTY
================================================
FILE: lib/tty/progressbar/formatter/rate.rb
================================================
# frozen_string_literal: true
require_relative "../converter"
require_relative "../formatter"
module TTY
class ProgressBar
# Used by {Pipeline} to format :rate token
#
# @api private
class RateFormatter
include TTY::ProgressBar::Formatter[/:rate/i.freeze]
# Format :rate token
#
# @param [String] value
# the value being formatted
#
# @api public
def call(value)
formatted = Converter.to_seconds(@progress.rate)
value.gsub(matcher, formatted)
end
end # RateFormatter
end # ProgressBar
end # TTY
================================================
FILE: lib/tty/progressbar/formatter/total.rb
================================================
# frozen_string_literal: true
require_relative "../formatter"
module TTY
class ProgressBar
# Used by {Pipeline} to format :total token
#
# @api private
class TotalFormatter
include TTY::ProgressBar::Formatter[/:total\b/i.freeze]
# Format :total token
#
# @param [String] value
# the value to format
#
# @api public
def call(value)
display = @progress.indeterminate? ? "-" : @progress.total.to_s
value.gsub(matcher, display)
end
end # TotalFormatter
end # ProgressBar
end # TTY
================================================
FILE: lib/tty/progressbar/formatter/total_byte.rb
================================================
# frozen_string_literal: true
require_relative "../converter"
require_relative "../formatter"
module TTY
class ProgressBar
# Used by {Pipeline} to format :total_byte token
#
# @api private
class TotalByteFormatter
include TTY::ProgressBar::Formatter[/:total_byte/i.freeze]
# Format :total_byte token
#
# @param [String] value
# the value to format
#
# @api public
def call(value)
bytes = if @progress.indeterminate?
"-B"
else
Converter.to_bytes(@progress.total)
end
value.gsub(matcher, bytes)
end
end # TotalByteFormatter
end # ProgressBar
end # TTY
================================================
FILE: lib/tty/progressbar/formatter.rb
================================================
# frozen_string_literal: true
module TTY
class ProgressBar
class Formatter < ::Module
# A helper for declaring a matching token pattern
#
# @api public
def self.[](token_match)
new(token_match)
end
# Initialize this module with token matching pattern
#
# @param [Regexp] token_match
# the token matching pattern
#
# @api public
def initialize(token_match)
pattern = token_match
module_eval do
define_method(:initialize) do |progress|
@progress = progress
end
define_method(:matcher) { pattern }
define_method(:progress) { @progress }
# Determines whether this formatter is applied or not.
#
# @param [Object] value
#
# @return [Boolean]
#
# @api private
define_method(:matches?) do |value|
!!(value.to_s =~ pattern)
end
end
end
end # Formatter
end # ProgressBar
end # TTY
================================================
FILE: lib/tty/progressbar/formatters.rb
================================================
# frozen_string_literal: true
require "forwardable"
require_relative "pipeline"
require_relative "formatter/bar"
require_relative "formatter/current"
require_relative "formatter/current_byte"
require_relative "formatter/elapsed"
require_relative "formatter/estimated"
require_relative "formatter/estimated_time"
require_relative "formatter/percent"
require_relative "formatter/rate"
require_relative "formatter/byte_rate"
require_relative "formatter/mean_rate"
require_relative "formatter/mean_byte"
require_relative "formatter/total"
require_relative "formatter/total_byte"
module TTY
class ProgressBar
class Formatters
extend Forwardable
def_delegators :@pipeline, :decorate, :use
# @api private
def initialize(pipeline = nil)
@pipeline = pipeline || TTY::ProgressBar::Pipeline.new
end
# Prepare default pipeline formatters
#
# @api private
def load(progress)
@pipeline.use TTY::ProgressBar::CurrentFormatter.new(progress)
@pipeline.use TTY::ProgressBar::TotalFormatter.new(progress)
@pipeline.use TTY::ProgressBar::TotalByteFormatter.new(progress)
@pipeline.use TTY::ProgressBar::ElapsedFormatter.new(progress)
@pipeline.use TTY::ProgressBar::EstimatedTimeFormatter.new(progress)
@pipeline.use TTY::ProgressBar::EstimatedFormatter.new(progress)
@pipeline.use TTY::ProgressBar::PercentFormatter.new(progress)
@pipeline.use TTY::ProgressBar::ByteFormatter.new(progress)
@pipeline.use TTY::ProgressBar::ByteRateFormatter.new(progress)
@pipeline.use TTY::ProgressBar::RateFormatter.new(progress)
@pipeline.use TTY::ProgressBar::MeanRateFormatter.new(progress)
@pipeline.use TTY::ProgressBar::MeanByteFormatter.new(progress)
@pipeline.use TTY::ProgressBar::BarFormatter.new(progress)
end
end # Formatters
end # ProgressBar
end # TTY
================================================
FILE: lib/tty/progressbar/meter.rb
================================================
# frozen_string_literal: true
module TTY
class ProgressBar
# Used by {ProgressBar} to measure progress rate per interval
# by default 1s
#
# @api private
class Meter
# Create Meter
#
# @param [Integer] interval
# the interval for measurement samples
#
# @api private
def initialize(interval)
@interval = interval || 1 # 1 sec
start
end
# Start sampling timer
#
# @api public
def start
@start_time = Time.now
@current = 0
@samples = [[@start_time, 0]]
@rates = []
@start_time
end
# Update meter with value
#
# @param [Time] at
# the time of the sampling
#
# @param [Integer] value
# the current value of progress
#
# @api public
def sample(at, value)
@current += value
prune_samples(at)
@samples << [at, @current]
save_rate(at)
end
# Remove samples that are obsolete
#
# @api private
def prune_samples(at)
cutoff = at - @interval
while @samples.size > 1 && (@samples.first.first < cutoff)
@samples.shift
end
end
# If we crossed a period boundary since @start_time,
# save the rate for {#rates}
#
# @api private
def save_rate(at)
period_index = ((at - @start_time) / @interval).floor
while period_index > @rates.size
@rates << rate
end
end
# The current rate of sampling for a given interval
#
# @return [Number]
# the current rate in decimal or 0 if cannot be determined
#
# @api public
def rate
first_at, first_value = @samples.first
last_at, last_value = @samples.last
if first_at == last_at
0
else
(last_value - first_value) / (last_at - first_at)
end
end
# Group all rates per interval
#
# @api public
def rates
@rates + [rate]
end
# The mean rate of all the sampled rates
#
# @return [Number]
# the mean rate
#
# @api public
def mean_rate
last_at, last_value = @samples.last
if last_at == @start_time
0
else
last_value / (last_at - @start_time)
end
end
alias avg_rate mean_rate
# Reset the meter by clearing out it's metrics
#
# @api public
def clear
start
end
end # Meter
end # ProgressBar
end # TTY
================================================
FILE: lib/tty/progressbar/multi.rb
================================================
# frozen_string_literal: true
require "forwardable"
require "monitor"
require_relative "../progressbar"
module TTY
class ProgressBar
# Used for managing multiple terminal progress bars
#
# @api public
class Multi
include Enumerable
include MonitorMixin
extend Forwardable
def_delegators :@bars, :each, :empty?, :length, :[]
def_delegators :@top_bar, :width, :width=
DEFAULT_INSET = {
top: Gem.win_platform? ? "+ " : "\u250c ",
middle: Gem.win_platform? ? "|-- " : "\u251c\u2500\u2500 ",
bottom: Gem.win_platform? ? "|__ " : "\u2514\u2500\u2500 "
}.freeze
# Number of currently occupied rows in terminal display
attr_reader :rows
# Create a multibar
#
# @example
# bars = TTY::ProgressBar::Multi.new
#
# @example
# bars = TTY::ProgressBar::Multi.new("main [:bar]")
#
# @param [String] format
# the formatting string to display this bar
#
# @param [Hash] options
#
# @api public
def initialize(*args)
super()
@options = args.last.is_a?(::Hash) ? args.pop : {}
format = args.empty? ? nil : args.pop
@inset_opts = @options.delete(:style) { DEFAULT_INSET }
@bars = []
@rows = 0
@top_bar = nil
@top_bar = register(format, observable: false) if format
@width = @options[:width]
@top_bar.update(width: @width) if @top_bar && @width
@callbacks = {
progress: [],
stopped: [],
paused: [],
done: []
}
end
# Register a new progress bar
#
# @param [String] format
# the formatting string to display the bar
#
# @api public
def register(format, options = {})
observable = options.delete(:observable) { true }
bar = TTY::ProgressBar.new(format, @options.merge(options))
synchronize do
bar.attach_to(self)
@bars << bar
observe(bar) if observable
if @top_bar
@top_bar.update(total: total)
@top_bar.resume if @top_bar.done?
@top_bar.update(width: total) unless @width
end
end
bar
end
# Increase row count
#
# @api public
def next_row
synchronize do
@rows += 1
end
end
# Observe a bar for emitted events
#
# @param [TTY::ProgressBar] bar
# the bar to observe for events
#
# @api private
def observe(bar)
bar.on(:progress, &progress_handler)
.on(:done) { emit(:done) if complete? }
.on(:stopped) { emit(:stopped) if stopped? }
.on(:paused) { emit(:paused) if paused? }
end
# Handle the progress event
#
# @api private
def progress_handler
->(progress) do
@top_bar.advance(progress) if @top_bar
emit(:progress, progress)
end
end
# Get the top level bar if it exists
#
# @api public
def top_bar
raise "No top level progress bar" unless @top_bar
@top_bar
end
def start
raise "No top level progress bar" unless @top_bar
@top_bar.start
end
# Calculate total maximum progress of all bars
#
# @return [Integer]
#
# @api public
def total
synchronize do
(@bars - [@top_bar]).map(&:total).compact.reduce(&:+)
end
end
# Calculate total current progress of all bars
#
# @return [Integer]
#
# @api public
def current
synchronize do
(@bars - [@top_bar]).map(&:current).reduce(&:+)
end
end
# Check if all progress bars are complete
#
# @return [Boolean]
#
# @api public
def complete?
synchronize do
(@bars - [@top_bar]).all?(&:complete?)
end
end
# Check if all of the registered progress bars is stopped
#
# @return [Boolean]
#
# @api public
def stopped?
synchronize do
(@bars - [@top_bar]).all?(&:stopped?)
end
end
# Check if all bars are stopped or finished
#
# @return [Boolean]
#
# @api public
def done?
synchronize do
(@bars - [@top_bar]).all?(&:done?)
end
end
# Check if all bars are paused
#
# @return [Boolean]
#
# @api public
def paused?
synchronize do
(@bars - [@top_bar]).all?(&:paused?)
end
end
# Stop all progress bars
#
# @api public
def stop
@bars.each(&:stop)
end
# Finish all progress bars
#
# @api public
def finish
@bars.each(&:finish)
end
# Pause all progress bars
#
# @api public
def pause
@bars.each(&:pause)
end
# Resume all progress bars
#
# @api public
def resume
@bars.each(&:resume)
end
# Find the number of characters to move into the line
# before printing the bar
#
# @param [TTY::ProgressBar] bar
# the progress bar for which line inset is calculated
#
# @return [String]
# the inset
#
# @api public
def line_inset(bar)
return "" if @top_bar.nil?
case bar.row
when @top_bar.row
@inset_opts[:top]
when rows
@inset_opts[:bottom]
else
@inset_opts[:middle]
end
end
# Listen on event
#
# @param [Symbol] name
# the event name to listen on
#
# @api public
def on(name, &callback)
unless @callbacks.key?(name)
raise ArgumentError, "The event #{name} does not exist. " \
"Use :progress, :stopped, :paused or " \
":done instead"
end
@callbacks[name] << callback
self
end
private
# Fire an event by name
#
# @api private
def emit(name, *args)
@callbacks[name].each do |callback|
callback.(*args)
end
end
end # Multi
end # ProgressBar
end # TTY
================================================
FILE: lib/tty/progressbar/pipeline.rb
================================================
# frozen_string_literal: true
module TTY
class ProgressBar
# Used by {ProgressBar} to decorate format string
#
# @api private
class Pipeline
include Enumerable
# Create formatting pipeline
#
# @api private
def initialize(formatters = [])
@formatters = formatters
freeze
end
# Add a new formatter
#
# @example
# use(TTY::ProgressBar::TotalFormatter.new(progress_bar))
#
# @api public
def use(formatter)
formatters << formatter
end
# Decorate the tokenized string with actual values
#
# @example
# decorate("[:bar] :current :elapsed")
#
# @param [String] tokenized
# the string with tokens
#
# @return [nil]
#
# @api private
def decorate(tokenized)
base = tokenized.dup
formatters.inject(base) do |formatted, formatter|
if formatter.respond_to?(:matches?) && formatter.matches?(formatted)
formatter.(formatted)
else
formatted
end
end
end
# Iterate over formatters
#
# @api public
def each(&block)
formatters.each(&block)
end
protected
attr_reader :formatters
end # Pipeline
end # ProgressBar
end # TTY
================================================
FILE: lib/tty/progressbar/timer.rb
================================================
# frozen_string_literal: true
module TTY
class ProgressBar
# Used to measure the elapsed time for multiple time intervals
#
# @api private
class Timer
attr_reader :start_time
# Create Timer
#
# @api private
def initialize
reset
end
# Reset the start time to nil and elapsed time to zero
#
# @api public
def reset
@running = false
@offset = 0
@start_time = nil
end
# Check whether or not the timer is running
#
# @return [Boolean]
#
# @api public
def running?
@running
end
# Total elapsed time
#
# @return [Float]
# the elapsed time in seconds
#
# @api public
def elapsed_time
if running?
elapsed_until_now + @offset
else
@offset
end
end
# Measure current time interval
#
# @api public
def elapsed_until_now
time_so_far = Time.now - @start_time
# protect against negative time drifting
time_so_far > 0 ? time_so_far : 0
end
# Start measuring elapsed time for a new interval
#
# @return [Time]
# return the start time
#
# @api public
def start
return @start_time if running?
@running = true
@start_time = Time.now
end
# Stop measuring elapsed time for the current interval
#
# @return [Float]
# return elapsed time for the stopped interval
#
# @api public
def stop
return 0 unless running?
interval = elapsed_until_now
@offset += interval
@running = false
@start_time = nil
interval
end
end # Timer
end # ProgressBar
end # TTY
================================================
FILE: lib/tty/progressbar/version.rb
================================================
# frozen_string_literal: true
module TTY
class ProgressBar
VERSION = "0.18.3"
end # ProgressBar
end # TTY
================================================
FILE: lib/tty/progressbar.rb
================================================
# frozen_string_literal: true
require "io/console"
require "forwardable"
require "monitor"
require "tty-cursor"
require "tty-screen"
require "strings-ansi"
require "unicode/display_width"
require_relative "progressbar/timer"
require_relative "progressbar/configuration"
require_relative "progressbar/formatters"
require_relative "progressbar/meter"
require_relative "progressbar/version"
module TTY
# Used for creating terminal progress bar
#
# @api public
class ProgressBar
extend Forwardable
include MonitorMixin
ECMA_CSI = "\e["
NEWLINE = "\n"
CURSOR_LOCK = Monitor.new
attr_accessor :format
attr_reader :current
attr_reader :row
def_delegators :@configuration, :total, :width, :complete, :incomplete,
:head, :clear_head, :hide_cursor, :clear, :output,
:frequency, :interval, :inset, :width=, :unknown, :bar_format
def_delegators :@meter, :rate, :mean_rate
def_delegators :@timer, :elapsed_time, :start_time
# Determine terminal width
#
# @return [Integer]
#
# @api public
def self.max_columns
TTY::Screen.width
end
# Determine the monospace display width of a string
#
# @param [String] value
# the value to determine width of
#
# @return [Integer]
#
# @api public
def self.display_columns(value)
Unicode::DisplayWidth.of(Strings::ANSI.sanitize(value))
end
# Create progress bar
#
# @example
# bar = TTY::Progressbar.new
# bar.configure do |config|
# config.total = 20
# end
#
# @param [String] format
# the tokenized string that displays the output
#
# @param [Hash] options
# @option options [Numeric] :total
# the total number of steps to completion
# @option options [Numeric] :width
# the maximum width for the progress bar except all formatting tokens
# @option option [String] :complete
# the complete character in progress animation
# @option options [String] :incomplete
# the incomplete character in progress animation
# @option options [String] :head
# the head character, defaults to complete
# @option options [String] :unknown
# the unknown character for indeterminate progress animation
# @option options [Boolean] :bar_format
# the preconfigured bar format name, defaults to :classic
# @option options [Object] :output
# the object that responds to print call, defaults to stderr
# @option options [Number] :frequency
# the frequency with which to display a progress bar per second
# @option options [Number] :interval
# the time interval for sampling of speed measurement, defaults to 1 second
# @option options [Boolean] :hide_cursor
# whether or not to hide the cursor, defaults to false
# @option options [Boolean] :clear
# whether or not to clear the progress line, defaults to false
# @option options [Boolean] :clear_head
# whether or not to replace head character with complete, defaults to false
#
# @api public
def initialize(format, options = {})
super()
@format = format
if format.is_a?(Hash)
raise ArgumentError, "Expected bar formatting string, " \
"got `#{format}` instead."
end
@configuration = TTY::ProgressBar::Configuration.new(options)
yield @configuration if block_given?
@formatters = TTY::ProgressBar::Formatters.new
@meter = TTY::ProgressBar::Meter.new(interval)
@timer = TTY::ProgressBar::Timer.new
@callbacks = Hash.new { |h, k| h[k] = [] }
@formatters.load(self)
reset
@first_render = true
@multibar = nil
@row = nil
end
# Reset progress to default configuration
#
# @api public
def reset
@width = 0 if indeterminate?
@render_period = frequency == 0 ? 0 : 1.0 / frequency
@current = 0
@unknown = 0
@last_render_time = Time.now
@last_render_width = 0
@done = false
@stopped = false
@paused = false
@tokens = {}
@meter.clear
@timer.reset
end
# Access instance configuration
#
# @api public
def configure
yield @configuration
end
# Check if progress can be determined or not
#
# @return [Boolean]
#
# @api public
def indeterminate?
total.nil?
end
# Attach this bar to multi bar
#
# @param [TTY::ProgressBar::Multi] multibar
# the multibar under which this bar is registered
#
# @api private
def attach_to(multibar)
@multibar = multibar
end
# Use custom token formatter
#
# @param [Object] formatter_class
# the formatter class to add to formatting pipeline
#
# @api public
def use(formatter_class)
unless formatter_class.is_a?(Class)
raise ArgumentError, "Formatter needs to be a class"
end
@formatters.use(formatter_class.new(self))
end
# Start progression by drawing bar and setting time
#
# @api public
def start
synchronize do
@timer.start
@meter.start
end
advance(0)
end
# Advance the progress bar
#
# @param [Object|Number] progress
#
# @api public
def advance(progress = 1, tokens = {})
return if done?
synchronize do
emit(:progress, progress)
if progress.respond_to?(:to_hash)
tokens, progress = progress, 1
end
@timer.start
@current += progress
# When progress is unknown increase by 2% up to max 200%, after
# that reset back to 0%
@unknown += 2 if indeterminate?
@unknown = 0 if @unknown > 199
@tokens = tokens
@meter.sample(Time.now, progress)
if !indeterminate? && @current >= total
finish && return
end
return if (Time.now - @last_render_time) < @render_period
render
end
end
# Iterate over collection either yielding computation to block
# or provided Enumerator. If the bar's `total` was not set,
# it would be taken from `collection.count`, otherwise previously
# set `total` would be used. This allows using the progressbar
# with infinite, lazy, or slowly-calculated enumerators.
#
# @note
# If `total` is set, iteration will NOT stop after this number of
# iterations, only when provided Enumerable is finished. It may
# be convenient in "unsure number of iterations" situations
# (like downloading in chunks, when server may eventually send
# more chunks than predicted), but be careful to not pass infinite
# enumerators without previously doing `.take(some_finite_number)`
# on them.
#
# @example
# bar.iterate(30.times) { ... }
#
# @param [Enumerable] collection
# the collection to iterate over
#
# @param [Integer] progress
# the amount to move progress bar by
#
# @return [Enumerator]
#
# @api public
def iterate(collection, progress = 1, &block)
update(total: collection.count * progress) unless total
progress_enum = Enumerator.new do |iter|
collection.each do |elem|
advance(progress)
iter.yield(elem)
end
end
block_given? ? progress_enum.each(&block) : progress_enum
end
# Update configuration options for this bar
#
# @param [Hash[Symbol]] options
# the configuration options to update
#
# @api public
def update(options = {})
synchronize do
options.each do |name, val|
if @configuration.respond_to?("#{name}=")
@configuration.public_send("#{name}=", val)
end
end
end
end
# Advance the progress bar to the updated value
#
# @param [Number] value
# the desired value to updated to
#
# @api public
def current=(value)
unless value.is_a?(Numeric)
raise ArgumentError, "Expected a numeric value, " \
"got #{value.inspect} instead."
end
value = [0, [value, total].compact.min].max
advance(value - @current)
end
# Advance the progress bar to an exact ratio.
# The target value is set to the closest available value.
#
# @param [Float] value
# the ratio between 0 and 1 inclusive
#
# @api public
def ratio=(value)
target = (value * total).floor
advance(target - @current)
end
# Ratio of completed over total steps
#
# When the total is unknown the progress ratio oscillates
# by going up from 0 to 1 and then down from 1 to 0 and
# up again to infinity.
#
# @return [Float]
#
# @api public
def ratio
synchronize do
proportion = if total
total > 0 ? (@current.to_f / total) : 0
else
(@unknown > 100 ? 200 - @unknown : @unknown).to_f / 100
end
[[proportion, 0].max, 1].min
end
end
# Render progress to the output
#
# @api private
def render
return if done?
if hide_cursor && @last_render_width == 0 &&
(indeterminate? || @current < total)
write(TTY::Cursor.hide)
end
if @multibar
characters_in = @multibar.line_inset(self)
update(inset: self.class.display_columns(characters_in))
end
formatted = @formatters.decorate(@format)
@tokens.each do |token, val|
formatted = formatted.gsub(":#{token}", val)
end
padded = padout(formatted)
write(padded, true)
@last_render_time = Time.now
@last_render_width = self.class.display_columns(formatted)
end
# Move cursor to a row of the current bar if the bar is rendered
# under a multibar. Otherwise, do not move and yield on current row.
#
# @api private
def move_to_row
if @multibar
CURSOR_LOCK.synchronize do
if @first_render
@row = @multibar.next_row
yield if block_given?
output.print NEWLINE
@first_render = false
else
lines_up = (@multibar.rows + 1) - @row
output.print TTY::Cursor.save
output.print TTY::Cursor.up(lines_up)
yield if block_given?
output.print TTY::Cursor.restore
end
end
else
yield if block_given?
end
end
# Write out to the output
#
# @param [String] data
#
# @api private
def write(data, clear_first = false)
return unless tty? # write only to terminal
move_to_row do
output.print(TTY::Cursor.column(1)) if clear_first
characters_in = @multibar.line_inset(self) if @multibar
output.print("#{characters_in}#{data}")
output.flush
end
end
# Resize progress bar with new configuration
#
# @param [Integer] new_width
# the new width for the bar display
#
# @api public
def resize(new_width = nil)
return if done?
synchronize do
clear_line
if new_width
self.width = new_width
end
end
end
# End the progress
#
# @api public
def finish
return if done?
@current = total unless indeterminate?
render
clear ? clear_line : write(NEWLINE, false)
ensure
@meter.clear
@done = true
@timer.stop
# reenable cursor if it is turned off
if hide_cursor && @last_render_width != 0
write(TTY::Cursor.show, false)
end
emit(:done)
end
# Resume rendering when bar is done, stopped or paused
#
# @api public
def resume
synchronize do
@done = false
@stopped = false
@paused = false
end
end
# Stop and cancel the progress at the current position
#
# @api public
def stop
return if done?
render
clear ? clear_line : write(NEWLINE, false)
ensure
@meter.clear
@stopped = true
@timer.stop
# reenable cursor if it is turned off
if hide_cursor && @last_render_width != 0
write(TTY::Cursor.show, false)
end
emit(:stopped)
end
# Pause the progress at the current position
#
# @api public
def pause
synchronize do
@paused = true
@timer.stop
emit(:paused)
end
end
# Clear current line
#
# @api public
def clear_line
output.print("#{ECMA_CSI}0m#{TTY::Cursor.clear_line}")
end
# Check if progress is finished
#
# @return [Boolean]
# true when progress finished, false otherwise
#
# @api public
def complete?
@done
end
# Check if progress is stopped
#
# @return [Boolean]
#
# @api public
def stopped?
@stopped
end
# Check if progress is paused
#
# @return [Boolean]
#
# @api public
def paused?
@paused
end
# Check if progress is finished, stopped or paused
#
# @return [Boolean]
#
# @api public
def done?
@done || @stopped || @paused
end
# Register callback with this bar
#
# @param [Symbol] name
# the name for the event to listen for, e.i. :complete
#
# @return [self]
#
# @api public
def on(name, &callback)
synchronize do
@callbacks[name] << callback
end
self
end
# Log message above the current progress bar
#
# @param [String] message
# the message to log out
#
# @api public
def log(message)
sanitized_message = message.gsub(/\r|\n/, " ")
if done?
write("#{sanitized_message}#{NEWLINE}", false)
return
end
sanitized_message = padout(sanitized_message)
write("#{sanitized_message}#{NEWLINE}", true)
render
end
# Show bar format
#
# @return [String]
#
# @api public
def to_s
@format.to_s
end
# Inspect bar properties
#
# @return [String]
#
# @api public
def inspect
"#<#{self.class.name} " \
"@format=\"#{@format}\", " \
"@current=\"#{@current}\", " \
"@total=\"#{total}\", " \
"@width=\"#{width}\", " \
"@complete=\"#{complete}\", " \
"@head=\"#{head}\", " \
"@incomplete=\"#{incomplete}\", " \
"@unknown=\"#{unknown}\", " \
"@interval=\"#{interval}\">"
end
private
# Pad message out with spaces
#
# @api private
def padout(message)
message_length = self.class.display_columns(message)
if @last_render_width > message_length
remaining_width = @last_render_width - message_length
message += " " * remaining_width
end
message
end
# Emit callback by name
#
# @param [Symbol]
# the event name
#
# @api private
def emit(name, *args)
@callbacks[name].each do |callback|
callback.(*args)
end
end
# Check if IO is attached to a terminal
#
# return [Boolean]
#
# @api public
def tty?
output.respond_to?(:tty?) && output.tty?
end
end # ProgressBar
end # TTY
================================================
FILE: lib/tty-progressbar.rb
================================================
require_relative "tty/progressbar"
require_relative "tty/progressbar/multi"
================================================
FILE: spec/perf/render_spec.rb
================================================
# frozen_string_literal: true
require "erb"
require "rspec-benchmark"
RSpec.describe TTY::ProgressBar, "rendering" do
include RSpec::Benchmark::Matchers
let(:done) { "本" }
let(:head) { "語" }
let(:rem) { "〜" }
it "performs bar rendering slower than ERB template substitution" do
output_progress = StringIO.new
output_write = StringIO.new
# Progress bar
progress = TTY::ProgressBar.new(
"[:bar]",
output: output_progress,
incomplete: rem, head: head, complete: done,
total: 5, width: 10
)
# ERB renderer
template = "<%= done %> - <%= head %> - <%= rem %>"
renderer = ERB.new(template)
expect {
progress.advance
progress.reset
}.to perform_slower_than {
output_write << renderer.result(binding)
}.at_most(10).times
end
it "performs bar rendering 2.4k i/s" do
output = StringIO.new
progress = described_class.new("[:bar]", output: output, total: 10,
width: 10)
expect {
progress.advance
progress.reset
}.to perform_at_least(2400).ips
end
end
================================================
FILE: spec/spec_helper.rb
================================================
# frozen_string_literal: true
if ENV["COVERAGE"] == "true"
require "simplecov"
require "coveralls"
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new([
SimpleCov::Formatter::HTMLFormatter,
Coveralls::SimpleCov::Formatter
])
SimpleCov.start do
command_name "spec"
add_filter "spec"
end
end
require "timecop"
require "tty-progressbar"
require "stringio"
class StringIO
undef_method :tty?
def tty?
true
end
end
Dir[::File.join(__dir__, "support/**/*.rb")].sort.each(&method(:require))
RSpec.configure do |config|
config.expect_with :rspec do |expectations|
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
expectations.max_formatted_output_length = nil
end
config.mock_with :rspec do |mocks|
mocks.verify_partial_doubles = true
end
# Limits the available syntax to the non-monkey patched syntax
# that is recommended.
config.disable_monkey_patching!
# This setting enables warnings. It's recommended, but in some cases may
# be too noisy due to issues in dependencies.
config.warnings = true
config.default_formatter = "doc" if config.files_to_run.one?
config.profile_examples = 2
config.order = :random
Kernel.srand config.seed
end
================================================
FILE: spec/support/output_io.rb
================================================
# frozen_string_literal: true
class OutputIO
def initialize(content = "")
@content = content
end
def print(string)
@content += string
end
def read
@content
end
def flush; end
def rewind; end
def tty?
true
end
end
================================================
FILE: spec/unit/advance_spec.rb
================================================
# frozen_string_literal: true
RSpec.describe TTY::ProgressBar, "#advance" do
let(:output) { StringIO.new }
it "advances by custom value" do
progress = TTY::ProgressBar.new("[:bar]", output: output, total: 10)
progress.advance(8)
expect(progress.current).to eq(8)
end
it "allows to go back" do
progress = TTY::ProgressBar.new("[:bar]", output: output, total: 10)
5.times { progress.advance(1) }
expect(progress.current).to eq(5)
5.times { progress.advance(-1) }
expect(progress.current).to eq(0)
end
it "cannot backtrack on finished" do
progress = TTY::ProgressBar.new("[:bar]", output: output, total: 10)
10.times { progress.advance(1) }
expect(progress.current).to eq(10)
5.times { progress.advance(-1) }
expect(progress.current).to eq(10)
end
end
================================================
FILE: spec/unit/bar_format_spec.rb
================================================
# frozen_string_literal: false
RSpec.describe TTY::ProgressBar, ":bar_format" do
let(:output) { StringIO.new("", "w+") }
let(:formats) { TTY::ProgressBar::Formats::FORMATS }
TTY::ProgressBar::Formats::FORMATS.each_key do |format|
it "displays progress with #{format.inspect} format characters" do
complete = formats[format][:complete]
incomplete = formats[format][:incomplete]
progress = TTY::ProgressBar.new("[:bar]", output: output, total: 10,
bar_format: format)
5.times { progress.advance(2) }
output.rewind
expect(output.read).to eq([
"\e[1G[#{complete * 2}#{incomplete * 8}]",
"\e[1G[#{complete * 4}#{incomplete * 6}]",
"\e[1G[#{complete * 6}#{incomplete * 4}]",
"\e[1G[#{complete * 8}#{incomplete * 2}]",
"\e[1G[#{complete * 10}#{incomplete * 0}]\n"
].join)
end
it "displays unknown progress for #{format.inspect} bar format" do
unknown = formats[format][:unknown]
left_chars = 10 - unknown.size
progress = TTY::ProgressBar.new("[:bar]", output: output, total: nil,
bar_format: format, width: 10)
2.times { progress.advance(2) }
output.rewind
expect(output.read).to eq([
"\e[1G[#{unknown}#{' ' * left_chars}]",
"\e[1G[#{unknown}#{' ' * left_chars}]"
].join)
end
end
end
================================================
FILE: spec/unit/clear_spec.rb
================================================
# frozen_string_literal: true
RSpec.describe TTY::ProgressBar, "clear" do
let(:output) { StringIO.new }
it "clears progress bar when finished" do
progress = TTY::ProgressBar.new("[:bar]", output: output, total: 5,
clear: true)
5.times { progress.advance }
output.rewind
expect(output.read).to eq([
"\e[1G[= ]",
"\e[1G[== ]",
"\e[1G[=== ]",
"\e[1G[==== ]",
"\e[1G[=====]\e[0m\e[2K\e[1G"
].join)
end
end
================================================
FILE: spec/unit/complete_spec.rb
================================================
# frozen_string_literal: true
RSpec.describe TTY::ProgressBar, "#complete?" do
let(:output) { StringIO.new }
it "checks for completness" do
progress = TTY::ProgressBar.new("[:bar]", output: output, total: 3)
completes = []
3.times do
completes << progress.complete?
progress.advance
end
completes << progress.complete?
expect(completes).to eq([false, false, false, true])
end
end
================================================
FILE: spec/unit/configure_spec.rb
================================================
# frozen_string_literal: true
RSpec.describe TTY::ProgressBar, "#configure" do
it "yields configuration instance" do
progress = described_class.new(":bar")
config = spy(:config)
allow(progress).to receive(:configure).and_yield(config)
expect { |block|
progress.configure(&block)
}.to yield_with_args(config)
end
it "overrides initial option configuration" do
progress = described_class.new(":bar", total: 10)
expect(progress.total).to eq(10)
progress.configure do |config|
config.total = 20
end
expect(progress.total).to eq(20)
end
it "sets total option to nil value via accessor" do
progress = described_class.new(":bar", total: 10)
progress.configure do |config|
config.total = nil
end
expect(progress.total).to eq(nil)
end
%i[complete incomplete unknown].each do |option|
it "fails to set #{option.inspect} to an empty string via initialize" do
expect {
described_class.new(":bar", total: 10, option => "")
}.to raise_error(
ArgumentError,
"cannot provide an empty string for #{option.inspect}"
)
end
it "fails to set #{option.inspect} to empty string via accessor" do
progress = described_class.new(":bar", total: 10)
expect {
progress.configure do |config|
config.public_send("#{option}=", "")
end
}.to raise_error(
ArgumentError,
"cannot provide an empty string for #{option.inspect}"
)
end
end
end
================================================
FILE: spec/unit/converter/to_bytes_spec.rb
================================================
# frozen_string_literal: true
RSpec.describe TTY::ProgressBar::Converter, "#to_bytes" do
subject(:converter) { described_class }
it "converts 1000 to bytes" do
expect(converter.to_bytes(1000)).to eq("1000B")
end
it "converts 1024 to bytes" do
expect(converter.to_bytes(1024)).to eq("1.00KB")
end
it "converts 1234 to bytes" do
expect(converter.to_bytes(1234)).to eq("1.21KB")
end
it "converts 12345 to bytes" do
expect(converter.to_bytes(12_345)).to eq("12.06KB")
end
it "converts 2000 to bytes" do
expect(converter.to_bytes(2000)).to eq("1.95KB")
end
it "converts 1234567 to bytes" do
expect(converter.to_bytes(1_234_567)).to eq("1.18MB")
end
it "converts 1234567 to bytes with :separator" do
expect(converter.to_bytes(1_234_567, separator: ",")).to eq("1,18MB")
end
it "converts 1234567 to bytes with :unit_separator" do
expect(converter.to_bytes(1_234_567, unit_separator: " ")).to eq("1.18 MB")
end
it "converts 1234567 to bytes with comma as a separator" do
expect(converter.to_bytes(1_234_567, decimals: 1)).to eq("1.2MB")
end
it "converts 10_000_000 to bytes" do
expect(converter.to_bytes(10_000_000)).to eq("9.54MB")
end
it "convert 10_000_000_000 to bytes" do
expect(converter.to_bytes(10_000_000_000)).to eq("9.31GB")
end
end
================================================
FILE: spec/unit/converter/to_seconds_spec.rb
================================================
# frozen_string_literal: true
RSpec.describe TTY::ProgressBar::Converter, "#to_seconds" do
subject(:converter) { described_class }
it "ensures 5-digit precision for < 1",
unless: RSpec::Support::Ruby.truffleruby? do
expect(converter.to_seconds(0.000005)).to eq("0.00001")
end
it "ensures 5-digit precision for < 1 on TruffleRuby",
if: RSpec::Support::Ruby.truffleruby? do
expect(converter.to_seconds(0.000005)).to eq("0.00000")
end
it "rounds 0 to 0.00" do
expect(converter.to_seconds(0)).to eq(" 0.00")
end
it "ensures 2-digit precision for > 1" do
expect(converter.to_seconds(11.2)).to eq("11.20")
end
it "specifies precision to be 3 digits" do
expect(converter.to_seconds(11.12345, precision: 3)).to eq("11.123")
end
end
================================================
FILE: spec/unit/converter/to_time_spec.rb
================================================
# frozen_string_literal: true
RSpec.describe TTY::ProgressBar::Converter, "#to_time" do
subject(:converter) { described_class }
it "converts zero seconds" do
expect(converter.to_time(0.0)).to eq(" 0s")
end
it "converts less than a second to zero" do
expect(converter.to_time(0.83)).to eq(" 0s")
end
it "converts seconds to a second" do
expect(converter.to_time(1.2)).to eq(" 1s")
end
it "converts seconds to seconds" do
expect(converter.to_time(15)).to eq("15s")
end
it "converts seconds to a minute" do
expect(converter.to_time(100)).to eq(" 1m40s")
end
it "converts seconds to minutes" do
expect(converter.to_time(2000)).to eq("33m20s")
end
it "converts seconds to an hour" do
expect(converter.to_time(3600)).to eq(" 1h 0m")
end
it "converts seconds to hours" do
expect(converter.to_time(23.5 * 3600)).to eq("23h30m")
end
it "converts seconds to days and hours" do
expect(converter.to_time(100 * 3600)).to eq("4d 4h 0m")
end
end
================================================
FILE: spec/unit/custom_formatter_spec.rb
================================================
# frozen_string_literal: true
RSpec.describe TTY::ProgressBar, "custom formatter" do
let(:output) { StringIO.new }
it "allows for custom tag" do
progress = TTY::ProgressBar.new(":hi", output: output, total: 10)
stub_const("HiFormatter", Class.new do
def initialize(progress)
@progress = progress
end
def matches?(value)
value.to_s =~ /:hi/
end
def call(value)
value.gsub(/:hi/, "Hello")
end
end)
progress.use(HiFormatter)
progress.advance
output.rewind
expect(output.read).to eq("\e[1GHello")
end
it "enforces class formatter" do
progress = TTY::ProgressBar.new(":hi", output: output, total: 10)
stub_const("HiFormatter", Class.new)
formatter = HiFormatter.new
expect {
progress.use(formatter)
}.to raise_error(ArgumentError, "Formatter needs to be a class")
end
end
================================================
FILE: spec/unit/custom_token_spec.rb
================================================
# frozen_string_literal: true
RSpec.describe TTY::ProgressBar, "custom token" do
let(:output) { StringIO.new }
it "allows to specify custom tokens" do
progress = TTY::ProgressBar.new("(:current) :title", output: output, total: 4)
progress.advance(title: "Hello Piotr!")
progress.advance(3, title: "Bye Piotr!")
output.rewind
expect(output.read).to eq([
"\e[1G(1) Hello Piotr!",
"\e[1G(4) Bye Piotr! \n"
].join)
end
end
================================================
FILE: spec/unit/events_spec.rb
================================================
# frozen_string_literal: true
RSpec.describe TTY::ProgressBar, "events" do
let(:output) { StringIO.new }
it "emits :progress event when advancing" do
events = []
bar = TTY::ProgressBar.new("[:bar]", output: output, total: 5)
bar.on(:progress) { events << :progress }
bar.advance
expect(events).to eq([:progress])
end
it "emits :done event when finished" do
events = []
bar = TTY::ProgressBar.new("[:bar]", output: output, total: 5)
bar.on(:done) { events << :done }
bar.finish
expect(events).to eq([:done])
end
it "emits :stopped event" do
events = []
bar = TTY::ProgressBar.new("[:bar]", output: output, total: 5)
bar.on(:stopped) { events << :stopped }
bar.stop
expect(events).to eq([:stopped])
end
it "emits :paused event" do
events = []
bar = TTY::ProgressBar.new("[:bar]", output: output, total: 5)
bar.on(:paused) { events << :paused }
bar.pause
expect(events).to eq([:paused])
end
end
================================================
FILE: spec/unit/finish_spec.rb
================================================
# frozen_string_literal: true
RSpec.describe TTY::ProgressBar, "#finish" do
let(:output) { StringIO.new }
it "finishes progress" do
progress = TTY::ProgressBar.new("[:bar]", output: output, total: 10)
progress.advance
progress.finish
expect(progress.complete?).to be(true)
output.rewind
expect(output.read).to eq([
"\e[1G[= ]",
"\e[1G[==========]\n"
].join)
end
end
================================================
FILE: spec/unit/formatter/bar_spec.rb
================================================
# frozen_string_literal: true
RSpec.describe TTY::ProgressBar, ":bar token" do
let(:output) { StringIO.new }
it "animates bar" do
progress = TTY::ProgressBar.new("[:bar]", output: output, total: 5)
5.times { progress.advance }
output.rewind
expect(output.read).to eq([
"\e[1G[= ]",
"\e[1G[== ]",
"\e[1G[=== ]",
"\e[1G[==== ]",
"\e[1G[=====]\n"
].join)
end
it "animates unknown progress without total" do
progress = TTY::ProgressBar.new("[:bar]", output: output, total: nil,
width: 5)
2.times { progress.advance }
progress.update(total: 5)
3.times { progress.advance }
output.rewind
expect(output.read).to eq([
"\e[1G[<=> ]",
"\e[1G[<=> ]",
"\e[1G[=== ]",
"\e[1G[==== ]",
"\e[1G[=====]\n"
].join)
end
it "animates colors correctly" do
red = "\e[31m \e[0m"
green = "\e[32m \e[0m"
progress = TTY::ProgressBar.new("[:bar]", total: 5, complete: green,
incomplete: red, output: output)
5.times { progress.advance }
output.rewind
expect(output.read).to eq([
"\e[1G[#{green}#{red}#{red}#{red}#{red}]",
"\e[1G[#{green}#{green}#{red}#{red}#{red}]",
"\e[1G[#{green}#{green}#{green}#{red}#{red}]",
"\e[1G[#{green}#{green}#{green}#{green}#{red}]",
"\e[1G[#{green}#{green}#{green}#{green}#{green}]\n"
].join)
end
describe "when unicode chars & odd width" do
let(:done) { "本" }
let(:head) { "語" }
let(:rem) { "〜" }
it "head, complete & incomplete are unicode chars" do
progress = TTY::ProgressBar.new("[:bar]", output: output, incomplete: rem,
head: head, complete: done,
total: 5, width: 9)
5.times { progress.advance }
output.rewind
expect(output.read).to eq([
"\e[1G[語〜〜〜]",
"\e[1G[本語〜〜]",
"\e[1G[本語〜〜]",
"\e[1G[本本語〜]",
"\e[1G[本本本語]\n"
].join)
end
it "head unicode & ascii vs complete & incomplete unicode chars" do
progress = TTY::ProgressBar.new("[:bar]", output: output, incomplete: rem,
head: "#{head}>", complete: done,
total: 5, width: 9)
5.times { progress.advance }
output.rewind
expect(output.read).to eq([
"\e[1G[語> 〜〜]",
"\e[1G[語> 〜〜]",
"\e[1G[語> 〜〜]",
"\e[1G[本語> 〜]",
"\e[1G[本本語> ]\n"
].join)
end
it "head & complete unicode chars vs incomplete ascii char" do
progress = TTY::ProgressBar.new("[:bar]", output: output, incomplete: "-",
head: head, complete: done,
total: 5, width: 9)
5.times { progress.advance }
output.rewind
expect(output.read).to eq([
"\e[1G[語------]",
"\e[1G[本語----]",
"\e[1G[本語----]",
"\e[1G[本本語--]",
"\e[1G[本本本語]\n"
].join)
end
it "head & complete ascii chars vs incomplete unicode char" do
progress = TTY::ProgressBar.new("[:bar]", output: output, incomplete: rem,
head: ">", complete: "#",
total: 5, width: 9)
5.times { progress.advance }
output.rewind
expect(output.read).to eq([
"\e[1G[#>〜〜〜]",
"\e[1G[###>〜〜]",
"\e[1G[###>〜〜]",
"\e[1G[#####>〜]",
"\e[1G[#######>]\n"
].join)
end
it "complete ascii chars vs head & incomplete unicode char" do
progress = TTY::ProgressBar.new("[:bar]", output: output, incomplete: rem,
head: head, complete: "#",
total: 5, width: 9)
5.times { progress.advance }
output.rewind
expect(output.read).to eq([
"\e[1G[語〜〜〜]",
"\e[1G[##語〜〜]",
"\e[1G[##語〜〜]",
"\e[1G[####語〜]",
"\e[1G[######語]\n"
].join)
end
it "head & incomplete ascii chars vs complete unicode char" do
progress = TTY::ProgressBar.new("[:bar]", output: output, incomplete: "-",
head: ">", complete: done,
total: 5, width: 9)
5.times { progress.advance }
output.rewind
expect(output.read).to eq([
"\e[1G[> ------]",
"\e[1G[本> ----]",
"\e[1G[本> ----]",
"\e[1G[本本> --]",
"\e[1G[本本本> ]\n"
].join)
end
it "head ascii char vs complete & incomplete unicode chars" do
progress = TTY::ProgressBar.new("[:bar]", output: output, incomplete: rem,
head: ">", complete: done,
total: 5, width: 9)
5.times { progress.advance }
output.rewind
expect(output.read).to eq([
"\e[1G[> 〜〜〜]",
"\e[1G[本> 〜〜]",
"\e[1G[本> 〜〜]",
"\e[1G[本本> 〜]",
"\e[1G[本本本> ]\n"
].join)
end
it "unknown with ascii and unicode chars" do
progress = TTY::ProgressBar.new("[:bar]", output: output, incomplete: rem,
complete: done, head: ">",
unknown: "<#{done}>",
total: nil, width: 9)
5.times { progress.advance }
progress.update(total: 10)
5.times { progress.advance }
output.rewind
expect(output.read).to eq([
"\e[1G[<本> ]",
"\e[1G[<本> ]",
"\e[1G[<本> ]",
"\e[1G[<本> ]",
"\e[1G[<本> ]",
"\e[1G[本> 〜〜]",
"\e[1G[本本> 〜]",
"\e[1G[本本> 〜]",
"\e[1G[本本本> ]",
"\e[1G[本本本> ]\n"
].join)
end
end
end
================================================
FILE: spec/unit/formatter/byte_rate_spec.rb
================================================
# frozen_string_literal: true
RSpec.describe TTY::ProgressBar, ":byte_rate token" do
let(:output) { StringIO.new }
before { Timecop.safe_mode = false }
it "shows current rate in bytes per sec" do
time_now = Time.local(2014, 10, 5, 12, 0, 0)
Timecop.freeze(time_now)
progress = TTY::ProgressBar.new(":byte_rate", output: output, total: 10_000,
interval: 1)
# Generate a serie of advances at 2s intervals
# t+0 advance=0 total=0
# t+2 advance=1000 total=1000
# t+4 advance=2000 total=3000
# t+6 advance=3000 total=6000
# t+8 advance=4000 total=10_000
# NOTE: mean_byte uses 1024 for the scale in K, M ...
5.times do |i|
time_now = Time.local(2014, 10, 5, 12, 0, i * 2)
Timecop.freeze(time_now)
progress.advance(i * 1000)
end
output.rewind
expect(output.read).to eq([
"\e[1G0B",
"\e[1G500B",
"\e[1G1000B",
"\e[1G1.46KB",
"\e[1G1.95KB\n"
].join)
Timecop.return
end
it "displays rate in bytes per sec when no total" do
time_now = Time.local(2014, 10, 5, 12, 0, 0)
Timecop.freeze(time_now)
progress = TTY::ProgressBar.new(":byte_rate", output: output, total: nil)
5.times do |i|
time_now = Time.local(2014, 10, 5, 12, 0, i * 2)
Timecop.freeze(time_now)
progress.advance(i * 1000)
end
output.rewind
expect(output.read).to eq([
"\e[1G0B",
"\e[1G500B",
"\e[1G1000B",
"\e[1G1.46KB",
"\e[1G1.95KB"
].join)
Timecop.return
end
end
================================================
FILE: spec/unit/formatter/current_byte_spec.rb
================================================
# frozen_string_literal: true
RSpec.describe TTY::ProgressBar, ":current_byte token" do
let(:output) { StringIO.new }
it "displays bytes processed" do
progress = described_class.new(":current_byte", output: output,
total: 102_400)
5.times { progress.advance(20_480) }
output.rewind
expect(output.read).to eq([
"\e[1G20.00KB",
"\e[1G40.00KB",
"\e[1G60.00KB",
"\e[1G80.00KB",
"\e[1G100.00KB\n"
].join)
end
end
================================================
FILE: spec/unit/formatter/current_spec.rb
================================================
# frozen_string_literal: true
RSpec.describe TTY::ProgressBar, ":current token" do
let(:output) { StringIO.new }
it "displays current value" do
progress = TTY::ProgressBar.new("|:current|", output: output, total: 10)
3.times { progress.advance }
output.rewind
expect(output.read).to eq([
"\e[1G|1|",
"\e[1G|2|",
"\e[1G|3|"
].join)
end
end
================================================
FILE: spec/unit/formatter/elapsed_spec.rb
================================================
# frozen_string_literal: true
RSpec.describe TTY::ProgressBar, ":elapsed token" do
let(:output) { StringIO.new }
before { Timecop.safe_mode = false }
after { Timecop.return }
it "displays elapsed time" do
Timecop.freeze(Time.local(2014, 10, 5, 12, 0, 0))
progress = TTY::ProgressBar.new(":elapsed", output: output, total: 10)
5.times do |sec|
Timecop.freeze(Time.local(2014, 10, 5, 12, 0, sec))
progress.advance
end
output.rewind
expect(output.read).to eq([
"\e[1G 0s",
"\e[1G 1s",
"\e[1G 2s",
"\e[1G 3s",
"\e[1G 4s"
].join)
end
it "resets elapsed time" do
Timecop.freeze(Time.local(2014, 10, 5, 12, 0, 0))
progress = TTY::ProgressBar.new(":elapsed", output: output, total: 5)
5.times do |sec|
Timecop.freeze(Time.local(2014, 10, 5, 12, 0, sec))
progress.advance
end
expect(progress.complete?).to be(true)
progress.reset
2.times do |sec|
Timecop.freeze(Time.local(2014, 10, 5, 13, 0, sec))
progress.advance
end
output.rewind
expect(output.read).to eq([
"\e[1G 0s",
"\e[1G 1s",
"\e[1G 2s",
"\e[1G 3s",
"\e[1G 4s\n",
"\e[1G 0s",
"\e[1G 1s"
].join)
end
it "resumes elapsed time measurement when stopped or finished" do
Timecop.freeze(Time.local(2021, 1, 10, 12, 0, 0))
progress = TTY::ProgressBar.new(":elapsed", output: output, total: 10)
5.times do |sec|
Timecop.freeze(Time.local(2021, 1, 10, 12, 0, 1 + sec))
progress.advance
end
progress.stop
progress.resume
3.times do |sec| # resume progression after 5 seconds
Timecop.freeze(Time.local(2021, 1, 10, 12, 0, 10 + sec))
progress.advance
end
progress.finish
progress.update(total: 12)
progress.resume
2.times do |sec| # resume progression after 2 seconds
Timecop.freeze(Time.local(2021, 1, 10, 12, 0, 15 + sec))
progress.advance
end
output.rewind
expect(output.read).to eq([
"\e[1G 0s",
"\e[1G 1s",
"\e[1G 2s",
"\e[1G 3s",
"\e[1G 4s",
"\e[1G 4s\n", # stopped
"\e[1G 4s",
"\e[1G 5s",
"\e[1G 6s",
"\e[1G 6s\n", # finished
"\e[1G 6s",
"\e[1G 7s\n"
].join)
end
end
================================================
FILE: spec/unit/formatter/estimated_spec.rb
================================================
# frozen_string_literal: true
RSpec.describe TTY::ProgressBar, ":eta token" do
let(:output) { StringIO.new }
before { Timecop.safe_mode = false }
after { Timecop.return }
it "displays elapsed time" do
Timecop.freeze(Time.local(2014, 10, 5, 12, 0, 0))
progress = TTY::ProgressBar.new(":eta", output: output, total: 5)
5.times do |sec|
Timecop.freeze(Time.local(2014, 10, 5, 12, 0, sec))
progress.advance
end
output.rewind
expect(output.read).to eq([
"\e[1G 0s",
"\e[1G 1s",
"\e[1G 1s",
"\e[1G 0s",
"\e[1G 0s\n"
].join)
end
it "displays unknown elapsed time when no total" do
Timecop.freeze(Time.local(2014, 10, 5, 12, 0, 0))
progress = TTY::ProgressBar.new(":eta", output: output, total: nil)
3.times { progress.advance }
progress.update(total: 5)
2.times do |sec|
Timecop.freeze(Time.local(2014, 10, 5, 12, 0, sec))
progress.advance
end
output.rewind
expect(output.read).to eq([
"\e[1G--s",
"\e[1G--s",
"\e[1G--s",
"\e[1G 0s",
"\e[1G 0s\n"
].join)
end
end
================================================
FILE: spec/unit/formatter/estimated_time_spec.rb
================================================
# frozen_string_literal: true
RSpec.describe TTY::ProgressBar, ":eta_time token" do
let(:output) { StringIO.new }
before { Timecop.safe_mode = false }
after { Timecop.return }
it "displays estimated completion time of day" do
Timecop.freeze(Time.local(2020, 1, 5, 12, 0, 0))
bar = described_class.new(" :eta_time", output: output, total: 5)
5.times do |sec|
Timecop.freeze(Time.local(2020, 1, 5, 12, 0, sec))
bar.advance
end
output.rewind
expect(output.read).to eq([
"\e[1G 12:00:00",
"\e[1G 12:00:02",
"\e[1G 12:00:03",
"\e[1G 12:00:03",
"\e[1G 12:00:04\n"
].join)
end
it "displays estimated completion time of day with date after 24 hours" do
Timecop.freeze(Time.local(2020, 1, 5, 12, 0, 0))
bar = described_class.new(" :eta_time", output: output, total: 5)
2.times do |day|
time_now = Time.local(2020, 1, day + 6, 12, 0, 0)
Timecop.freeze(time_now)
bar.advance
end
output.rewind
expect(output.read).to eq([
"\e[1G 12:00:00",
"\e[1G 2020-01-09 00:00:00"
].join)
end
it "displays unknown estimated completion time of day as --:--:--" do
Timecop.freeze(Time.local(2020, 1, 5, 12, 0, 0))
bar = described_class.new(" :eta_time", output: output, total: nil)
3.times { bar.advance }
bar.update(total: 5)
2.times do |sec|
Timecop.freeze(Time.local(2020, 1, 5, 12, 0, sec))
bar.advance
end
output.rewind
expect(output.read).to eq([
"\e[1G --:--:--",
"\e[1G --:--:--",
"\e[1G --:--:--",
"\e[1G 12:00:00",
"\e[1G 12:00:01\n"
].join)
end
end
================================================
FILE: spec/unit/formatter/mean_byte_spec.rb
================================================
# frozen_string_literal: true
RSpec.describe TTY::ProgressBar, ":mean_byte token" do
let(:output) { StringIO.new }
before { Timecop.safe_mode = false }
after { Timecop.return }
it "shows mean rate in bytes per sec" do
time_now = Time.local(2014, 10, 5, 12, 0, 0)
Timecop.freeze(time_now)
progress = TTY::ProgressBar.new(":mean_byte", output: output, total: 10_000,
interval: 1)
# Generate a serie of advances at 2s intervals
# t+0 advance=0 total=0
# t+2 advance=1000 total=1000
# t+4 advance=2000 total=3000
# t+6 advance=3000 total=6000
# t+8 advance=4000 total=10_000
# NOTE: mean_byte uses 1024 for the scale in K, M ...
5.times do |i|
time_now = Time.local(2014, 10, 5, 12, 0, i * 2)
Timecop.freeze(time_now)
progress.advance(i * 1000)
end
output.rewind
expect(output.read).to eq([
"\e[1G0B",
"\e[1G500B",
"\e[1G750B",
"\e[1G1000B",
"\e[1G1.22KB\n"
].join)
end
it "displays mean rate in bytes per sec when no total" do
time_now = Time.local(2014, 10, 5, 12, 0, 0)
Timecop.freeze(time_now)
progress = TTY::ProgressBar.new(":mean_byte", output: output, total: nil,
interval: 1)
5.times do |i|
time_now = Time.local(2014, 10, 5, 12, 0, i * 2)
Timecop.freeze(time_now)
progress.advance(i * 1000)
end
output.rewind
expect(output.read).to eq([
"\e[1G0B",
"\e[1G500B",
"\e[1G750B",
"\e[1G1000B",
"\e[1G1.22KB"
].join)
end
end
================================================
FILE: spec/unit/formatter/mean_rate_spec.rb
================================================
# frozen_string_literal: true
RSpec.describe TTY::ProgressBar, ":mean_rate token" do
let(:output) { StringIO.new }
before { Timecop.safe_mode = false }
after { Timecop.return }
it "shows current rate per sec" do
time_now = Time.local(2014, 10, 5, 12, 0, 0)
Timecop.freeze(time_now)
progress = TTY::ProgressBar.new(":mean_rate", output: output, total: 100,
interval: 1)
# Generate a serie of advances at 2s intervals
# t+0 advance=0 total=0
# t+2 advance=10 total=10
# t+4 advance=20 total=30
# t+6 advance=30 total=60
# t+8 advance=40 total=100
5.times do |i|
time_now = Time.local(2014, 10, 5, 12, 0, i * 2)
Timecop.freeze(time_now)
progress.advance(i * 10)
end
output.rewind
expect(output.read).to eq([
"\e[1G 0.00",
"\e[1G 5.00",
"\e[1G 7.50",
"\e[1G10.00",
"\e[1G12.50\n"
].join)
end
it "displays mean rate per sec when no total" do
time_now = Time.local(2014, 10, 5, 12, 0, 0)
Timecop.freeze(time_now)
progress = TTY::ProgressBar.new(":mean_rate", output: output, total: nil)
5.times do |i|
time_now = Time.local(2014, 10, 5, 12, 0, i * 2)
Timecop.freeze(time_now)
progress.advance(i * 10)
end
output.rewind
expect(output.read).to eq([
"\e[1G 0.00",
"\e[1G 5.00",
"\e[1G 7.50",
"\e[1G10.00",
"\e[1G12.50"
].join)
end
end
================================================
FILE: spec/unit/formatter/percent_spec.rb
================================================
# frozen_string_literal: true
RSpec.describe TTY::ProgressBar, ":percent token" do
let(:output) { StringIO.new }
it "displays percent finished" do
progress = TTY::ProgressBar.new(":percent", output: output, total: 5)
5.times { progress.advance }
output.rewind
expect(output.read).to eq([
"\e[1G20%",
"\e[1G40%",
"\e[1G60%",
"\e[1G80%",
"\e[1G100%\n"
].join)
end
it "displays unknown percent when no total" do
progress = TTY::ProgressBar.new(":percent", output: output, total: nil)
3.times { progress.advance }
progress.update(total: 5)
2.times { progress.advance }
output.rewind
expect(output.read).to eq([
"\e[1G-%",
"\e[1G-%",
"\e[1G-%",
"\e[1G80%",
"\e[1G100%\n"
].join)
end
end
================================================
FILE: spec/unit/formatter/rate_spec.rb
================================================
# frozen_string_literal: true
RSpec.describe TTY::ProgressBar, ":rate token" do
let(:output) { StringIO.new }
before { Timecop.safe_mode = false }
after { Timecop.return }
it "shows current rate per sec" do
time_now = Time.local(2014, 10, 5, 12, 0, 0)
Timecop.freeze(time_now)
progress = TTY::ProgressBar.new(":rate", output: output, total: 100,
interval: 1)
# Generate a serie of advances at 2s intervals
# t+0 advance=0 total=0
# t+2 advance=10 total=10
# t+4 advance=20 total=30
# t+6 advance=30 total=60
# t+8 advance=40 total=100
5.times do |i|
time_now = Time.local(2014, 10, 5, 12, 0, i * 2)
Timecop.freeze(time_now)
progress.advance(i * 10)
end
output.rewind
expect(output.read).to eq([
"\e[1G 0.00",
"\e[1G 5.00",
"\e[1G10.00",
"\e[1G15.00",
"\e[1G20.00\n"
].join)
end
it "displays rate per sec when no total" do
time_now = Time.local(2014, 10, 5, 12, 0, 0)
Timecop.freeze(time_now)
progress = TTY::ProgressBar.new(":rate", output: output, total: nil)
5.times do |i|
time_now = Time.local(2014, 10, 5, 12, 0, i * 2)
Timecop.freeze(time_now)
progress.advance(i * 10)
end
output.rewind
expect(output.read).to eq([
"\e[1G 0.00",
"\e[1G 5.00",
"\e[1G10.00",
"\e[1G15.00",
"\e[1G20.00"
].join)
end
end
================================================
FILE: spec/unit/formatter/total_byte_spec.rb
================================================
# frozen_string_literal: true
RSpec.describe TTY::ProgressBar, ":total_byte token" do
let(:output) { StringIO.new }
it "displays bytes total" do
progress = described_class.new(":total_byte", output: output, total: 102_400)
5.times { progress.advance(20_480) }
output.rewind
expect(output.read).to eq([
"\e[1G100.00KB",
"\e[1G100.00KB",
"\e[1G100.00KB",
"\e[1G100.00KB",
"\e[1G100.00KB\n"
].join)
end
it "displays unknown bytes progress without total" do
progress = described_class.new(":total_byte", output: output, total: nil)
3.times { progress.advance(20_480) }
progress.update(total: 102_400)
2.times { progress.advance(20_480) }
output.rewind
expect(output.read).to eq([
"\e[1G-B",
"\e[1G-B",
"\e[1G-B",
"\e[1G100.00KB",
"\e[1G100.00KB\n"
].join)
end
end
================================================
FILE: spec/unit/formatter/total_spec.rb
================================================
# frozen_string_literal: true
RSpec.describe TTY::ProgressBar, ":total token" do
let(:output) { StringIO.new }
it "displays total amount" do
progress = described_class.new(":total", output: output, total: 102_400)
5.times { progress.advance(20_480) }
output.rewind
expect(output.read).to eq([
"\e[1G102400",
"\e[1G102400",
"\e[1G102400",
"\e[1G102400",
"\e[1G102400\n"
].join)
end
it "displays unknown progress without total" do
progress = described_class.new(":total", output: output, total: nil)
3.times { progress.advance(20_480) }
progress.update(total: 102_400)
2.times { progress.advance(20_480) }
output.rewind
expect(output.read).to eq([
"\e[1G-",
"\e[1G-",
"\e[1G-",
"\e[1G102400",
"\e[1G102400\n"
].join)
end
end
================================================
FILE: spec/unit/frequency_spec.rb
================================================
# frozen_string_literal: true
RSpec.describe TTY::ProgressBar, "frequency" do
let(:output) { StringIO.new }
before { Timecop.safe_mode = false }
it "limits frequency to 500Hz, permiting every second one" do
time_now = Time.local(2014, 10, 5, 12, 0, 0, 0)
Timecop.freeze(time_now)
progress = TTY::ProgressBar.new("[:bar]", output: output, total: 10,
frequency: 500)
10.times do |sec|
time_now = Time.local(2014, 10, 5, 12, 0, 0, sec * 1000)
Timecop.freeze(time_now)
progress.advance
end
output.rewind
expect(output.read).to eq([
"\e[1G[=== ]",
"\e[1G[===== ]",
"\e[1G[======= ]",
"\e[1G[========= ]",
"\e[1G[==========]\n"
].join)
Timecop.return
end
end
================================================
FILE: spec/unit/head_spec.rb
================================================
# frozen_string_literal: true
RSpec.describe TTY::ProgressBar, ":head" do
let(:output) { StringIO.new }
it "animates head" do
progress = TTY::ProgressBar.new("[:bar]", output: output, head: ">", total: 5)
5.times { progress.advance }
output.rewind
expect(output.read).to eq([
"\e[1G[> ]",
"\e[1G[=> ]",
"\e[1G[==> ]",
"\e[1G[===> ]",
"\e[1G[====>]\n"
].join)
end
it "customises all output characters" do
progress = TTY::ProgressBar.new(
"[:bar]",
output: output,
head: "ᗧ",
complete: "-", incomplete: ".", total: 5
)
5.times { progress.advance }
output.rewind
expect(output.read).to eq([
"\e[1G[ᗧ....]",
"\e[1G[-ᗧ...]",
"\e[1G[--ᗧ..]",
"\e[1G[---ᗧ.]",
"\e[1G[----ᗧ]\n"
].join)
end
it "exceeds 2 characters" do
progress = TTY::ProgressBar.new(
"[:bar]",
output: output,
head: ">>>",
complete: "-", incomplete: ".", total: 20
)
5.times { progress.advance(4) }
output.rewind
expect(output.read).to eq([
"\e[1G[->>>................]",
"\e[1G[----->>>............]",
"\e[1G[--------->>>........]",
"\e[1G[------------->>>....]",
"\e[1G[----------------->>>]\n"
].join)
end
it "clears head after finishing" do
progress = TTY::ProgressBar.new("[:bar]", output: output, total: 5,
head: ">", clear_head: true)
5.times { progress.advance }
output.rewind
expect(output.read).to eq([
"\e[1G[> ]",
"\e[1G[=> ]",
"\e[1G[==> ]",
"\e[1G[===> ]",
"\e[1G[=====]\n"
].join)
end
end
================================================
FILE: spec/unit/hide_cursor_spec.rb
================================================
# frozen_string_literal: true
RSpec.describe TTY::ProgressBar, "#hide_cursor" do
let(:output) { StringIO.new }
it "hides cursor" do
progress = TTY::ProgressBar.new("[:bar]", output: output, total: 5,
hide_cursor: true)
5.times { progress.advance }
output.rewind
expect(output.read).to eq([
"\e[?25l\e[1G[= ]",
"\e[1G[== ]",
"\e[1G[=== ]",
"\e[1G[==== ]",
"\e[1G[=====]\n\e[?25h"
].join)
end
it "hides cursor when indeterminate" do
progress = TTY::ProgressBar.new("[:bar]", output: output, width: 5,
hide_cursor: true)
5.times { progress.advance }
progress.stop
output.rewind
expect(output.read).to eq([
"\e[?25l\e[1G[<=> ]",
"\e[1G[<=> ]",
"\e[1G[<=> ]",
"\e[1G[<=> ]",
"\e[1G[<=> ]",
"\e[1G[<=> ]\n\e[?25h"
].join)
end
it "reenables cursor on finish" do
progress = TTY::ProgressBar.new("[:bar]", output: output, total: 5,
hide_cursor: true)
progress.advance(6)
expect(progress.complete?).to eq(true)
output.rewind
expect(output.read).to eq("\e[1G[=====]\n\e[?25h")
end
end
================================================
FILE: spec/unit/indeterminate_spec.rb
================================================
# frozen_string_literal: true
RSpec.describe TTY::ProgressBar, "indeterminate" do
let(:output) { StringIO.new }
it "animates indeterminate progress and finishes" do
progress = described_class.new("[:bar]", output: output, width: 10)
104.times { progress.advance }
progress.update(total: 110)
6.times { progress.advance }
output.rewind
expect(output.read).to eq([
"\e[1G[<=> ]" * 3,
"\e[1G[ <=> ]" * 7,
"\e[1G[ <=> ]" * 7,
"\e[1G[ <=> ]" * 7,
"\e[1G[ <=> ]" * 8,
"\e[1G[ <=> ]" * 7,
"\e[1G[ <=> ]" * 7,
"\e[1G[ <=>]" * 7,
"\e[1G[ <=> ]" * 7,
"\e[1G[ <=> ]" * 7,
"\e[1G[ <=> ]" * 8,
"\e[1G[ <=> ]" * 7,
"\e[1G[ <=> ]" * 7,
"\e[1G[ <=> ]" * 7,
"\e[1G[<=> ]" * 7,
"\e[1G[ <=> ]",
"\e[1G[==========]" * 5,
"\e[1G[==========]\n"
].join)
end
it "animates indeterminate progress with all metrics and finishes" do
Timecop.freeze(Time.local(2021, 1, 16, 12, 0, 0))
format = "[:bar] :current/:total :percent " \
":current_byte/:total_byte :byte_rate/s :mean_byte/s " \
":rate/s :mean_rate/s :elapsed :eta"
progress = described_class.new(format, output: output, total: nil, width: 10)
3.times do |sec|
Timecop.freeze(Time.local(2021, 1, 16, 12, 0, 1 + sec))
progress.advance
end
progress.update(total: 6)
3.times do |i|
Timecop.freeze(Time.local(2021, 1, 16, 12, 0, 4 + i))
progress.advance
end
output.rewind
expect(output.read).to eq([
"\e[1G[<=> ] 1/- -% 1B/-B 1B/s 1B/s 1.00/s 1.00/s 0s --s",
"\e[1G[<=> ] 2/- -% 2B/-B 1B/s 1B/s 1.00/s 1.00/s 1s --s",
"\e[1G[<=> ] 3/- -% 3B/-B 1B/s 1B/s 1.00/s 1.00/s 2s --s",
"\e[1G[======= ] 4/6 66% 4B/6B 1B/s 1B/s 1.00/s 1.00/s 3s 1s",
"\e[1G[======== ] 5/6 83% 5B/6B 1B/s 1B/s 1.00/s 1.00/s 4s 0s",
"\e[1G[==========] 6/6 100% 6B/6B 1B/s 1B/s 1.00/s 1.00/s 5s 0s\n"
].join)
Timecop.return
end
end
================================================
FILE: spec/unit/inspect_spec.rb
================================================
# frozen_string_literal: true
RSpec.describe TTY::ProgressBar, "#inspect" do
it "inspects bar properties" do
bar = described_class.new("downloading [:bar] :total", total: 30)
expect(bar.inspect).to eq(
'#<TTY::ProgressBar @format="downloading [:bar] :total", ' \
'@current="0", @total="30", @width="30", @complete="=", @head="=", ' \
'@incomplete=" ", @unknown="<=>", @interval="1">'
)
end
it "prints string format" do
bar = described_class.new("downloading [:bar] :total", total: 30)
expect(bar.to_s).to eq("downloading [:bar] :total")
end
end
================================================
FILE: spec/unit/iterate_spec.rb
================================================
# frozen_string_literal: true
RSpec.describe TTY::ProgressBar, "#iterate" do
let(:output) { StringIO.new }
it "iterates over a collection and yields" do
bar = TTY::ProgressBar.new("[:bar]", output: output)
values = []
bar.iterate(5.times) { |val| values << val }
expect(bar.complete?).to eq(true)
output.rewind
expect(output.read).to eq([
"\e[1G[= ]",
"\e[1G[== ]",
"\e[1G[=== ]",
"\e[1G[==== ]",
"\e[1G[=====]\n"
].join)
expect(values).to eq([0, 1, 2, 3, 4])
end
it "iterates over a collection with a step" do
bar = TTY::ProgressBar.new("[:bar]", output: output)
values = []
bar.iterate(4.times, 5) { |val| values << val }
expect(bar.complete?).to eq(true)
output.rewind
expect(output.read).to eq([
"\e[1G[===== ]",
"\e[1G[========== ]",
"\e[1G[=============== ]",
"\e[1G[====================]\n"
].join)
expect(values).to eq([0, 1, 2, 3])
end
it "iterates over a collection and returns enumerable" do
bar = TTY::ProgressBar.new("[:bar]", output: output)
values = []
progress = bar.iterate(5.times)
expect(bar.complete?).to eq(false)
progress.each { |v| values << v }
expect(values).to eq([0, 1, 2, 3, 4])
end
it "does not uses collection's count if total is provided" do
bar = TTY::ProgressBar.new("[:bar]", output: output, total: 5)
iterable = 5.times
expect(iterable).not_to receive(:count)
progress = bar.iterate(iterable)
values = []
progress.each { |v| values << v }
expect(values).to eq([0, 1, 2, 3, 4])
end
it "iterates over an infinite enumerator and renders bar correctly" do
bar = TTY::ProgressBar.new("[:bar]", output: output, total: 5)
infinite_iter = (1..Float::INFINITY).lazy
progress = bar.iterate(infinite_iter)
10.times { progress.next }
expect(bar.complete?).to eq(true)
output.rewind
expect(output.read).to eq([
"\e[1G[= ]",
"\e[1G[== ]",
"\e[1G[=== ]",
"\e[1G[==== ]",
"\e[1G[=====]\n"
].join)
end
end
================================================
FILE: spec/unit/log_spec.rb
================================================
# frozen_string_literal: true
RSpec.describe TTY::ProgressBar, "#log" do
let(:output) { StringIO.new }
it "logs message" do
progress = TTY::ProgressBar.new("[:bar]", output: output, total: 10)
2.times {
progress.log "foo bar"
progress.advance
}
output.rewind
expect(output.read).to eq([
"\e[1Gfoo bar\n",
"\e[1G[ ]",
"\e[1G[= ]",
"\e[1Gfoo bar \n",
"\e[1G[= ]",
"\e[1G[== ]"
].join)
end
it "logs message under when complete" do
progress = TTY::ProgressBar.new("[:bar]", output: output, total: 10)
progress.advance(10)
expect(progress.complete?).to eq(true)
progress.log "foo bar"
output.rewind
expect(output.read).to eq("\e[1G[==========]\nfoo bar\n")
end
it "logs a message first with time and rate metrics" do
Timecop.freeze(Time.local(2021, 1, 23, 12, 0, 0))
format = "[:bar] :elapsed :eta :eta_time :rate/s"
progress = TTY::ProgressBar.new(format, output: output, total: 10)
3.times do |sec|
Timecop.freeze(Time.local(2021, 1, 23, 12, 0, 1 + sec)) do
progress.log "foo bar"
progress.advance
end
end
output.rewind
expect(output.read).to eq([
"\e[1Gfoo bar\n",
"\e[1G[ ] 0s --s --:--:-- 0.00/s", # render without starting
"\e[1G[= ] 0s 0s 12:00:01 1.00/s",
"\e[1Gfoo bar \n",
"\e[1G[= ] 1s 9s 12:00:11 1.00/s", # render without advancing
"\e[1G[== ] 1s 4s 12:00:06 1.00/s",
"\e[1Gfoo bar \n",
"\e[1G[== ] 2s 8s 12:00:11 1.00/s", # render without advancing
"\e[1G[=== ] 2s 4s 12:00:07 1.00/s"
].join)
Timecop.return
end
it "starts timer and logs a message with time and rate metrics" do
Timecop.freeze(Time.local(2021, 1, 23, 12, 0, 0))
format = "[:bar] :elapsed :eta :eta_time :rate/s"
progress = TTY::ProgressBar.new(format, output: output, total: 10)
Timecop.freeze(Time.local(2021, 1, 23, 12, 0, 1)) do
progress.start
end
3.times do |sec|
Timecop.freeze(Time.local(2021, 1, 23, 12, 0, 2 + sec)) do
progress.log "foo bar"
progress.advance
end
end
output.rewind
expect(output.read).to eq([
"\e[1G[ ] 0s --s --:--:-- 0.00/s", # render start
"\e[1Gfoo bar \n",
"\e[1G[ ] 1s 0s 12:00:02 0.00/s", # render without starting
"\e[1G[= ] 1s 9s 12:00:11 1.00/s",
"\e[1Gfoo bar \n",
"\e[1G[= ] 2s 18s 12:00:21 1.00/s", # render without advancing
"\e[1G[== ] 2s 8s 12:00:11 1.00/s",
"\e[1Gfoo bar \n",
"\e[1G[== ] 3s 12s 12:00:16 1.00/s", # render without advancing
"\e[1G[=== ] 3s 7s 12:00:11 1.00/s"
].join)
Timecop.return
end
end
================================================
FILE: spec/unit/meter_spec.rb
================================================
# frozen_string_literal: true
RSpec.describe TTY::ProgressBar::Meter, "#rate" do
before { Timecop.safe_mode = false }
after { Timecop.return }
it "measures with no samples" do
meter = TTY::ProgressBar::Meter.new(1)
meter.start
expect(meter.rate).to eq(0)
expect(meter.mean_rate).to eq(0)
end
it "measures with a single sample" do
meter = TTY::ProgressBar::Meter.new(1)
start_time = Time.at(10, 0)
Timecop.freeze(start_time)
meter.start
meter.sample(Time.at(10, 100_000), 10)
expect(meter.rate).to eq(100)
expect(meter.mean_rate).to eq(100)
end
it "measures rate per second" do
meter = TTY::ProgressBar::Meter.new(1)
start_time = Time.at(10, 0)
Timecop.freeze(start_time)
meter.start
# First sample batch
meter.sample Time.at(10, 100_000), 5
expect(meter.rate).to eq(50)
expect(meter.mean_rate).to eq(50)
meter.sample Time.at(10, 500_000), 5
expect(meter.rate).to eq(20)
expect(meter.mean_rate).to eq(20)
meter.sample Time.at(11, 000_000), 5
expect(meter.rate).to eq(15)
expect(meter.mean_rate).to eq(15)
meter.sample Time.at(11, 500_000), 5
expect(meter.rate).to eq(10)
expect(meter.mean_rate).to be_within(0.001).of(13.333)
meter.sample Time.at(12, 000_000), 10
expect(meter.rate).to eq(15)
expect(meter.mean_rate).to eq(15)
end
it "clears measurements" do
meter = TTY::ProgressBar::Meter.new(1)
start_time = Time.at(10, 0)
Timecop.freeze(start_time)
meter.start
meter.sample(start_time + 1, 1000)
expect(meter.rates).to eq([1000, 1000])
expect(meter.rate).to eq(1000)
meter.clear
expect(meter.rates).to eq([0])
expect(meter.rate).to eq(0)
end
end
================================================
FILE: spec/unit/multi/advance_spec.rb
================================================
# frozen_string_literal: true
RSpec.describe TTY::ProgressBar::Multi, "advance" do
let(:output) { RSpec::Support::Ruby.truffleruby? ? OutputIO.new : StringIO.new }
let(:save) { TTY::Cursor.save }
let(:restore) { TTY::Cursor.restore }
let(:top) { TTY::ProgressBar::Multi::DEFAULT_INSET[:top] }
let(:middle) { TTY::ProgressBar::Multi::DEFAULT_INSET[:middle] }
let(:bottom) { TTY::ProgressBar::Multi::DEFAULT_INSET[:bottom] }
it "advances progress bars correctly under multibar" do
bars = described_class.new(output: output)
bar1 = bars.register("[:bar] one", total: 5)
bar2 = bars.register("[:bar] two", total: 5)
bar2.advance
bar1.advance
output.rewind
expect(output.read).to eq([
"\e[1G[= ] two\n",
"\e[1G[= ] one\n"
].join)
bar2.advance
output.rewind
expect(output.read).to eq([
"\e[1G[= ] two\n",
"\e[1G[= ] one\n",
save,
"\e[2A", # up 2 lines
"\e[1G[== ] two",
restore
].join)
bar1.advance
output.rewind
expect(output.read).to eq([
"\e[1G[= ] two\n",
"\e[1G[= ] one\n",
save,
"\e[2A", # up 2 lines
"\e[1G[== ] two",
restore,
save,
"\e[1A", # up 1 line
"\e[1G[== ] one",
restore
].join)
end
it "advances progress bars correctly under top level multibar" do
bars = described_class.new("[:bar] main", output: output)
bar1 = bars.register("[:bar] one", total: 5)
bar2 = bars.register("[:bar] two", total: 5)
bar2.advance
bar1.advance
output.rewind
expect(output.read).to eq([
"\e[1G#{top}[= ] main\n",
"\e[1G#{bottom}[= ] two\n",
save,
"\e[2A", # up 2 lines
"\e[1G#{top}[== ] main",
restore,
"\e[1G#{bottom}[= ] one\n"
].join)
bar2.advance
output.rewind
expect(output.read).to eq([
"\e[1G#{top}[= ] main\n",
"\e[1G#{bottom}[= ] two\n",
save,
"\e[2A", # up 2 lines
"\e[1G#{top}[== ] main",
restore,
"\e[1G#{bottom}[= ] one\n",
save,
"\e[3A", # up 3 lines
"\e[1G#{top}[=== ] main",
restore,
save,
"\e[2A", # up 2 lines,
"\e[1G#{middle}[== ] two",
restore
].join)
bar1.advance
output.rewind
expect(output.read).to eq([
"\e[1G#{top}[= ] main\n",
"\e[1G#{bottom}[= ] two\n",
save,
"\e[2A", # up 2 lines
"\e[1G#{top}[== ] main",
restore,
"\e[1G#{bottom}[= ] one\n",
save,
"\e[3A", # up 3 lines
"\e[1G#{top}[=== ] main",
restore,
save,
"\e[2A", # up 2 lines,
"\e[1G#{middle}[== ] two",
restore,
save,
"\e[3A", # up 3 lines
"\e[1G#{top}[==== ] main",
restore,
save,
"\e[1A", # up 1 line
"\e[1G#{bottom}[== ] one",
restore
].join)
end
it "advances progress bars correctly with indeterminate children" do
bars = described_class.new("total: [:bar] :current", width: 10,
output: output)
bar1 = bars.register("one: [:bar] :current", width: 10)
bar2 = bars.register("two: [:bar] :current", width: 10)
bar1.advance
output.rewind
expect(output.read).to eq([
"\e[1G#{top}total: [<=> ] 1\n",
"\e[1G#{bottom}one: [<=> ] 1\n"
].join)
bar2.advance
output.rewind
expect(output.read).to eq([
"\e[1G#{top}total: [<=> ] 1\n",
"\e[1G#{bottom}one: [<=> ] 1\n",
save,
"\e[2A", # up 2 lines
"\e[1G#{top}total: [<=> ] 2",
restore,
"\e[1G#{bottom}two: [<=> ] 1\n"
].join)
bar1.advance
output.rewind
expect(output.read).to eq([
"\e[1G#{top}total: [<=> ] 1\n",
"\e[1G#{bottom}one: [<=> ] 1\n",
save,
"\e[2A", # up 2 lines
"\e[1G#{top}total: [<=> ] 2",
restore,
"\e[1G#{bottom}two: [<=> ] 1\n",
save,
"\e[3A", # up 3 lines
"\e[1G#{top}total: [<=> ] 3",
restore,
save,
"\e[2A", # up 2 lines
"\e[1G#{middle}one: [<=> ] 2",
restore
].join)
end
end
================================================
FILE: spec/unit/multi/events_spec.rb
================================================
# frozen_string_literal: true
RSpec.describe TTY::ProgressBar::Multi, "events" do
let(:output) { StringIO.new }
it "emits :progress event when any of the registerd bars advances" do
events = []
bars = described_class.new("[:bar]", output: output, total: 5)
bars.on(:progress) { events << :progress }
bar = bars.register "one [:bar]"
bar.advance
expect(events).to eq([:progress])
end
it "emits :done event when all progress bars finished under top level" do
events = []
bars = described_class.new("[:bar]", output: output, total: 5)
bars.on(:done) { events << :done }
bar = bars.register "one [:bar]"
bar.finish
expect(events).to eq([:done])
expect(bar.complete?).to eq(true)
end
it "emits :done event when all progress bars finished without top level" do
events = []
bars = described_class.new(output: output)
bars.on(:done) { events << :done }
bar = bars.register "one [:bar]", total: 5
bar.finish
expect(events).to eq([:done])
expect(bars.complete?).to eq(true)
end
it "emits :done event when top level registered bar finished" do
events = []
bars = described_class.new("[:bar]", output: output, total: 5)
bars.on(:done) { events << :done }
bar = bars.register "one [:bar]", total: 5
bars.finish
expect(events).to eq([:done])
expect(bar.complete?).to eq(true)
end
it "emits :done event when top level bar finished" do
events = []
bars = described_class.new(output: output)
bars.on(:done) { events << :done }
bar = bars.register "one [:bar]", total: 5
bars.finish
expect(events).to eq([:done])
expect(bar.complete?).to eq(true)
end
it "emits :stopped event when all registerd bars are stopped under top level" do
events = []
bars = described_class.new("[:bar]", output: output, total: 5)
bars.on(:stopped) { events << :stopped }
bar = bars.register "one [:bar]"
bar.stop
expect(events).to eq([:stopped])
expect(bars.stopped?).to eq(true)
end
it "emits :stopped event when all registerd bars are stopped without top level" do
events = []
bars = described_class.new(output: output)
bars.on(:stopped) { events << :stopped }
bar = bars.register "one [:bar]", total: 5
bar.stop
expect(events).to eq([:stopped])
expect(bars.stopped?).to eq(true)
end
it "emits :stopped event when registerd multi bar finished" do
events = []
bars = described_class.new("[:bar]", output: output, total: 5)
bars.on(:stopped) { events << :stopped }
bars.register "one [:bar]"
bars.stop
expect(events).to eq([:stopped])
end
it "emits :stopped event when multi bar finished" do
events = []
bars = described_class.new(output: output)
bars.on(:stopped) { events << :stopped }
bars.register "one [:bar]", total: 5
bars.stop
expect(events).to eq([:stopped])
expect(bars.stopped?).to eq(true)
end
it "emits :paused event when all registerd bars are paused under top level" do
events = []
bars = described_class.new("[:bar]", output: output, total: 5)
bars.on(:paused) { events << :paused }
bar = bars.register "one [:bar]"
bar.pause
expect(events).to eq([:paused])
expect(bars.paused?).to eq(true)
end
it "emits :paused event when all registerd bars are paused without top level" do
events = []
bars = described_class.new(output: output)
bars.on(:paused) { events << :paused }
bar = bars.register "one [:bar]", total: 5
bar.pause
expect(events).to eq([:paused])
expect(bars.paused?).to eq(true)
end
it "emits :paused event when registerd multi bar pauses" do
events = []
bars = described_class.new("[:bar]", output: output, total: 5)
bars.on(:paused) { events << :paused }
bars.register "one [:bar]"
bars.pause
expect(events).to eq([:paused])
end
it "raises when trying to register an unknown event" do
bars = described_class.new("[:bar]")
expect {
bars.on(:unknown)
}.to raise_error(
ArgumentError,
"The event unknown does not exist. Use :progress, :stopped, :paused or " \
":done instead"
)
end
end
================================================
FILE: spec/unit/multi/finish_spec.rb
================================================
# frozen_string_literal: true
RSpec.describe TTY::ProgressBar::Multi, "#finish" do
let(:output) { StringIO.new }
it "finishes all bars with top level" do
bars = described_class.new("main [:bar]", output: output)
bar1 = bars.register("[:bar]", total: 5)
bar2 = bars.register("[:bar]", total: 10)
expect(bars.complete?).to eq(false)
bar1.finish
bar2.finish
expect(bars.complete?).to eq(true)
end
it "finishes all bars without top level" do
bars = described_class.new(output: output)
bar1 = bars.register("[:bar]", total: 5)
bar2 = bars.register("[:bar]", total: 10)
bar1.finish
bar2.finish
expect(bars.complete?).to eq(true)
end
it "finishes top level" do
bars = described_class.new(output: output)
bar1 = bars.register("[:bar]", total: 5)
bar2 = bars.register("[:bar]", total: 10)
bars.finish
expect(bar1.complete?).to eq(true)
expect(bar2.complete?).to eq(true)
end
end
================================================
FILE: spec/unit/multi/line_inset_spec.rb
================================================
# frozen_string_literal: true
RSpec.describe TTY::ProgressBar::Multi, "#line_inset" do
let(:output) { StringIO.new }
it "doesn't create inset when no top level bar" do
bars = TTY::ProgressBar::Multi.new(output: output)
bar = bars.register "example"
expect(bars.line_inset(bar)).to eq("")
end
it "defaults to the empty string for the top level bar" do
bars = TTY::ProgressBar::Multi.new("Top level spinner", output: output)
expect(bars.line_inset(bars.top_bar))
.to eq(TTY::ProgressBar::Multi::DEFAULT_INSET[:top])
end
it "returns middle character for a top level bar" do
bars = TTY::ProgressBar::Multi.new("Top level bar", output: output)
bar = bars.register "middle", total: 10
bar2 = bars.register "bottom", total: 10
bar.start
bar2.start
expect(bars.line_inset(bar))
.to eq(TTY::ProgressBar::Multi::DEFAULT_INSET[:middle])
end
it "decorates last bar" do
bars = TTY::ProgressBar::Multi.new("Top spinner", output: output)
bar1 = bars.register "middle", total: 10
bar = bars.register "bottom", total: 10
bar1.start
bar.start
expect(bars.line_inset(bar))
.to eq(TTY::ProgressBar::Multi::DEFAULT_INSET[:bottom])
end
it "allows customization" do
opts = {
output: output,
style: {
top: ". ",
middle: "--",
bottom: "---"
}
}
bars = TTY::ProgressBar::Multi.new("Top level spinner", opts)
middle_bar = bars.register "", total: 10
bottom_bar = bars.register "", total: 10
middle_bar.start
bottom_bar.start
expect(bars.line_inset(bars.top_bar)).to eq(". ")
expect(bars.line_inset(middle_bar)).to eq("--")
expect(bars.line_inset(bottom_bar)).to eq("---")
end
end
================================================
FILE: spec/unit/multi/pause_spec.rb
================================================
# frozen_string_literal: true
RSpec.describe TTY::ProgressBar::Multi, "#pause" do
let(:output) { StringIO.new }
it "pauses all bars when top level pauses" do
bars = TTY::ProgressBar::Multi.new("main [:bar]", output: output)
bar1 = bars.register("[:bar]", total: 5)
bar2 = bars.register("[:bar]", total: 10)
bars.pause
expect(bar1.paused?).to eq(true)
expect(bar2.paused?).to eq(true)
expect(bars.paused?).to eq(true)
end
it "doesn't pause top bar when child pauses" do
bars = TTY::ProgressBar::Multi.new("main [:bar]", output: output)
bar1 = bars.register("[:bar]", total: 5)
bar2 = bars.register("[:bar]", total: 10)
bar1.pause
expect(bars.paused?).to eq(false)
expect(bar1.paused?).to eq(true)
expect(bar2.paused?).to eq(false)
end
end
================================================
FILE: spec/unit/multi/register_spec.rb
================================================
# frozen_string_literal: true
RSpec.describe TTY::ProgressBar::Multi, "#register" do
let(:output) { StringIO.new }
it "registers a TTY::ProgressBar instance" do
bars = TTY::ProgressBar::Multi.new(output: output)
allow_any_instance_of(TTY::ProgressBar).to receive(:attach_to)
expect_any_instance_of(TTY::ProgressBar).to receive(:attach_to)
bar = bars.register("[:bar]")
expect(bar).to be_instance_of(TTY::ProgressBar)
expect(bars.length).to eq(1)
end
it "uses global options to register instance" do
bars = TTY::ProgressBar::Multi.new(output: output, total: 100)
bar = double(:bar, attach_to: nil)
allow(bar).to receive(:on).and_return(bar)
allow(TTY::ProgressBar).to receive(:new).and_return(bar)
bars.register("[:bar]")
expect(TTY::ProgressBar).to have_received(:new)
.with("[:bar]", { total: 100, output: output })
end
it "registers bars with top level" do
bars = TTY::ProgressBar::Multi.new("main [:bar]", output: output)
bars.register("[:bar]", total: 5)
bars.register("[:bar]", total: 10)
expect(bars.total).to eq(15)
expect(bars.current).to eq(0)
end
it "updates total based on children totals" do
bars = TTY::ProgressBar::Multi.new("main [:bar]", output: output)
bars.register("[:bar]", total: 1)
expect(bars.total).to eq(1)
bars.register("[:bar]", total: 1)
expect(bars.total).to eq(2)
end
it "can register indeterminate children" do
bars = TTY::ProgressBar::Multi.new("main [:bar]", output: output)
bars.register("[:bar]")
bars.register("[:bar]")
expect(bars.total).to eq(nil)
end
end
================================================
FILE: spec/unit/multi/reset_spec.rb
================================================
# frozen_string_literal: true
RSpec.describe TTY::ProgressBar::Multi, "#reset" do
let(:output) { StringIO.new }
it "leaves multibar state alone" do
main = TTY::ProgressBar::Multi.new("", output: output, total: 10)
progress = main.register("[:bar]")
progress.advance(10)
expect(progress.complete?).to be(true)
progress.reset
expect(progress.complete?).to be(false)
progress.advance(10)
output.rewind
top = TTY::ProgressBar::Multi::DEFAULT_INSET[:top]
bottom = TTY::ProgressBar::Multi::DEFAULT_INSET[:bottom]
progress_updates =
output.read.scan(/#{Regexp.escape top}|#{Regexp.escape bottom}/)
expect(progress_updates.shift).to match(top)
expect(progress_updates.shift).to match(top)
expect(progress_updates.shift).to match(bottom)
expect(progress_updates.shift).to match(bottom)
expect(progress_updates.shift).to match(bottom)
expect(progress_updates.shift).to match(bottom)
expect(progress_updates).to be_empty
end
end
================================================
FILE: spec/unit/multi/resume_spec.rb
================================================
# frozen_string_literal: true
RSpec.describe TTY::ProgressBar::Multi, "#resume" do
let(:output) { StringIO.new }
let(:save) { TTY::Cursor.save }
let(:restore) { TTY::Cursor.restore }
let(:top) { TTY::ProgressBar::Multi::DEFAULT_INSET[:top] }
let(:middle) { TTY::ProgressBar::Multi::DEFAULT_INSET[:middle] }
let(:bottom) { TTY::ProgressBar::Multi::DEFAULT_INSET[:bottom] }
it "resumes top bar when new bar registered and advanced" do
top_bar = described_class.new("[:bar] top (:current/:total)", output: output)
bar1 = top_bar.register("[:bar] one", total: 5)
bar1.advance(5)
expect(top_bar.done?).to eq(true)
expect(bar1.done?).to eq(true)
bar2 = top_bar.register("[:bar] two", total: 5)
bar2.advance
output.rewind
expect(output.string).to eq([
"\e[1G#{top}[=====] top (5/5)\n",
save,
"\e[1A",
"#{top}\n",
restore,
"\e[1G#{bottom}[=====] one\n",
save,
"\e[1A#{bottom}\n",
restore,
save,
"\e[2A", # up 2 lines
"\e[1G#{top}[====== ] top (6/10)",
restore,
"\e[1G#{bottom}[= ] two\n"
].join)
expect(top_bar.done?).to eq(false)
expect(bar1.done?).to eq(true)
expect(bar2.done?).to eq(false)
end
it "resumes all paused bars" do
bars = described_class.new("[:bar] top (:current/:total)", output: output)
bar1 = bars.register("[:bar] one", total: 5)
bar2 = bars.register("[:bar] two", total: 5)
bar1.pause
bar1.advance
bar2.advance
expect(bar1.paused?).to eq(true)
expect(bar2.paused?).to eq(false)
expect(bars.paused?).to eq(false)
output.rewind
expect(output.string).to eq([
"\e[1G#{top}[= ] top (1/10)\n",
"\e[1G#{bottom}[= ] two\n"
].join)
bars.resume
bar1.advance
bar2.advance
expect(bar1.paused?).to eq(false)
expect(bar2.paused?).to eq(false)
expect(bars.paused?).to eq(false)
output.rewind
expect(output.string).to eq([
save,
"\e[2A", # up 2 lines
"\e[1G#{top}[== ] top (2/10)",
restore,
"\e[1G#{bottom}[= ] one\n",
save,
"\e[3A", # up 3 lines
"\e[1G#{top}[=== ] top (3/10)",
restore,
save,
"\e[2A", # up 2 lines
"\e[1G#{middle}[== ] two",
restore
].join)
end
end
================================================
FILE: spec/unit/multi/stop_spec.rb
================================================
# frozen_string_literal: true
RSpec.describe TTY::ProgressBar::Multi, "#stop" do
let(:output) { StringIO.new }
it "stops all bars when top level stops" do
bars = described_class.new("main [:bar]", output: output)
bar1 = bars.register("[:bar]", total: 5)
bar2 = bars.register("[:bar]", total: 10)
bars.stop
expect(bar1.stopped?).to eq(true)
expect(bar2.stopped?).to eq(true)
expect(bars.stopped?).to eq(true)
end
it "doesn't stop top bar when child stops" do
bars = described_class.new("main [:bar]", output: output)
bar1 = bars.register("[:bar]", total: 5)
bar2 = bars.register("[:bar]", total: 10)
bar1.stop
expect(bars.stopped?).to eq(false)
expect(bar1.stopped?).to eq(true)
expect(bar2.stopped?).to eq(false)
end
end
================================================
FILE: spec/unit/multi/width_spec.rb
================================================
# frozen_string_literal: true
RSpec.describe TTY::ProgressBar::Multi, "width" do
let(:output) { RSpec::Support::Ruby.truffleruby? ? OutputIO.new : StringIO.new }
let(:save) { TTY::Cursor.save }
let(:restore) { TTY::Cursor.restore }
let(:top) { TTY::ProgressBar::Multi::DEFAULT_INSET[:top] }
let(:middle) { TTY::ProgressBar::Multi::DEFAULT_INSET[:middle] }
let(:bottom) { TTY::ProgressBar::Multi::DEFAULT_INSET[:bottom] }
it "sets top level bar width to maximum columns when exceeds terminal width" do
allow(TTY::Screen).to receive(:width).and_return(15)
bars = TTY::ProgressBar::Multi.new("[:bar] main", output: output)
bar1 = bars.register("[:bar] one", total: 20)
bar2 = bars.register("[:bar] two", total: 20)
bar1.advance(10)
bar2.advance(10)
output.rewind
expect(output.read).to eq([
"\e[1G#{top}[== ] main\n",
"\e[1G#{bottom}[=== ] one\n",
save,
"\e[2A", # up 2 lines
"\e[1G#{top}[=== ] main",
restore,
"\e[1G#{bottom}[=== ] two\n"
].join)
bar1.advance(10)
output.rewind
expect(output.read).to eq([
"\e[1G#{top}[== ] main\n",
"\e[1G#{bottom}[=== ] one\n",
save,
"\e[2A", # up 2 lines
"\e[1G#{top}[=== ] main",
restore,
"\e[1G#{bottom}[=== ] two\n",
save,
"\e[3A", # up 3 lines
"\e[1G#{top}[===== ] main",
restore,
save,
"\e[2A", # up 2 lines
"\e[1G#{middle}[=====] one",
restore,
save,
"\e[2A", # up 2 lines
"#{middle}\n", # bar finished
restore
].join)
bar2.advance(10)
output.rewind
expect(output.read).to eq([
"\e[1G#{top}[== ] main\n",
"\e[1G#{bottom}[=== ] one\n",
save,
"\e[2A", # up 2 lines
"\e[1G#{top}[=== ] main",
restore,
"\e[1G#{bottom}[=== ] two\n",
save,
"\e[3A", # up 3 lines
"\e[1G#{top}[===== ] main",
restore,
save,
"\e[2A", # up 2 lines
"\e[1G#{middle}[=====] one",
restore,
save,
"\e[2A", # up 2 lines
"#{middle}\n", # bar finished
restore,
save,
"\e[3A", # up 3 lines
"\e[1G#{top}[======] main",
restore,
save,
"\e[3A", # up 1 line
"#{top}\n",
restore,
save,
"\e[1A", # up 1 line
"\e[1G#{bottom}[=====] two",
restore,
save,
"\e[1A", # up 1 line
"#{bottom}\n",
restore
].join)
end
it "sets top level bar width to a custom value" do
bars = TTY::ProgressBar::Multi.new("[:bar] main", output: output, width: 20)
bar1 = bars.register("[:bar] one", total: 20)
bar2 = bars.register("[:bar] two", total: 20)
bar1.advance(10)
bar2.advance(10)
output.rewind
expect(output.read).to eq([
"\e[1G#{top}[===== ] main\n",
"\e[1G#{bottom}[========== ] one\n",
save,
"\e[2A", # up 2 lines
"\e[1G#{top}[========== ] main",
restore,
"\e[1G#{bottom}[========== ] two\n"
].join)
end
it "doesn't attempt setting top level bar width without the format" do
expect {
TTY::ProgressBar::Multi.new(output: output, width: 20)
}.to_not raise_error
end
end
================================================
FILE: spec/unit/new_spec.rb
================================================
# frozen_string_literal: true
RSpec.describe TTY::ProgressBar, ".new" do
let(:output) { StringIO.new }
it "fails to initialize without a bar formatting string" do
expect {
TTY::ProgressBar.new(total: 10)
}.to raise_error(
ArgumentError,
/Expected bar formatting string, got `{:?total(=>|: )10}` instead\./
)
end
it "allows to change formatting string" do
bar = TTY::ProgressBar.new("[:bar]", output: output, total: 4)
bar.advance(2)
bar.format = "(:bar)"
bar.advance(2)
output.rewind
expect(output.read).to eq("\e[1G[== ]\e[1G(====)\n")
end
it "displays output where width == total" do
progress = described_class.new("[:bar]", output: output, total: 10)
progress.advance
output.rewind
expect(output.read).to eq("\e[1G[= ]")
end
it "yields configuration to block" do
progress = described_class.new "[:bar]" do |config|
config.output = output
config.total = 10
config.width = 40
config.interval = 30
config.frequency = 1.5
config.head = ">>"
config.complete = "#"
config.incomplete = "-"
config.unknown = "?"
config.clear = true
config.clear_head = true
config.hide_cursor = true
config.bar_format = :block
end
expect(progress.output).to eq(output)
expect(progress.total).to eq(10)
expect(progress.width).to eq(40)
expect(progress.clear).to eq(true)
expect(progress.interval).to eq(30)
expect(progress.frequency).to eq(1.5)
expect(progress.head).to eq(">>")
expect(progress.complete).to eq("#")
expect(progress.incomplete).to eq("-")
expect(progress.unknown).to eq("?")
expect(progress.clear_head).to eq(true)
expect(progress.hide_cursor).to eq(true)
expect(progress.bar_format).to eq(:block)
end
it "raises error when bar format is set to unsupported type" do
expect {
described_class.new "[:bar]", bar_format: :unknown
}.to raise_error(
ArgumentError,
"unsupported bar format: :unknown. Available formats are: " \
":arrow, :asterisk, :blade, :block, :box, :bracket, " \
":burger, :button, :chevron, :circle, :classic, :crate, :diamond, :dot, " \
":heart, :rectangle, :square, :star, :track, :tread, :triangle, :wave"
)
end
it "overrides option configuration inside a block" do
bar = described_class.new(":bar", complete: "#") do |config|
config.complete = "x"
end
expect(bar.complete).to eq("x")
end
it "displays output where width > total" do
progress = TTY::ProgressBar.new("[:bar]", output: output, total: 5, width: 10)
5.times { progress.advance }
output.rewind
expect(output.read).to eq([
"\e[1G[== ]",
"\e[1G[==== ]",
"\e[1G[====== ]",
"\e[1G[======== ]",
"\e[1G[==========]\n"
].join)
end
it "displays output where width < total" do
progress = TTY::ProgressBar.new("[:bar]", output: output, total: 10, width: 5)
10.times { progress.advance }
output.rewind
expect(output.read).to eq([
"\e[1G[= ]",
"\e[
gitextract_x1cqiz4d/ ├── .editorconfig ├── .github/ │ ├── FUNDING.yml │ ├── ISSUE_TEMPLATE/ │ │ ├── BUG_REPORT.md │ │ ├── FEATURE_REQUEST.md │ │ └── config.yml │ ├── PULL_REQUEST_TEMPLATE.md │ └── workflows/ │ └── ci.yml ├── .gitignore ├── .rspec ├── .rubocop.yml ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── Gemfile ├── LICENSE.txt ├── README.md ├── Rakefile ├── appveyor.yml ├── examples/ │ ├── color.rb │ ├── failure.rb │ ├── indeterminate.rb │ ├── iterator.rb │ ├── lazy.rb │ ├── log.rb │ ├── multi/ │ │ ├── formats.rb │ │ ├── main_bar.rb │ │ ├── resume.rb │ │ ├── simple.rb │ │ ├── single.rb │ │ └── width.rb │ ├── pause.rb │ ├── simple.rb │ ├── slow_process.rb │ ├── speed.rb │ ├── threaded.rb │ ├── tokens.rb │ ├── unicode.rb │ └── unicode_unknown.rb ├── lib/ │ ├── tty/ │ │ ├── progressbar/ │ │ │ ├── configuration.rb │ │ │ ├── converter.rb │ │ │ ├── formats.rb │ │ │ ├── formatter/ │ │ │ │ ├── bar.rb │ │ │ │ ├── byte_rate.rb │ │ │ │ ├── current.rb │ │ │ │ ├── current_byte.rb │ │ │ │ ├── elapsed.rb │ │ │ │ ├── estimated.rb │ │ │ │ ├── estimated_time.rb │ │ │ │ ├── mean_byte.rb │ │ │ │ ├── mean_rate.rb │ │ │ │ ├── percent.rb │ │ │ │ ├── rate.rb │ │ │ │ ├── total.rb │ │ │ │ └── total_byte.rb │ │ │ ├── formatter.rb │ │ │ ├── formatters.rb │ │ │ ├── meter.rb │ │ │ ├── multi.rb │ │ │ ├── pipeline.rb │ │ │ ├── timer.rb │ │ │ └── version.rb │ │ └── progressbar.rb │ └── tty-progressbar.rb ├── spec/ │ ├── perf/ │ │ └── render_spec.rb │ ├── spec_helper.rb │ ├── support/ │ │ └── output_io.rb │ └── unit/ │ ├── advance_spec.rb │ ├── bar_format_spec.rb │ ├── clear_spec.rb │ ├── complete_spec.rb │ ├── configure_spec.rb │ ├── converter/ │ │ ├── to_bytes_spec.rb │ │ ├── to_seconds_spec.rb │ │ └── to_time_spec.rb │ ├── custom_formatter_spec.rb │ ├── custom_token_spec.rb │ ├── events_spec.rb │ ├── finish_spec.rb │ ├── formatter/ │ │ ├── bar_spec.rb │ │ ├── byte_rate_spec.rb │ │ ├── current_byte_spec.rb │ │ ├── current_spec.rb │ │ ├── elapsed_spec.rb │ │ ├── estimated_spec.rb │ │ ├── estimated_time_spec.rb │ │ ├── mean_byte_spec.rb │ │ ├── mean_rate_spec.rb │ │ ├── percent_spec.rb │ │ ├── rate_spec.rb │ │ ├── total_byte_spec.rb │ │ └── total_spec.rb │ ├── frequency_spec.rb │ ├── head_spec.rb │ ├── hide_cursor_spec.rb │ ├── indeterminate_spec.rb │ ├── inspect_spec.rb │ ├── iterate_spec.rb │ ├── log_spec.rb │ ├── meter_spec.rb │ ├── multi/ │ │ ├── advance_spec.rb │ │ ├── events_spec.rb │ │ ├── finish_spec.rb │ │ ├── line_inset_spec.rb │ │ ├── pause_spec.rb │ │ ├── register_spec.rb │ │ ├── reset_spec.rb │ │ ├── resume_spec.rb │ │ ├── stop_spec.rb │ │ └── width_spec.rb │ ├── new_spec.rb │ ├── pause_spec.rb │ ├── pipeline_spec.rb │ ├── ratio_spec.rb │ ├── render_spec.rb │ ├── reset_spec.rb │ ├── resize_spec.rb │ ├── resume_spec.rb │ ├── set_current_spec.rb │ ├── start_spec.rb │ ├── stop_spec.rb │ ├── timer_spec.rb │ ├── update_spec.rb │ └── width_spec.rb ├── tasks/ │ ├── console.rake │ ├── coverage.rake │ └── spec.rake └── tty-progressbar.gemspec
SYMBOL INDEX (188 symbols across 28 files)
FILE: examples/slow_process.rb
function download_from_server (line 9) | def download_from_server(offset, limit)
function download_finished? (line 14) | def download_finished?(position)
FILE: lib/tty/progressbar.rb
type TTY (line 17) | module TTY
class ProgressBar (line 21) | class ProgressBar
method max_columns (line 50) | def self.max_columns
method display_columns (line 62) | def self.display_columns(value)
method initialize (line 106) | def initialize(format, options = {})
method reset (line 132) | def reset
method configure (line 151) | def configure
method indeterminate? (line 160) | def indeterminate?
method attach_to (line 170) | def attach_to(multibar)
method use (line 180) | def use(formatter_class)
method start (line 191) | def start
method advance (line 205) | def advance(progress = 1, tokens = {})
method iterate (line 259) | def iterate(collection, progress = 1, &block)
method update (line 276) | def update(options = {})
method current= (line 292) | def current=(value)
method ratio= (line 309) | def ratio=(value)
method ratio (line 323) | def ratio
method render (line 337) | def render
method move_to_row (line 367) | def move_to_row
method write (line 393) | def write(data, clear_first = false)
method resize (line 410) | def resize(new_width = nil)
method finish (line 424) | def finish
method resume (line 446) | def resume
method stop (line 457) | def stop
method pause (line 478) | def pause
method clear_line (line 489) | def clear_line
method complete? (line 499) | def complete?
method stopped? (line 508) | def stopped?
method paused? (line 517) | def paused?
method done? (line 526) | def done?
method on (line 538) | def on(name, &callback)
method log (line 551) | def log(message)
method to_s (line 568) | def to_s
method inspect (line 577) | def inspect
method padout (line 595) | def padout(message)
method emit (line 611) | def emit(name, *args)
method tty? (line 622) | def tty?
FILE: lib/tty/progressbar/configuration.rb
type TTY (line 5) | module TTY
class ProgressBar (line 6) | class ProgressBar
class Configuration (line 7) | class Configuration
method initialize (line 66) | def initialize(options)
method complete= (line 88) | def complete=(value)
method incomplete= (line 99) | def incomplete=(value)
method unknown= (line 110) | def unknown=(value)
method total= (line 121) | def total=(value)
method fetch_char (line 134) | def fetch_char(name, property)
method raise_if_empty (line 149) | def raise_if_empty(name, value)
FILE: lib/tty/progressbar/converter.rb
type TTY (line 3) | module TTY
class ProgressBar (line 4) | class ProgressBar
type Converter (line 8) | module Converter
function to_time (line 17) | def to_time(seconds)
function to_seconds (line 46) | def to_seconds(seconds, precision: nil)
function to_bytes (line 68) | def to_bytes(value, decimals: 2, separator: ".", unit_separator: "")
FILE: lib/tty/progressbar/formats.rb
type TTY (line 3) | module TTY
class ProgressBar (line 4) | class ProgressBar
type Formats (line 5) | module Formats
FILE: lib/tty/progressbar/formatter.rb
type TTY (line 3) | module TTY
class ProgressBar (line 4) | class ProgressBar
class Formatter (line 5) | class Formatter < ::Module
method [] (line 9) | def self.[](token_match)
method initialize (line 19) | def initialize(token_match)
FILE: lib/tty/progressbar/formatter/bar.rb
type TTY (line 5) | module TTY
class ProgressBar (line 6) | class ProgressBar
class BarFormatter (line 10) | class BarFormatter
method call (line 19) | def call(value)
method format_indeterminate (line 40) | def format_indeterminate(value, width)
method format_determinate (line 65) | def format_determinate(value, width)
FILE: lib/tty/progressbar/formatter/byte_rate.rb
type TTY (line 6) | module TTY
class ProgressBar (line 7) | class ProgressBar
class ByteRateFormatter (line 11) | class ByteRateFormatter
method call (line 20) | def call(value)
FILE: lib/tty/progressbar/formatter/current.rb
type TTY (line 3) | module TTY
class ProgressBar (line 4) | class ProgressBar
class CurrentFormatter (line 8) | class CurrentFormatter
method call (line 17) | def call(value)
FILE: lib/tty/progressbar/formatter/current_byte.rb
type TTY (line 6) | module TTY
class ProgressBar (line 7) | class ProgressBar
class ByteFormatter (line 11) | class ByteFormatter
method call (line 20) | def call(value)
FILE: lib/tty/progressbar/formatter/elapsed.rb
type TTY (line 6) | module TTY
class ProgressBar (line 7) | class ProgressBar
class ElapsedFormatter (line 11) | class ElapsedFormatter
method call (line 20) | def call(value)
FILE: lib/tty/progressbar/formatter/estimated.rb
type TTY (line 6) | module TTY
class ProgressBar (line 7) | class ProgressBar
class EstimatedFormatter (line 11) | class EstimatedFormatter
method call (line 20) | def call(value)
FILE: lib/tty/progressbar/formatter/estimated_time.rb
type TTY (line 3) | module TTY
class ProgressBar (line 4) | class ProgressBar
class EstimatedTimeFormatter (line 8) | class EstimatedTimeFormatter
method call (line 17) | def call(value)
FILE: lib/tty/progressbar/formatter/mean_byte.rb
type TTY (line 6) | module TTY
class ProgressBar (line 7) | class ProgressBar
class MeanByteFormatter (line 11) | class MeanByteFormatter
method call (line 20) | def call(value)
FILE: lib/tty/progressbar/formatter/mean_rate.rb
type TTY (line 6) | module TTY
class ProgressBar (line 7) | class ProgressBar
class MeanRateFormatter (line 11) | class MeanRateFormatter
method call (line 20) | def call(value)
FILE: lib/tty/progressbar/formatter/percent.rb
type TTY (line 5) | module TTY
class ProgressBar (line 6) | class ProgressBar
class PercentFormatter (line 10) | class PercentFormatter
method call (line 19) | def call(value)
FILE: lib/tty/progressbar/formatter/rate.rb
type TTY (line 6) | module TTY
class ProgressBar (line 7) | class ProgressBar
class RateFormatter (line 11) | class RateFormatter
method call (line 20) | def call(value)
FILE: lib/tty/progressbar/formatter/total.rb
type TTY (line 5) | module TTY
class ProgressBar (line 6) | class ProgressBar
class TotalFormatter (line 10) | class TotalFormatter
method call (line 19) | def call(value)
FILE: lib/tty/progressbar/formatter/total_byte.rb
type TTY (line 6) | module TTY
class ProgressBar (line 7) | class ProgressBar
class TotalByteFormatter (line 11) | class TotalByteFormatter
method call (line 20) | def call(value)
FILE: lib/tty/progressbar/formatters.rb
type TTY (line 21) | module TTY
class ProgressBar (line 22) | class ProgressBar
class Formatters (line 23) | class Formatters
method initialize (line 29) | def initialize(pipeline = nil)
method load (line 36) | def load(progress)
FILE: lib/tty/progressbar/meter.rb
type TTY (line 3) | module TTY
class ProgressBar (line 4) | class ProgressBar
class Meter (line 9) | class Meter
method initialize (line 16) | def initialize(interval)
method start (line 24) | def start
method sample (line 41) | def sample(at, value)
method prune_samples (line 51) | def prune_samples(at)
method save_rate (line 62) | def save_rate(at)
method rate (line 75) | def rate
method rates (line 88) | def rates
method mean_rate (line 98) | def mean_rate
method clear (line 111) | def clear
FILE: lib/tty/progressbar/multi.rb
type TTY (line 8) | module TTY
class ProgressBar (line 9) | class ProgressBar
class Multi (line 13) | class Multi
method initialize (line 46) | def initialize(*args)
method register (line 73) | def register(format, options = {})
method next_row (line 94) | def next_row
method observe (line 106) | def observe(bar)
method progress_handler (line 116) | def progress_handler
method top_bar (line 126) | def top_bar
method start (line 132) | def start
method total (line 143) | def total
method current (line 154) | def current
method complete? (line 165) | def complete?
method stopped? (line 176) | def stopped?
method done? (line 187) | def done?
method paused? (line 198) | def paused?
method stop (line 207) | def stop
method finish (line 214) | def finish
method pause (line 221) | def pause
method resume (line 228) | def resume
method line_inset (line 242) | def line_inset(bar)
method on (line 261) | def on(name, &callback)
method emit (line 276) | def emit(name, *args)
FILE: lib/tty/progressbar/pipeline.rb
type TTY (line 3) | module TTY
class ProgressBar (line 4) | class ProgressBar
class Pipeline (line 8) | class Pipeline
method initialize (line 14) | def initialize(formatters = [])
method use (line 25) | def use(formatter)
method decorate (line 40) | def decorate(tokenized)
method each (line 54) | def each(&block)
FILE: lib/tty/progressbar/timer.rb
type TTY (line 3) | module TTY
class ProgressBar (line 4) | class ProgressBar
class Timer (line 8) | class Timer
method initialize (line 14) | def initialize
method reset (line 21) | def reset
method running? (line 32) | def running?
method elapsed_time (line 42) | def elapsed_time
method elapsed_until_now (line 53) | def elapsed_until_now
method start (line 65) | def start
method stop (line 78) | def stop
FILE: lib/tty/progressbar/version.rb
type TTY (line 3) | module TTY
class ProgressBar (line 4) | class ProgressBar
FILE: spec/spec_helper.rb
class StringIO (line 22) | class StringIO
method tty? (line 24) | def tty?
FILE: spec/support/output_io.rb
class OutputIO (line 3) | class OutputIO
method initialize (line 4) | def initialize(content = "")
method print (line 8) | def print(string)
method read (line 12) | def read
method flush (line 16) | def flush; end
method rewind (line 18) | def rewind; end
method tty? (line 20) | def tty?
FILE: spec/unit/custom_formatter_spec.rb
function initialize (line 10) | def initialize(progress)
function matches? (line 14) | def matches?(value)
function call (line 18) | def call(value)
Condensed preview — 126 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (229K chars).
[
{
"path": ".editorconfig",
"chars": 150,
"preview": "root = true\n\n[*.rb]\ncharset = utf-8\nend_of_line = lf\ninsert_final_newline = true\nindent_style = space\nindent_size = 2\ntr"
},
{
"path": ".github/FUNDING.yml",
"chars": 20,
"preview": "github: piotrmurach\n"
},
{
"path": ".github/ISSUE_TEMPLATE/BUG_REPORT.md",
"chars": 509,
"preview": "---\nname: Bug report\nabout: Report something not working correctly or as expected\ntitle: ''\nlabels: bug\nassignees: ''\n--"
},
{
"path": ".github/ISSUE_TEMPLATE/FEATURE_REQUEST.md",
"chars": 372,
"preview": "---\nname: Feature request\nabout: Suggest new functionality\ntitle: ''\nlabels: enhancement\nassignees: ''\n---\n\n### Describe"
},
{
"path": ".github/ISSUE_TEMPLATE/config.yml",
"chars": 186,
"preview": "blank_issues_enabled: false\ncontact_links:\n - name: TTY Community Discussions\n url: https://github.com/piotrmurach/t"
},
{
"path": ".github/PULL_REQUEST_TEMPLATE.md",
"chars": 494,
"preview": "### Describe the change\nWhat does this Pull Request do?\n\n### Why are we doing this?\nAny related context as to why is thi"
},
{
"path": ".github/workflows/ci.yml",
"chars": 1286,
"preview": "---\nname: CI\non:\n push:\n branches:\n - master\n paths-ignore:\n - \"examples/**\"\n - \"*.md\"\n pull_requ"
},
{
"path": ".gitignore",
"chars": 118,
"preview": "/.bundle/\n/.yardoc\n/Gemfile.lock\n/_yardoc/\n/coverage/\n/doc/\n/pkg/\n/spec/reports/\n/tmp/\n*.bundle\n*.so\n*.o\n*.a\nmkmf.log\n"
},
{
"path": ".rspec",
"chars": 41,
"preview": "--color\n--require spec_helper\n--warnings\n"
},
{
"path": ".rubocop.yml",
"chars": 953,
"preview": "AllCops:\n NewCops: enable\n TargetRubyVersion: 2.0\n\nGemspec/DevelopmentDependencies:\n Enabled: false\n\nLayout/FirstArra"
},
{
"path": "CHANGELOG.md",
"chars": 9518,
"preview": "# Change log\n\n## [v0.18.3] - 2024-11-10\n\n### Fixed\n* Fix setting the current progress for an indeterminate bar by Alex W"
},
{
"path": "CODE_OF_CONDUCT.md",
"chars": 5486,
"preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participa"
},
{
"path": "Gemfile",
"chars": 408,
"preview": "# frozen_string_literal: true\n\nsource \"https://rubygems.org\"\n\ngemspec\n\ngem \"json\", \"2.4.1\" if RUBY_VERSION == \"2.0.0\"\nge"
},
{
"path": "LICENSE.txt",
"chars": 1087,
"preview": "Copyright (c) 2014 Piotr Murach (piotrmurach.com)\n\nMIT License\n\nPermission is hereby granted, free of charge, to any per"
},
{
"path": "README.md",
"chars": 32011,
"preview": "<div align=\"center\">\n <a href=\"https://ttytoolkit.org\"><img width=\"130\" src=\"https://github.com/piotrmurach/tty/raw/mas"
},
{
"path": "Rakefile",
"chars": 143,
"preview": "require \"bundler/gem_tasks\"\n\nFileList[\"tasks/**/*.rake\"].each(&method(:import))\n\ndesc \"Run all specs\"\ntask ci: %w[ spec "
},
{
"path": "appveyor.yml",
"chars": 670,
"preview": "---\nskip_commits:\n files:\n - \"examples/**\"\n - \"*.md\"\ninstall:\n - SET PATH=C:\\Ruby%ruby_version%\\bin;%PATH%\n - g"
},
{
"path": "examples/color.rb",
"chars": 293,
"preview": "# frozen_string_literal: true\n\nrequire \"pastel\"\n\nrequire_relative \"../lib/tty-progressbar\"\n\npastel = Pastel.new\ngreen = "
},
{
"path": "examples/failure.rb",
"chars": 269,
"preview": "# frozen_string_literal: true\n\nrequire_relative \"../lib/tty-progressbar\"\n\nbar = TTY::ProgressBar.new(\"downloading [:bar]"
},
{
"path": "examples/indeterminate.rb",
"chars": 430,
"preview": "# frozen_string_literal: true\n\nrequire_relative \"../lib/tty-progressbar\"\n\nbar = TTY::ProgressBar.new(\"downloading [:bar]"
},
{
"path": "examples/iterator.rb",
"chars": 160,
"preview": "# frozen_string_literal: true\n\nrequire_relative \"../lib/tty-progressbar\"\n\nbar = TTY::ProgressBar.new(\"[:bar]\", total: 30"
},
{
"path": "examples/lazy.rb",
"chars": 218,
"preview": "# frozen_string_literal: true\n\nrequire_relative \"../lib/tty-progressbar\"\n\nbar = TTY::ProgressBar.new(\"[:bar] :current\", "
},
{
"path": "examples/log.rb",
"chars": 261,
"preview": "# frozen_string_literal: true\n\nrequire_relative \"../lib/tty-progressbar\"\n\nformat = \"[:bar] :percent :elapsed :mean_rate/"
},
{
"path": "examples/multi/formats.rb",
"chars": 456,
"preview": "# frozen_string_literal: true\n\nrequire_relative \"../../lib/tty-progressbar\"\n\nbars = []\nmulti_bar = TTY::ProgressBar::Mul"
},
{
"path": "examples/multi/main_bar.rb",
"chars": 507,
"preview": "# frozen_string_literal: true\n\nrequire_relative \"../../lib/tty-progressbar\"\n\nbars = TTY::ProgressBar::Multi.new(\"main [:"
},
{
"path": "examples/multi/resume.rb",
"chars": 331,
"preview": "# frozen_string_literal: true\n\nrequire_relative \"../../lib/tty-progressbar\"\n\nbars = TTY::ProgressBar::Multi.new(\"main [:"
},
{
"path": "examples/multi/simple.rb",
"chars": 483,
"preview": "# frozen_string_literal: true\n\nrequire_relative \"../../lib/tty-progressbar\"\n\nbars = TTY::ProgressBar::Multi.new\n\nbar1 = "
},
{
"path": "examples/multi/single.rb",
"chars": 430,
"preview": "# frozen_string_literal: true\n\nrequire_relative \"../../lib/tty-progressbar\"\n\nbars = TTY::ProgressBar::Multi.new(\"main [:"
},
{
"path": "examples/multi/width.rb",
"chars": 519,
"preview": "# frozen_string_literal: true\n\nrequire_relative \"../../lib/tty-progressbar\"\n\nbars = TTY::ProgressBar::Multi.new(\"main [:"
},
{
"path": "examples/pause.rb",
"chars": 345,
"preview": "# frozen_string_literal: true\n\nrequire_relative \"../lib/tty-progressbar\"\n\nbar = TTY::ProgressBar.new(\"[:bar] :current/:t"
},
{
"path": "examples/simple.rb",
"chars": 195,
"preview": "# frozen_string_literal: true\n\nrequire_relative \"../lib/tty-progressbar\"\n\nbar = TTY::ProgressBar.new(\"downloading [:bar]"
},
{
"path": "examples/slow_process.rb",
"chars": 664,
"preview": "# frozen_string_literal: true\n\nrequire_relative \"../lib/tty-progressbar\"\n\nCONTENT_SIZE = 2048\nCHUNK_SIZE = 255\n\n# Dummy "
},
{
"path": "examples/speed.rb",
"chars": 257,
"preview": "# frozen_string_literal: true\n\nrequire_relative \"../lib/tty-progressbar\"\n\nbar = TTY::ProgressBar.new \"downloading [:bar]"
},
{
"path": "examples/threaded.rb",
"chars": 381,
"preview": "# frozen_string_literal: true\n\nrequire_relative \"../lib/tty-progressbar\"\n\nthreads = []\n\nbar = TTY::ProgressBar.new(\"[:ba"
},
{
"path": "examples/tokens.rb",
"chars": 364,
"preview": "# frozen_string_literal: true\n\nrequire_relative \"../lib/tty-progressbar\"\n\nfiles = [\n \"file1.txt\", \"file2.txt\", \"file3.t"
},
{
"path": "examples/unicode.rb",
"chars": 243,
"preview": "# frozen_string_literal: true\n\nrequire_relative \"../lib/tty-progressbar\"\n\nbar = TTY::ProgressBar.new(\"Unicode [:bar]\", t"
},
{
"path": "examples/unicode_unknown.rb",
"chars": 356,
"preview": "# frozen_string_literal: true\n\nrequire_relative \"../lib/tty-progressbar\"\n\nbar = TTY::ProgressBar.new(\"Unicode [:bar]\", t"
},
{
"path": "lib/tty/progressbar/configuration.rb",
"chars": 4402,
"preview": "# frozen_string_literal: true\n\nrequire_relative \"formats\"\n\nmodule TTY\n class ProgressBar\n class Configuration\n "
},
{
"path": "lib/tty/progressbar/converter.rb",
"chars": 2471,
"preview": "# frozen_string_literal: true\n\nmodule TTY\n class ProgressBar\n # Responsible for converting values to different forma"
},
{
"path": "lib/tty/progressbar/formats.rb",
"chars": 2766,
"preview": "# frozen_string_literal: true\n\nmodule TTY\n class ProgressBar\n module Formats\n FORMATS = {\n arrow: { # ▸▸"
},
{
"path": "lib/tty/progressbar/formatter/bar.rb",
"chars": 4020,
"preview": "# frozen_string_literal: true\n\nrequire_relative \"../formatter\"\n\nmodule TTY\n class ProgressBar\n # Used by {Pipeline} "
},
{
"path": "lib/tty/progressbar/formatter/byte_rate.rb",
"chars": 608,
"preview": "# frozen_string_literal: true\n\nrequire_relative \"../converter\"\nrequire_relative \"../formatter\"\n\nmodule TTY\n class Progr"
},
{
"path": "lib/tty/progressbar/formatter/current.rb",
"chars": 495,
"preview": "# frozen_string_literal: true\n\nmodule TTY\n class ProgressBar\n # Used by {Pipeline} to format :current token\n #\n "
},
{
"path": "lib/tty/progressbar/formatter/current_byte.rb",
"chars": 625,
"preview": "# frozen_string_literal: true\n\nrequire_relative \"../converter\"\nrequire_relative \"../formatter\"\n\nmodule TTY\n class Progr"
},
{
"path": "lib/tty/progressbar/formatter/elapsed.rb",
"chars": 576,
"preview": "# frozen_string_literal: true\n\nrequire_relative \"../converter\"\nrequire_relative \"../formatter\"\n\nmodule TTY\n class Progr"
},
{
"path": "lib/tty/progressbar/formatter/estimated.rb",
"chars": 912,
"preview": "# frozen_string_literal: true\n\nrequire_relative \"../converter\"\nrequire_relative \"../formatter\"\n\nmodule TTY\n class Progr"
},
{
"path": "lib/tty/progressbar/formatter/estimated_time.rb",
"chars": 1158,
"preview": "# frozen_string_literal: true\n\nmodule TTY\n class ProgressBar\n # Used by {Pipeline} to format :eta_time token\n #\n "
},
{
"path": "lib/tty/progressbar/formatter/mean_byte.rb",
"chars": 619,
"preview": "# frozen_string_literal: true\n\nrequire_relative \"../converter\"\nrequire_relative \"../formatter\"\n\nmodule TTY\n class Progr"
},
{
"path": "lib/tty/progressbar/formatter/mean_rate.rb",
"chars": 621,
"preview": "# frozen_string_literal: true\n\nrequire_relative \"../converter\"\nrequire_relative \"../formatter\"\n\nmodule TTY\n class Progr"
},
{
"path": "lib/tty/progressbar/formatter/percent.rb",
"chars": 658,
"preview": "# frozen_string_literal: true\n\nrequire_relative \"../formatter\"\n\nmodule TTY\n class ProgressBar\n # Used by {Pipeline} "
},
{
"path": "lib/tty/progressbar/formatter/rate.rb",
"chars": 593,
"preview": "# frozen_string_literal: true\n\nrequire_relative \"../converter\"\nrequire_relative \"../formatter\"\n\nmodule TTY\n class Progr"
},
{
"path": "lib/tty/progressbar/formatter/total.rb",
"chars": 575,
"preview": "# frozen_string_literal: true\n\nrequire_relative \"../formatter\"\n\nmodule TTY\n class ProgressBar\n # Used by {Pipeline} "
},
{
"path": "lib/tty/progressbar/formatter/total_byte.rb",
"chars": 716,
"preview": "# frozen_string_literal: true\n\nrequire_relative \"../converter\"\nrequire_relative \"../formatter\"\n\nmodule TTY\n class Progr"
},
{
"path": "lib/tty/progressbar/formatter.rb",
"chars": 1055,
"preview": "# frozen_string_literal: true\n\nmodule TTY\n class ProgressBar\n class Formatter < ::Module\n # A helper for declar"
},
{
"path": "lib/tty/progressbar/formatters.rb",
"chars": 1922,
"preview": "# frozen_string_literal: true\n\nrequire \"forwardable\"\n\nrequire_relative \"pipeline\"\n\nrequire_relative \"formatter/bar\"\nrequ"
},
{
"path": "lib/tty/progressbar/meter.rb",
"chars": 2621,
"preview": "# frozen_string_literal: true\n\nmodule TTY\n class ProgressBar\n # Used by {ProgressBar} to measure progress rate per i"
},
{
"path": "lib/tty/progressbar/multi.rb",
"chars": 6403,
"preview": "# frozen_string_literal: true\n\nrequire \"forwardable\"\nrequire \"monitor\"\n\nrequire_relative \"../progressbar\"\n\nmodule TTY\n "
},
{
"path": "lib/tty/progressbar/pipeline.rb",
"chars": 1351,
"preview": "# frozen_string_literal: true\n\nmodule TTY\n class ProgressBar\n # Used by {ProgressBar} to decorate format string\n "
},
{
"path": "lib/tty/progressbar/timer.rb",
"chars": 1827,
"preview": "# frozen_string_literal: true\n\nmodule TTY\n class ProgressBar\n # Used to measure the elapsed time for multiple time i"
},
{
"path": "lib/tty/progressbar/version.rb",
"chars": 115,
"preview": "# frozen_string_literal: true\n\nmodule TTY\n class ProgressBar\n VERSION = \"0.18.3\"\n end # ProgressBar\nend # TTY\n"
},
{
"path": "lib/tty/progressbar.rb",
"chars": 15493,
"preview": "# frozen_string_literal: true\n\nrequire \"io/console\"\nrequire \"forwardable\"\nrequire \"monitor\"\nrequire \"tty-cursor\"\nrequire"
},
{
"path": "lib/tty-progressbar.rb",
"chars": 76,
"preview": "require_relative \"tty/progressbar\"\nrequire_relative \"tty/progressbar/multi\"\n"
},
{
"path": "spec/perf/render_spec.rb",
"chars": 1121,
"preview": "# frozen_string_literal: true\n\nrequire \"erb\"\nrequire \"rspec-benchmark\"\n\nRSpec.describe TTY::ProgressBar, \"rendering\" do\n"
},
{
"path": "spec/spec_helper.rb",
"chars": 1258,
"preview": "# frozen_string_literal: true\n\nif ENV[\"COVERAGE\"] == \"true\"\n require \"simplecov\"\n require \"coveralls\"\n\n SimpleCov.for"
},
{
"path": "spec/support/output_io.rb",
"chars": 255,
"preview": "# frozen_string_literal: true\n\nclass OutputIO\n def initialize(content = \"\")\n @content = content\n end\n\n def print(s"
},
{
"path": "spec/unit/advance_spec.rb",
"chars": 819,
"preview": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \"#advance\" do\n let(:output) { StringIO.new }\n\n it \"adv"
},
{
"path": "spec/unit/bar_format_spec.rb",
"chars": 1444,
"preview": "# frozen_string_literal: false\n\nRSpec.describe TTY::ProgressBar, \":bar_format\" do\n let(:output) { StringIO.new(\"\", \"w+\""
},
{
"path": "spec/unit/clear_spec.rb",
"chars": 512,
"preview": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \"clear\" do\n let(:output) { StringIO.new }\n\n it \"clears"
},
{
"path": "spec/unit/complete_spec.rb",
"chars": 424,
"preview": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \"#complete?\" do\n let(:output) { StringIO.new }\n\n it \"c"
},
{
"path": "spec/unit/configure_spec.rb",
"chars": 1530,
"preview": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \"#configure\" do\n it \"yields configuration instance\" do\n"
},
{
"path": "spec/unit/converter/to_bytes_spec.rb",
"chars": 1338,
"preview": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar::Converter, \"#to_bytes\" do\n subject(:converter) { descri"
},
{
"path": "spec/unit/converter/to_seconds_spec.rb",
"chars": 782,
"preview": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar::Converter, \"#to_seconds\" do\n subject(:converter) { desc"
},
{
"path": "spec/unit/converter/to_time_spec.rb",
"chars": 1015,
"preview": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar::Converter, \"#to_time\" do\n subject(:converter) { describ"
},
{
"path": "spec/unit/custom_formatter_spec.rb",
"chars": 899,
"preview": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \"custom formatter\" do\n let(:output) { StringIO.new }\n\n "
},
{
"path": "spec/unit/custom_token_spec.rb",
"chars": 464,
"preview": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \"custom token\" do\n let(:output) { StringIO.new }\n\n it "
},
{
"path": "spec/unit/events_spec.rb",
"chars": 1003,
"preview": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \"events\" do\n let(:output) { StringIO.new }\n\n it \"emits"
},
{
"path": "spec/unit/finish_spec.rb",
"chars": 422,
"preview": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \"#finish\" do\n let(:output) { StringIO.new }\n\n it \"fini"
},
{
"path": "spec/unit/formatter/bar_spec.rb",
"chars": 6091,
"preview": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \":bar token\" do\n let(:output) { StringIO.new }\n\n it \"a"
},
{
"path": "spec/unit/formatter/byte_rate_spec.rb",
"chars": 1638,
"preview": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \":byte_rate token\" do\n let(:output) { StringIO.new }\n\n "
},
{
"path": "spec/unit/formatter/current_byte_spec.rb",
"chars": 518,
"preview": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \":current_byte token\" do\n let(:output) { StringIO.new }"
},
{
"path": "spec/unit/formatter/current_spec.rb",
"chars": 385,
"preview": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \":current token\" do\n let(:output) { StringIO.new }\n\n i"
},
{
"path": "spec/unit/formatter/elapsed_spec.rb",
"chars": 2298,
"preview": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \":elapsed token\" do\n let(:output) { StringIO.new }\n\n b"
},
{
"path": "spec/unit/formatter/estimated_spec.rb",
"chars": 1128,
"preview": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \":eta token\" do\n let(:output) { StringIO.new }\n\n befor"
},
{
"path": "spec/unit/formatter/estimated_time_spec.rb",
"chars": 1670,
"preview": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \":eta_time token\" do\n let(:output) { StringIO.new }\n\n "
},
{
"path": "spec/unit/formatter/mean_byte_spec.rb",
"chars": 1689,
"preview": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \":mean_byte token\" do\n let(:output) { StringIO.new }\n\n "
},
{
"path": "spec/unit/formatter/mean_rate_spec.rb",
"chars": 1535,
"preview": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \":mean_rate token\" do\n let(:output) { StringIO.new }\n\n "
},
{
"path": "spec/unit/formatter/percent_spec.rb",
"chars": 803,
"preview": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \":percent token\" do\n let(:output) { StringIO.new }\n\n i"
},
{
"path": "spec/unit/formatter/rate_spec.rb",
"chars": 1510,
"preview": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \":rate token\" do\n let(:output) { StringIO.new }\n\n befo"
},
{
"path": "spec/unit/formatter/total_byte_spec.rb",
"chars": 881,
"preview": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \":total_byte token\" do\n let(:output) { StringIO.new }\n\n"
},
{
"path": "spec/unit/formatter/total_spec.rb",
"chars": 844,
"preview": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \":total token\" do\n let(:output) { StringIO.new }\n\n it "
},
{
"path": "spec/unit/frequency_spec.rb",
"chars": 808,
"preview": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \"frequency\" do\n let(:output) { StringIO.new }\n\n before"
},
{
"path": "spec/unit/head_spec.rb",
"chars": 1700,
"preview": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \":head\" do\n let(:output) { StringIO.new }\n\n it \"animat"
},
{
"path": "spec/unit/hide_cursor_spec.rb",
"chars": 1273,
"preview": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \"#hide_cursor\" do\n let(:output) { StringIO.new }\n\n it "
},
{
"path": "spec/unit/indeterminate_spec.rb",
"chars": 2142,
"preview": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \"indeterminate\" do\n let(:output) { StringIO.new }\n\n it"
},
{
"path": "spec/unit/inspect_spec.rb",
"chars": 593,
"preview": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \"#inspect\" do\n it \"inspects bar properties\" do\n bar "
},
{
"path": "spec/unit/iterate_spec.rb",
"chars": 2133,
"preview": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \"#iterate\" do\n let(:output) { StringIO.new }\n\n it \"ite"
},
{
"path": "spec/unit/log_spec.rb",
"chars": 3020,
"preview": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \"#log\" do\n let(:output) { StringIO.new }\n\n it \"logs me"
},
{
"path": "spec/unit/meter_spec.rb",
"chars": 1750,
"preview": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar::Meter, \"#rate\" do\n before { Timecop.safe_mode = false }"
},
{
"path": "spec/unit/multi/advance_spec.rb",
"chars": 4326,
"preview": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar::Multi, \"advance\" do\n let(:output) { RSpec::Support::Rub"
},
{
"path": "spec/unit/multi/events_spec.rb",
"chars": 4235,
"preview": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar::Multi, \"events\" do\n let(:output) { StringIO.new }\n\n it"
},
{
"path": "spec/unit/multi/finish_spec.rb",
"chars": 974,
"preview": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar::Multi, \"#finish\" do\n let(:output) { StringIO.new }\n\n i"
},
{
"path": "spec/unit/multi/line_inset_spec.rb",
"chars": 1760,
"preview": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar::Multi, \"#line_inset\" do\n let(:output) { StringIO.new }\n"
},
{
"path": "spec/unit/multi/pause_spec.rb",
"chars": 813,
"preview": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar::Multi, \"#pause\" do\n let(:output) { StringIO.new }\n\n it"
},
{
"path": "spec/unit/multi/register_spec.rb",
"chars": 1641,
"preview": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar::Multi, \"#register\" do\n let(:output) { StringIO.new }\n\n "
},
{
"path": "spec/unit/multi/reset_spec.rb",
"chars": 1010,
"preview": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar::Multi, \"#reset\" do\n let(:output) { StringIO.new }\n\n it"
},
{
"path": "spec/unit/multi/resume_spec.rb",
"chars": 2348,
"preview": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar::Multi, \"#resume\" do\n let(:output) { StringIO.new }\n le"
},
{
"path": "spec/unit/multi/stop_spec.rb",
"chars": 796,
"preview": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar::Multi, \"#stop\" do\n let(:output) { StringIO.new }\n\n it "
},
{
"path": "spec/unit/multi/width_spec.rb",
"chars": 3297,
"preview": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar::Multi, \"width\" do\n let(:output) { RSpec::Support::Ruby."
},
{
"path": "spec/unit/new_spec.rb",
"chars": 3541,
"preview": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \".new\" do\n let(:output) { StringIO.new }\n\n it \"fails t"
},
{
"path": "spec/unit/pause_spec.rb",
"chars": 549,
"preview": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \"#pause\" do\n let(:output) { StringIO.new }\n\n it \"pause"
},
{
"path": "spec/unit/pipeline_spec.rb",
"chars": 859,
"preview": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar::Pipeline, \"#decorate\" do\n subject(:pipeline) { describe"
},
{
"path": "spec/unit/ratio_spec.rb",
"chars": 947,
"preview": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \"#ratio=\" do\n let(:output) { StringIO.new }\n\n it \"allo"
},
{
"path": "spec/unit/render_spec.rb",
"chars": 601,
"preview": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \"#render\" do\n let(:output) { StringIO.new }\n\n it \"pads"
},
{
"path": "spec/unit/reset_spec.rb",
"chars": 873,
"preview": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \"#reset\" do\n let(:output) { StringIO.new }\n\n it \"reset"
},
{
"path": "spec/unit/resize_spec.rb",
"chars": 1008,
"preview": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \"#resize\" do\n let(:output) { StringIO.new }\n\n it \"resi"
},
{
"path": "spec/unit/resume_spec.rb",
"chars": 4689,
"preview": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \"#resume\" do\n let(:output) { StringIO.new }\n\n it \"resu"
},
{
"path": "spec/unit/set_current_spec.rb",
"chars": 1801,
"preview": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \"#current=\" do\n let(:output) { StringIO.new }\n\n it \"al"
},
{
"path": "spec/unit/start_spec.rb",
"chars": 1158,
"preview": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \"#start\" do\n let(:output) { StringIO.new }\n\n it \"start"
},
{
"path": "spec/unit/stop_spec.rb",
"chars": 1438,
"preview": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \"#stop\" do\n let(:output) { StringIO.new }\n\n it \"stops "
},
{
"path": "spec/unit/timer_spec.rb",
"chars": 3006,
"preview": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar::Timer do\n before { Timecop.safe_mode = false }\n\n after"
},
{
"path": "spec/unit/update_spec.rb",
"chars": 589,
"preview": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \"#update\" do\n let(:output) { StringIO.new }\n\n it \"upda"
},
{
"path": "spec/unit/width_spec.rb",
"chars": 2453,
"preview": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \"#width\" do\n let(:output) { StringIO.new }\n\n it \"handl"
},
{
"path": "tasks/console.rake",
"chars": 219,
"preview": "# frozen_string_literal: true\n\ndesc \"Load gem inside irb console\"\ntask :console do\n require \"irb\"\n require \"irb/comple"
},
{
"path": "tasks/coverage.rake",
"chars": 222,
"preview": "# frozen_string_literal: true\n\ndesc \"Measure code coverage\"\ntask :coverage do\n begin\n original, ENV[\"COVERAGE\"] = EN"
},
{
"path": "tasks/spec.rake",
"chars": 837,
"preview": "# frozen_string_literal: true\n\nbegin\n require \"rspec/core/rake_task\"\n\n desc \"Run all specs\"\n RSpec::Core::RakeTask.ne"
},
{
"path": "tty-progressbar.gemspec",
"chars": 1871,
"preview": "# frozen_string_literal: true\n\nrequire_relative \"lib/tty/progressbar/version\"\n\nGem::Specification.new do |spec|\n spec.n"
}
]
About this extraction
This page contains the full source code of the piotrmurach/tty-progressbar GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 126 files (204.0 KB), approximately 63.5k tokens, and a symbol index with 188 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.