Full Code of piotrmurach/tty-progressbar for AI

master 9cd539e4b2ab cached
126 files
204.0 KB
63.5k tokens
188 symbols
1 requests
Download .txt
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 Version](https://badge.fury.io/rb/tty-progressbar.svg)][gem]
[![Actions CI](https://github.com/piotrmurach/tty-progressbar/actions/workflows/ci.yml/badge.svg)][gh_actions_ci]
[![Build status](https://ci.appveyor.com/api/projects/status/w3jafjeatt1ulufa?svg=true)][appveyor]
[![Maintainability](https://api.codeclimate.com/v1/badges/e85416137d2057169575/maintainability)][codeclimate]
[![Coverage Status](https://coveralls.io/repos/github/piotrmurach/tty-progressbar/badge.svg)][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[
Download .txt
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
Download .txt
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.

Copied to clipboard!