[
  {
    "path": ".editorconfig",
    "content": "root = true\n\n[*.rb]\ncharset = utf-8\nend_of_line = lf\ninsert_final_newline = true\nindent_style = space\nindent_size = 2\ntrim_trailing_whitespace = true\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "github: piotrmurach\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/BUG_REPORT.md",
    "content": "---\nname: Bug report\nabout: Report something not working correctly or as expected\ntitle: ''\nlabels: bug\nassignees: ''\n---\n\n### Describe the problem\n\nA brief description of the issue.\n\n### Steps to reproduce the problem\n\n```\nYour code here to reproduce the issue\n```\n\n### Actual behaviour\n\nWhat happened? This could be a description, log output, error raised etc.\n\n### Expected behaviour\n\nWhat did you expect to happen?\n\n### Describe your environment\n\n* OS version:\n* Ruby version:\n* TTY::ProgressBar version:\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/FEATURE_REQUEST.md",
    "content": "---\nname: Feature request\nabout: Suggest new functionality\ntitle: ''\nlabels: enhancement\nassignees: ''\n---\n\n### Describe the problem\n\nA brief description of the problem you're trying to solve.\n\n### How would the new feature work?\n\nA short explanation of the new feature.\n\n```\nExample code that shows possible usage\n```\n\n### Drawbacks\n\nCan you see any potential drawbacks?\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\ncontact_links:\n  - name: TTY Community Discussions\n    url: https://github.com/piotrmurach/tty/discussions\n    about: Suggest ideas, ask and answer questions\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "### Describe the change\nWhat does this Pull Request do?\n\n### Why are we doing this?\nAny related context as to why is this is a desirable change.\n\n### Benefits\nHow will the library improve?\n\n### Drawbacks\nPossible drawbacks applying this change.\n\n### Requirements\n<!--- Put an X between brackets on each line if you have done the item: -->\n- [ ] Tests written & passing locally?\n- [ ] Code style checked?\n- [ ] Rebased with `master` branch?\n- [ ] Documentation updated?\n- [ ] Changelog updated?\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "---\nname: CI\non:\n  push:\n    branches:\n      - master\n    paths-ignore:\n      - \"examples/**\"\n      - \"*.md\"\n  pull_request:\n    branches:\n      - master\n    paths-ignore:\n      - \"examples/**\"\n      - \"*.md\"\njobs:\n  tests:\n    name: Ruby ${{ matrix.ruby }}\n    runs-on: ${{ matrix.os || 'ubuntu-latest' }}\n    strategy:\n      fail-fast: false\n      matrix:\n        ruby:\n          - \"2.0\"\n          - \"2.1\"\n          - \"2.3\"\n          - \"2.4\"\n          - \"2.5\"\n          - \"2.6\"\n          - \"3.0\"\n          - \"3.1\"\n          - \"3.2\"\n          - \"3.3\"\n          - \"3.4\"\n          - ruby-head\n          - jruby-9.3\n          - jruby-9.4\n          - jruby-head\n          - truffleruby-head\n        include:\n          - ruby: \"2.2\"\n            os: ubuntu-20.04\n          - ruby: \"2.7\"\n            coverage: true\n          - ruby: jruby-9.2\n            os: ubuntu-20.04\n    env:\n      COVERAGE: ${{ matrix.coverage }}\n      COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }}\n    continue-on-error: ${{ endsWith(matrix.ruby, 'head') }}\n    steps:\n      - uses: actions/checkout@v4\n      - name: Set up Ruby\n        uses: ruby/setup-ruby@v1\n        with:\n          ruby-version: ${{ matrix.ruby }}\n          bundler-cache: true\n      - name: Run tests\n        run: bundle exec rake ci\n"
  },
  {
    "path": ".gitignore",
    "content": "/.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",
    "content": "--color\n--require spec_helper\n--warnings\n"
  },
  {
    "path": ".rubocop.yml",
    "content": "AllCops:\n  NewCops: enable\n  TargetRubyVersion: 2.0\n\nGemspec/DevelopmentDependencies:\n  Enabled: false\n\nLayout/FirstArrayElementIndentation:\n  Enabled: false\n\nLayout/LineLength:\n  Max: 82\n  Exclude:\n    - \"**/*.gemspec\"\n\nLint/AssignmentInCondition:\n  Enabled: false\n\nMetrics/AbcSize:\n  Max: 30\n\nMetrics/BlockLength:\n  Exclude:\n    - \"spec/**/*\"\n    - \"**/*.gemspec\"\n\nMetrics/ClassLength:\n  Max: 1500\n\nMetrics/CyclomaticComplexity:\n  Enabled: false\n\nMetrics/MethodLength:\n  Max: 20\n\nNaming/BinaryOperatorParameterName:\n  Enabled: false\n\nStyle/AccessorGrouping:\n  Enabled: false\n\nStyle/AsciiComments:\n  Enabled: false\n\nStyle/Lambda:\n  Enabled: false\n\nStyle/LambdaCall:\n  EnforcedStyle: braces\n\nStyle/FormatStringToken:\n  Enabled: false\n\nStyle/StringLiterals:\n  EnforcedStyle: double_quotes\n\nStyle/TrivialAccessors:\n  Enabled: false\n\n# { ... } for multi-line blocks is okay\nStyle/BlockDelimiters:\n  Enabled: false\n\nStyle/CommentedKeyword:\n  Enabled: false\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# 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 Watt\n  (@alexcwatt)\n\n## [v0.18.2] - 2021-03-08\n\n### Fixed\n* Fix calculating total in MultiBar with indeterminate children by Tim Tilberg(@ttilberg)\n\n## [v0.18.1] - 2021-01-25\n\n### Fixed\n* Fix :eta and :eta_time format tokens display when progress isn't started\n\n## [v0.18.0] - 2021-01-20\n\n### Added\n* Add #resume to allow stopped or paused bar to continue progressing\n* Add :clear_head option to remove head when progress is done\n* Add #configure to allow runtime configuration\n* Add Multi#done? to check if all bar are stopped or finished\n* Add indeterminate progress support when no total is given\n* Add :bar_format option to allow selecting preconfigured bar displays\n* Add :eta_time format token to display the estimated time of day at completion\n* Add measurement of the total elapsed time that ignores stopped time intervals\n* Add #pause to prevent bar from continuing progression and suspend time measurements\n* Add Multi#pause to allow suspending progression of all registered bars at once\n* Add Multi#resume to start again all registered bars that are stopped or paused\n* Add Timer class to handle the total elapsed time measurements\n\n### Changed\n* Change Multi#stopped? to check that all bars are stopped\n* Change gemspec to load version directly and remove test artifacts\n* Change to update strings-ansi and tty-screen dependencies\n* Change Pipeline to inject progress bar instance only once\n* Change :elapsed and :eta to show days after running for 24 hours\n* Change to ensure complete, incomplete and unknown option cannot be an empty string\n* Change to allow setting total to nil via accessor\n* Change gemspec to allow version 2.0 of unicode-display_width dependency\n* Change #stop to show hidden cursor after render similar to #finish\n\n### Fixed\n* Fix MultiBar top bar to allow resuming progress when stopped/done (@d4be4st)\n* Fix MultiBar to only set width when top bar present\n\n## [v0.17.0] - 2019-05-31\n\n### Changed\n* Change gemspec to load files directly without git\n* Change to update tty-cursor and tty-screen dependencies\n\n## [v0.16.0] - 2018-08-27\n\n### Added\n* Add strings-ansi dependency\n\n### Changed\n* Change tty-cursor dependency version\n\n### Fixed\n* Fix to handle ANSI codes in bar formatting to allow correct size calculation\n\n## [v0.15.1] - 2018-07-19\n\n### Fixed\n* Fix to always restore hidden cursor by Eric Hodel(@drbrain)\n\n## [v0.15.0] - 2018-06-24\n\n### Added\n* Add #format= for overriding formatting string\n* Add #display_columns for determining display width of multibyte characters\n* Add :inset option to bar configuration options\n* Add ability to configure width for multi bar with top level bar\n* Add unicode-display_width dependency\n\n### Changed\n* Change #update to only set configuration if actually present\n* Change bar formatter to handle multibyte characters\n\n### Fixed\n* Fix to stop reseting multibar state when registered bar reset by Eric Hodel(@drbrain)\n* Fix rendered bar to pad formatted output when it gets shorter by Eric Hodel(@drbrain)\n* Fix multi bar to advance in steps matching each bar advance progress\n* Fix multi bar rendering for widths exceeding screen columns count\n\n## [v0.14.0] - 2018-01-17\n\n### Changed\n* Change to only output to a console and stop output to a file, pipe etc...\n* Change #iterate to accept enumerators as collection type by Victor Shepelev(@zverok)\n\n### Fixed\n* Fix #iterate to take into account progress value in total steps calculation\n\n## [v0.13.0] - 2017-10-29\n\n### Changed\n* Change tty-screen dependency version\n* Change gemspec to require Ruby >= 2.0.0\n* Remove encoding comments\n\n## [v0.12.2] - 2017-09-15\n\n### Changed\n* Change to automatically start & update top level progress bar\n  when registered bars advance\n\n## [v0.12.1] - 2017-09-09\n\n### Added\n* Add rspec to gem development dependencies\n\n### Changed\n* Change line clearing to rely on tty-cursor\n\n### Fixed\n* Fix multi bar finishing before registered progress bars\n\n## [v0.12.0] - 2017-09-03\n\n### Added\n* Add :head option to allow changing bar head progression character\n* Add thread safety to allow sharing progress between multiple threads\n* Add #update to allow changing bar configuration options\n* Add #stop to stop bar in current position and terminate any further progress\n* Add #iterate to progress over a collection\n* Add validation to check if bar formatting string is provided\n* Add ability to listen for completion events such as :done, :progress and :stopped\n* Add TTY::ProgressBar::Multi for creating parallel multiple progress bars\n\n### Changed\n* Change to stop mutating strings\n* Change #reset to stop drawing and use for initialization\n\n### Fixed\n* Fix configuration to add interval option\n\n## [v0.11.0] - 2017-04-04\n\n### Added\n* Add :decimals, :separator, :unit_separator to Converter#to_bytes\n* Add ability to Converter#to_bytes to calculate higher sizes TB, PB & EB\n\n### Changed\n* Change files loading\n* Change Converter to be a module\n\n### Fixed\n* Fix :byte_rate token to correctly format bytes\n\n## [v0.10.1] - 2016-12-26\n\n### Fixed\n* Fix redefinition of Configuration#total=\n\n## [v0.10.0] - 2016-06-25\n\n### Fixed\n* Fix Meter#sample to accurately calculate rate and mean_rate by Sylvain Joyeux\n\n## [v0.9.0] - 2016-04-09\n\n### Fixed\n* Fix #resize to stop raising error when finished\n\n### Changed\n* Remove #register_signals and leave the choice on how exit and resize are handled to developer\n\n## [v0.8.1] - 2016-02-27\n\n### Added\n* Add progress bar #inspect\n\n### Fixed\n* Fix the progressbar resizing call, help from @squarism\n\n## [v0.8.0] - 2016-02-07\n\n### Changed\n* Update tty-screen dependency\n\n## [v0.7.0] - 2015-09-20\n\n* Update tty-screen dependency\n\n## [v0.6.0] - 2015-06-27\n\n### Added\n* Add ability to add custom tokens\n\n### Changed\n* Internal cleanup of parameters for formatters and pipeline\n* Fix ratio to avoid division by zero by @sleewoo issue #9\n\n## [v0.5.1] - 2015-05-31\n\n* Update tty-screen dependency with bug fixes\n\n## [v0.5.0] - 2015-01-01\n\n### Added\n* Add ability to reset progress\n* Add start method for manually setting the timer\n* Add meter to measure speed rate\n* Add to_seconds converter\n* Add :rate, :mean_rate, :byte_rate & :mean_byte formatters\n\n### Changed\n* Fix bug with finish not rendering the bar full\n\n## [v0.4.0] - 2014-12-25\n\n### Added\n* Add :total_byte, :current_byte formatters by @vincentjames501\n* Add current= method for updating progress to a given value by @vincentjames501\n* Add ratio= method for updating progress ratio\n\n## [v0.3.0] - 2014-12-21\n\n### Added\n* Add tty-screen dependency for terminal size detection\n* Add to_bytes converter\n* Add formatter for managing formats pipeline\n* Add block configuration\n\n### Changed\n* Catch INT signal and cleanly end progress\n* Change to add matching condition to formatter\n\n## [v0.2.0] - 2014-11-09\n\n### Added\n* Add estimated time formatter.\n* Add frequency option to limit repainting of progress.\n* Add log method for printing out during progress rendering.\n* Add complete? for checking progress bar state\n\n### Changed\n* Fix bug with hide_cursor option\n* Increase test coverage\n\n## [v0.1.0] - 2014-11-01\n\n* Initial implementation and release\n\n[v0.18.3]: https://github.com/piotrmurach/tty-progressbar/compare/v0.18.2...v0.18.3\n[v0.18.2]: https://github.com/piotrmurach/tty-progressbar/compare/v0.18.1...v0.18.2\n[v0.18.1]: https://github.com/piotrmurach/tty-progressbar/compare/v0.18.0...v0.18.1\n[v0.18.0]: https://github.com/piotrmurach/tty-progressbar/compare/v0.17.0...v0.18.0\n[v0.17.0]: https://github.com/piotrmurach/tty-progressbar/compare/v0.16.0...v0.17.0\n[v0.16.0]: https://github.com/piotrmurach/tty-progressbar/compare/v0.15.1...v0.16.0\n[v0.15.1]: https://github.com/piotrmurach/tty-progressbar/compare/v0.15.0...v0.15.1\n[v0.15.0]: https://github.com/piotrmurach/tty-progressbar/compare/v0.14.0...v0.15.0\n[v0.14.0]: https://github.com/piotrmurach/tty-progressbar/compare/v0.13.0...v0.14.0\n[v0.13.0]: https://github.com/piotrmurach/tty-progressbar/compare/v0.12.2...v0.13.0\n[v0.12.2]: https://github.com/piotrmurach/tty-progressbar/compare/v0.12.1...v0.12.2\n[v0.12.1]: https://github.com/piotrmurach/tty-progressbar/compare/v0.12.0...v0.12.1\n[v0.12.0]: https://github.com/piotrmurach/tty-progressbar/compare/v0.11.0...v0.12.0\n[v0.11.0]: https://github.com/piotrmurach/tty-progressbar/compare/v0.10.1...v0.11.0\n[v0.10.1]: https://github.com/piotrmurach/tty-progressbar/compare/v0.10.0...v0.10.1\n[v0.10.0]: https://github.com/piotrmurach/tty-progressbar/compare/v0.9.0...v0.10.0\n[v0.9.0]: https://github.com/piotrmurach/tty-progressbar/compare/v0.8.2...v0.9.0\n[v0.8.2]: https://github.com/piotrmurach/tty-progressbar/compare/v0.8.1...v0.8.2\n[v0.8.1]: https://github.com/piotrmurach/tty-progressbar/compare/v0.8.0...v0.8.1\n[v0.8.0]: https://github.com/piotrmurach/tty-progressbar/compare/v0.7.0...v0.8.0\n[v0.7.0]: https://github.com/piotrmurach/tty-progressbar/compare/v0.6.0...v0.7.0\n[v0.6.0]: https://github.com/piotrmurach/tty-progressbar/compare/v0.5.1...v0.6.0\n[v0.5.1]: https://github.com/piotrmurach/tty-progressbar/compare/v0.5.0...v0.5.1\n[v0.5.0]: https://github.com/piotrmurach/tty-progressbar/compare/v0.4.0...v0.5.0\n[v0.4.0]: https://github.com/piotrmurach/tty-progressbar/compare/v0.3.0...v0.4.0\n[v0.3.0]: https://github.com/piotrmurach/tty-progressbar/compare/v0.2.0...v0.3.0\n[v0.2.0]: https://github.com/piotrmurach/tty-progressbar/compare/v0.1.0...v0.2.0\n[v0.1.0]: https://github.com/piotrmurach/tty-progressbar/compare/v0.1.0\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participation in our\ncommunity a harassment-free experience for everyone, regardless of age, body\nsize, visible or invisible disability, ethnicity, sex characteristics, gender\nidentity and expression, level of experience, education, socio-economic status,\nnationality, personal appearance, race, caste, color, religion, or sexual\nidentity and orientation.\n\nWe pledge to act and interact in ways that contribute to an open, welcoming,\ndiverse, inclusive, and healthy community.\n\n## Our Standards\n\nExamples of behavior that contributes to a positive environment for our\ncommunity include:\n\n* Demonstrating empathy and kindness toward other people\n* Being respectful of differing opinions, viewpoints, and experiences\n* Giving and gracefully accepting constructive feedback\n* Accepting responsibility and apologizing to those affected by our mistakes,\n  and learning from the experience\n* Focusing on what is best not just for us as individuals, but for the overall\n  community\n\nExamples of unacceptable behavior include:\n\n* The use of sexualized language or imagery, and sexual attention or advances of\n  any kind\n* Trolling, insulting or derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or email address,\n  without their explicit permission\n* Other conduct which could reasonably be considered inappropriate in a\n  professional setting\n\n## Enforcement Responsibilities\n\nCommunity leaders are responsible for clarifying and enforcing our standards of\nacceptable behavior and will take appropriate and fair corrective action in\nresponse to any behavior that they deem inappropriate, threatening, offensive,\nor harmful.\n\nCommunity leaders have the right and responsibility to remove, edit, or reject\ncomments, commits, code, wiki edits, issues, and other contributions that are\nnot aligned to this Code of Conduct, and will communicate reasons for moderation\ndecisions when appropriate.\n\n## Scope\n\nThis Code of Conduct applies within all community spaces, and also applies when\nan individual is officially representing the community in public spaces.\nExamples of representing our community include using an official e-mail address,\nposting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported to the community leaders responsible for enforcement at\npiotr@piotrmurach.com.\nAll complaints will be reviewed and investigated promptly and fairly.\n\nAll community leaders are obligated to respect the privacy and security of the\nreporter of any incident.\n\n## Enforcement Guidelines\n\nCommunity leaders will follow these Community Impact Guidelines in determining\nthe consequences for any action they deem in violation of this Code of Conduct:\n\n### 1. Correction\n\n**Community Impact**: Use of inappropriate language or other behavior deemed\nunprofessional or unwelcome in the community.\n\n**Consequence**: A private, written warning from community leaders, providing\nclarity around the nature of the violation and an explanation of why the\nbehavior was inappropriate. A public apology may be requested.\n\n### 2. Warning\n\n**Community Impact**: A violation through a single incident or series of\nactions.\n\n**Consequence**: A warning with consequences for continued behavior. No\ninteraction with the people involved, including unsolicited interaction with\nthose enforcing the Code of Conduct, for a specified period of time. This\nincludes avoiding interactions in community spaces as well as external channels\nlike social media. Violating these terms may lead to a temporary or permanent\nban.\n\n### 3. Temporary Ban\n\n**Community Impact**: A serious violation of community standards, including\nsustained inappropriate behavior.\n\n**Consequence**: A temporary ban from any sort of interaction or public\ncommunication with the community for a specified period of time. No public or\nprivate interaction with the people involved, including unsolicited interaction\nwith those enforcing the Code of Conduct, is allowed during this period.\nViolating these terms may lead to a permanent ban.\n\n### 4. Permanent Ban\n\n**Community Impact**: Demonstrating a pattern of violation of community\nstandards, including sustained inappropriate behavior, harassment of an\nindividual, or aggression toward or disparagement of classes of individuals.\n\n**Consequence**: A permanent ban from any sort of public interaction within the\ncommunity.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage],\nversion 2.1, available at\n[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].\n\nCommunity Impact Guidelines were inspired by\n[Mozilla's code of conduct enforcement ladder][Mozilla CoC].\n\nFor answers to common questions about this code of conduct, see the FAQ at\n[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at\n[https://www.contributor-covenant.org/translations][translations].\n\n[homepage]: https://www.contributor-covenant.org\n[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html\n[Mozilla CoC]: https://github.com/mozilla/diversity\n[FAQ]: https://www.contributor-covenant.org/faq\n[translations]: https://www.contributor-covenant.org/translations\n"
  },
  {
    "path": "Gemfile",
    "content": "# frozen_string_literal: true\n\nsource \"https://rubygems.org\"\n\ngemspec\n\ngem \"json\", \"2.4.1\" if RUBY_VERSION == \"2.0.0\"\ngem \"pastel\", \"~> 0.8\"\ngem \"yardstick\", \"~> 0.9.9\"\n\nif Gem::Version.new(RUBY_VERSION) >= Gem::Version.new(\"2.1.0\")\n  gem \"rspec-benchmark\", \"~> 0.6\"\nend\n\nif Gem::Version.new(RUBY_VERSION) >= Gem::Version.new(\"2.7.0\")\n  gem \"coveralls_reborn\", \"~> 0.28.0\"\n  gem \"simplecov\", \"~> 0.22.0\"\nend\n"
  },
  {
    "path": "LICENSE.txt",
    "content": "Copyright (c) 2014 Piotr Murach (piotrmurach.com)\n\nMIT License\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\nLIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "<div align=\"center\">\n  <a href=\"https://ttytoolkit.org\"><img width=\"130\" src=\"https://github.com/piotrmurach/tty/raw/master/images/tty.png\" alt=\"TTY Toolkit logo\"/></a>\n</div>\n\n# TTY::ProgressBar\n\n[![Gem Version](https://badge.fury.io/rb/tty-progressbar.svg)][gem]\n[![Actions CI](https://github.com/piotrmurach/tty-progressbar/actions/workflows/ci.yml/badge.svg)][gh_actions_ci]\n[![Build status](https://ci.appveyor.com/api/projects/status/w3jafjeatt1ulufa?svg=true)][appveyor]\n[![Maintainability](https://api.codeclimate.com/v1/badges/e85416137d2057169575/maintainability)][codeclimate]\n[![Coverage Status](https://coveralls.io/repos/github/piotrmurach/tty-progressbar/badge.svg)][coverage]\n\n[gem]: https://badge.fury.io/rb/tty-progressbar\n[gh_actions_ci]: https://github.com/piotrmurach/tty-progressbar/actions/workflows/ci.yml\n[appveyor]: https://ci.appveyor.com/project/piotrmurach/tty-progressbar\n[codeclimate]: https://codeclimate.com/github/piotrmurach/tty-progressbar/maintainability\n[coverage]: https://coveralls.io/github/piotrmurach/tty-progressbar\n\n> A flexible and extensible progress bar for terminal applications.\n\n**TTY::ProgressBar** provides independent progress bar component for [TTY](https://github.com/piotrmurach/tty) toolkit.\n\n## Features\n\n* **Customisable.** Choose from many [configuration](#3-configuration) options to get the behaviour you want.\n* **Flexible.** Describe bar [format](#4-formatting) and pick from many predefined [tokens](#41-tokens) and [bar styles](#37-bar_format).\n* **Extensible.** Define [custom tokens](#42-custom-formatters) to fit your needs.\n* **Powerful.** Display [multi](#6-ttyprogressbarmulti-api) progress bars in parallel.\n* Show an unbounded operation with [indeterminate](#31-total) progress.\n* [Pause](#210-pause) and [resume](#212-resume) progress at any time.\n* Include [Unicode](#44-unicode) characters in progress bar.\n* Works on all ECMA-48 compatible terminals.\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem \"tty-progressbar\"\n```\n\nAnd then execute:\n\n```\n$ bundle\n```\n\nOr install it yourself as:\n\n```\n$ gem install tty-progressbar\n```\n\n## Contents\n\n* [1. Usage](#1-usage)\n* [2. TTY::ProgressBar::API](#2-ttyprogressbar-api)\n  * [2.1 advance](#21-advance)\n  * [2.2 iterate](#22-iterate)\n  * [2.3 current=](#23-current)\n  * [2.4 ratio=](#24-ratio)\n  * [2.5 width=](#25-width)\n  * [2.6 start](#26-start)\n  * [2.7 update](#27-update)\n  * [2.8 finish](#28-finish)\n  * [2.9 stop](#29-stop)\n  * [2.10 pause](#210-pause)\n  * [2.11 reset](#211-reset)\n  * [2.12 resume](#212-resume)\n  * [2.13 complete?](#213-complete)\n  * [2.14 paused?](#214-paused)\n  * [2.15 stopped?](#215-stopped)\n  * [2.16 indeterminate?](#216-indeterminate)\n  * [2.17 resize](#217-resize)\n  * [2.18 on](#218-on)\n* [3. Configuration](#3-configuration)\n  * [3.1 :total](#31-total)\n  * [3.1 :width](#32-width)\n  * [3.3 :complete](#33-complete)\n  * [3.4 :incomplete](#34-incomplete)\n  * [3.5 :head](#35-head)\n  * [3.6 :unknown](#36-unknown)\n  * [3.7 :bar_format](#37-bar_format)\n  * [3.8 :output](#38-output)\n  * [3.9 :frequency](#39-frequency)\n  * [3.10 :interval](#310-interval)\n  * [3.11 :hide_cursor](#311-hide_cursor)\n  * [3.12 :clear](#312-clear)\n  * [3.13 :clear_head](#313-clear_head)\n* [4. Formatting](#4-formatting)\n  * [4.1 Tokens](#41-tokens)\n  * [4.2 Custom Formatters](#42-custom-formatters)\n  * [4.3 Custom Tokens](#43-custom-tokens)\n  * [4.4 Unicode](#44-unicode)\n* [5. Logging](#5-logging)\n* [6. TTY::ProgressBar::Multi API](#6-ttyprogressbarmulti-api)\n  * [6.1 new](#61-new)\n  * [6.2 register](#62-register)\n  * [6.3 advance](#63-advance)\n  * [6.4 start](#64-start)\n  * [6.5 finish](#65-finish)\n  * [6.6 stop](#66-stop)\n  * [6.7 pause](#67-pause)\n  * [6.8 resume](#68-resume)\n  * [6.9 complete?](#69-complete)\n  * [6.10 paused?](#610-paused)\n  * [6.11 stopped?](#611-stopped)\n  * [6.12 on](#612-on)\n  * [6.13 :style](#613-style)\n* [7. Examples](#7-examples)\n  * [7.1 Colors](#71-colors)\n  * [7.2 Speed](#72-speed)\n\n## 1. Usage\n\n**TTY::ProgressBar** requires only a format string with `:bar` [token](#41-tokens) and total number of steps to completion:\n\n```ruby\nbar = TTY::ProgressBar.new(\"downloading [:bar]\", total: 30)\n```\n\nOnce initialized, use [advance](#21-advance) method to indicated progress:\n\n```ruby\n30.times do\n  sleep(0.1)\n  bar.advance  # by default increases by 1\nend\n```\n\nThis would produce the following animation in your terminal:\n\n```ruby\n# downloading [=======================       ]\n```\n\nYou 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).\n\nWhen you don't know the total yet, you can set it to `nil` to switch to [indeterminate](#31-total) progress:\n\n```ruby\n# downloading [       <=>                    ]\n```\n\nUse [TTY::ProgressBar::Multi](#6-ttyprogressbarmulti-api) to display multiple parallel progress bars.\n\nDeclare a top level bar and then register child bars:\n\n```ruby\nbars = TTY::ProgressBar::Multi.new(\"main [:bar] :percent\")\n\nbar1 = bars.register(\"one [:bar] :percent\", total: 15)\nbar2 = bars.register(\"two [:bar] :percent\", total: 15)\n```\n\nThen progress the child bars in parallel:\n\n```ruby\nbars.start  # starts all registered bars timers\n\nth1 = Thread.new { 15.times { sleep(0.1); bar1.advance } }\nth2 = Thread.new { 15.times { sleep(0.1); bar2.advance } }\n\n[th1, th2].each { |t| t.join }\n```\n\nA possible terminal output may look like this:\n\n```ruby\n# ┌ main [===============               ] 50%\n# ├── one [=====          ] 34%\n# └── two [==========     ] 67%\n```\n\n## 2. TTY::ProgressBar API\n\n### 2.1 advance\n\nOnce 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:\n\n```ruby\nbar.advance(1024)\n```\n\nYou can also pass negative steps if you wish to backtrack the progress:\n\n```ruby\nbar.advance(-1)\n```\n\n*Note:* If a progress bar has already finished then any negative steps will not set it back to desired value.\n\n### 2.2 iterate\n\nTo 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.\n\nFirst, create a progress bar without a total which will be automatically updated for you once iteration starts:\n\n```ruby\nbar = TTY::ProgressBar.new(\"[:bar]\")\n```\n\nThen, either directly iterate over a collection by yielding values to a block:\n\n```ruby\nbar.iterate(30.times) { |v| ... }\n```\n\nOr return an `Enumerator`:\n\n```ruby\nprogress = bar.iterate(30.times)\n# => #<Enumerator: #<Enumerator::Generator:0x...:each>\n```\n\nBy default, progress bar is advanced by `1` but you can change it by passing second argument:\n\n```ruby\nbar.iterate(30.times, 5)\n```\n\nOne 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.\n\nFor example, an `Enumerator` that downloads content from a remote server chunk at a time:\n\n```ruby\ndownloader = Enumerator.new do |y|\n  start = 0\n  loop do\n    yield(download_from_server(start, CHUNK_SIZE))\n    raise StopIteration if download_finished?\n    start += CHUNK_SIZE\n  end\nend\n```\n\nWould be used with progress bar with the total size matching the content size like so:\n\n```ruby\nbar = TTY::ProgressBar.new(\"[:bar]\", total: content_size)\n# you need to provide the total for the iterate to avoid calling enumerator.count\nresponse = bar.iterate(downloader, CHUNK_SIZE).to_a.join\n```\n\nThis 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.\n\nPlease run [slow_process example](examples/slow_process.rb) to see this in action.\n\n### 2.3 current=\n\nA progress doesn't have to start from zero. You can set it to a given value using `current=` method:\n\n```ruby\nbar.current = 50\n```\n\n*Note:* If a progress bar has already finished then setting current value will not have any effect.\n\n### 2.4 ratio=\n\nIn 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:\n\n```ruby\nbar.ratio = 0.5\n```\n\n### 2.5 width=\n\nYou can set how many terminal columns will the `:bar` actually span excluding any other tokens and/or text.\n\nFor example, if you need the bar to be always 20 columns wide do:\n\n```ruby\nbar.width = 20\n```\n\nOr with configuration options:\n\n```ruby\nbar = TTY::ProgressBar.new(\"[:bar]\", width: 20)\n```\n\n### 2.6 start\n\nBy 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:\n\n```ruby\nbar.start  # => sets timer and draws initial progress bar\n```\n\n### 2.7 update\n\nOnce a progress bar has been started, you can change its configuration option(s) by calling `update`:\n\n```ruby\nbar.update(complete: \"+\", frequency: 10)\n```\n\n### 2.8 finish\n\nIn 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.\n\n```ruby\nbar.finish\n```\n\n### 2.9 stop\n\nIn order to immediately stop a bar in the current position and thus prevent any further progress use `stop`:\n\n```ruby\nbar.stop\n```\n\n### 2.10 pause\n\nA running progress bar can be paused at the current position using `pause` method:\n\n```ruby\nbar.pause\n```\n\nA 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.\n\n### 2.11 reset\n\nIn order to reset currently running or finished progress bar to its original configuration and initial position use `reset` like so:\n\n```ruby\nbar.reset\n```\n\nAfter resetting a progress bar, if you wish to draw and start a bar and its timers use `start` call.\n\n### 2.12 resume\n\nWhen a bar is stopped or paused, you can continue its progression using the `resume` method.\n\n```ruby\nbar.resume\n```\n\nA resumed progression will continue accumulating the total elapsed time without including time intervals for pausing or stopping.\n\n### 2.13 complete?\n\nDuring 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.\n\n```ruby\nbar.complete? # => false\n```\n\n### 2.14 paused?\n\nTo check whether a progress bar is paused or not use `paused?`:\n\n```ruby\nbar.paused? # => true\n```\n\n### 2.15 stopped?\n\nTo check whether a progress bar is stopped or not use `stopped?`:\n\n```ruby\nbar.stopped? # => true\n```\n\n### 2.16 indeterminate?\n\nYou 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:\n\n```ruby\nbar.indeterminate? # => false\n```\n\n### 2.17 resize\n\nIf 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.\n\n```ruby\nbar.resize      # determine terminal width and scale accordingly\nbar.resize(50)  # will resize bar proportionately from this point onwards\n```\n\nTo handle automatic resizing you can trap `:WINCH` signal:\n\n```ruby\ntrap(:WINCH) { bar.resize }\n```\n\n### 2.18 on\n\nA progress bar fires events when it is progressing, paused, stopped or finished. You can register to listen for these events using the `on` message.\n\nEvery 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:\n\n```ruby\nbar.on(:progress) { |amount| ... }\n```\n\nWhen a progress bar finishes and completes then the `:done` event is fired. You can listen for this event:\n\n```ruby\nbar.on(:done) { ... }\n```\n\nAlternatively, when a progress bar gets stopped the `:stopped` event is fired. You can listen for this event:\n\n```ruby\nbar.on(:stopped) { ... }\n```\n\nAnytime a progress bar is paused the `:paused` event will be fired. To listen for this event do:\n\n```ruby\nbar.on(:paused) { ... }\n```\n\n## 3. Configuration\n\nThere are number of configuration options that can be provided:\n\n* [:total](#31-total) - the total number of steps to completion.\n* [:width](#32-width) - the number of terminal columns for displaying a bar excluding other tokens. Defaults to total steps.\n* [:complete](#33-complete) - the completion character, by default `=`.\n* [:incomplete](#34-incomplete) - the incomplete character, by default single space.\n* [:head](#35-head) - the head character, by default `=`.\n* [:unknown](#36-unknown) - the character(s) used to show indeterminate progress, defaults to `<=>`.\n* [:bar_format](#37-bar_format) - the predefined bar format, by default `:classic`.\n* [:output](#38-output) - the output stream defaulting to `stderr`.\n* [:frequency](#39-frequency) - used to throttle the output, by default `0`.\n* [:interval](#310-interval) - the time interval used to measure rate, by default `1 sec`.\n* [:hide_cursor](#311-hide_cursor) - whether to hide the console cursor or not, defaults to `false`.\n* [:clear](#312-clear) - whether to clear the finished bar or not, defaults to `false`.\n* [:clear_head](#313-clear_head) - whether to clear the head character when the progress is done or not, defaults to `false`.\n\nAll the above options can be passed in as hash options or block parameters:\n\n```ruby\nbar = TTY::ProgressBar.new(\"[:bar]\") do |config|\n  config.total = 30\n  config.frequency = 10\n  config.clear = true\nend\n```\n\nThe progress bar's configuration can also be changed at runtime with `configure`:\n\n```ruby\nbar.configure do |config|\n  config.total = 100   # takes precedence over the original value\n  config.frequency = 20\nend\n```\n\nOr with the [update](#27-update) method:\n\n```ruby\nbar.update(total: 100, frequency: 20)\n```\n\n### 3.1 :total\n\nThe `:total` option determines the final value at which the progress bar fills up and stops.\n\n```ruby\nTTY::ProgressBar.new(\"[:bar]\", total: 30)\n```\n\nSetting `: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:\n\n```ruby\n# [                    <=>                 ]\n```\n\nThe indeterminate mode is useful to show time-consuming and unbounded task.\n\nRun [examples/indeterminate](https://github.com/piotrmurach/tty-progressbar/blob/master/examples/indeterminate.rb) to see indeterminate progress animation in action.\n\n### 3.2 :width\n\nThe 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:\n\n```ruby\nTTY::ProgressBar.new(\"[:bar]\", width: 30)\n```\n\n### 3.3 :complete\n\nBy default, the `=` character is used to mark progression but this can be changed with `:complete` option:\n\n```ruby\nTTY::ProgressBar.new(\"[:bar]\", complete: \"x\")\n```\n\nThen the output could look like this:\n\n```ruby\n# [xxxxxxxx      ]\n```\n\n### 3.4 :incomplete\n\nBy 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:\n\n```ruby\nTTY::ProgressBar.new(\"[:bar]\", incomplete: \"_\")\n```\n\nA possible output may look like this:\n\n```ruby\n# [======_________]\n```\n\n### 3.5 :head\n\nIf you prefer for the animated bar to display a specific character for a head of progression then use `:head` option:\n\n```ruby\nTTY::ProgressBar.new(\"[:bar]\", head: \">\")\n```\n\nThis could result in output like this:\n\n```ruby\n# [=======>      ]\n```\n\n### 3.6 :unknown\n\nBy default, a progress bar shows indeterminate progress using `<=>` characters:\n\n```ruby\n# [     <=>      ]\n```\n\nOther [bar formats](#37-bar_format) use different characters.\n\nYou can change this with the `:unknown` option:\n\n```ruby\nTTY::ProgressBar.new(\"[:bar]\", unknown: \"<?>\")\n```\n\nThis may result in the following output:\n\n```ruby\n# [     <?>      ]\n````\n\n### 3.7 :bar_format\n\nThere are number of preconfigured bar formats you can choose from.\n\n| Name       | Determinate  | Indeterminate |\n|:-----------|:-------------|:--------------|\n| `:arrow`   | `▸▸▸▸▸▹▹▹▹▹` | `◂▸`          |\n| `:asterisk`| `✱✱✱✱✱✳✳✳✳✳` | `✳✱✳`         |\n| `:blade`   | `▰▰▰▰▰▱▱▱▱▱` | `▱▰▱`         |\n| `:block`   | `█████░░░░░` | `█`           |\n| `:box`     | `■■■■■□□□□□` | `□■□`         |\n| `:bracket` | `❭❭❭❭❭❭❭❭❭❭` | `❬=❭`         |\n| `:burger`  | `≡≡≡≡≡≡≡≡≡≡` | `<≡>`         |\n| `:button`  | `⦿⦿⦿⦿⦿⦾⦾⦾⦾⦾` | `⦾⦿⦾`         |\n| `:chevron` | `››››››››››` | `‹=›`         |\n| `:circle`  | `●●●●●○○○○○` | `○●○`         |\n| `:classic` | `==========` | `<=>`         |\n| `:crate`   | `▣▣▣▣▣⬚⬚⬚⬚⬚` | `⬚▣⬚`         |\n| `:diamond` | `♦♦♦♦♦♢♢♢♢♢` | `♢♦♢`         |\n| `:dot`     | `･･････････` | `･･･`         |\n| `:heart`   | `♥♥♥♥♥♡♡♡♡♡` | `♡♥♡`         |\n| `:rectangle` | `▮▮▮▮▮▯▯▯▯▯` | `▯▮▯`       |\n| `:square`  | `▪▪▪▪▪▫▫▫▫▫` | `▫▪▫`         |\n| `:star`    | `★★★★★☆☆☆☆☆` | `☆★☆`         |\n| `:track`   | `▬▬▬▬▬═════` | `═▬═`         |\n| `:tread`   | `❱❱❱❱❱❱❱❱❱❱` | `❰=❱`         |\n| `:triangle`| `▶▶▶▶▶▷▷▷▷▷` | `◀▶`          |\n| `:wave`    | `~~~~~_____` | `<~>`         |\n\nFor example, you can specify `:box` format with the `:bar_format` option:\n\n```ruby\nTTY::ProgressBar.new(\"[:bar]\", bar_format: :box)\n```\n\nThis will result in output like this:\n\n```ruby\n# [■■■■■□□□□□□□□□□]\n```\n\nYou can overwrite `:complete`, `:incomplete`, `:head` and `:unknown` characters:\n\n```ruby\nTTY::ProgressBar.new(\"[:bar]\", bar_format: :box, incomplete: \" \", unknown: \"?\")\n```\n\nThis will display the following when total is given:\n\n```ruby\n# [■■■■■          ]\n```\n\nAnd for the unknown progress the `?` character will move from left to right:\n\n```ruby\n# [   ?           ]\n```\n\n### 3.8 :output\n\nA 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.\n\nYou can change where console output is streamed with `:output` option:\n\n```ruby\nbar = TTY::ProgressBar.new(output: $stdout)\n```\n\nThe output stream defaults to `stderr`.\n\n### 3.9 :frequency\n\nEach 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.\n\nThe `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.\n\n```ruby\nTTY::ProgressBar.new(\"[:bar]\", total: 30, frequency: 10) # 10 Hz\n```\n\n### 3.10 :interval\n\nEvery 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.\n\nThe `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.\n\n```ruby\nTTY::ProgressBar.new(\":rate/minute\", total: 100, interval: 60) # 1 minute\n\nTTY::ProgressBar.new(\":rate/hour\", total: 100, interval: 3600) # 1 hour\n```\n\n### 3.11 :hide_cursor\n\nBy default the cursor is visible during progress bar rendering. If you wish to hide it, you can do so with the `:hide_cursor` option.\n\nPlease 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.\n\nOne solution is to wrap your progress rendering inside the `begin` and `ensure` like so:\n\n```ruby\nprogress = TTY::ProgressBar.new(\"[:bar]\", hide_cursor: true)\n\nbegin\n  # logic to advance progress bar\nensure\n  progress.stop # or progress.finish\n  # both methods will ensure that cursor is made visible again\nend\n```\n\n### 3.12 :clear\n\nBy default, when a progress bar finishes it returns to a new line leaving the last progress output behind.\n\nIf you prefer to erase a progress bar when it is finished use `:clear` option:\n\n```ruby\nTTY::ProgressBar.new(\"[:bar]\", clear: true)\n```\n\n### 3.13 :clear_head\n\nWhen a progress bar finishes and its animation includes [:head](#35-head) character, the character will remain in the output:\n\n```ruby\n# [=============>]\n```\n\nTo replace a head character when a progress bar is finished use `:clear_head` option:\n\n```ruby\nTTY::ProgressBar.new(\"[:bar]\", clear_head: true)\n```\n\nThis will result in the following output:\n\n```ruby\n# [==============]\n```\n\n## 4. Formatting\n\nEvery **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:\n\n```ruby\n\"downloading [:bar] :elapsed :percent\"\n```\n\n### 4.1 Tokens\n\nThese are the tokens that are currently supported:\n\n* `:bar` the progress bar\n* `:current` the current progress number\n* `:current_byte` the current progress in bytes\n* `:total` the total progress number\n* `:total_byte` the total progress in bytes\n* `:percent` the completion percentage\n* `:elapsed` the elapsed time in seconds\n* `:eta` the estimated time to completion in seconds\n* `:eta_time` the estimated time of day at completion\n* `:rate` the current rate of progression per second\n* `:byte_rate` the current rate of progression in bytes per second\n* `:mean_rate` the averaged rate of progression per second\n* `:mean_byte` the averaged rate of progression in bytes per second\n\nIn the indeterminate mode, the progress bar displays `-` for tokens that cannot be calculated like `:total`, `:total_byte`, `:percent` and `:eta`. The following format:\n\n```ruby\n\"[:bar] :current/:total :total_byte :percent ET::elapsed ETA::eta :rate/s\"\n```\n\nWill result in:\n\n```ruby\n# [                 <=>                    ] 23/- -B -% ET: 1s ETA:--s 18.01/s\n```\n\n### 4.2 Custom Formatters\n\nIf 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.\n\nFor 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:\n\n```ruby\nclass TimeFormatter\n  include TTY::ProgressBar::Formatter[/:time/i]\n  ...\nend\n```\n\nNext, add `call` method that will substitute the matched token with an actual value. For example, to see the time elapsed since the start do:\n\n```ruby\nclass TimeFormatter\n  include TTY::ProgressBar::Formatter[/:time/i]\n\n  def call(value)  # specify how display string is formatted\n    # access current progress bar instance to read start time\n    elapsed = (Time.now - progress.start_time).to_s\n    value.gsub(matcher, elapsed)   # replace :time token with a value\n  end\nend\n```\n\nNotice that you have access to all the configuration options inside the formatter by simply invoking them on the `progress` instance.\n\nCreate **TTY::ProgressBar** instance using the new token:\n\n```ruby\nbar = TTY::ProgressBar.new(\":time\", total: 30)\n```\n\nThen add `TimeFormatter` to the pipeline like so:\n\n```ruby\nbar.use TimeFormatter\n```\n\nThen invoke progression:\n\n```ruby\nbar.advance\n```\n\n### 4.3 Custom Tokens\n\nYou 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:\n\n```ruby\nbar = TTY::ProgressBar.new(\"(:current) :title\", total: 4)\nbar.advance(title: \"Hello Piotr!\")\nbar.advance(3, title: \"Bye Piotr!\")\n```\n\nThis will output:\n\n```ruby\n# (1) Hello Piotr!\n# (4) Bye Piotr!\n```\n\n### 4.4 Unicode\n\nThe 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.\n\nFor example, you can specify complete bar progression character to be Unicode non-monospaced:\n\n```ruby\nbar = TTY::ProgressBar.new(\"Unicode [:bar]\", total: 30, complete: \"あ\")\n```\n\nAdvancing above progress bar to completion will fit `あ` characters in 30 terminal columns:\n\n```ruby\n# Unicode [あああああああああああああああ]\n```\n\nSimilarly, the formatted string can include Unicode characters:\n\n```ruby\nbar = TTY::ProgressBar.new(\"あめかんむり[:bar]\", total: 20)\n```\n\nA finished progress bar will also fit within allowed width:\n\n```ruby\n# あめかんむり[==    ]\n```\n\n## 5. Logging\n\nIf 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.\n\n```ruby\nbar.log(\"Piotrrrrr\")\nbar.advance\n```\n\nThis could result in the following output:\n\n```ruby\n# Piotrrrrr\n# downloading [=======================       ]\n```\n\n## 6. TTY::ProgressBar::Multi API\n\n### 6.1 new\n\nThe 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:\n\n```ruby\nTTY::ProgressBar::Multi.new\n```\n\nHowever, if you want a top level multibar that tracks progress of all the registered progress bars then you need to provide a formatted string:\n\n```ruby\nTTY::ProgressBar::Multi.new(\"main [:bar] :percent\")\n```\n\n### 6.2 register\n\nTo create a `TTY::ProgressBar` under the multibar use `register` like so:\n\n```ruby\nmultibar = TTY::ProgressBar::Multi.new\nbar = multibar.register(\"[:bar]\", total: 30)\n```\n\nThe `register` call returns the newly created progress bar that can be changed using all the available [progress bar API](#2-ttyprogressbar-api) methods.\n\n*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.\n\n### 6.3 advance\n\nOnce 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.\n\nFor example, to display two bars asynchronously, first register them with the multi bar:\n\n```ruby\nbar1 = multibar.register(\"one [:bar]\", total: 20)\nbar2 = multibar.register(\"two [:bar]\", total: 30)\n```\n\nNext place the progress behaviour in separate process or thread:\n\n```ruby\nth1 = Thread.new { 20.times { expensive_work(); bar1.advance } }\nth2 = Thread.new { 30.times { expensive_work(); bar2.advance } }\n```\n\nFinally, wait for the threads to finish:\n\n```ruby\n[th1, th2].each { |t| t.join }\n```\n\n### 6.4 start\n\nBy 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:\n\n```ruby\nmultibar.start  # => sets timer and draws top level multi progress bar\n```\n\n### 6.5 finish\n\nIn 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.\n\n```ruby\nmultibar.finish\n```\n\n### 6.6 stop\n\nUse `stop` to terminate immediately all progress bars registered with the multibar.\n\n```ruby\nmultibar.stop\n```\n\n### 6.7 pause\n\nAll running progress bars can be paused at their current positions using the `pause` method:\n\n```ruby\nmultibar.pause\n````\n\n### 6.8 resume\n\nWhen one or more registered progress bar is stopped or paused, they can be resumed all at once using the `resume` method:\n\n```ruby\nmultibar.resume\n```\n\n### 6.9 complete?\n\nTo check if all registered progress bars have been successfully finished use `complete?`\n\n```ruby\nmultibar.complete? # => true\n```\n\n### 6.10 paused?\n\nTo check whether all progress bars are paused or not use `paused?`:\n\n```ruby\nmultibar.paused? # => true\n```\n\n### 6.11 stopped?\n\nTo check whether all progress bars are stopped or not use `stopped?`:\n\n```ruby\nmultibar.stopped? # => true\n```\n\n### 6.12 on\n\nSimilar 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.\n\nEvery time any of the registered progress bars progresses the `:progress` event is fired which you can listen for:\n\n```ruby\nmultibar.on(:progress) { ... }\n```\n\nWhen all the registered progress bars finish and complete then the `:done` event is fired. You can listen for this event:\n\n```ruby\nmultibar.on(:done) { ... }\n```\n\nWhen any of the progress bars gets stopped the `:stopped` event is fired. You can listen for this event:\n\n```ruby\nmultibar.on(:stopped) { ... }\n```\n\nAnytime a registered progress bar pauses, a `:paused` event will be fired. To listen for this event do:\n\n```ruby\nmultibar.on(:paused) { ... }\n```\n\n### 6.13 :style\n\nIn addition to all [configuration options](#3-configuration) you can style multi progress bar:\n\n```ruby\nTTY::ProgressBar::Multi.new(\"[:bar]\", style: {\n  top: \". \",\n  middle: \"|-> \",\n  bottom: \"|__ \"\n})\n```\n\n## 7. Examples\n\nThis section demonstrates some of the possible uses for the **TTY::ProgressBar**, for more please see examples folder in the source directory.\n\n### 7.1 Colors\n\nCreating 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:\n\n```ruby\nrequire \"pastel\"\n\npastel = Pastel.new\ngreen  = pastel.on_green(\" \")\nred    = pastel.on_red(\" \")\n```\n\nAnd then pass in the colored strings as options to **TTY::ProgressBar**:\n\n```ruby\nbar = TTY::ProgressBar.new(\"|:bar|\",\n  total: 30,\n  complete: green,\n  incomplete: red\n)\n```\n\nTo see how a progress bar is reported in terminal you can do:\n\n```ruby\n30.times do\n  sleep(0.1)\n  bar.advance\nend\n```\n\n### 7.2 Speed\n\nCommonly a progress bar is utilized to measure download speed per second. This can be done like so:\n\n```ruby\nTTY::ProgressBar.new(\"[:bar] :byte_rate/s\") do |config|\n  config.total = 300000\n  config.interval = 1     # => 1 sec\nend\n```\n\nThis will result in output similar to:\n\n```ruby\n# downloading [=======================       ] 4.12MB/s\n```\n\n## Contributing\n\n1. Fork it ( https://github.com/piotrmurach/tty-progressbar/fork )\n2. Create your feature branch (`git checkout -b my-new-feature`)\n3. Commit your changes (`git commit -am 'Add some feature'`)\n4. Push to the branch (`git push origin my-new-feature`)\n5. Create a new Pull Request\n\nThis 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.\n\n## Code of Conduct\n\nEveryone 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).\n\n## Copyright\n\nCopyright (c) 2014 Piotr Murach. See LICENSE for further details.\n"
  },
  {
    "path": "Rakefile",
    "content": "require \"bundler/gem_tasks\"\n\nFileList[\"tasks/**/*.rake\"].each(&method(:import))\n\ndesc \"Run all specs\"\ntask ci: %w[ spec ]\n\ntask default: :spec\n"
  },
  {
    "path": "appveyor.yml",
    "content": "---\nskip_commits:\n  files:\n    - \"examples/**\"\n    - \"*.md\"\ninstall:\n  - SET PATH=C:\\Ruby%ruby_version%\\bin;%PATH%\n  - gem install bundler -v '< 2.0'\n  - bundle install\nbefore_test:\n  - ruby -v\n  - gem -v\n  - bundle -v\nbuild: off\ntest_script:\n  - bundle exec rake ci\nenvironment:\n  matrix:\n    - ruby_version: \"200\"\n    - ruby_version: \"200-x64\"\n    - ruby_version: \"21\"\n    - ruby_version: \"21-x64\"\n    - ruby_version: \"22\"\n    - ruby_version: \"22-x64\"\n    - ruby_version: \"23\"\n    - ruby_version: \"23-x64\"\n    - ruby_version: \"24\"\n    - ruby_version: \"24-x64\"\n    - ruby_version: \"25\"\n    - ruby_version: \"25-x64\"\n    - ruby_version: \"26\"\n    - ruby_version: \"26-x64\"\n"
  },
  {
    "path": "examples/color.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"pastel\"\n\nrequire_relative \"../lib/tty-progressbar\"\n\npastel = Pastel.new\ngreen = pastel.on_green(\" \")\nred = pastel.on_red(\" \")\n\nbar = TTY::ProgressBar.new(\"|:bar|\", total: 30, complete: green, incomplete: red)\n\n30.times do\n  sleep(0.1)\n  bar.advance\nend\n"
  },
  {
    "path": "examples/failure.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative \"../lib/tty-progressbar\"\n\nbar = TTY::ProgressBar.new(\"downloading [:bar] :percent\", head: \">\", total: 30)\n30.times do |i|\n  if i == 15\n    bar.update(head: \"x\")\n    bar.stop\n    break\n  end\n  sleep(0.1)\n  bar.advance\nend\n"
  },
  {
    "path": "examples/indeterminate.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative \"../lib/tty-progressbar\"\n\nbar = TTY::ProgressBar.new(\"downloading [:bar] :current/:total :current_byte \" \\\n                           \":total_byte :percent ET::elapsed ETA::eta \" \\\n                           \":rate/s :mean_rate/s :byte_rate/s :mean_byte/s\",\n                           width: 40)\n\n170.times do |i|\n  sleep(0.05)\n  bar.advance\n  bar.update(total: 170) if i == 69\nend\n"
  },
  {
    "path": "examples/iterator.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative \"../lib/tty-progressbar\"\n\nbar = TTY::ProgressBar.new(\"[:bar]\", total: 30)\n\nbar.iterate(30.times) { sleep(0.1) }\n"
  },
  {
    "path": "examples/lazy.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative \"../lib/tty-progressbar\"\n\nbar = TTY::ProgressBar.new(\"[:bar] :current\", total: 10, width: 20)\n\nrange = 1..Float::INFINITY\nbar.iterate(range.lazy.take(10)) { sleep(0.1) }\n"
  },
  {
    "path": "examples/log.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative \"../lib/tty-progressbar\"\n\nformat = \"[:bar] :percent :elapsed :mean_rate/s ETA :eta :eta_time\"\nbar = TTY::ProgressBar.new(format, total: 10)\n\n10.times do |i|\n  bar.log(\"[#{i}] Task\")\n  sleep(0.2)\n  bar.advance\nend\n"
  },
  {
    "path": "examples/multi/formats.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative \"../../lib/tty-progressbar\"\n\nbars = []\nmulti_bar = TTY::ProgressBar::Multi.new(width: 50)\n\nTTY::ProgressBar::Formats::FORMATS.each_key do |format|\n  bars << multi_bar.register(\"%10s |:bar|\" % [format], hide_cursor: true,\n                             total: 50, bar_format: format)\nend\n\nbegin\n  50.times do\n    bars.each do |bar|\n      sleep(0.002)\n      bar.advance\n    end\n  end\nensure\n  multi_bar.stop\nend\n"
  },
  {
    "path": "examples/multi/main_bar.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative \"../../lib/tty-progressbar\"\n\nbars = TTY::ProgressBar::Multi.new(\"main [:bar] :percent\")\n\nbar1 = bars.register \"foo [:bar] :percent\", total: 15\nbar2 = bars.register \"bar [:bar] :percent\", total: 15\nbar3 = bars.register \"baz [:bar] :percent\", total: 45\n\nth1 = Thread.new { 15.times { sleep(0.1); bar1.advance } }\nth2 = Thread.new { 15.times { sleep(0.1); bar2.advance } }\nth3 = Thread.new { 45.times { sleep(0.1); bar3.advance } }\n\n[th1, th2, th3].each(&:join)\n"
  },
  {
    "path": "examples/multi/resume.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative \"../../lib/tty-progressbar\"\n\nbars = TTY::ProgressBar::Multi.new(\"main [:bar] (:current/:total)\")\n\nbar1 = bars.register \"foo [:bar] :percent\", total: 10\n10.times { bar1.advance; sleep(0.1) }\n\nbar2 = bars.register \"bar [:bar] :percent\", total: 15\n15.times { bar2.advance; sleep(0.1) }\n"
  },
  {
    "path": "examples/multi/simple.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative \"../../lib/tty-progressbar\"\n\nbars = TTY::ProgressBar::Multi.new\n\nbar1 = bars.register \"foo [:bar] :percent\", total: 20\nbar2 = bars.register \"bar [:bar] :percent\", total: 30\nbar3 = bars.register \"baz [:bar] :percent\", total: 10\n\nth1 = Thread.new { 20.times { sleep(0.2); bar1.advance } }\nth2 = Thread.new { 30.times { sleep(0.1); bar2.advance } }\nth3 = Thread.new { 10.times { sleep(0.3); bar3.advance } }\n\n[th1, th2, th3].each(&:join)\n"
  },
  {
    "path": "examples/multi/single.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative \"../../lib/tty-progressbar\"\n\nbars = TTY::ProgressBar::Multi.new(\"main [:bar] :percent\")\n\nfoo_bar = bars.register \"foo [:bar] :percent\", total: 30\nbar_bar = bars.register \"bar [:bar] :percent\", total: 30\nbaz_bar = bars.register \"baz [:bar] :percent\", total: 30\n\n30.times do |i|\n  foo_bar.advance\n  bar_bar.advance(2) if i.even?\n  baz_bar.advance(3) if (i % 3).zero?\n  sleep(0.1)\nend\n"
  },
  {
    "path": "examples/multi/width.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative \"../../lib/tty-progressbar\"\n\nbars = TTY::ProgressBar::Multi.new(\"main [:bar] :percent\")\n\nbar1 = bars.register \"foo [:bar] :percent\", total: 150\nbar2 = bars.register \"bar [:bar] :percent\", total: 250\nbar3 = bars.register \"baz [:bar] :percent\", total: 100\n\nth1 = Thread.new { 15.times { sleep(0.1); bar1.advance(10) } }\nth2 = Thread.new { 50.times { sleep(0.1); bar2.advance(5)} }\nth3 = Thread.new { 50.times { sleep(0.1); bar3.advance(5) } }\n\n[th1, th2, th3].each(&:join)\n"
  },
  {
    "path": "examples/pause.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative \"../lib/tty-progressbar\"\n\nbar = TTY::ProgressBar.new(\"[:bar] :current/:total :current_byte/:total_byte \" \\\n                           \":rate/s :mean_rate/s ET::elapsed ETA::eta\", total: 40)\n\n20.times { sleep(0.1); bar.advance }\n\nbar.pause\nsleep(1)\nbar.resume\n\n20.times { sleep(0.1); bar.advance }\n"
  },
  {
    "path": "examples/simple.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative \"../lib/tty-progressbar\"\n\nbar = TTY::ProgressBar.new(\"downloading [:bar] :elapsed :percent\", total: 30)\n30.times do\n  sleep(0.1)\n  bar.advance\nend\n"
  },
  {
    "path": "examples/slow_process.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative \"../lib/tty-progressbar\"\n\nCONTENT_SIZE = 2048\nCHUNK_SIZE = 255\n\n# Dummy \"long responding server\"\ndef download_from_server(offset, limit)\n  sleep(0.1)\n  \"<chunk #{offset}..#{offset + limit}>\"\nend\n\ndef download_finished?(position)\n  position >= CONTENT_SIZE\nend\n\ndownloader = Enumerator.new do |y|\n  start = 0\n  loop do\n    y.yield(download_from_server(start, CHUNK_SIZE))\n    start += CHUNK_SIZE\n    raise StopIteration if download_finished?(start)\n  end\nend\n\nbar = TTY::ProgressBar.new(\"[:bar] :current_byte/:total_byte\", total: CONTENT_SIZE)\n\nresponse = bar.iterate(downloader, CHUNK_SIZE).to_a.join\n\nputs response\n"
  },
  {
    "path": "examples/speed.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative \"../lib/tty-progressbar\"\n\nbar = TTY::ProgressBar.new \"downloading [:bar] :rate/s :mean_rate/s\" do |conf|\n  conf.total = 100\n  conf.interval = 1\nend\n\n30.times do\n  sleep(0.1)\n  bar.advance(Random.rand(10))\nend\n"
  },
  {
    "path": "examples/threaded.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative \"../lib/tty-progressbar\"\n\nthreads = []\n\nbar = TTY::ProgressBar.new(\"[:bar] :percent\", total: 30)\n\nthreads << Thread.new do\n  15.times { sleep(0.1); bar.update(complete: \"-\", head: \"-\"); bar.advance; }\nend\nthreads << Thread.new do\n  15.times { sleep(0.1); bar.update(complete: \"+\", head: \"+\"); bar.advance; }\nend\n\nthreads.map(&:join)\n"
  },
  {
    "path": "examples/tokens.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative \"../lib/tty-progressbar\"\n\nfiles = [\n  \"file1.txt\", \"file2.txt\", \"file3.txt\", \"file4.txt\", \"file5.txt\",\n  \"file6.txt\", \"file7.txt\", \"file8.txt\", \"file9.txt\", \"file10.txt\"\n]\n\nbar = TTY::ProgressBar.new(\"downloading :file :percent\", total: files.size)\n10.times do |num|\n  sleep(0.1)\n  bar.advance(file: files[num])\nend\n"
  },
  {
    "path": "examples/unicode.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative \"../lib/tty-progressbar\"\n\nbar = TTY::ProgressBar.new(\"Unicode [:bar]\", total: 30,\n                           head: \">\", complete: \"本\", incomplete: \"〜\")\n30.times do\n  sleep(0.1)\n  bar.advance\nend\n"
  },
  {
    "path": "examples/unicode_unknown.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative \"../lib/tty-progressbar\"\n\nbar = TTY::ProgressBar.new(\"Unicode [:bar]\", total: nil,\n                           head: \">\", complete: \"本\", incomplete: \"〜\",\n                           unknown: \"<本>\", width: 31)\n\n60.times { sleep(0.05); bar.advance }\n\nbar.update(total: 100)\n\n40.times { sleep(0.1); bar.advance }\n"
  },
  {
    "path": "lib/tty/progressbar/configuration.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative \"formats\"\n\nmodule TTY\n  class ProgressBar\n    class Configuration\n      include TTY::ProgressBar::Formats\n\n      # The total number of steps to completion\n      # @api public\n      attr_reader :total\n\n      # The maximum width for the progress bar except all formatting tokens\n      # @api public\n      attr_accessor :width\n\n      # The complete character in progress animation\n      # @api public\n      attr_reader :complete\n\n      # The incomplete character in progress animation\n      # @api public\n      attr_reader :incomplete\n\n      # The head character, defaults to complete\n      # @api public\n      attr_accessor :head\n\n      # The unknown character for indeterminate progress animation\n      # @api public\n      attr_reader :unknown\n\n      # The amount of indentation before a progress animation\n      # @api private\n      attr_accessor :inset\n\n      # The preconfigured bar format name, defaults to :classic\n      # @api public\n      attr_accessor :bar_format\n\n      # The object that responds to print call, defaults to stderr\n      # @api public\n      attr_accessor :output\n\n      # The frequency with which to display a progress bar per second\n      # @api public\n      attr_accessor :frequency\n\n      # The time interval for sampling of speed measurement, defaults to 1 second\n      # @api public\n      attr_accessor :interval\n\n      # Whether or not to hide the cursor, defaults to false\n      # @api public\n      attr_accessor :hide_cursor\n\n      # Whether or not to clear the progress line, defaults to false\n      # @api public\n      attr_accessor :clear\n\n      # Whether or not to replace head character with complete, defaults to false\n      # @api public\n      attr_accessor :clear_head\n\n      def initialize(options)\n        self.total   = options[:total] if options[:total]\n        @width       = options.fetch(:width) { total }\n        @bar_format  = options.fetch(:bar_format, :classic)\n        self.incomplete = options.fetch(:incomplete) { fetch_char(@bar_format, :incomplete) }\n        self.complete = options.fetch(:complete) { fetch_char(@bar_format, :complete) }\n        self.unknown = options.fetch(:unknown) { fetch_char(@bar_format, :unknown) }\n        @head        = options.fetch(:head) { @complete || \"=\" }\n        @clear_head  = options.fetch(:clear_head, false)\n        @hide_cursor = options.fetch(:hide_cursor, false)\n        @clear       = options.fetch(:clear, false)\n        @output      = options.fetch(:output) { $stderr }\n        @frequency   = options.fetch(:frequency, 0) # 0Hz\n        @interval    = options.fetch(:interval, 1) # 1 sec\n        @inset       = options.fetch(:inset, 0)\n      end\n\n      # Set complete character(s)\n      #\n      # @param [String] value\n      #\n      # @api public\n      def complete=(value)\n        raise_if_empty(:complete, value)\n\n        @complete = value\n      end\n\n      # Set incomplete character(s)\n      #\n      # @param [String] value\n      #\n      # @api public\n      def incomplete=(value)\n        raise_if_empty(:incomplete, value)\n\n        @incomplete = value\n      end\n\n      # Set unknown character(s)\n      #\n      # @param [String] value\n      #\n      # @api public\n      def unknown=(value)\n        raise_if_empty(:unknown, value)\n\n        @unknown = value\n      end\n\n      # Set total and adjust width if unset\n      #\n      # @param [Integer,nil] value\n      #\n      # @api public\n      def total=(value)\n        @total = value\n        self.width = value if width.nil?\n      end\n\n      private\n\n      # Find bar char by type name and property\n      #\n      # @param [Symbol] name\n      # @param [Symbol] property\n      #\n      # @api private\n      def fetch_char(name, property)\n        if FORMATS.key?(name)\n          FORMATS[name][property]\n        else\n          raise ArgumentError, \"unsupported bar format: #{name.inspect}. \" \\\n                               \"Available formats are: \" \\\n                               \"#{FORMATS.keys.sort.map(&:inspect).join(', ')}\"\n        end\n      end\n\n      # Check whether a parameter's value is empty or not\n      #\n      # @raise [ArgumentError]\n      #\n      # @api private\n      def raise_if_empty(name, value)\n        return value unless value.to_s.empty?\n\n        raise ArgumentError, \"cannot provide an empty string for #{name.inspect}\"\n      end\n    end # Configuration\n  end # ProgressBar\nend # TTY\n"
  },
  {
    "path": "lib/tty/progressbar/converter.rb",
    "content": "# frozen_string_literal: true\n\nmodule TTY\n  class ProgressBar\n    # Responsible for converting values to different formats\n    #\n    # @api public\n    module Converter\n      HOURSECONDS = 3600\n\n      # Convert seconds to time notation\n      #\n      # @param [Numeric] seconds\n      #   the seconds to convert to time\n      #\n      # @api public\n      def to_time(seconds)\n        days = (seconds / (24 * HOURSECONDS).to_f).floor\n        seconds -= days * 24 * HOURSECONDS\n        hours = (seconds / HOURSECONDS.to_f).floor\n        seconds -= hours * HOURSECONDS\n        minutes = (seconds / 60).floor\n        seconds -= minutes * 60\n\n        if days > 0 # over 24 hours switch to days\n          format(\"%dd%2dh%2dm\", days, hours, minutes)\n        elsif hours > 0\n          format(\"%2dh%2dm\", hours, minutes)\n        elsif minutes > 0\n          format(\"%2dm%2ds\", minutes, seconds)\n        else\n          format(\"%2ds\", seconds)\n        end\n      end\n      module_function :to_time\n\n      # Convert seconds to set precision\n      #\n      # @param [Numeric] seconds\n      #   the seconds to convert\n      #\n      # @return [String]\n      #   the formatted result\n      #\n      # @api public\n      def to_seconds(seconds, precision: nil)\n        precision ||= (seconds < 1 && !seconds.zero?) ? 5 : 2\n        format \"%5.#{precision}f\", seconds\n      end\n      module_function :to_seconds\n\n      BYTE_UNITS = %w[b kb mb gb tb pb eb].freeze\n\n      # Convert value to bytes\n      #\n      # @param [Numeric] value\n      #   the value to convert to bytes\n      # @param [Integer] decimals\n      #   the number of decimals parts\n      # @param [String] separator\n      #   the separator to use for thousands in a number\n      # @param [String] unit_separator\n      #   the separtor to use between number and unit\n      #\n      # @return [String]\n      #\n      # @api public\n      def to_bytes(value, decimals: 2, separator: \".\", unit_separator: \"\")\n        base    = 1024\n        pattern = \"%.#{decimals}f\"\n\n        unit = BYTE_UNITS.find.with_index { |_, i| value < base**(i + 1) }\n\n        if value < base\n          formatted_value = value.to_i.to_s\n        else\n          value_to_size = value / (base**BYTE_UNITS.index(unit)).to_f\n          formatted_value = format(pattern, value_to_size)\n        end\n\n        formatted_value.gsub(/\\./, separator) + unit_separator + unit.to_s.upcase\n      end\n      module_function :to_bytes\n    end # Converter\n  end # ProgressBar\nend # TTY\n"
  },
  {
    "path": "lib/tty/progressbar/formats.rb",
    "content": "# frozen_string_literal: true\n\nmodule TTY\n  class ProgressBar\n    module Formats\n      FORMATS = {\n        arrow: { # ▸▸▸▸▸▹▹▹▹▹\n          complete: \"▸\",\n          incomplete: \"▹\",\n          unknown: \"◂▸\"\n        },\n        asterisk: { # ✱✱✱✱✱✳✳✳✳✳\n          complete: \"✱\",\n          incomplete: \"✳\",\n          unknown: \"✳✱✳\"\n        },\n        blade: { # ▰▰▰▰▰▱▱▱▱▱\n          complete: \"▰\",\n          incomplete: \"▱\",\n          unknown: \"▱▰▱\"\n        },\n        block: { # █████░░░░░\n          complete: \"█\",\n          incomplete: \"░\",\n          unknown: \"█\"\n        },\n        box: { # ■■■■■□□□□□\n          complete: \"■\",\n          incomplete: \"□\",\n          unknown: \"□■□\"\n        },\n        bracket: { # ❭❭❭❭❭❭❭❭❭❭\n         complete: \"❭\",\n         incomplete: \" \",\n         unknown: \"❬=❭\"\n        },\n        burger: { # ≡≡≡≡≡≡≡≡≡≡\n          complete: \"≡\",\n          incomplete: \" \",\n          unknown: \"<≡>\"\n        },\n        button: { # ⦿⦿⦿⦿⦿⦾⦾⦾⦾⦾\n          complete: \"⦿\",\n          incomplete: \"⦾\",\n          unknown: \"⦾⦿⦾\"\n        },\n        chevron: { # ››››››››››\n          complete: \"›\",\n          incomplete: \" \",\n          unknown: \"‹=›\"\n        },\n        circle: { # ●●●●●○○○○○\n          complete: \"●\",\n          incomplete: \"○\",\n          unknown: \"○●○\"\n        },\n        classic: { # ==========\n          complete: \"=\",\n          incomplete: \" \",\n          unknown: \"<=>\"\n        },\n        crate: { # ▣▣▣▣▣⬚⬚⬚⬚⬚\n          complete: \"▣\",\n          incomplete: \"⬚\",\n          unknown: \"⬚▣⬚\"\n        },\n        diamond: { # ♦♦♦♦♦♢♢♢♢♢\n          complete: \"♦\",\n          incomplete: \"♢\",\n          unknown: \"♢♦♢\"\n        },\n        dot: { # ･･････････\n          complete: \"･\",\n          incomplete: \" \",\n          unknown: \"･･･\"\n        },\n        heart: { # ♥♥♥♥♥♡♡♡♡♡\n          complete: \"♥\",\n          incomplete: \"♡\",\n          unknown: \"♡♥♡\"\n        },\n        rectangle: { # ▮▮▮▮▮▯▯▯▯▯\n          complete: \"▮\",\n          incomplete: \"▯\",\n          unknown: \"▯▮▯\"\n        },\n        square: { # ▪▪▪▪▪▫▫▫▫▫\n          complete: \"▪\",\n          incomplete: \"▫\",\n          unknown: \"▫▪▫\"\n        },\n        star: { # ★★★★★☆☆☆☆☆\n          complete: \"★\",\n          incomplete: \"☆\",\n          unknown: \"☆★☆\"\n        },\n        track: { # ▬▬▬▬▬═════\n          complete: \"▬\",\n          incomplete: \"═\",\n          unknown: \"═▬═\"\n        },\n        tread: { # ❱❱❱❱❱❱❱❱❱❱\n          complete: \"❱\",\n          incomplete: \" \",\n          unknown: \"❰=❱\"\n        },\n        triangle: { # ▶▶▶▶▶▷▷▷▷▷\n          complete: \"▶\",\n          incomplete: \"▷\",\n          unknown: \"◀▶\"\n        },\n        wave: { # ~~~~~_____\n          complete: \"~\",\n          incomplete: \"_\",\n          unknown: \"<~>\"\n        }\n      }.freeze\n    end # Formats\n  end # ProgressBar\nend # TTY\n"
  },
  {
    "path": "lib/tty/progressbar/formatter/bar.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative \"../formatter\"\n\nmodule TTY\n  class ProgressBar\n    # Used by {Pipeline} to format bar\n    #\n    # @api private\n    class BarFormatter\n      include TTY::ProgressBar::Formatter[/:bar/i.freeze]\n\n      # Format :bar token\n      #\n      # @param [String] value\n      #  the value being formatted\n      #\n      # @api public\n      def call(value)\n        without_bar = value.gsub(/:bar/, \"\")\n        available_space = [0, ProgressBar.max_columns -\n                              ProgressBar.display_columns(without_bar) -\n                              @progress.inset].max\n        width = [@progress.width.to_i, available_space].min\n\n        # When we don't know the total progress, use either user\n        # defined width or rely on terminal width detection\n        if @progress.indeterminate?\n          width = available_space if width.zero?\n\n          format_indeterminate(value, width)\n        else\n          format_determinate(value, width)\n        end\n      end\n\n      private\n\n      # @api private\n      def format_indeterminate(value, width)\n        buffer = []\n        possible_width = width\n        unknown_char_length    = ProgressBar.display_columns(@progress.unknown)\n        complete_char_length   = ProgressBar.display_columns(@progress.complete)\n        incomplete_char_length = ProgressBar.display_columns(@progress.incomplete)\n        head_char_length       = ProgressBar.display_columns(@progress.head)\n\n        possible_width -= unknown_char_length\n        max_char_length = [complete_char_length, incomplete_char_length,\n                           head_char_length].max\n        # figure out how many unicode chars would fit normally\n        # when the bar has total to prevent resizing\n        possible_width = (possible_width / max_char_length) * max_char_length\n        complete = (possible_width * @progress.ratio).round\n        incomplete = possible_width - complete\n\n        buffer << \" \" * complete\n        buffer << @progress.unknown\n        buffer << \" \" * incomplete\n\n        value.gsub(matcher, buffer.join)\n      end\n\n      # @api private\n      def format_determinate(value, width)\n        complete_bar_length    = (width * @progress.ratio).round\n        complete_char_length   = ProgressBar.display_columns(@progress.complete)\n        incomplete_char_length = ProgressBar.display_columns(@progress.incomplete)\n        head_char_length       = ProgressBar.display_columns(@progress.head)\n\n        # division by char length only when unicode chars are used\n        # otherwise it has no effect on regular ascii chars\n        complete_items = [\n          complete_bar_length / complete_char_length,\n          # or see how many incomplete (unicode) items fit\n          (complete_bar_length / incomplete_char_length) * incomplete_char_length\n        ].min\n\n        complete_width = complete_items * complete_char_length\n        incomplete_width = width - complete_width\n        incomplete_items = [\n          incomplete_width / incomplete_char_length,\n          # or see how many complete (unicode) items fit\n          (incomplete_width / complete_char_length) * complete_char_length\n        ].min\n\n        complete   = Array.new(complete_items, @progress.complete)\n        incomplete = Array.new(incomplete_items, @progress.incomplete)\n\n        if complete_items > 0 && head_char_length > 0 &&\n           (incomplete_items > 0 || incomplete_items.zero? && !@progress.clear_head)\n          # see how many head chars per complete char\n          times = (head_char_length / complete_char_length.to_f).round\n          if complete_items < times # not enough complete chars to fit\n            incomplete.pop(times - complete_items)\n          end\n          complete.pop(times)\n          extra_space = \" \" * (times * complete_char_length - head_char_length)\n          complete << \"#{@progress.head}#{extra_space}\"\n        end\n\n        value.gsub(matcher, \"#{complete.join}#{incomplete.join}\")\n      end\n    end # BarFormatter\n  end #  ProgressBar\nend # TTY\n"
  },
  {
    "path": "lib/tty/progressbar/formatter/byte_rate.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative \"../converter\"\nrequire_relative \"../formatter\"\n\nmodule TTY\n  class ProgressBar\n    # Used by {Pipeline} to format :byte_rate token\n    #\n    # @api private\n    class ByteRateFormatter\n      include TTY::ProgressBar::Formatter[/:byte_rate/i.freeze]\n\n      # Format :byte_rate token\n      #\n      # @param [String] value\n      #  the value to format\n      #\n      # @api public\n      def call(value)\n        formatted = Converter.to_bytes(@progress.rate)\n        value.gsub(matcher, formatted)\n      end\n    end # ByteRateFormatter\n  end # ProgressBar\nend # TTY\n"
  },
  {
    "path": "lib/tty/progressbar/formatter/current.rb",
    "content": "# frozen_string_literal: true\n\nmodule TTY\n  class ProgressBar\n    # Used by {Pipeline} to format :current token\n    #\n    # @api private\n    class CurrentFormatter\n      include TTY::ProgressBar::Formatter[/:current\\b/i.freeze]\n\n      # Format :current token\n      #\n      # @param [String] value\n      #  the value to format\n      #\n      # @api public\n      def call(value)\n        value.gsub(matcher, @progress.current.to_s)\n      end\n    end # CurrentFormatter\n  end # ProgressBar\nend # TTY\n"
  },
  {
    "path": "lib/tty/progressbar/formatter/current_byte.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative \"../converter\"\nrequire_relative \"../formatter\"\n\nmodule TTY\n  class ProgressBar\n    # Used by {Pipeline} to format :byte and :current_byte tokens\n    #\n    # @api private\n    class ByteFormatter\n      include TTY::ProgressBar::Formatter[/(:current_byte|:byte)\\b/i.freeze]\n\n      # Format :current_byte token\n      #\n      # @param [String] value\n      #  the value to format\n      #\n      # @api public\n      def call(value)\n        bytes = Converter.to_bytes(@progress.current)\n        value.gsub(matcher, bytes)\n      end\n    end # ByteFormatter\n  end # ProgressBar\nend # TTY\n"
  },
  {
    "path": "lib/tty/progressbar/formatter/elapsed.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative \"../converter\"\nrequire_relative \"../formatter\"\n\nmodule TTY\n  class ProgressBar\n    # Used by {Pipeline} to format :elapsed token\n    #\n    # @api private\n    class ElapsedFormatter\n      include TTY::ProgressBar::Formatter[/:elapsed/.freeze]\n\n      # Format :elapsed token\n      #\n      # @param [String] value\n      #  the value to format\n      #\n      # @api public\n      def call(value)\n        value.gsub(matcher, Converter.to_time(@progress.elapsed_time))\n      end\n    end # ElapsedFormatter\n  end # ProgressBar\nend # TTY\n"
  },
  {
    "path": "lib/tty/progressbar/formatter/estimated.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative \"../converter\"\nrequire_relative \"../formatter\"\n\nmodule TTY\n  class ProgressBar\n    # Used by {Pipeline} to format :eta token\n    #\n    # @api private\n    class EstimatedFormatter\n      include TTY::ProgressBar::Formatter[/:eta/.freeze]\n\n      # Format :eta token\n      #\n      # @param [String] value\n      #  the value to format\n      #\n      # @api public\n      def call(value)\n        if @progress.indeterminate? ||\n           (@progress.elapsed_time.zero? && @progress.ratio.zero?)\n          return value.gsub(matcher, \"--s\")\n        end\n\n        elapsed = @progress.elapsed_time\n        estimated = @progress.ratio.zero? ? 0.0 : (elapsed / @progress.ratio).to_f\n        estimated -= elapsed\n        estimated = 0.0 if estimated < 0\n        value.gsub(matcher, Converter.to_time(estimated))\n      end\n    end # EstimatedFormatter\n  end # ProgressBar\nend # TTY\n"
  },
  {
    "path": "lib/tty/progressbar/formatter/estimated_time.rb",
    "content": "# frozen_string_literal: true\n\nmodule TTY\n  class ProgressBar\n    # Used by {Pipeline} to format :eta_time token\n    #\n    # @api private\n    class EstimatedTimeFormatter\n      include TTY::ProgressBar::Formatter[/:eta_time/.freeze]\n\n      # Format :eta_time token\n      #\n      # @param [String] value\n      #   the value to format\n      #\n      # @api public\n      def call(value)\n        if @progress.indeterminate? ||\n           (@progress.elapsed_time.zero? && @progress.ratio.zero?)\n          return value.gsub(matcher, \"--:--:--\")\n        end\n\n        elapsed = @progress.elapsed_time\n        estimated = @progress.ratio.zero? ? 0.0 : (elapsed / @progress.ratio).to_f\n        estimated -= elapsed\n        estimated = 0.0 if estimated < 0\n\n        time_format = if estimated >= 86_400 # longer than a day\n                        \"%Y-%m-%d %H:%M:%S\"\n                      else\n                        \"%H:%M:%S\"\n                      end\n        completion_time = Time.now + estimated\n        eta_time = completion_time.strftime(time_format)\n        value.gsub(matcher, eta_time)\n      end\n    end # EstimatedTimeFormatter\n  end # ProgressBar\nend # TTY\n"
  },
  {
    "path": "lib/tty/progressbar/formatter/mean_byte.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative \"../converter\"\nrequire_relative \"../formatter\"\n\nmodule TTY\n  class ProgressBar\n    # Used by {Pipeline} to format :mean_byte token\n    #\n    # @api private\n    class MeanByteFormatter\n      include TTY::ProgressBar::Formatter[/:mean_byte/i.freeze]\n\n      # Format :mean_byte token\n      #\n      # @param [String] value\n      #  the value being formatted\n      #\n      # @api public\n      def call(value)\n        formatted = Converter.to_bytes(@progress.mean_rate)\n        value.gsub(matcher, formatted)\n      end\n    end # MeanByteFormatter\n  end # ProgressBar\nend # TTY\n"
  },
  {
    "path": "lib/tty/progressbar/formatter/mean_rate.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative \"../converter\"\nrequire_relative \"../formatter\"\n\nmodule TTY\n  class ProgressBar\n    # Used by {Pipeline} to format :mean_rate token\n    #\n    # @api private\n    class MeanRateFormatter\n      include TTY::ProgressBar::Formatter[/:mean_rate/i.freeze]\n\n      # Format :mean_rate token\n      #\n      # @param [String] value\n      #  the value being formatted\n      #\n      # @api public\n      def call(value)\n        formatted = Converter.to_seconds(@progress.mean_rate)\n        value.gsub(matcher, formatted)\n      end\n    end # MeanRateFormatter\n  end # ProgressBar\nend # TTY\n"
  },
  {
    "path": "lib/tty/progressbar/formatter/percent.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative \"../formatter\"\n\nmodule TTY\n  class ProgressBar\n    # Used by {Pipeline} to format :percent token\n    #\n    # @api private\n    class PercentFormatter\n      include TTY::ProgressBar::Formatter[/:percent\\b/.freeze]\n\n      # Format :percent token\n      #\n      # @param [String] value\n      #  the value to format\n      #\n      # @api public\n      def call(value)\n        percent = @progress.width == 0 ? 100 : (@progress.ratio * 100).to_i\n        display = @progress.indeterminate? ? \"-\" : percent.to_s\n        value.gsub(matcher, \"#{display}%\")\n      end\n    end # PercentFormatter\n  end # ProgressBar\nend # TTY\n"
  },
  {
    "path": "lib/tty/progressbar/formatter/rate.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative \"../converter\"\nrequire_relative \"../formatter\"\n\nmodule TTY\n  class ProgressBar\n    # Used by {Pipeline} to format :rate token\n    #\n    # @api private\n    class RateFormatter\n      include TTY::ProgressBar::Formatter[/:rate/i.freeze]\n\n      # Format :rate token\n      #\n      # @param [String] value\n      #  the value being formatted\n      #\n      # @api public\n      def call(value)\n        formatted = Converter.to_seconds(@progress.rate)\n        value.gsub(matcher, formatted)\n      end\n    end # RateFormatter\n  end # ProgressBar\nend # TTY\n"
  },
  {
    "path": "lib/tty/progressbar/formatter/total.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative \"../formatter\"\n\nmodule TTY\n  class ProgressBar\n    # Used by {Pipeline} to format :total token\n    #\n    # @api private\n    class TotalFormatter\n      include TTY::ProgressBar::Formatter[/:total\\b/i.freeze]\n\n      # Format :total token\n      #\n      # @param [String] value\n      #  the value to format\n      #\n      # @api public\n      def call(value)\n        display = @progress.indeterminate? ? \"-\" : @progress.total.to_s\n        value.gsub(matcher, display)\n      end\n    end # TotalFormatter\n  end # ProgressBar\nend # TTY\n"
  },
  {
    "path": "lib/tty/progressbar/formatter/total_byte.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative \"../converter\"\nrequire_relative \"../formatter\"\n\nmodule TTY\n  class ProgressBar\n    # Used by {Pipeline} to format :total_byte token\n    #\n    # @api private\n    class TotalByteFormatter\n      include TTY::ProgressBar::Formatter[/:total_byte/i.freeze]\n\n      # Format :total_byte token\n      #\n      # @param [String] value\n      #  the value to format\n      #\n      # @api public\n      def call(value)\n        bytes = if @progress.indeterminate?\n                  \"-B\"\n                else\n                  Converter.to_bytes(@progress.total)\n                end\n        value.gsub(matcher, bytes)\n      end\n    end # TotalByteFormatter\n  end # ProgressBar\nend # TTY\n"
  },
  {
    "path": "lib/tty/progressbar/formatter.rb",
    "content": "# frozen_string_literal: true\n\nmodule TTY\n  class ProgressBar\n    class Formatter < ::Module\n      # A helper for declaring a matching token pattern\n      #\n      # @api public\n      def self.[](token_match)\n        new(token_match)\n      end\n\n      # Initialize this module with token matching pattern\n      #\n      # @param [Regexp] token_match\n      #   the token matching pattern\n      #\n      # @api public\n      def initialize(token_match)\n        pattern = token_match\n\n        module_eval do\n          define_method(:initialize) do |progress|\n            @progress = progress\n          end\n\n          define_method(:matcher) { pattern }\n          define_method(:progress) { @progress }\n\n          # Determines whether this formatter is applied or not.\n          #\n          # @param [Object] value\n          #\n          # @return [Boolean]\n          #\n          # @api private\n          define_method(:matches?) do |value|\n            !!(value.to_s =~ pattern)\n          end\n        end\n      end\n    end # Formatter\n  end # ProgressBar\nend # TTY\n"
  },
  {
    "path": "lib/tty/progressbar/formatters.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"forwardable\"\n\nrequire_relative \"pipeline\"\n\nrequire_relative \"formatter/bar\"\nrequire_relative \"formatter/current\"\nrequire_relative \"formatter/current_byte\"\nrequire_relative \"formatter/elapsed\"\nrequire_relative \"formatter/estimated\"\nrequire_relative \"formatter/estimated_time\"\nrequire_relative \"formatter/percent\"\nrequire_relative \"formatter/rate\"\nrequire_relative \"formatter/byte_rate\"\nrequire_relative \"formatter/mean_rate\"\nrequire_relative \"formatter/mean_byte\"\nrequire_relative \"formatter/total\"\nrequire_relative \"formatter/total_byte\"\n\nmodule TTY\n  class ProgressBar\n    class Formatters\n      extend Forwardable\n\n      def_delegators :@pipeline, :decorate, :use\n\n      # @api private\n      def initialize(pipeline = nil)\n        @pipeline = pipeline || TTY::ProgressBar::Pipeline.new\n      end\n\n      # Prepare default pipeline formatters\n      #\n      # @api private\n      def load(progress)\n        @pipeline.use TTY::ProgressBar::CurrentFormatter.new(progress)\n        @pipeline.use TTY::ProgressBar::TotalFormatter.new(progress)\n        @pipeline.use TTY::ProgressBar::TotalByteFormatter.new(progress)\n        @pipeline.use TTY::ProgressBar::ElapsedFormatter.new(progress)\n        @pipeline.use TTY::ProgressBar::EstimatedTimeFormatter.new(progress)\n        @pipeline.use TTY::ProgressBar::EstimatedFormatter.new(progress)\n        @pipeline.use TTY::ProgressBar::PercentFormatter.new(progress)\n        @pipeline.use TTY::ProgressBar::ByteFormatter.new(progress)\n        @pipeline.use TTY::ProgressBar::ByteRateFormatter.new(progress)\n        @pipeline.use TTY::ProgressBar::RateFormatter.new(progress)\n        @pipeline.use TTY::ProgressBar::MeanRateFormatter.new(progress)\n        @pipeline.use TTY::ProgressBar::MeanByteFormatter.new(progress)\n        @pipeline.use TTY::ProgressBar::BarFormatter.new(progress)\n      end\n    end # Formatters\n  end # ProgressBar\nend # TTY\n"
  },
  {
    "path": "lib/tty/progressbar/meter.rb",
    "content": "# frozen_string_literal: true\n\nmodule TTY\n  class ProgressBar\n    # Used by {ProgressBar} to measure progress rate per interval\n    # by default 1s\n    #\n    # @api private\n    class Meter\n      # Create Meter\n      #\n      # @param [Integer] interval\n      #   the interval for measurement samples\n      #\n      # @api private\n      def initialize(interval)\n        @interval = interval || 1 # 1 sec\n        start\n      end\n\n      # Start sampling timer\n      #\n      # @api public\n      def start\n        @start_time = Time.now\n        @current    = 0\n        @samples    = [[@start_time, 0]]\n        @rates      = []\n        @start_time\n      end\n\n      # Update meter with value\n      #\n      # @param [Time] at\n      #   the time of the sampling\n      #\n      # @param [Integer] value\n      #   the current value of progress\n      #\n      # @api public\n      def sample(at, value)\n        @current += value\n        prune_samples(at)\n        @samples << [at, @current]\n        save_rate(at)\n      end\n\n      # Remove samples that are obsolete\n      #\n      # @api private\n      def prune_samples(at)\n        cutoff = at - @interval\n        while @samples.size > 1 && (@samples.first.first < cutoff)\n          @samples.shift\n        end\n      end\n\n      # If we crossed a period boundary since @start_time,\n      # save the rate for {#rates}\n      #\n      # @api private\n      def save_rate(at)\n        period_index = ((at - @start_time) / @interval).floor\n        while period_index > @rates.size\n          @rates << rate\n        end\n      end\n\n      # The current rate of sampling for a given interval\n      #\n      # @return [Number]\n      #   the current rate in decimal or 0 if cannot be determined\n      #\n      # @api public\n      def rate\n        first_at, first_value = @samples.first\n        last_at,  last_value  = @samples.last\n        if first_at == last_at\n          0\n        else\n          (last_value - first_value) / (last_at - first_at)\n        end\n      end\n\n      # Group all rates per interval\n      #\n      # @api public\n      def rates\n        @rates + [rate]\n      end\n\n      # The mean rate of all the sampled rates\n      #\n      # @return [Number]\n      #   the mean rate\n      #\n      # @api public\n      def mean_rate\n        last_at, last_value = @samples.last\n        if last_at == @start_time\n          0\n        else\n          last_value / (last_at - @start_time)\n        end\n      end\n      alias avg_rate mean_rate\n\n      # Reset the meter by clearing out it's metrics\n      #\n      # @api public\n      def clear\n        start\n      end\n    end # Meter\n  end # ProgressBar\nend # TTY\n"
  },
  {
    "path": "lib/tty/progressbar/multi.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"forwardable\"\nrequire \"monitor\"\n\nrequire_relative \"../progressbar\"\n\nmodule TTY\n  class ProgressBar\n    # Used for managing multiple terminal progress bars\n    #\n    # @api public\n    class Multi\n      include Enumerable\n      include MonitorMixin\n\n      extend Forwardable\n\n      def_delegators :@bars, :each, :empty?, :length, :[]\n\n      def_delegators :@top_bar, :width, :width=\n\n      DEFAULT_INSET = {\n        top:    Gem.win_platform? ? \"+ \"   : \"\\u250c \",\n        middle: Gem.win_platform? ? \"|-- \" : \"\\u251c\\u2500\\u2500 \",\n        bottom: Gem.win_platform? ? \"|__ \" : \"\\u2514\\u2500\\u2500 \"\n      }.freeze\n\n      # Number of currently occupied rows in terminal display\n      attr_reader :rows\n\n      # Create a multibar\n      #\n      # @example\n      #   bars = TTY::ProgressBar::Multi.new\n      #\n      # @example\n      #   bars = TTY::ProgressBar::Multi.new(\"main [:bar]\")\n      #\n      # @param [String] format\n      #   the formatting string to display this bar\n      #\n      # @param [Hash] options\n      #\n      # @api public\n      def initialize(*args)\n        super()\n        @options = args.last.is_a?(::Hash) ? args.pop : {}\n        format = args.empty? ? nil : args.pop\n        @inset_opts = @options.delete(:style) { DEFAULT_INSET }\n        @bars = []\n        @rows = 0\n        @top_bar = nil\n        @top_bar = register(format, observable: false) if format\n\n        @width = @options[:width]\n        @top_bar.update(width: @width) if @top_bar && @width\n\n        @callbacks = {\n          progress: [],\n          stopped: [],\n          paused: [],\n          done: []\n        }\n      end\n\n      # Register a new progress bar\n      #\n      # @param [String] format\n      #   the formatting string to display the bar\n      #\n      # @api public\n      def register(format, options = {})\n        observable = options.delete(:observable) { true }\n        bar = TTY::ProgressBar.new(format, @options.merge(options))\n\n        synchronize do\n          bar.attach_to(self)\n          @bars << bar\n          observe(bar) if observable\n          if @top_bar\n            @top_bar.update(total: total)\n            @top_bar.resume if @top_bar.done?\n            @top_bar.update(width: total) unless @width\n          end\n        end\n\n        bar\n      end\n\n      # Increase row count\n      #\n      # @api public\n      def next_row\n        synchronize do\n          @rows += 1\n        end\n      end\n\n      # Observe a bar for emitted events\n      #\n      # @param [TTY::ProgressBar] bar\n      #   the bar to observe for events\n      #\n      # @api private\n      def observe(bar)\n        bar.on(:progress, &progress_handler)\n           .on(:done) { emit(:done) if complete? }\n           .on(:stopped) { emit(:stopped) if stopped? }\n           .on(:paused) { emit(:paused) if paused? }\n      end\n\n      # Handle the progress event\n      #\n      # @api private\n      def progress_handler\n        ->(progress) do\n          @top_bar.advance(progress) if @top_bar\n          emit(:progress, progress)\n        end\n      end\n\n      # Get the top level bar if it exists\n      #\n      # @api public\n      def top_bar\n        raise \"No top level progress bar\" unless @top_bar\n\n        @top_bar\n      end\n\n      def start\n        raise \"No top level progress bar\" unless @top_bar\n\n        @top_bar.start\n      end\n\n      # Calculate total maximum progress of all bars\n      #\n      # @return [Integer]\n      #\n      # @api public\n      def total\n        synchronize do\n          (@bars - [@top_bar]).map(&:total).compact.reduce(&:+)\n        end\n      end\n\n      # Calculate total current progress of all bars\n      #\n      # @return [Integer]\n      #\n      # @api public\n      def current\n        synchronize do\n          (@bars - [@top_bar]).map(&:current).reduce(&:+)\n        end\n      end\n\n      # Check if all progress bars are complete\n      #\n      # @return [Boolean]\n      #\n      # @api public\n      def complete?\n        synchronize do\n          (@bars - [@top_bar]).all?(&:complete?)\n        end\n      end\n\n      # Check if all of the registered progress bars is stopped\n      #\n      # @return [Boolean]\n      #\n      # @api public\n      def stopped?\n        synchronize do\n          (@bars - [@top_bar]).all?(&:stopped?)\n        end\n      end\n\n      # Check if all bars are stopped or finished\n      #\n      # @return [Boolean]\n      #\n      # @api public\n      def done?\n        synchronize do\n          (@bars - [@top_bar]).all?(&:done?)\n        end\n      end\n\n      # Check if all bars are paused\n      #\n      # @return [Boolean]\n      #\n      # @api public\n      def paused?\n        synchronize do\n          (@bars - [@top_bar]).all?(&:paused?)\n        end\n      end\n\n      # Stop all progress bars\n      #\n      # @api public\n      def stop\n        @bars.each(&:stop)\n      end\n\n      # Finish all progress bars\n      #\n      # @api public\n      def finish\n        @bars.each(&:finish)\n      end\n\n      # Pause all progress bars\n      #\n      # @api public\n      def pause\n        @bars.each(&:pause)\n      end\n\n      # Resume all progress bars\n      #\n      # @api public\n      def resume\n        @bars.each(&:resume)\n      end\n\n      # Find the number of characters to move into the line\n      # before printing the bar\n      #\n      # @param [TTY::ProgressBar] bar\n      #   the progress bar for which line inset is calculated\n      #\n      # @return [String]\n      #   the inset\n      #\n      # @api public\n      def line_inset(bar)\n        return \"\" if @top_bar.nil?\n\n        case bar.row\n        when @top_bar.row\n          @inset_opts[:top]\n        when rows\n          @inset_opts[:bottom]\n        else\n          @inset_opts[:middle]\n        end\n      end\n\n      # Listen on event\n      #\n      # @param [Symbol] name\n      #   the event name to listen on\n      #\n      # @api public\n      def on(name, &callback)\n        unless @callbacks.key?(name)\n          raise ArgumentError, \"The event #{name} does not exist. \" \\\n                               \"Use :progress, :stopped, :paused or \" \\\n                               \":done instead\"\n        end\n        @callbacks[name] << callback\n        self\n      end\n\n      private\n\n      # Fire an event by name\n      #\n      # @api private\n      def emit(name, *args)\n        @callbacks[name].each do |callback|\n          callback.(*args)\n        end\n      end\n    end # Multi\n  end # ProgressBar\nend # TTY\n"
  },
  {
    "path": "lib/tty/progressbar/pipeline.rb",
    "content": "# frozen_string_literal: true\n\nmodule TTY\n  class ProgressBar\n    # Used by {ProgressBar} to decorate format string\n    #\n    # @api private\n    class Pipeline\n      include Enumerable\n\n      # Create formatting pipeline\n      #\n      # @api private\n      def initialize(formatters = [])\n        @formatters = formatters\n        freeze\n      end\n\n      # Add a new formatter\n      #\n      # @example\n      #   use(TTY::ProgressBar::TotalFormatter.new(progress_bar))\n      #\n      # @api public\n      def use(formatter)\n        formatters << formatter\n      end\n\n      # Decorate the tokenized string with actual values\n      #\n      # @example\n      #   decorate(\"[:bar] :current :elapsed\")\n      #\n      # @param [String] tokenized\n      #   the string with tokens\n      #\n      # @return [nil]\n      #\n      # @api private\n      def decorate(tokenized)\n        base = tokenized.dup\n        formatters.inject(base) do |formatted, formatter|\n          if formatter.respond_to?(:matches?) && formatter.matches?(formatted)\n            formatter.(formatted)\n          else\n            formatted\n          end\n        end\n      end\n\n      # Iterate over formatters\n      #\n      # @api public\n      def each(&block)\n        formatters.each(&block)\n      end\n\n      protected\n\n      attr_reader :formatters\n    end # Pipeline\n  end # ProgressBar\nend # TTY\n"
  },
  {
    "path": "lib/tty/progressbar/timer.rb",
    "content": "# frozen_string_literal: true\n\nmodule TTY\n  class ProgressBar\n    # Used to measure the elapsed time for multiple time intervals\n    #\n    # @api private\n    class Timer\n      attr_reader :start_time\n\n      # Create Timer\n      #\n      # @api private\n      def initialize\n        reset\n      end\n\n      # Reset the start time to nil and elapsed time to zero\n      #\n      # @api public\n      def reset\n        @running = false\n        @offset = 0\n        @start_time = nil\n      end\n\n      # Check whether or not the timer is running\n      #\n      # @return [Boolean]\n      #\n      # @api public\n      def running?\n        @running\n      end\n\n      # Total elapsed time\n      #\n      # @return [Float]\n      #   the elapsed time in seconds\n      #\n      # @api public\n      def elapsed_time\n        if running?\n          elapsed_until_now + @offset\n        else\n          @offset\n        end\n      end\n\n      # Measure current time interval\n      #\n      # @api public\n      def elapsed_until_now\n        time_so_far = Time.now - @start_time\n        # protect against negative time drifting\n        time_so_far > 0 ? time_so_far : 0\n      end\n\n      # Start measuring elapsed time for a new interval\n      #\n      # @return [Time]\n      #   return the start time\n      #\n      # @api public\n      def start\n        return @start_time if running?\n\n        @running = true\n        @start_time = Time.now\n      end\n\n      # Stop measuring elapsed time for the current interval\n      #\n      # @return [Float]\n      #   return elapsed time for the stopped interval\n      #\n      # @api public\n      def stop\n        return 0 unless running?\n\n        interval = elapsed_until_now\n        @offset += interval\n        @running = false\n        @start_time = nil\n        interval\n      end\n    end # Timer\n  end # ProgressBar\nend # TTY\n"
  },
  {
    "path": "lib/tty/progressbar/version.rb",
    "content": "# 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",
    "content": "# frozen_string_literal: true\n\nrequire \"io/console\"\nrequire \"forwardable\"\nrequire \"monitor\"\nrequire \"tty-cursor\"\nrequire \"tty-screen\"\nrequire \"strings-ansi\"\nrequire \"unicode/display_width\"\n\nrequire_relative \"progressbar/timer\"\nrequire_relative \"progressbar/configuration\"\nrequire_relative \"progressbar/formatters\"\nrequire_relative \"progressbar/meter\"\nrequire_relative \"progressbar/version\"\n\nmodule TTY\n  # Used for creating terminal progress bar\n  #\n  # @api public\n  class ProgressBar\n    extend Forwardable\n    include MonitorMixin\n\n    ECMA_CSI = \"\\e[\"\n\n    NEWLINE = \"\\n\"\n\n    CURSOR_LOCK = Monitor.new\n\n    attr_accessor :format\n\n    attr_reader :current\n\n    attr_reader :row\n\n    def_delegators :@configuration, :total, :width, :complete, :incomplete,\n                   :head, :clear_head, :hide_cursor, :clear, :output,\n                   :frequency, :interval, :inset, :width=, :unknown, :bar_format\n\n    def_delegators :@meter, :rate, :mean_rate\n\n    def_delegators :@timer, :elapsed_time, :start_time\n\n    # Determine terminal width\n    #\n    # @return [Integer]\n    #\n    # @api public\n    def self.max_columns\n      TTY::Screen.width\n    end\n\n    # Determine the monospace display width of a string\n    #\n    # @param [String] value\n    #   the value to determine width of\n    #\n    # @return [Integer]\n    #\n    # @api public\n    def self.display_columns(value)\n      Unicode::DisplayWidth.of(Strings::ANSI.sanitize(value))\n    end\n\n    # Create progress bar\n    #\n    # @example\n    #   bar = TTY::Progressbar.new\n    #   bar.configure do |config|\n    #     config.total = 20\n    #   end\n    #\n    # @param [String] format\n    #   the tokenized string that displays the output\n    #\n    # @param [Hash] options\n    # @option options [Numeric] :total\n    #   the total number of steps to completion\n    # @option options [Numeric] :width\n    #   the maximum width for the progress bar except all formatting tokens\n    # @option option [String] :complete\n    #   the complete character in progress animation\n    # @option options [String] :incomplete\n    #   the incomplete character in progress animation\n    # @option options [String] :head\n    #   the head character, defaults to complete\n    # @option options [String] :unknown\n    #   the unknown character for indeterminate progress animation\n    # @option options [Boolean] :bar_format\n    #   the preconfigured bar format name, defaults to :classic\n    # @option options [Object] :output\n    #   the object that responds to print call, defaults to stderr\n    # @option options [Number] :frequency\n    #   the frequency with which to display a progress bar per second\n    # @option options [Number] :interval\n    #   the time interval for sampling of speed measurement, defaults to 1 second\n    # @option options [Boolean] :hide_cursor\n    #   whether or not to hide the cursor, defaults to false\n    # @option options [Boolean] :clear\n    #   whether or not to clear the progress line, defaults to false\n    # @option options [Boolean] :clear_head\n    #   whether or not to replace head character with complete, defaults to false\n    #\n    # @api public\n    def initialize(format, options = {})\n      super()\n      @format = format\n      if format.is_a?(Hash)\n        raise ArgumentError, \"Expected bar formatting string, \" \\\n                             \"got `#{format}` instead.\"\n      end\n      @configuration = TTY::ProgressBar::Configuration.new(options)\n      yield @configuration if block_given?\n\n      @formatters = TTY::ProgressBar::Formatters.new\n      @meter = TTY::ProgressBar::Meter.new(interval)\n      @timer = TTY::ProgressBar::Timer.new\n      @callbacks = Hash.new { |h, k| h[k] = [] }\n\n      @formatters.load(self)\n      reset\n\n      @first_render = true\n      @multibar = nil\n      @row = nil\n    end\n\n    # Reset progress to default configuration\n    #\n    # @api public\n    def reset\n      @width             = 0 if indeterminate?\n      @render_period     = frequency == 0 ? 0 : 1.0 / frequency\n      @current           = 0\n      @unknown           = 0\n      @last_render_time  = Time.now\n      @last_render_width = 0\n      @done              = false\n      @stopped           = false\n      @paused            = false\n      @tokens            = {}\n\n      @meter.clear\n      @timer.reset\n    end\n\n    # Access instance configuration\n    #\n    # @api public\n    def configure\n      yield @configuration\n    end\n\n    # Check if progress can be determined or not\n    #\n    # @return [Boolean]\n    #\n    # @api public\n    def indeterminate?\n      total.nil?\n    end\n\n    # Attach this bar to multi bar\n    #\n    # @param [TTY::ProgressBar::Multi] multibar\n    #   the multibar under which this bar is registered\n    #\n    # @api private\n    def attach_to(multibar)\n      @multibar = multibar\n    end\n\n    # Use custom token formatter\n    #\n    # @param [Object] formatter_class\n    #   the formatter class to add to formatting pipeline\n    #\n    # @api public\n    def use(formatter_class)\n      unless formatter_class.is_a?(Class)\n        raise ArgumentError, \"Formatter needs to be a class\"\n      end\n\n      @formatters.use(formatter_class.new(self))\n    end\n\n    # Start progression by drawing bar and setting time\n    #\n    # @api public\n    def start\n      synchronize do\n        @timer.start\n        @meter.start\n      end\n\n      advance(0)\n    end\n\n    # Advance the progress bar\n    #\n    # @param [Object|Number] progress\n    #\n    # @api public\n    def advance(progress = 1, tokens = {})\n      return if done?\n\n      synchronize do\n        emit(:progress, progress)\n        if progress.respond_to?(:to_hash)\n          tokens, progress = progress, 1\n        end\n        @timer.start\n        @current += progress\n        # When progress is unknown increase by 2% up to max 200%, after\n        # that reset back to 0%\n        @unknown += 2 if indeterminate?\n        @unknown = 0 if @unknown > 199\n        @tokens = tokens\n        @meter.sample(Time.now, progress)\n\n        if !indeterminate? && @current >= total\n          finish && return\n        end\n\n        return if (Time.now - @last_render_time) < @render_period\n\n        render\n      end\n    end\n\n    # Iterate over collection either yielding computation to block\n    # or provided Enumerator. If the bar's `total` was not set,\n    # it would be taken from `collection.count`, otherwise previously\n    # set `total` would be used. This allows using the progressbar\n    # with infinite, lazy, or slowly-calculated enumerators.\n    #\n    # @note\n    #   If `total` is set, iteration will NOT stop after this number of\n    #   iterations, only when provided Enumerable is finished. It may\n    #   be convenient in \"unsure number of iterations\" situations\n    #   (like downloading in chunks, when server may eventually send\n    #   more chunks than predicted), but be careful to not pass infinite\n    #   enumerators without previously doing `.take(some_finite_number)`\n    #   on them.\n    #\n    # @example\n    #   bar.iterate(30.times) { ... }\n    #\n    # @param [Enumerable] collection\n    #   the collection to iterate over\n    #\n    # @param [Integer] progress\n    #   the amount to move progress bar by\n    #\n    # @return [Enumerator]\n    #\n    # @api public\n    def iterate(collection, progress = 1, &block)\n      update(total: collection.count * progress) unless total\n      progress_enum = Enumerator.new do |iter|\n        collection.each do |elem|\n          advance(progress)\n          iter.yield(elem)\n        end\n      end\n      block_given? ? progress_enum.each(&block) : progress_enum\n    end\n\n    # Update configuration options for this bar\n    #\n    # @param [Hash[Symbol]] options\n    #   the configuration options to update\n    #\n    # @api public\n    def update(options = {})\n      synchronize do\n        options.each do |name, val|\n          if @configuration.respond_to?(\"#{name}=\")\n            @configuration.public_send(\"#{name}=\", val)\n          end\n        end\n      end\n    end\n\n    # Advance the progress bar to the updated value\n    #\n    # @param [Number] value\n    #   the desired value to updated to\n    #\n    # @api public\n    def current=(value)\n      unless value.is_a?(Numeric)\n        raise ArgumentError, \"Expected a numeric value, \" \\\n                             \"got #{value.inspect} instead.\"\n      end\n\n      value = [0, [value, total].compact.min].max\n      advance(value - @current)\n    end\n\n    # Advance the progress bar to an exact ratio.\n    # The target value is set to the closest available value.\n    #\n    # @param [Float] value\n    #   the ratio between 0 and 1 inclusive\n    #\n    # @api public\n    def ratio=(value)\n      target = (value * total).floor\n      advance(target - @current)\n    end\n\n    # Ratio of completed over total steps\n    #\n    # When the total is unknown the progress ratio oscillates\n    # by going up from 0 to 1 and then down from 1 to 0 and\n    # up again to infinity.\n    #\n    # @return [Float]\n    #\n    # @api public\n    def ratio\n      synchronize do\n        proportion = if total\n                       total > 0 ? (@current.to_f / total) : 0\n                     else\n                       (@unknown > 100 ? 200 - @unknown : @unknown).to_f / 100\n                     end\n        [[proportion, 0].max, 1].min\n      end\n    end\n\n    # Render progress to the output\n    #\n    # @api private\n    def render\n      return if done?\n\n      if hide_cursor && @last_render_width == 0 &&\n         (indeterminate? || @current < total)\n        write(TTY::Cursor.hide)\n      end\n\n      if @multibar\n        characters_in = @multibar.line_inset(self)\n        update(inset: self.class.display_columns(characters_in))\n      end\n\n      formatted = @formatters.decorate(@format)\n      @tokens.each do |token, val|\n        formatted = formatted.gsub(\":#{token}\", val)\n      end\n\n      padded = padout(formatted)\n\n      write(padded, true)\n\n      @last_render_time  = Time.now\n      @last_render_width = self.class.display_columns(formatted)\n    end\n\n    # Move cursor to a row of the current bar if the bar is rendered\n    # under a multibar. Otherwise, do not move and yield on current row.\n    #\n    # @api private\n    def move_to_row\n      if @multibar\n        CURSOR_LOCK.synchronize do\n          if @first_render\n            @row = @multibar.next_row\n            yield if block_given?\n            output.print NEWLINE\n            @first_render = false\n          else\n            lines_up = (@multibar.rows + 1) - @row\n            output.print TTY::Cursor.save\n            output.print TTY::Cursor.up(lines_up)\n            yield if block_given?\n            output.print TTY::Cursor.restore\n          end\n        end\n      else\n        yield if block_given?\n      end\n    end\n\n    # Write out to the output\n    #\n    # @param [String] data\n    #\n    # @api private\n    def write(data, clear_first = false)\n      return unless tty? # write only to terminal\n\n      move_to_row do\n        output.print(TTY::Cursor.column(1)) if clear_first\n        characters_in = @multibar.line_inset(self) if @multibar\n        output.print(\"#{characters_in}#{data}\")\n        output.flush\n      end\n    end\n\n    # Resize progress bar with new configuration\n    #\n    # @param [Integer] new_width\n    #   the new width for the bar display\n    #\n    # @api public\n    def resize(new_width = nil)\n      return if done?\n\n      synchronize do\n        clear_line\n        if new_width\n          self.width = new_width\n        end\n      end\n    end\n\n    # End the progress\n    #\n    # @api public\n    def finish\n      return if done?\n\n      @current = total unless indeterminate?\n      render\n      clear ? clear_line : write(NEWLINE, false)\n    ensure\n      @meter.clear\n      @done = true\n      @timer.stop\n\n      # reenable cursor if it is turned off\n      if hide_cursor && @last_render_width != 0\n        write(TTY::Cursor.show, false)\n      end\n\n      emit(:done)\n    end\n\n    # Resume rendering when bar is done, stopped or paused\n    #\n    # @api public\n    def resume\n      synchronize do\n        @done = false\n        @stopped = false\n        @paused = false\n      end\n    end\n\n    # Stop and cancel the progress at the current position\n    #\n    # @api public\n    def stop\n      return if done?\n\n      render\n      clear ? clear_line : write(NEWLINE, false)\n    ensure\n      @meter.clear\n      @stopped = true\n      @timer.stop\n\n      # reenable cursor if it is turned off\n      if hide_cursor && @last_render_width != 0\n        write(TTY::Cursor.show, false)\n      end\n\n      emit(:stopped)\n    end\n\n    # Pause the progress at the current position\n    #\n    # @api public\n    def pause\n      synchronize do\n        @paused = true\n        @timer.stop\n        emit(:paused)\n      end\n    end\n\n    # Clear current line\n    #\n    # @api public\n    def clear_line\n      output.print(\"#{ECMA_CSI}0m#{TTY::Cursor.clear_line}\")\n    end\n\n    # Check if progress is finished\n    #\n    # @return [Boolean]\n    #   true when progress finished, false otherwise\n    #\n    # @api public\n    def complete?\n      @done\n    end\n\n    # Check if progress is stopped\n    #\n    # @return [Boolean]\n    #\n    # @api public\n    def stopped?\n      @stopped\n    end\n\n    # Check if progress is paused\n    #\n    # @return [Boolean]\n    #\n    # @api public\n    def paused?\n      @paused\n    end\n\n    # Check if progress is finished, stopped or paused\n    #\n    # @return [Boolean]\n    #\n    # @api public\n    def done?\n      @done || @stopped || @paused\n    end\n\n    # Register callback with this bar\n    #\n    # @param [Symbol] name\n    #   the name for the event to listen for, e.i. :complete\n    #\n    # @return [self]\n    #\n    # @api public\n    def on(name, &callback)\n      synchronize do\n        @callbacks[name] << callback\n      end\n      self\n    end\n\n    # Log message above the current progress bar\n    #\n    # @param [String] message\n    #   the message to log out\n    #\n    # @api public\n    def log(message)\n      sanitized_message = message.gsub(/\\r|\\n/, \" \")\n      if done?\n        write(\"#{sanitized_message}#{NEWLINE}\", false)\n        return\n      end\n      sanitized_message = padout(sanitized_message)\n\n      write(\"#{sanitized_message}#{NEWLINE}\", true)\n      render\n    end\n\n    # Show bar format\n    #\n    # @return [String]\n    #\n    # @api public\n    def to_s\n      @format.to_s\n    end\n\n    # Inspect bar properties\n    #\n    # @return [String]\n    #\n    # @api public\n    def inspect\n      \"#<#{self.class.name} \" \\\n      \"@format=\\\"#{@format}\\\", \" \\\n      \"@current=\\\"#{@current}\\\", \" \\\n      \"@total=\\\"#{total}\\\", \" \\\n      \"@width=\\\"#{width}\\\", \" \\\n      \"@complete=\\\"#{complete}\\\", \" \\\n      \"@head=\\\"#{head}\\\", \" \\\n      \"@incomplete=\\\"#{incomplete}\\\", \" \\\n      \"@unknown=\\\"#{unknown}\\\", \" \\\n      \"@interval=\\\"#{interval}\\\">\"\n    end\n\n    private\n\n    # Pad message out with spaces\n    #\n    # @api private\n    def padout(message)\n      message_length = self.class.display_columns(message)\n\n      if @last_render_width > message_length\n        remaining_width = @last_render_width - message_length\n        message += \" \" * remaining_width\n      end\n      message\n    end\n\n    # Emit callback by name\n    #\n    # @param [Symbol]\n    #   the event name\n    #\n    # @api private\n    def emit(name, *args)\n      @callbacks[name].each do |callback|\n        callback.(*args)\n      end\n    end\n\n    # Check if IO is attached to a terminal\n    #\n    # return [Boolean]\n    #\n    # @api public\n    def tty?\n      output.respond_to?(:tty?) && output.tty?\n    end\n  end # ProgressBar\nend # TTY\n"
  },
  {
    "path": "lib/tty-progressbar.rb",
    "content": "require_relative \"tty/progressbar\"\nrequire_relative \"tty/progressbar/multi\"\n"
  },
  {
    "path": "spec/perf/render_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"erb\"\nrequire \"rspec-benchmark\"\n\nRSpec.describe TTY::ProgressBar, \"rendering\" do\n  include RSpec::Benchmark::Matchers\n\n  let(:done) { \"本\" }\n  let(:head) { \"語\" }\n  let(:rem)  { \"〜\" }\n\n  it \"performs bar rendering slower than ERB template substitution\" do\n    output_progress = StringIO.new\n    output_write = StringIO.new\n    # Progress bar\n    progress = TTY::ProgressBar.new(\n      \"[:bar]\",\n      output: output_progress,\n      incomplete: rem, head: head, complete: done,\n      total: 5, width: 10\n    )\n    # ERB renderer\n    template = \"<%= done %> - <%= head %> - <%= rem %>\"\n    renderer = ERB.new(template)\n\n    expect {\n      progress.advance\n      progress.reset\n    }.to perform_slower_than {\n      output_write << renderer.result(binding)\n    }.at_most(10).times\n  end\n\n  it \"performs bar rendering 2.4k i/s\" do\n    output = StringIO.new\n    progress = described_class.new(\"[:bar]\", output: output, total: 10,\n                                             width: 10)\n\n    expect {\n      progress.advance\n      progress.reset\n    }.to perform_at_least(2400).ips\n  end\nend\n"
  },
  {
    "path": "spec/spec_helper.rb",
    "content": "# frozen_string_literal: true\n\nif ENV[\"COVERAGE\"] == \"true\"\n  require \"simplecov\"\n  require \"coveralls\"\n\n  SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new([\n    SimpleCov::Formatter::HTMLFormatter,\n    Coveralls::SimpleCov::Formatter\n  ])\n\n  SimpleCov.start do\n    command_name \"spec\"\n    add_filter \"spec\"\n  end\nend\n\nrequire \"timecop\"\nrequire \"tty-progressbar\"\nrequire \"stringio\"\n\nclass StringIO\n  undef_method :tty?\n  def tty?\n    true\n  end\nend\n\nDir[::File.join(__dir__, \"support/**/*.rb\")].sort.each(&method(:require))\n\nRSpec.configure do |config|\n  config.expect_with :rspec do |expectations|\n    expectations.include_chain_clauses_in_custom_matcher_descriptions = true\n    expectations.max_formatted_output_length = nil\n  end\n\n  config.mock_with :rspec do |mocks|\n    mocks.verify_partial_doubles = true\n  end\n\n  # Limits the available syntax to the non-monkey patched syntax\n  # that is recommended.\n  config.disable_monkey_patching!\n\n  # This setting enables warnings. It's recommended, but in some cases may\n  # be too noisy due to issues in dependencies.\n  config.warnings = true\n\n  config.default_formatter = \"doc\" if config.files_to_run.one?\n\n  config.profile_examples = 2\n\n  config.order = :random\n\n  Kernel.srand config.seed\nend\n"
  },
  {
    "path": "spec/support/output_io.rb",
    "content": "# frozen_string_literal: true\n\nclass OutputIO\n  def initialize(content = \"\")\n    @content = content\n  end\n\n  def print(string)\n    @content += string\n  end\n\n  def read\n    @content\n  end\n\n  def flush; end\n\n  def rewind; end\n\n  def tty?\n    true\n  end\nend\n"
  },
  {
    "path": "spec/unit/advance_spec.rb",
    "content": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \"#advance\" do\n  let(:output) { StringIO.new }\n\n  it \"advances by custom value\" do\n    progress = TTY::ProgressBar.new(\"[:bar]\", output: output, total: 10)\n    progress.advance(8)\n    expect(progress.current).to eq(8)\n  end\n\n  it \"allows to go back\" do\n    progress = TTY::ProgressBar.new(\"[:bar]\", output: output, total: 10)\n    5.times { progress.advance(1) }\n    expect(progress.current).to eq(5)\n    5.times { progress.advance(-1) }\n    expect(progress.current).to eq(0)\n  end\n\n  it \"cannot backtrack on finished\" do\n    progress = TTY::ProgressBar.new(\"[:bar]\", output: output, total: 10)\n    10.times { progress.advance(1) }\n    expect(progress.current).to eq(10)\n    5.times { progress.advance(-1) }\n    expect(progress.current).to eq(10)\n  end\nend\n"
  },
  {
    "path": "spec/unit/bar_format_spec.rb",
    "content": "# frozen_string_literal: false\n\nRSpec.describe TTY::ProgressBar, \":bar_format\" do\n  let(:output) { StringIO.new(\"\", \"w+\") }\n  let(:formats) { TTY::ProgressBar::Formats::FORMATS }\n\n  TTY::ProgressBar::Formats::FORMATS.each_key do |format|\n    it \"displays progress with #{format.inspect} format characters\" do\n      complete = formats[format][:complete]\n      incomplete = formats[format][:incomplete]\n      progress = TTY::ProgressBar.new(\"[:bar]\", output: output, total: 10,\n                                                bar_format: format)\n\n      5.times { progress.advance(2) }\n\n      output.rewind\n      expect(output.read).to eq([\n        \"\\e[1G[#{complete * 2}#{incomplete * 8}]\",\n        \"\\e[1G[#{complete * 4}#{incomplete * 6}]\",\n        \"\\e[1G[#{complete * 6}#{incomplete * 4}]\",\n        \"\\e[1G[#{complete * 8}#{incomplete * 2}]\",\n        \"\\e[1G[#{complete * 10}#{incomplete * 0}]\\n\"\n      ].join)\n    end\n\n    it \"displays unknown progress for #{format.inspect} bar format\" do\n      unknown = formats[format][:unknown]\n      left_chars = 10 - unknown.size\n      progress = TTY::ProgressBar.new(\"[:bar]\", output: output, total: nil,\n                                                bar_format: format, width: 10)\n\n      2.times { progress.advance(2) }\n\n      output.rewind\n      expect(output.read).to eq([\n        \"\\e[1G[#{unknown}#{' ' * left_chars}]\",\n        \"\\e[1G[#{unknown}#{' ' * left_chars}]\"\n      ].join)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/clear_spec.rb",
    "content": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \"clear\" do\n  let(:output) { StringIO.new }\n\n  it \"clears progress bar when finished\" do\n    progress = TTY::ProgressBar.new(\"[:bar]\", output: output, total: 5,\n                                              clear: true)\n    5.times { progress.advance }\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G[=    ]\",\n      \"\\e[1G[==   ]\",\n      \"\\e[1G[===  ]\",\n      \"\\e[1G[==== ]\",\n      \"\\e[1G[=====]\\e[0m\\e[2K\\e[1G\"\n    ].join)\n  end\nend\n"
  },
  {
    "path": "spec/unit/complete_spec.rb",
    "content": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \"#complete?\" do\n  let(:output) { StringIO.new }\n\n  it \"checks for completness\" do\n    progress = TTY::ProgressBar.new(\"[:bar]\", output: output, total: 3)\n    completes = []\n    3.times do\n      completes << progress.complete?\n      progress.advance\n    end\n    completes << progress.complete?\n    expect(completes).to eq([false, false, false, true])\n  end\nend\n"
  },
  {
    "path": "spec/unit/configure_spec.rb",
    "content": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \"#configure\" do\n  it \"yields configuration instance\" do\n    progress = described_class.new(\":bar\")\n    config = spy(:config)\n    allow(progress).to receive(:configure).and_yield(config)\n\n    expect { |block|\n      progress.configure(&block)\n    }.to yield_with_args(config)\n  end\n\n  it \"overrides initial option configuration\" do\n    progress = described_class.new(\":bar\", total: 10)\n    expect(progress.total).to eq(10)\n\n    progress.configure do |config|\n      config.total = 20\n    end\n\n    expect(progress.total).to eq(20)\n  end\n\n  it \"sets total option to nil value via accessor\" do\n    progress = described_class.new(\":bar\", total: 10)\n\n    progress.configure do |config|\n      config.total = nil\n    end\n\n    expect(progress.total).to eq(nil)\n  end\n\n  %i[complete incomplete unknown].each do |option|\n    it \"fails to set #{option.inspect} to an empty string via initialize\" do\n      expect {\n        described_class.new(\":bar\", total: 10, option => \"\")\n      }.to raise_error(\n        ArgumentError,\n        \"cannot provide an empty string for #{option.inspect}\"\n      )\n    end\n\n    it \"fails to set #{option.inspect} to empty string via accessor\" do\n      progress = described_class.new(\":bar\", total: 10)\n\n      expect {\n        progress.configure do |config|\n          config.public_send(\"#{option}=\", \"\")\n        end\n      }.to raise_error(\n        ArgumentError,\n        \"cannot provide an empty string for #{option.inspect}\"\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/converter/to_bytes_spec.rb",
    "content": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar::Converter, \"#to_bytes\" do\n  subject(:converter) { described_class }\n\n  it \"converts 1000 to bytes\" do\n    expect(converter.to_bytes(1000)).to eq(\"1000B\")\n  end\n\n  it \"converts 1024 to bytes\" do\n    expect(converter.to_bytes(1024)).to eq(\"1.00KB\")\n  end\n\n  it \"converts 1234 to bytes\" do\n    expect(converter.to_bytes(1234)).to eq(\"1.21KB\")\n  end\n\n  it \"converts 12345 to bytes\" do\n    expect(converter.to_bytes(12_345)).to eq(\"12.06KB\")\n  end\n\n  it \"converts 2000 to bytes\" do\n    expect(converter.to_bytes(2000)).to eq(\"1.95KB\")\n  end\n\n  it \"converts 1234567 to bytes\" do\n    expect(converter.to_bytes(1_234_567)).to eq(\"1.18MB\")\n  end\n\n  it \"converts 1234567 to bytes with :separator\" do\n    expect(converter.to_bytes(1_234_567, separator: \",\")).to eq(\"1,18MB\")\n  end\n\n  it \"converts 1234567 to bytes with :unit_separator\" do\n    expect(converter.to_bytes(1_234_567, unit_separator: \" \")).to eq(\"1.18 MB\")\n  end\n\n  it \"converts 1234567 to bytes with comma as a separator\" do\n    expect(converter.to_bytes(1_234_567, decimals: 1)).to eq(\"1.2MB\")\n  end\n\n  it \"converts 10_000_000 to bytes\" do\n    expect(converter.to_bytes(10_000_000)).to eq(\"9.54MB\")\n  end\n\n  it \"convert 10_000_000_000 to bytes\" do\n    expect(converter.to_bytes(10_000_000_000)).to eq(\"9.31GB\")\n  end\nend\n"
  },
  {
    "path": "spec/unit/converter/to_seconds_spec.rb",
    "content": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar::Converter, \"#to_seconds\" do\n  subject(:converter) { described_class }\n\n  it \"ensures 5-digit precision for < 1\",\n     unless: RSpec::Support::Ruby.truffleruby? do\n    expect(converter.to_seconds(0.000005)).to eq(\"0.00001\")\n  end\n\n  it \"ensures 5-digit precision for < 1 on TruffleRuby\",\n     if: RSpec::Support::Ruby.truffleruby? do\n    expect(converter.to_seconds(0.000005)).to eq(\"0.00000\")\n  end\n\n  it \"rounds 0 to 0.00\" do\n    expect(converter.to_seconds(0)).to eq(\" 0.00\")\n  end\n\n  it \"ensures 2-digit precision for > 1\" do\n    expect(converter.to_seconds(11.2)).to eq(\"11.20\")\n  end\n\n  it \"specifies precision to be 3 digits\" do\n    expect(converter.to_seconds(11.12345, precision: 3)).to eq(\"11.123\")\n  end\nend\n"
  },
  {
    "path": "spec/unit/converter/to_time_spec.rb",
    "content": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar::Converter, \"#to_time\" do\n  subject(:converter) { described_class }\n\n  it \"converts zero seconds\" do\n    expect(converter.to_time(0.0)).to eq(\" 0s\")\n  end\n\n  it \"converts less than a second to zero\" do\n    expect(converter.to_time(0.83)).to eq(\" 0s\")\n  end\n\n  it \"converts seconds to a second\" do\n    expect(converter.to_time(1.2)).to eq(\" 1s\")\n  end\n\n  it \"converts seconds to seconds\" do\n    expect(converter.to_time(15)).to eq(\"15s\")\n  end\n\n  it \"converts seconds to a minute\" do\n    expect(converter.to_time(100)).to eq(\" 1m40s\")\n  end\n\n  it \"converts seconds to minutes\" do\n    expect(converter.to_time(2000)).to eq(\"33m20s\")\n  end\n\n  it \"converts seconds to an hour\" do\n    expect(converter.to_time(3600)).to eq(\" 1h 0m\")\n  end\n\n  it \"converts seconds to hours\" do\n    expect(converter.to_time(23.5 * 3600)).to eq(\"23h30m\")\n  end\n\n  it \"converts seconds to days and hours\" do\n    expect(converter.to_time(100 * 3600)).to eq(\"4d 4h 0m\")\n  end\nend\n"
  },
  {
    "path": "spec/unit/custom_formatter_spec.rb",
    "content": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \"custom formatter\" do\n  let(:output) { StringIO.new }\n\n  it \"allows for custom tag\" do\n    progress = TTY::ProgressBar.new(\":hi\", output: output, total: 10)\n\n    stub_const(\"HiFormatter\", Class.new do\n      def initialize(progress)\n        @progress = progress\n      end\n\n      def matches?(value)\n        value.to_s =~ /:hi/\n      end\n\n      def call(value)\n        value.gsub(/:hi/, \"Hello\")\n      end\n    end)\n\n    progress.use(HiFormatter)\n    progress.advance\n    output.rewind\n    expect(output.read).to eq(\"\\e[1GHello\")\n  end\n\n  it \"enforces class formatter\" do\n    progress = TTY::ProgressBar.new(\":hi\", output: output, total: 10)\n    stub_const(\"HiFormatter\", Class.new)\n    formatter = HiFormatter.new\n\n    expect {\n      progress.use(formatter)\n    }.to raise_error(ArgumentError, \"Formatter needs to be a class\")\n  end\nend\n"
  },
  {
    "path": "spec/unit/custom_token_spec.rb",
    "content": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \"custom token\" do\n  let(:output) { StringIO.new }\n\n  it \"allows to specify custom tokens\" do\n    progress = TTY::ProgressBar.new(\"(:current) :title\", output: output, total: 4)\n    progress.advance(title: \"Hello Piotr!\")\n    progress.advance(3, title: \"Bye Piotr!\")\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G(1) Hello Piotr!\",\n      \"\\e[1G(4) Bye Piotr!  \\n\"\n    ].join)\n  end\nend\n"
  },
  {
    "path": "spec/unit/events_spec.rb",
    "content": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \"events\" do\n  let(:output) { StringIO.new }\n\n  it \"emits :progress event when advancing\" do\n    events = []\n    bar = TTY::ProgressBar.new(\"[:bar]\", output: output, total: 5)\n    bar.on(:progress) { events << :progress }\n\n    bar.advance\n\n    expect(events).to eq([:progress])\n  end\n\n  it \"emits :done event when finished\" do\n    events = []\n    bar = TTY::ProgressBar.new(\"[:bar]\", output: output, total: 5)\n    bar.on(:done) { events << :done }\n\n    bar.finish\n\n    expect(events).to eq([:done])\n  end\n\n  it \"emits :stopped event\" do\n    events = []\n    bar = TTY::ProgressBar.new(\"[:bar]\", output: output, total: 5)\n    bar.on(:stopped) { events << :stopped }\n\n    bar.stop\n\n    expect(events).to eq([:stopped])\n  end\n\n  it \"emits :paused event\" do\n    events = []\n    bar = TTY::ProgressBar.new(\"[:bar]\", output: output, total: 5)\n    bar.on(:paused) { events << :paused }\n\n    bar.pause\n\n    expect(events).to eq([:paused])\n  end\nend\n"
  },
  {
    "path": "spec/unit/finish_spec.rb",
    "content": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \"#finish\" do\n  let(:output) { StringIO.new }\n\n  it \"finishes progress\" do\n    progress = TTY::ProgressBar.new(\"[:bar]\", output: output, total: 10)\n    progress.advance\n    progress.finish\n    expect(progress.complete?).to be(true)\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G[=         ]\",\n      \"\\e[1G[==========]\\n\"\n    ].join)\n  end\nend\n"
  },
  {
    "path": "spec/unit/formatter/bar_spec.rb",
    "content": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \":bar token\" do\n  let(:output) { StringIO.new }\n\n  it \"animates bar\" do\n    progress = TTY::ProgressBar.new(\"[:bar]\", output: output, total: 5)\n    5.times { progress.advance }\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G[=    ]\",\n      \"\\e[1G[==   ]\",\n      \"\\e[1G[===  ]\",\n      \"\\e[1G[==== ]\",\n      \"\\e[1G[=====]\\n\"\n    ].join)\n  end\n\n  it \"animates unknown progress without total\" do\n    progress = TTY::ProgressBar.new(\"[:bar]\", output: output, total: nil,\n                                              width: 5)\n    2.times { progress.advance }\n    progress.update(total: 5)\n    3.times { progress.advance }\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G[<=>  ]\",\n      \"\\e[1G[<=>  ]\",\n      \"\\e[1G[===  ]\",\n      \"\\e[1G[==== ]\",\n      \"\\e[1G[=====]\\n\"\n    ].join)\n  end\n\n  it \"animates colors correctly\" do\n    red = \"\\e[31m \\e[0m\"\n    green = \"\\e[32m \\e[0m\"\n    progress = TTY::ProgressBar.new(\"[:bar]\", total: 5, complete: green,\n                                              incomplete: red, output: output)\n\n    5.times { progress.advance }\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G[#{green}#{red}#{red}#{red}#{red}]\",\n      \"\\e[1G[#{green}#{green}#{red}#{red}#{red}]\",\n      \"\\e[1G[#{green}#{green}#{green}#{red}#{red}]\",\n      \"\\e[1G[#{green}#{green}#{green}#{green}#{red}]\",\n      \"\\e[1G[#{green}#{green}#{green}#{green}#{green}]\\n\"\n    ].join)\n  end\n\n  describe \"when unicode chars & odd width\" do\n    let(:done) { \"本\" }\n    let(:head) { \"語\" }\n    let(:rem)  { \"〜\" }\n\n    it \"head, complete & incomplete are unicode chars\" do\n      progress = TTY::ProgressBar.new(\"[:bar]\", output: output, incomplete: rem,\n                                                head: head, complete: done,\n                                                total: 5, width: 9)\n\n      5.times { progress.advance }\n      output.rewind\n\n      expect(output.read).to eq([\n        \"\\e[1G[語〜〜〜]\",\n        \"\\e[1G[本語〜〜]\",\n        \"\\e[1G[本語〜〜]\",\n        \"\\e[1G[本本語〜]\",\n        \"\\e[1G[本本本語]\\n\"\n      ].join)\n    end\n\n    it \"head unicode & ascii vs complete & incomplete unicode chars\" do\n      progress = TTY::ProgressBar.new(\"[:bar]\", output: output, incomplete: rem,\n                                                head: \"#{head}>\", complete: done,\n                                                total: 5, width: 9)\n\n      5.times { progress.advance }\n      output.rewind\n\n      expect(output.read).to eq([\n        \"\\e[1G[語> 〜〜]\",\n        \"\\e[1G[語> 〜〜]\",\n        \"\\e[1G[語> 〜〜]\",\n        \"\\e[1G[本語> 〜]\",\n        \"\\e[1G[本本語> ]\\n\"\n      ].join)\n    end\n\n    it \"head & complete unicode chars vs incomplete ascii char\" do\n      progress = TTY::ProgressBar.new(\"[:bar]\", output: output, incomplete: \"-\",\n                                                head: head, complete: done,\n                                                total: 5, width: 9)\n\n      5.times { progress.advance }\n      output.rewind\n\n      expect(output.read).to eq([\n        \"\\e[1G[語------]\",\n        \"\\e[1G[本語----]\",\n        \"\\e[1G[本語----]\",\n        \"\\e[1G[本本語--]\",\n        \"\\e[1G[本本本語]\\n\"\n      ].join)\n    end\n\n    it \"head & complete ascii chars vs incomplete unicode char\" do\n      progress = TTY::ProgressBar.new(\"[:bar]\", output: output, incomplete: rem,\n                                                head: \">\", complete: \"#\",\n                                                total: 5, width: 9)\n\n      5.times { progress.advance }\n      output.rewind\n\n      expect(output.read).to eq([\n        \"\\e[1G[#>〜〜〜]\",\n        \"\\e[1G[###>〜〜]\",\n        \"\\e[1G[###>〜〜]\",\n        \"\\e[1G[#####>〜]\",\n        \"\\e[1G[#######>]\\n\"\n      ].join)\n    end\n\n    it \"complete ascii chars vs head & incomplete unicode char\" do\n      progress = TTY::ProgressBar.new(\"[:bar]\", output: output, incomplete: rem,\n                                                head: head, complete: \"#\",\n                                                total: 5, width: 9)\n\n      5.times { progress.advance }\n      output.rewind\n\n      expect(output.read).to eq([\n        \"\\e[1G[語〜〜〜]\",\n        \"\\e[1G[##語〜〜]\",\n        \"\\e[1G[##語〜〜]\",\n        \"\\e[1G[####語〜]\",\n        \"\\e[1G[######語]\\n\"\n      ].join)\n    end\n\n    it \"head & incomplete ascii chars vs complete unicode char\" do\n      progress = TTY::ProgressBar.new(\"[:bar]\", output: output, incomplete: \"-\",\n                                                head: \">\", complete: done,\n                                                total: 5, width: 9)\n\n      5.times { progress.advance }\n      output.rewind\n\n      expect(output.read).to eq([\n        \"\\e[1G[> ------]\",\n        \"\\e[1G[本> ----]\",\n        \"\\e[1G[本> ----]\",\n        \"\\e[1G[本本> --]\",\n        \"\\e[1G[本本本> ]\\n\"\n      ].join)\n    end\n\n    it \"head ascii char vs complete & incomplete unicode chars\" do\n      progress = TTY::ProgressBar.new(\"[:bar]\", output: output, incomplete: rem,\n                                                head: \">\", complete: done,\n                                                total: 5, width: 9)\n\n      5.times { progress.advance }\n      output.rewind\n\n      expect(output.read).to eq([\n        \"\\e[1G[> 〜〜〜]\",\n        \"\\e[1G[本> 〜〜]\",\n        \"\\e[1G[本> 〜〜]\",\n        \"\\e[1G[本本> 〜]\",\n        \"\\e[1G[本本本> ]\\n\"\n      ].join)\n    end\n\n    it \"unknown with ascii and unicode chars\" do\n      progress = TTY::ProgressBar.new(\"[:bar]\", output: output, incomplete: rem,\n                                                complete: done, head: \">\",\n                                                unknown: \"<#{done}>\",\n                                                total: nil, width: 9)\n\n      5.times { progress.advance }\n      progress.update(total: 10)\n      5.times { progress.advance }\n      output.rewind\n\n      expect(output.read).to eq([\n        \"\\e[1G[<本>    ]\",\n        \"\\e[1G[<本>    ]\",\n        \"\\e[1G[<本>    ]\",\n        \"\\e[1G[<本>    ]\",\n        \"\\e[1G[<本>    ]\",\n        \"\\e[1G[本> 〜〜]\",\n        \"\\e[1G[本本> 〜]\",\n        \"\\e[1G[本本> 〜]\",\n        \"\\e[1G[本本本> ]\",\n        \"\\e[1G[本本本> ]\\n\"\n      ].join)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/formatter/byte_rate_spec.rb",
    "content": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \":byte_rate token\" do\n  let(:output) { StringIO.new }\n\n  before { Timecop.safe_mode = false }\n\n  it \"shows current rate in bytes per sec\" do\n    time_now = Time.local(2014, 10, 5, 12, 0, 0)\n    Timecop.freeze(time_now)\n    progress = TTY::ProgressBar.new(\":byte_rate\", output: output, total: 10_000,\n                                                  interval: 1)\n    # Generate a serie of advances at 2s intervals\n    #   t+0     advance=0         total=0\n    #   t+2     advance=1000      total=1000\n    #   t+4     advance=2000      total=3000\n    #   t+6     advance=3000      total=6000\n    #   t+8     advance=4000      total=10_000\n    # NOTE: mean_byte uses 1024 for the scale in K, M ...\n    5.times do |i|\n      time_now = Time.local(2014, 10, 5, 12, 0, i * 2)\n      Timecop.freeze(time_now)\n      progress.advance(i * 1000)\n    end\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G0B\",\n      \"\\e[1G500B\",\n      \"\\e[1G1000B\",\n      \"\\e[1G1.46KB\",\n      \"\\e[1G1.95KB\\n\"\n    ].join)\n    Timecop.return\n  end\n\n  it \"displays rate in bytes per sec when no total\" do\n    time_now = Time.local(2014, 10, 5, 12, 0, 0)\n    Timecop.freeze(time_now)\n    progress = TTY::ProgressBar.new(\":byte_rate\", output: output, total: nil)\n    5.times do |i|\n      time_now = Time.local(2014, 10, 5, 12, 0, i * 2)\n      Timecop.freeze(time_now)\n      progress.advance(i * 1000)\n    end\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G0B\",\n      \"\\e[1G500B\",\n      \"\\e[1G1000B\",\n      \"\\e[1G1.46KB\",\n      \"\\e[1G1.95KB\"\n    ].join)\n    Timecop.return\n  end\nend\n"
  },
  {
    "path": "spec/unit/formatter/current_byte_spec.rb",
    "content": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \":current_byte token\" do\n  let(:output) { StringIO.new }\n\n  it \"displays bytes processed\" do\n    progress = described_class.new(\":current_byte\", output: output,\n                                                    total: 102_400)\n    5.times { progress.advance(20_480) }\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G20.00KB\",\n      \"\\e[1G40.00KB\",\n      \"\\e[1G60.00KB\",\n      \"\\e[1G80.00KB\",\n      \"\\e[1G100.00KB\\n\"\n    ].join)\n  end\nend\n"
  },
  {
    "path": "spec/unit/formatter/current_spec.rb",
    "content": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \":current token\" do\n  let(:output) { StringIO.new }\n\n  it \"displays current value\" do\n    progress = TTY::ProgressBar.new(\"|:current|\", output: output, total: 10)\n    3.times { progress.advance }\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G|1|\",\n      \"\\e[1G|2|\",\n      \"\\e[1G|3|\"\n    ].join)\n  end\nend\n"
  },
  {
    "path": "spec/unit/formatter/elapsed_spec.rb",
    "content": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \":elapsed token\" do\n  let(:output) { StringIO.new }\n\n  before { Timecop.safe_mode = false }\n\n  after { Timecop.return }\n\n  it \"displays elapsed time\" do\n    Timecop.freeze(Time.local(2014, 10, 5, 12, 0, 0))\n    progress = TTY::ProgressBar.new(\":elapsed\", output: output, total: 10)\n\n    5.times do |sec|\n      Timecop.freeze(Time.local(2014, 10, 5, 12, 0, sec))\n      progress.advance\n    end\n\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G 0s\",\n      \"\\e[1G 1s\",\n      \"\\e[1G 2s\",\n      \"\\e[1G 3s\",\n      \"\\e[1G 4s\"\n    ].join)\n  end\n\n  it \"resets elapsed time\" do\n    Timecop.freeze(Time.local(2014, 10, 5, 12, 0, 0))\n    progress = TTY::ProgressBar.new(\":elapsed\", output: output, total: 5)\n\n    5.times do |sec|\n      Timecop.freeze(Time.local(2014, 10, 5, 12, 0, sec))\n      progress.advance\n    end\n    expect(progress.complete?).to be(true)\n    progress.reset\n    2.times do |sec|\n      Timecop.freeze(Time.local(2014, 10, 5, 13, 0, sec))\n      progress.advance\n    end\n\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G 0s\",\n      \"\\e[1G 1s\",\n      \"\\e[1G 2s\",\n      \"\\e[1G 3s\",\n      \"\\e[1G 4s\\n\",\n      \"\\e[1G 0s\",\n      \"\\e[1G 1s\"\n    ].join)\n  end\n\n  it \"resumes elapsed time measurement when stopped or finished\" do\n    Timecop.freeze(Time.local(2021, 1, 10, 12, 0, 0))\n    progress = TTY::ProgressBar.new(\":elapsed\", output: output, total: 10)\n\n    5.times do |sec|\n      Timecop.freeze(Time.local(2021, 1, 10, 12, 0, 1 + sec))\n      progress.advance\n    end\n\n    progress.stop\n    progress.resume\n\n    3.times do |sec| # resume progression after 5 seconds\n      Timecop.freeze(Time.local(2021, 1, 10, 12, 0, 10 + sec))\n      progress.advance\n    end\n\n    progress.finish\n    progress.update(total: 12)\n    progress.resume\n\n    2.times do |sec| # resume progression after 2 seconds\n      Timecop.freeze(Time.local(2021, 1, 10, 12, 0, 15 + sec))\n      progress.advance\n    end\n\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G 0s\",\n      \"\\e[1G 1s\",\n      \"\\e[1G 2s\",\n      \"\\e[1G 3s\",\n      \"\\e[1G 4s\",\n      \"\\e[1G 4s\\n\", # stopped\n      \"\\e[1G 4s\",\n      \"\\e[1G 5s\",\n      \"\\e[1G 6s\",\n      \"\\e[1G 6s\\n\", # finished\n      \"\\e[1G 6s\",\n      \"\\e[1G 7s\\n\"\n    ].join)\n  end\nend\n"
  },
  {
    "path": "spec/unit/formatter/estimated_spec.rb",
    "content": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \":eta token\" do\n  let(:output) { StringIO.new }\n\n  before { Timecop.safe_mode = false }\n\n  after { Timecop.return }\n\n  it \"displays elapsed time\" do\n    Timecop.freeze(Time.local(2014, 10, 5, 12, 0, 0))\n    progress = TTY::ProgressBar.new(\":eta\", output: output, total: 5)\n\n    5.times do |sec|\n      Timecop.freeze(Time.local(2014, 10, 5, 12, 0, sec))\n      progress.advance\n    end\n\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G 0s\",\n      \"\\e[1G 1s\",\n      \"\\e[1G 1s\",\n      \"\\e[1G 0s\",\n      \"\\e[1G 0s\\n\"\n    ].join)\n  end\n\n  it \"displays unknown elapsed time when no total\" do\n    Timecop.freeze(Time.local(2014, 10, 5, 12, 0, 0))\n    progress = TTY::ProgressBar.new(\":eta\", output: output, total: nil)\n\n    3.times { progress.advance }\n    progress.update(total: 5)\n\n    2.times do |sec|\n      Timecop.freeze(Time.local(2014, 10, 5, 12, 0, sec))\n      progress.advance\n    end\n\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G--s\",\n      \"\\e[1G--s\",\n      \"\\e[1G--s\",\n      \"\\e[1G 0s\",\n      \"\\e[1G 0s\\n\"\n    ].join)\n  end\nend\n"
  },
  {
    "path": "spec/unit/formatter/estimated_time_spec.rb",
    "content": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \":eta_time token\" do\n  let(:output) { StringIO.new }\n\n  before { Timecop.safe_mode = false }\n\n  after { Timecop.return }\n\n  it \"displays estimated completion time of day\" do\n    Timecop.freeze(Time.local(2020, 1, 5, 12, 0, 0))\n    bar = described_class.new(\" :eta_time\", output: output, total: 5)\n\n    5.times do |sec|\n      Timecop.freeze(Time.local(2020, 1, 5, 12, 0, sec))\n      bar.advance\n    end\n\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G 12:00:00\",\n      \"\\e[1G 12:00:02\",\n      \"\\e[1G 12:00:03\",\n      \"\\e[1G 12:00:03\",\n      \"\\e[1G 12:00:04\\n\"\n    ].join)\n  end\n\n  it \"displays estimated completion time of day with date after 24 hours\" do\n    Timecop.freeze(Time.local(2020, 1, 5, 12, 0, 0))\n    bar = described_class.new(\" :eta_time\", output: output, total: 5)\n\n    2.times do |day|\n      time_now = Time.local(2020, 1, day + 6, 12, 0, 0)\n      Timecop.freeze(time_now)\n      bar.advance\n    end\n\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G 12:00:00\",\n      \"\\e[1G 2020-01-09 00:00:00\"\n    ].join)\n  end\n\n  it \"displays unknown estimated completion time of day as --:--:--\" do\n    Timecop.freeze(Time.local(2020, 1, 5, 12, 0, 0))\n    bar = described_class.new(\" :eta_time\", output: output, total: nil)\n\n    3.times { bar.advance }\n    bar.update(total: 5)\n\n    2.times do |sec|\n      Timecop.freeze(Time.local(2020, 1, 5, 12, 0, sec))\n      bar.advance\n    end\n\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G --:--:--\",\n      \"\\e[1G --:--:--\",\n      \"\\e[1G --:--:--\",\n      \"\\e[1G 12:00:00\",\n      \"\\e[1G 12:00:01\\n\"\n    ].join)\n  end\nend\n"
  },
  {
    "path": "spec/unit/formatter/mean_byte_spec.rb",
    "content": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \":mean_byte token\" do\n  let(:output) { StringIO.new }\n\n  before { Timecop.safe_mode = false }\n\n  after { Timecop.return }\n\n  it \"shows mean rate in bytes per sec\" do\n    time_now = Time.local(2014, 10, 5, 12, 0, 0)\n    Timecop.freeze(time_now)\n    progress = TTY::ProgressBar.new(\":mean_byte\", output: output, total: 10_000,\n                                                  interval: 1)\n    # Generate a serie of advances at 2s intervals\n    #   t+0     advance=0         total=0\n    #   t+2     advance=1000      total=1000\n    #   t+4     advance=2000      total=3000\n    #   t+6     advance=3000      total=6000\n    #   t+8     advance=4000      total=10_000\n    # NOTE: mean_byte uses 1024 for the scale in K, M ...\n    5.times do |i|\n      time_now = Time.local(2014, 10, 5, 12, 0, i * 2)\n      Timecop.freeze(time_now)\n      progress.advance(i * 1000)\n    end\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G0B\",\n      \"\\e[1G500B\",\n      \"\\e[1G750B\",\n      \"\\e[1G1000B\",\n      \"\\e[1G1.22KB\\n\"\n    ].join)\n  end\n\n  it \"displays mean rate in bytes per sec when no total\" do\n    time_now = Time.local(2014, 10, 5, 12, 0, 0)\n    Timecop.freeze(time_now)\n    progress = TTY::ProgressBar.new(\":mean_byte\", output: output, total: nil,\n                                                  interval: 1)\n    5.times do |i|\n      time_now = Time.local(2014, 10, 5, 12, 0, i * 2)\n      Timecop.freeze(time_now)\n      progress.advance(i * 1000)\n    end\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G0B\",\n      \"\\e[1G500B\",\n      \"\\e[1G750B\",\n      \"\\e[1G1000B\",\n      \"\\e[1G1.22KB\"\n    ].join)\n  end\nend\n"
  },
  {
    "path": "spec/unit/formatter/mean_rate_spec.rb",
    "content": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \":mean_rate token\" do\n  let(:output) { StringIO.new }\n\n  before { Timecop.safe_mode = false }\n\n  after { Timecop.return }\n\n  it \"shows current rate per sec\" do\n    time_now = Time.local(2014, 10, 5, 12, 0, 0)\n    Timecop.freeze(time_now)\n    progress = TTY::ProgressBar.new(\":mean_rate\", output: output, total: 100,\n                                                  interval: 1)\n    # Generate a serie of advances at 2s intervals\n    #   t+0     advance=0       total=0\n    #   t+2     advance=10      total=10\n    #   t+4     advance=20      total=30\n    #   t+6     advance=30      total=60\n    #   t+8     advance=40      total=100\n    5.times do |i|\n      time_now = Time.local(2014, 10, 5, 12, 0, i * 2)\n      Timecop.freeze(time_now)\n      progress.advance(i * 10)\n    end\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G 0.00\",\n      \"\\e[1G 5.00\",\n      \"\\e[1G 7.50\",\n      \"\\e[1G10.00\",\n      \"\\e[1G12.50\\n\"\n    ].join)\n  end\n\n  it \"displays mean rate per sec when no total\" do\n    time_now = Time.local(2014, 10, 5, 12, 0, 0)\n    Timecop.freeze(time_now)\n    progress = TTY::ProgressBar.new(\":mean_rate\", output: output, total: nil)\n    5.times do |i|\n      time_now = Time.local(2014, 10, 5, 12, 0, i * 2)\n      Timecop.freeze(time_now)\n      progress.advance(i * 10)\n    end\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G 0.00\",\n      \"\\e[1G 5.00\",\n      \"\\e[1G 7.50\",\n      \"\\e[1G10.00\",\n      \"\\e[1G12.50\"\n    ].join)\n  end\nend\n"
  },
  {
    "path": "spec/unit/formatter/percent_spec.rb",
    "content": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \":percent token\" do\n  let(:output) { StringIO.new }\n\n  it \"displays percent finished\" do\n    progress = TTY::ProgressBar.new(\":percent\", output: output, total: 5)\n    5.times { progress.advance }\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G20%\",\n      \"\\e[1G40%\",\n      \"\\e[1G60%\",\n      \"\\e[1G80%\",\n      \"\\e[1G100%\\n\"\n    ].join)\n  end\n\n  it \"displays unknown percent when no total\" do\n    progress = TTY::ProgressBar.new(\":percent\", output: output, total: nil)\n    3.times { progress.advance }\n    progress.update(total: 5)\n    2.times { progress.advance }\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G-%\",\n      \"\\e[1G-%\",\n      \"\\e[1G-%\",\n      \"\\e[1G80%\",\n      \"\\e[1G100%\\n\"\n    ].join)\n  end\nend\n"
  },
  {
    "path": "spec/unit/formatter/rate_spec.rb",
    "content": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \":rate token\" do\n  let(:output) { StringIO.new }\n\n  before { Timecop.safe_mode = false }\n\n  after { Timecop.return }\n\n  it \"shows current rate per sec\" do\n    time_now = Time.local(2014, 10, 5, 12, 0, 0)\n    Timecop.freeze(time_now)\n    progress = TTY::ProgressBar.new(\":rate\", output: output, total: 100,\n                                             interval: 1)\n    # Generate a serie of advances at 2s intervals\n    #   t+0     advance=0       total=0\n    #   t+2     advance=10      total=10\n    #   t+4     advance=20      total=30\n    #   t+6     advance=30      total=60\n    #   t+8     advance=40      total=100\n    5.times do |i|\n      time_now = Time.local(2014, 10, 5, 12, 0, i * 2)\n      Timecop.freeze(time_now)\n      progress.advance(i * 10)\n    end\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G 0.00\",\n      \"\\e[1G 5.00\",\n      \"\\e[1G10.00\",\n      \"\\e[1G15.00\",\n      \"\\e[1G20.00\\n\"\n    ].join)\n  end\n\n  it \"displays rate per sec when no total\" do\n    time_now = Time.local(2014, 10, 5, 12, 0, 0)\n    Timecop.freeze(time_now)\n    progress = TTY::ProgressBar.new(\":rate\", output: output, total: nil)\n    5.times do |i|\n      time_now = Time.local(2014, 10, 5, 12, 0, i * 2)\n      Timecop.freeze(time_now)\n      progress.advance(i * 10)\n    end\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G 0.00\",\n      \"\\e[1G 5.00\",\n      \"\\e[1G10.00\",\n      \"\\e[1G15.00\",\n      \"\\e[1G20.00\"\n    ].join)\n  end\nend\n"
  },
  {
    "path": "spec/unit/formatter/total_byte_spec.rb",
    "content": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \":total_byte token\" do\n  let(:output) { StringIO.new }\n\n  it \"displays bytes total\" do\n    progress = described_class.new(\":total_byte\", output: output, total: 102_400)\n    5.times { progress.advance(20_480) }\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G100.00KB\",\n      \"\\e[1G100.00KB\",\n      \"\\e[1G100.00KB\",\n      \"\\e[1G100.00KB\",\n      \"\\e[1G100.00KB\\n\"\n    ].join)\n  end\n\n  it \"displays unknown bytes progress without total\" do\n    progress = described_class.new(\":total_byte\", output: output, total: nil)\n    3.times { progress.advance(20_480) }\n    progress.update(total: 102_400)\n    2.times { progress.advance(20_480) }\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G-B\",\n      \"\\e[1G-B\",\n      \"\\e[1G-B\",\n      \"\\e[1G100.00KB\",\n      \"\\e[1G100.00KB\\n\"\n    ].join)\n  end\nend\n"
  },
  {
    "path": "spec/unit/formatter/total_spec.rb",
    "content": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \":total token\" do\n  let(:output) { StringIO.new }\n\n  it \"displays total amount\" do\n    progress = described_class.new(\":total\", output: output, total: 102_400)\n    5.times { progress.advance(20_480) }\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G102400\",\n      \"\\e[1G102400\",\n      \"\\e[1G102400\",\n      \"\\e[1G102400\",\n      \"\\e[1G102400\\n\"\n    ].join)\n  end\n\n  it \"displays unknown progress without total\" do\n    progress = described_class.new(\":total\", output: output, total: nil)\n    3.times { progress.advance(20_480) }\n    progress.update(total: 102_400)\n    2.times { progress.advance(20_480) }\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G-\",\n      \"\\e[1G-\",\n      \"\\e[1G-\",\n      \"\\e[1G102400\",\n      \"\\e[1G102400\\n\"\n    ].join)\n  end\nend\n"
  },
  {
    "path": "spec/unit/frequency_spec.rb",
    "content": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \"frequency\" do\n  let(:output) { StringIO.new }\n\n  before { Timecop.safe_mode = false }\n\n  it \"limits frequency to 500Hz, permiting every second one\" do\n    time_now = Time.local(2014, 10, 5, 12, 0, 0, 0)\n    Timecop.freeze(time_now)\n    progress = TTY::ProgressBar.new(\"[:bar]\", output: output, total: 10,\n                                              frequency: 500)\n\n    10.times do |sec|\n      time_now = Time.local(2014, 10, 5, 12, 0, 0, sec * 1000)\n      Timecop.freeze(time_now)\n      progress.advance\n    end\n\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G[===       ]\",\n      \"\\e[1G[=====     ]\",\n      \"\\e[1G[=======   ]\",\n      \"\\e[1G[========= ]\",\n      \"\\e[1G[==========]\\n\"\n    ].join)\n    Timecop.return\n  end\nend\n"
  },
  {
    "path": "spec/unit/head_spec.rb",
    "content": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \":head\" do\n  let(:output) { StringIO.new }\n\n  it \"animates head\" do\n    progress = TTY::ProgressBar.new(\"[:bar]\", output: output, head: \">\", total: 5)\n    5.times { progress.advance }\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G[>    ]\",\n      \"\\e[1G[=>   ]\",\n      \"\\e[1G[==>  ]\",\n      \"\\e[1G[===> ]\",\n      \"\\e[1G[====>]\\n\"\n    ].join)\n  end\n\n  it \"customises all output characters\" do\n    progress = TTY::ProgressBar.new(\n      \"[:bar]\",\n      output: output,\n      head: \"ᗧ\",\n      complete: \"-\", incomplete: \".\", total: 5\n    )\n    5.times { progress.advance }\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G[ᗧ....]\",\n      \"\\e[1G[-ᗧ...]\",\n      \"\\e[1G[--ᗧ..]\",\n      \"\\e[1G[---ᗧ.]\",\n      \"\\e[1G[----ᗧ]\\n\"\n    ].join)\n  end\n\n  it \"exceeds 2 characters\" do\n    progress = TTY::ProgressBar.new(\n      \"[:bar]\",\n      output: output,\n      head: \">>>\",\n      complete: \"-\", incomplete: \".\", total: 20\n    )\n    5.times { progress.advance(4) }\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G[->>>................]\",\n      \"\\e[1G[----->>>............]\",\n      \"\\e[1G[--------->>>........]\",\n      \"\\e[1G[------------->>>....]\",\n      \"\\e[1G[----------------->>>]\\n\"\n    ].join)\n  end\n\n  it \"clears head after finishing\" do\n    progress = TTY::ProgressBar.new(\"[:bar]\", output: output, total: 5,\n                                              head: \">\", clear_head: true)\n    5.times { progress.advance }\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G[>    ]\",\n      \"\\e[1G[=>   ]\",\n      \"\\e[1G[==>  ]\",\n      \"\\e[1G[===> ]\",\n      \"\\e[1G[=====]\\n\"\n    ].join)\n  end\nend\n"
  },
  {
    "path": "spec/unit/hide_cursor_spec.rb",
    "content": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \"#hide_cursor\" do\n  let(:output) { StringIO.new }\n\n  it \"hides cursor\" do\n    progress = TTY::ProgressBar.new(\"[:bar]\", output: output, total: 5,\n                                              hide_cursor: true)\n    5.times { progress.advance }\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[?25l\\e[1G[=    ]\",\n      \"\\e[1G[==   ]\",\n      \"\\e[1G[===  ]\",\n      \"\\e[1G[==== ]\",\n      \"\\e[1G[=====]\\n\\e[?25h\"\n    ].join)\n  end\n\n  it \"hides cursor when indeterminate\" do\n    progress = TTY::ProgressBar.new(\"[:bar]\", output: output, width: 5,\n                                              hide_cursor: true)\n    5.times { progress.advance }\n    progress.stop\n\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[?25l\\e[1G[<=>  ]\",\n      \"\\e[1G[<=>  ]\",\n      \"\\e[1G[<=>  ]\",\n      \"\\e[1G[<=>  ]\",\n      \"\\e[1G[<=>  ]\",\n      \"\\e[1G[<=>  ]\\n\\e[?25h\"\n    ].join)\n  end\n\n  it \"reenables cursor on finish\" do\n    progress = TTY::ProgressBar.new(\"[:bar]\", output: output, total: 5,\n                                              hide_cursor: true)\n    progress.advance(6)\n    expect(progress.complete?).to eq(true)\n    output.rewind\n    expect(output.read).to eq(\"\\e[1G[=====]\\n\\e[?25h\")\n  end\nend\n"
  },
  {
    "path": "spec/unit/indeterminate_spec.rb",
    "content": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \"indeterminate\" do\n  let(:output) { StringIO.new }\n\n  it \"animates indeterminate progress and finishes\" do\n    progress = described_class.new(\"[:bar]\", output: output, width: 10)\n    104.times { progress.advance }\n\n    progress.update(total: 110)\n    6.times { progress.advance }\n\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G[<=>       ]\" * 3,\n      \"\\e[1G[ <=>      ]\" * 7,\n      \"\\e[1G[  <=>     ]\" * 7,\n      \"\\e[1G[   <=>    ]\" * 7,\n      \"\\e[1G[    <=>   ]\" * 8,\n      \"\\e[1G[     <=>  ]\" * 7,\n      \"\\e[1G[      <=> ]\" * 7,\n      \"\\e[1G[       <=>]\" * 7,\n      \"\\e[1G[      <=> ]\" * 7,\n      \"\\e[1G[     <=>  ]\" * 7,\n      \"\\e[1G[    <=>   ]\" * 8,\n      \"\\e[1G[   <=>    ]\" * 7,\n      \"\\e[1G[  <=>     ]\" * 7,\n      \"\\e[1G[ <=>      ]\" * 7,\n      \"\\e[1G[<=>       ]\" * 7,\n      \"\\e[1G[ <=>      ]\",\n      \"\\e[1G[==========]\" * 5,\n      \"\\e[1G[==========]\\n\"\n    ].join)\n  end\n\n  it \"animates indeterminate progress with all metrics and finishes\" do\n    Timecop.freeze(Time.local(2021, 1, 16, 12, 0, 0))\n    format = \"[:bar] :current/:total :percent \" \\\n             \":current_byte/:total_byte :byte_rate/s :mean_byte/s \" \\\n             \":rate/s :mean_rate/s :elapsed :eta\"\n\n    progress = described_class.new(format, output: output, total: nil, width: 10)\n\n    3.times do |sec|\n      Timecop.freeze(Time.local(2021, 1, 16, 12, 0, 1 + sec))\n      progress.advance\n    end\n\n    progress.update(total: 6)\n    3.times do |i|\n      Timecop.freeze(Time.local(2021, 1, 16, 12, 0, 4 + i))\n      progress.advance\n    end\n\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G[<=>       ] 1/- -% 1B/-B 1B/s 1B/s  1.00/s  1.00/s  0s --s\",\n      \"\\e[1G[<=>       ] 2/- -% 2B/-B 1B/s 1B/s  1.00/s  1.00/s  1s --s\",\n      \"\\e[1G[<=>       ] 3/- -% 3B/-B 1B/s 1B/s  1.00/s  1.00/s  2s --s\",\n      \"\\e[1G[=======   ] 4/6 66% 4B/6B 1B/s 1B/s  1.00/s  1.00/s  3s  1s\",\n      \"\\e[1G[========  ] 5/6 83% 5B/6B 1B/s 1B/s  1.00/s  1.00/s  4s  0s\",\n      \"\\e[1G[==========] 6/6 100% 6B/6B 1B/s 1B/s  1.00/s  1.00/s  5s  0s\\n\"\n    ].join)\n\n    Timecop.return\n  end\nend\n"
  },
  {
    "path": "spec/unit/inspect_spec.rb",
    "content": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \"#inspect\" do\n  it \"inspects bar properties\" do\n    bar = described_class.new(\"downloading [:bar] :total\", total: 30)\n    expect(bar.inspect).to eq(\n      '#<TTY::ProgressBar @format=\"downloading [:bar] :total\", ' \\\n      '@current=\"0\", @total=\"30\", @width=\"30\", @complete=\"=\", @head=\"=\", ' \\\n      '@incomplete=\" \", @unknown=\"<=>\", @interval=\"1\">'\n    )\n  end\n\n  it \"prints string format\" do\n    bar = described_class.new(\"downloading [:bar] :total\", total: 30)\n    expect(bar.to_s).to eq(\"downloading [:bar] :total\")\n  end\nend\n"
  },
  {
    "path": "spec/unit/iterate_spec.rb",
    "content": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \"#iterate\" do\n  let(:output) { StringIO.new }\n\n  it \"iterates over a collection and yields\" do\n    bar = TTY::ProgressBar.new(\"[:bar]\", output: output)\n    values = []\n    bar.iterate(5.times) { |val| values << val }\n\n    expect(bar.complete?).to eq(true)\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G[=    ]\",\n      \"\\e[1G[==   ]\",\n      \"\\e[1G[===  ]\",\n      \"\\e[1G[==== ]\",\n      \"\\e[1G[=====]\\n\"\n    ].join)\n    expect(values).to eq([0, 1, 2, 3, 4])\n  end\n\n  it \"iterates over a collection with a step\" do\n    bar = TTY::ProgressBar.new(\"[:bar]\", output: output)\n    values = []\n    bar.iterate(4.times, 5) { |val| values << val }\n\n    expect(bar.complete?).to eq(true)\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G[=====               ]\",\n      \"\\e[1G[==========          ]\",\n      \"\\e[1G[===============     ]\",\n      \"\\e[1G[====================]\\n\"\n    ].join)\n    expect(values).to eq([0, 1, 2, 3])\n  end\n\n  it \"iterates over a collection and returns enumerable\" do\n    bar = TTY::ProgressBar.new(\"[:bar]\", output: output)\n    values = []\n    progress = bar.iterate(5.times)\n\n    expect(bar.complete?).to eq(false)\n\n    progress.each { |v| values << v }\n\n    expect(values).to eq([0, 1, 2, 3, 4])\n  end\n\n  it \"does not uses collection's count if total is provided\" do\n    bar = TTY::ProgressBar.new(\"[:bar]\", output: output, total: 5)\n    iterable = 5.times\n    expect(iterable).not_to receive(:count)\n    progress = bar.iterate(iterable)\n    values = []\n\n    progress.each { |v| values << v }\n\n    expect(values).to eq([0, 1, 2, 3, 4])\n  end\n\n  it \"iterates over an infinite enumerator and renders bar correctly\" do\n    bar = TTY::ProgressBar.new(\"[:bar]\", output: output, total: 5)\n    infinite_iter = (1..Float::INFINITY).lazy\n\n    progress = bar.iterate(infinite_iter)\n\n    10.times { progress.next }\n\n    expect(bar.complete?).to eq(true)\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G[=    ]\",\n      \"\\e[1G[==   ]\",\n      \"\\e[1G[===  ]\",\n      \"\\e[1G[==== ]\",\n      \"\\e[1G[=====]\\n\"\n    ].join)\n  end\nend\n"
  },
  {
    "path": "spec/unit/log_spec.rb",
    "content": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \"#log\" do\n  let(:output) { StringIO.new }\n\n  it \"logs message\" do\n    progress = TTY::ProgressBar.new(\"[:bar]\", output: output, total: 10)\n    2.times {\n      progress.log \"foo bar\"\n      progress.advance\n    }\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1Gfoo bar\\n\",\n      \"\\e[1G[          ]\",\n      \"\\e[1G[=         ]\",\n      \"\\e[1Gfoo bar     \\n\",\n      \"\\e[1G[=         ]\",\n      \"\\e[1G[==        ]\"\n    ].join)\n  end\n\n  it \"logs message under when complete\" do\n    progress = TTY::ProgressBar.new(\"[:bar]\", output: output, total: 10)\n    progress.advance(10)\n    expect(progress.complete?).to eq(true)\n    progress.log \"foo bar\"\n    output.rewind\n    expect(output.read).to eq(\"\\e[1G[==========]\\nfoo bar\\n\")\n  end\n\n  it \"logs a message first with time and rate metrics\" do\n    Timecop.freeze(Time.local(2021, 1, 23, 12, 0, 0))\n    format = \"[:bar] :elapsed :eta :eta_time :rate/s\"\n    progress = TTY::ProgressBar.new(format, output: output, total: 10)\n\n    3.times do |sec|\n      Timecop.freeze(Time.local(2021, 1, 23, 12, 0, 1 + sec)) do\n        progress.log \"foo bar\"\n        progress.advance\n      end\n    end\n\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1Gfoo bar\\n\",\n      \"\\e[1G[          ]  0s --s --:--:--  0.00/s\", # render without starting\n      \"\\e[1G[=         ]  0s  0s 12:00:01  1.00/s\",\n      \"\\e[1Gfoo bar                              \\n\",\n      \"\\e[1G[=         ]  1s  9s 12:00:11  1.00/s\", # render without advancing\n      \"\\e[1G[==        ]  1s  4s 12:00:06  1.00/s\",\n      \"\\e[1Gfoo bar                              \\n\",\n      \"\\e[1G[==        ]  2s  8s 12:00:11  1.00/s\", # render without advancing\n      \"\\e[1G[===       ]  2s  4s 12:00:07  1.00/s\"\n    ].join)\n    Timecop.return\n  end\n\n  it \"starts timer and logs a message with time and rate metrics\" do\n    Timecop.freeze(Time.local(2021, 1, 23, 12, 0, 0))\n    format = \"[:bar] :elapsed :eta :eta_time :rate/s\"\n    progress = TTY::ProgressBar.new(format, output: output, total: 10)\n\n    Timecop.freeze(Time.local(2021, 1, 23, 12, 0, 1)) do\n      progress.start\n    end\n\n    3.times do |sec|\n      Timecop.freeze(Time.local(2021, 1, 23, 12, 0, 2 + sec)) do\n        progress.log \"foo bar\"\n        progress.advance\n      end\n    end\n\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G[          ]  0s --s --:--:--  0.00/s\", # render start\n      \"\\e[1Gfoo bar                              \\n\",\n      \"\\e[1G[          ]  1s  0s 12:00:02  0.00/s\", # render without starting\n      \"\\e[1G[=         ]  1s  9s 12:00:11  1.00/s\",\n      \"\\e[1Gfoo bar                              \\n\",\n      \"\\e[1G[=         ]  2s 18s 12:00:21  1.00/s\", # render without advancing\n      \"\\e[1G[==        ]  2s  8s 12:00:11  1.00/s\",\n      \"\\e[1Gfoo bar                              \\n\",\n      \"\\e[1G[==        ]  3s 12s 12:00:16  1.00/s\", # render without advancing\n      \"\\e[1G[===       ]  3s  7s 12:00:11  1.00/s\"\n    ].join)\n    Timecop.return\n  end\nend\n"
  },
  {
    "path": "spec/unit/meter_spec.rb",
    "content": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar::Meter, \"#rate\" do\n  before { Timecop.safe_mode = false }\n\n  after { Timecop.return }\n\n  it \"measures with no samples\" do\n    meter = TTY::ProgressBar::Meter.new(1)\n\n    meter.start\n\n    expect(meter.rate).to eq(0)\n    expect(meter.mean_rate).to eq(0)\n  end\n\n  it \"measures with a single sample\" do\n    meter = TTY::ProgressBar::Meter.new(1)\n    start_time = Time.at(10, 0)\n    Timecop.freeze(start_time)\n    meter.start\n\n    meter.sample(Time.at(10, 100_000), 10)\n\n    expect(meter.rate).to eq(100)\n    expect(meter.mean_rate).to eq(100)\n  end\n\n  it \"measures rate per second\" do\n    meter = TTY::ProgressBar::Meter.new(1)\n    start_time = Time.at(10, 0)\n    Timecop.freeze(start_time)\n    meter.start\n\n    # First sample batch\n    meter.sample Time.at(10, 100_000), 5\n    expect(meter.rate).to eq(50)\n    expect(meter.mean_rate).to eq(50)\n\n    meter.sample Time.at(10, 500_000), 5\n    expect(meter.rate).to eq(20)\n    expect(meter.mean_rate).to eq(20)\n\n    meter.sample Time.at(11, 000_000), 5\n    expect(meter.rate).to eq(15)\n    expect(meter.mean_rate).to eq(15)\n\n    meter.sample Time.at(11, 500_000), 5\n    expect(meter.rate).to eq(10)\n    expect(meter.mean_rate).to be_within(0.001).of(13.333)\n\n    meter.sample Time.at(12, 000_000), 10\n    expect(meter.rate).to eq(15)\n    expect(meter.mean_rate).to eq(15)\n  end\n\n  it \"clears measurements\" do\n    meter = TTY::ProgressBar::Meter.new(1)\n    start_time = Time.at(10, 0)\n    Timecop.freeze(start_time)\n    meter.start\n\n    meter.sample(start_time + 1, 1000)\n    expect(meter.rates).to eq([1000, 1000])\n    expect(meter.rate).to eq(1000)\n\n    meter.clear\n    expect(meter.rates).to eq([0])\n    expect(meter.rate).to eq(0)\n  end\nend\n"
  },
  {
    "path": "spec/unit/multi/advance_spec.rb",
    "content": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar::Multi, \"advance\" do\n  let(:output) { RSpec::Support::Ruby.truffleruby? ? OutputIO.new : StringIO.new }\n  let(:save) { TTY::Cursor.save }\n  let(:restore) { TTY::Cursor.restore }\n  let(:top) { TTY::ProgressBar::Multi::DEFAULT_INSET[:top] }\n  let(:middle) { TTY::ProgressBar::Multi::DEFAULT_INSET[:middle] }\n  let(:bottom) { TTY::ProgressBar::Multi::DEFAULT_INSET[:bottom] }\n\n  it \"advances progress bars correctly under multibar\" do\n    bars = described_class.new(output: output)\n\n    bar1 = bars.register(\"[:bar] one\", total: 5)\n    bar2 = bars.register(\"[:bar] two\", total: 5)\n\n    bar2.advance\n    bar1.advance\n\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G[=    ] two\\n\",\n      \"\\e[1G[=    ] one\\n\"\n    ].join)\n\n    bar2.advance\n\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G[=    ] two\\n\",\n      \"\\e[1G[=    ] one\\n\",\n      save,\n      \"\\e[2A\", # up 2 lines\n      \"\\e[1G[==   ] two\",\n      restore\n    ].join)\n\n    bar1.advance\n\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G[=    ] two\\n\",\n      \"\\e[1G[=    ] one\\n\",\n      save,\n      \"\\e[2A\", # up 2 lines\n      \"\\e[1G[==   ] two\",\n      restore,\n      save,\n      \"\\e[1A\", # up 1 line\n      \"\\e[1G[==   ] one\",\n      restore\n    ].join)\n  end\n\n  it \"advances progress bars correctly under top level multibar\" do\n    bars = described_class.new(\"[:bar] main\", output: output)\n\n    bar1 = bars.register(\"[:bar] one\", total: 5)\n    bar2 = bars.register(\"[:bar] two\", total: 5)\n\n    bar2.advance\n    bar1.advance\n\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G#{top}[=         ] main\\n\",\n      \"\\e[1G#{bottom}[=    ] two\\n\",\n      save,\n      \"\\e[2A\",   # up 2 lines\n      \"\\e[1G#{top}[==        ] main\",\n      restore,\n      \"\\e[1G#{bottom}[=    ] one\\n\"\n    ].join)\n\n    bar2.advance\n\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G#{top}[=         ] main\\n\",\n      \"\\e[1G#{bottom}[=    ] two\\n\",\n      save,\n      \"\\e[2A\",   # up 2 lines\n      \"\\e[1G#{top}[==        ] main\",\n      restore,\n      \"\\e[1G#{bottom}[=    ] one\\n\",\n      save,\n      \"\\e[3A\",   # up 3 lines\n      \"\\e[1G#{top}[===       ] main\",\n      restore,\n      save,\n      \"\\e[2A\",   # up 2 lines,\n      \"\\e[1G#{middle}[==   ] two\",\n      restore\n    ].join)\n\n    bar1.advance\n\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G#{top}[=         ] main\\n\",\n      \"\\e[1G#{bottom}[=    ] two\\n\",\n      save,\n      \"\\e[2A\",   # up 2 lines\n      \"\\e[1G#{top}[==        ] main\",\n      restore,\n      \"\\e[1G#{bottom}[=    ] one\\n\",\n      save,\n      \"\\e[3A\",   # up 3 lines\n      \"\\e[1G#{top}[===       ] main\",\n      restore,\n      save,\n      \"\\e[2A\",   # up 2 lines,\n      \"\\e[1G#{middle}[==   ] two\",\n      restore,\n      save,\n      \"\\e[3A\",   # up 3 lines\n      \"\\e[1G#{top}[====      ] main\",\n      restore,\n      save,\n      \"\\e[1A\",   # up 1 line\n      \"\\e[1G#{bottom}[==   ] one\",\n      restore\n    ].join)\n  end\n\n  it \"advances progress bars correctly with indeterminate children\" do\n    bars = described_class.new(\"total: [:bar] :current\", width: 10,\n                                                         output: output)\n\n    bar1 = bars.register(\"one: [:bar] :current\", width: 10)\n    bar2 = bars.register(\"two: [:bar] :current\", width: 10)\n\n    bar1.advance\n\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G#{top}total: [<=>       ] 1\\n\",\n      \"\\e[1G#{bottom}one: [<=>       ] 1\\n\"\n    ].join)\n\n    bar2.advance\n\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G#{top}total: [<=>       ] 1\\n\",\n      \"\\e[1G#{bottom}one: [<=>       ] 1\\n\",\n      save,\n      \"\\e[2A\", # up 2 lines\n      \"\\e[1G#{top}total: [<=>       ] 2\",\n      restore,\n      \"\\e[1G#{bottom}two: [<=>       ] 1\\n\"\n    ].join)\n\n    bar1.advance\n\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G#{top}total: [<=>       ] 1\\n\",\n      \"\\e[1G#{bottom}one: [<=>       ] 1\\n\",\n      save,\n      \"\\e[2A\", # up 2 lines\n      \"\\e[1G#{top}total: [<=>       ] 2\",\n      restore,\n      \"\\e[1G#{bottom}two: [<=>       ] 1\\n\",\n      save,\n      \"\\e[3A\", # up 3 lines\n      \"\\e[1G#{top}total: [<=>       ] 3\",\n      restore,\n      save,\n      \"\\e[2A\", # up 2 lines\n      \"\\e[1G#{middle}one: [<=>       ] 2\",\n      restore\n    ].join)\n  end\nend\n"
  },
  {
    "path": "spec/unit/multi/events_spec.rb",
    "content": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar::Multi, \"events\" do\n  let(:output) { StringIO.new }\n\n  it \"emits :progress event when any of the registerd bars advances\" do\n    events = []\n    bars = described_class.new(\"[:bar]\", output: output, total: 5)\n    bars.on(:progress) { events << :progress }\n\n    bar = bars.register \"one [:bar]\"\n    bar.advance\n\n    expect(events).to eq([:progress])\n  end\n\n  it \"emits :done event when all progress bars finished under top level\" do\n    events = []\n    bars = described_class.new(\"[:bar]\", output: output, total: 5)\n    bars.on(:done) { events << :done }\n    bar = bars.register \"one [:bar]\"\n\n    bar.finish\n\n    expect(events).to eq([:done])\n    expect(bar.complete?).to eq(true)\n  end\n\n  it \"emits :done event when all progress bars finished without top level\" do\n    events = []\n    bars = described_class.new(output: output)\n    bars.on(:done) { events << :done }\n    bar = bars.register \"one [:bar]\", total: 5\n\n    bar.finish\n\n    expect(events).to eq([:done])\n    expect(bars.complete?).to eq(true)\n  end\n\n  it \"emits :done event when top level registered bar finished\" do\n    events = []\n    bars = described_class.new(\"[:bar]\", output: output, total: 5)\n    bars.on(:done) { events << :done }\n\n    bar = bars.register \"one [:bar]\", total: 5\n\n    bars.finish\n\n    expect(events).to eq([:done])\n    expect(bar.complete?).to eq(true)\n  end\n\n  it \"emits :done event when top level bar finished\" do\n    events = []\n    bars = described_class.new(output: output)\n    bars.on(:done) { events << :done }\n\n    bar = bars.register \"one [:bar]\", total: 5\n\n    bars.finish\n\n    expect(events).to eq([:done])\n    expect(bar.complete?).to eq(true)\n  end\n\n  it \"emits :stopped event when all registerd bars are stopped under top level\" do\n    events = []\n    bars = described_class.new(\"[:bar]\", output: output, total: 5)\n    bars.on(:stopped) { events << :stopped }\n\n    bar = bars.register \"one [:bar]\"\n\n    bar.stop\n\n    expect(events).to eq([:stopped])\n    expect(bars.stopped?).to eq(true)\n  end\n\n  it \"emits :stopped event when all registerd bars are stopped without top level\" do\n    events = []\n    bars = described_class.new(output: output)\n    bars.on(:stopped) { events << :stopped }\n\n    bar = bars.register \"one [:bar]\", total: 5\n\n    bar.stop\n\n    expect(events).to eq([:stopped])\n    expect(bars.stopped?).to eq(true)\n  end\n\n  it \"emits :stopped event when registerd multi bar finished\" do\n    events = []\n    bars = described_class.new(\"[:bar]\", output: output, total: 5)\n    bars.on(:stopped) { events << :stopped }\n\n    bars.register \"one [:bar]\"\n\n    bars.stop\n\n    expect(events).to eq([:stopped])\n  end\n\n  it \"emits :stopped event when multi bar finished\" do\n    events = []\n    bars = described_class.new(output: output)\n    bars.on(:stopped) { events << :stopped }\n\n    bars.register \"one [:bar]\", total: 5\n\n    bars.stop\n\n    expect(events).to eq([:stopped])\n    expect(bars.stopped?).to eq(true)\n  end\n\n  it \"emits :paused event when all registerd bars are paused under top level\" do\n    events = []\n    bars = described_class.new(\"[:bar]\", output: output, total: 5)\n    bars.on(:paused) { events << :paused }\n\n    bar = bars.register \"one [:bar]\"\n\n    bar.pause\n\n    expect(events).to eq([:paused])\n    expect(bars.paused?).to eq(true)\n  end\n\n  it \"emits :paused event when all registerd bars are paused without top level\" do\n    events = []\n    bars = described_class.new(output: output)\n    bars.on(:paused) { events << :paused }\n\n    bar = bars.register \"one [:bar]\", total: 5\n\n    bar.pause\n\n    expect(events).to eq([:paused])\n    expect(bars.paused?).to eq(true)\n  end\n\n  it \"emits :paused event when registerd multi bar pauses\" do\n    events = []\n    bars = described_class.new(\"[:bar]\", output: output, total: 5)\n    bars.on(:paused) { events << :paused }\n\n    bars.register \"one [:bar]\"\n\n    bars.pause\n\n    expect(events).to eq([:paused])\n  end\n\n  it \"raises when trying to register an unknown event\" do\n    bars = described_class.new(\"[:bar]\")\n\n    expect {\n      bars.on(:unknown)\n    }.to raise_error(\n      ArgumentError,\n      \"The event unknown does not exist. Use :progress, :stopped, :paused or \" \\\n      \":done instead\"\n    )\n  end\nend\n"
  },
  {
    "path": "spec/unit/multi/finish_spec.rb",
    "content": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar::Multi, \"#finish\" do\n  let(:output) { StringIO.new }\n\n  it \"finishes all bars with top level\" do\n    bars = described_class.new(\"main [:bar]\", output: output)\n\n    bar1 = bars.register(\"[:bar]\", total: 5)\n    bar2 = bars.register(\"[:bar]\", total: 10)\n\n    expect(bars.complete?).to eq(false)\n\n    bar1.finish\n    bar2.finish\n\n    expect(bars.complete?).to eq(true)\n  end\n\n  it \"finishes all bars without top level\" do\n    bars = described_class.new(output: output)\n\n    bar1 = bars.register(\"[:bar]\", total: 5)\n    bar2 = bars.register(\"[:bar]\", total: 10)\n\n    bar1.finish\n    bar2.finish\n\n    expect(bars.complete?).to eq(true)\n  end\n\n  it \"finishes top level\" do\n    bars = described_class.new(output: output)\n\n    bar1 = bars.register(\"[:bar]\", total: 5)\n    bar2 = bars.register(\"[:bar]\", total: 10)\n\n    bars.finish\n\n    expect(bar1.complete?).to eq(true)\n    expect(bar2.complete?).to eq(true)\n  end\nend\n"
  },
  {
    "path": "spec/unit/multi/line_inset_spec.rb",
    "content": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar::Multi, \"#line_inset\" do\n  let(:output) { StringIO.new }\n\n  it \"doesn't create inset when no top level bar\" do\n    bars = TTY::ProgressBar::Multi.new(output: output)\n\n    bar = bars.register \"example\"\n\n    expect(bars.line_inset(bar)).to eq(\"\")\n  end\n\n  it \"defaults to the empty string for the top level bar\" do\n    bars = TTY::ProgressBar::Multi.new(\"Top level spinner\", output: output)\n\n    expect(bars.line_inset(bars.top_bar))\n      .to eq(TTY::ProgressBar::Multi::DEFAULT_INSET[:top])\n  end\n\n  it \"returns middle character for a top level bar\" do\n    bars = TTY::ProgressBar::Multi.new(\"Top level bar\", output: output)\n\n    bar = bars.register \"middle\", total: 10\n    bar2 = bars.register \"bottom\", total: 10\n\n    bar.start\n    bar2.start\n\n    expect(bars.line_inset(bar))\n      .to eq(TTY::ProgressBar::Multi::DEFAULT_INSET[:middle])\n  end\n\n  it \"decorates last bar\" do\n    bars = TTY::ProgressBar::Multi.new(\"Top spinner\", output: output)\n\n    bar1 = bars.register \"middle\", total: 10\n    bar = bars.register \"bottom\", total: 10\n\n    bar1.start\n    bar.start\n\n    expect(bars.line_inset(bar))\n      .to eq(TTY::ProgressBar::Multi::DEFAULT_INSET[:bottom])\n  end\n\n  it \"allows customization\" do\n    opts = {\n      output: output,\n      style: {\n        top: \". \",\n        middle: \"--\",\n        bottom: \"---\"\n      }\n    }\n    bars = TTY::ProgressBar::Multi.new(\"Top level spinner\", opts)\n    middle_bar = bars.register \"\", total: 10\n    bottom_bar = bars.register \"\", total: 10\n\n    middle_bar.start\n    bottom_bar.start\n\n    expect(bars.line_inset(bars.top_bar)).to eq(\". \")\n    expect(bars.line_inset(middle_bar)).to eq(\"--\")\n    expect(bars.line_inset(bottom_bar)).to eq(\"---\")\n  end\nend\n"
  },
  {
    "path": "spec/unit/multi/pause_spec.rb",
    "content": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar::Multi, \"#pause\" do\n  let(:output) { StringIO.new }\n\n  it \"pauses all bars when top level pauses\" do\n    bars = TTY::ProgressBar::Multi.new(\"main [:bar]\", output: output)\n\n    bar1 = bars.register(\"[:bar]\", total: 5)\n    bar2 = bars.register(\"[:bar]\", total: 10)\n\n    bars.pause\n\n    expect(bar1.paused?).to eq(true)\n    expect(bar2.paused?).to eq(true)\n    expect(bars.paused?).to eq(true)\n  end\n\n  it \"doesn't pause top bar when child pauses\" do\n    bars = TTY::ProgressBar::Multi.new(\"main [:bar]\", output: output)\n\n    bar1 = bars.register(\"[:bar]\", total: 5)\n    bar2 = bars.register(\"[:bar]\", total: 10)\n\n    bar1.pause\n\n    expect(bars.paused?).to eq(false)\n    expect(bar1.paused?).to eq(true)\n    expect(bar2.paused?).to eq(false)\n  end\nend\n"
  },
  {
    "path": "spec/unit/multi/register_spec.rb",
    "content": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar::Multi, \"#register\" do\n  let(:output) { StringIO.new }\n\n  it \"registers a TTY::ProgressBar instance\" do\n    bars = TTY::ProgressBar::Multi.new(output: output)\n    allow_any_instance_of(TTY::ProgressBar).to receive(:attach_to)\n    expect_any_instance_of(TTY::ProgressBar).to receive(:attach_to)\n\n    bar = bars.register(\"[:bar]\")\n\n    expect(bar).to be_instance_of(TTY::ProgressBar)\n    expect(bars.length).to eq(1)\n  end\n\n  it \"uses global options to register instance\" do\n    bars = TTY::ProgressBar::Multi.new(output: output, total: 100)\n    bar = double(:bar, attach_to: nil)\n    allow(bar).to receive(:on).and_return(bar)\n    allow(TTY::ProgressBar).to receive(:new).and_return(bar)\n\n    bars.register(\"[:bar]\")\n\n    expect(TTY::ProgressBar).to have_received(:new)\n      .with(\"[:bar]\", { total: 100, output: output })\n  end\n\n  it \"registers bars with top level\" do\n    bars = TTY::ProgressBar::Multi.new(\"main [:bar]\", output: output)\n\n    bars.register(\"[:bar]\", total: 5)\n    bars.register(\"[:bar]\", total: 10)\n\n    expect(bars.total).to eq(15)\n    expect(bars.current).to eq(0)\n  end\n\n  it \"updates total based on children totals\" do\n    bars = TTY::ProgressBar::Multi.new(\"main [:bar]\", output: output)\n\n    bars.register(\"[:bar]\", total: 1)\n    expect(bars.total).to eq(1)\n\n    bars.register(\"[:bar]\", total: 1)\n    expect(bars.total).to eq(2)\n  end\n\n  it \"can register indeterminate children\" do\n    bars = TTY::ProgressBar::Multi.new(\"main [:bar]\", output: output)\n    bars.register(\"[:bar]\")\n    bars.register(\"[:bar]\")\n\n    expect(bars.total).to eq(nil)\n  end\nend\n"
  },
  {
    "path": "spec/unit/multi/reset_spec.rb",
    "content": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar::Multi, \"#reset\" do\n  let(:output) { StringIO.new }\n\n  it \"leaves multibar state alone\" do\n    main = TTY::ProgressBar::Multi.new(\"\", output: output, total: 10)\n    progress = main.register(\"[:bar]\")\n    progress.advance(10)\n    expect(progress.complete?).to be(true)\n    progress.reset\n    expect(progress.complete?).to be(false)\n    progress.advance(10)\n    output.rewind\n\n    top    = TTY::ProgressBar::Multi::DEFAULT_INSET[:top]\n    bottom = TTY::ProgressBar::Multi::DEFAULT_INSET[:bottom]\n\n    progress_updates =\n      output.read.scan(/#{Regexp.escape top}|#{Regexp.escape bottom}/)\n    expect(progress_updates.shift).to match(top)\n    expect(progress_updates.shift).to match(top)\n    expect(progress_updates.shift).to match(bottom)\n    expect(progress_updates.shift).to match(bottom)\n    expect(progress_updates.shift).to match(bottom)\n    expect(progress_updates.shift).to match(bottom)\n\n    expect(progress_updates).to be_empty\n  end\nend\n"
  },
  {
    "path": "spec/unit/multi/resume_spec.rb",
    "content": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar::Multi, \"#resume\" do\n  let(:output) { StringIO.new }\n  let(:save)    { TTY::Cursor.save }\n  let(:restore) { TTY::Cursor.restore }\n  let(:top) { TTY::ProgressBar::Multi::DEFAULT_INSET[:top] }\n  let(:middle) { TTY::ProgressBar::Multi::DEFAULT_INSET[:middle] }\n  let(:bottom) { TTY::ProgressBar::Multi::DEFAULT_INSET[:bottom] }\n\n  it \"resumes top bar when new bar registered and advanced\" do\n    top_bar = described_class.new(\"[:bar] top (:current/:total)\", output: output)\n\n    bar1 = top_bar.register(\"[:bar] one\", total: 5)\n    bar1.advance(5)\n\n    expect(top_bar.done?).to eq(true)\n    expect(bar1.done?).to eq(true)\n\n    bar2 = top_bar.register(\"[:bar] two\", total: 5)\n    bar2.advance\n\n    output.rewind\n    expect(output.string).to eq([\n      \"\\e[1G#{top}[=====] top (5/5)\\n\",\n      save,\n      \"\\e[1A\",\n      \"#{top}\\n\",\n      restore,\n      \"\\e[1G#{bottom}[=====] one\\n\",\n      save,\n      \"\\e[1A#{bottom}\\n\",\n      restore,\n      save,\n      \"\\e[2A\", # up 2 lines\n      \"\\e[1G#{top}[======    ] top (6/10)\",\n      restore,\n      \"\\e[1G#{bottom}[=    ] two\\n\"\n    ].join)\n\n    expect(top_bar.done?).to eq(false)\n    expect(bar1.done?).to eq(true)\n    expect(bar2.done?).to eq(false)\n  end\n\n  it \"resumes all paused bars\" do\n    bars = described_class.new(\"[:bar] top (:current/:total)\", output: output)\n\n    bar1 = bars.register(\"[:bar] one\", total: 5)\n    bar2 = bars.register(\"[:bar] two\", total: 5)\n\n    bar1.pause\n\n    bar1.advance\n    bar2.advance\n\n    expect(bar1.paused?).to eq(true)\n    expect(bar2.paused?).to eq(false)\n    expect(bars.paused?).to eq(false)\n\n    output.rewind\n    expect(output.string).to eq([\n      \"\\e[1G#{top}[=         ] top (1/10)\\n\",\n      \"\\e[1G#{bottom}[=    ] two\\n\"\n    ].join)\n\n    bars.resume\n    bar1.advance\n    bar2.advance\n\n    expect(bar1.paused?).to eq(false)\n    expect(bar2.paused?).to eq(false)\n    expect(bars.paused?).to eq(false)\n\n    output.rewind\n    expect(output.string).to eq([\n      save,\n      \"\\e[2A\", # up 2 lines\n      \"\\e[1G#{top}[==        ] top (2/10)\",\n      restore,\n      \"\\e[1G#{bottom}[=    ] one\\n\",\n      save,\n      \"\\e[3A\", # up 3 lines\n      \"\\e[1G#{top}[===       ] top (3/10)\",\n      restore,\n      save,\n      \"\\e[2A\", # up 2 lines\n      \"\\e[1G#{middle}[==   ] two\",\n      restore\n    ].join)\n  end\nend\n"
  },
  {
    "path": "spec/unit/multi/stop_spec.rb",
    "content": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar::Multi, \"#stop\" do\n  let(:output) { StringIO.new }\n\n  it \"stops all bars when top level stops\" do\n    bars = described_class.new(\"main [:bar]\", output: output)\n\n    bar1 = bars.register(\"[:bar]\", total: 5)\n    bar2 = bars.register(\"[:bar]\", total: 10)\n\n    bars.stop\n\n    expect(bar1.stopped?).to eq(true)\n    expect(bar2.stopped?).to eq(true)\n    expect(bars.stopped?).to eq(true)\n  end\n\n  it \"doesn't stop top bar when child stops\" do\n    bars = described_class.new(\"main [:bar]\", output: output)\n\n    bar1 = bars.register(\"[:bar]\", total: 5)\n    bar2 = bars.register(\"[:bar]\", total: 10)\n\n    bar1.stop\n\n    expect(bars.stopped?).to eq(false)\n    expect(bar1.stopped?).to eq(true)\n    expect(bar2.stopped?).to eq(false)\n  end\nend\n"
  },
  {
    "path": "spec/unit/multi/width_spec.rb",
    "content": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar::Multi, \"width\" do\n  let(:output) { RSpec::Support::Ruby.truffleruby? ? OutputIO.new : StringIO.new }\n  let(:save) { TTY::Cursor.save }\n  let(:restore) { TTY::Cursor.restore }\n  let(:top) { TTY::ProgressBar::Multi::DEFAULT_INSET[:top] }\n  let(:middle) { TTY::ProgressBar::Multi::DEFAULT_INSET[:middle] }\n  let(:bottom) { TTY::ProgressBar::Multi::DEFAULT_INSET[:bottom] }\n\n  it \"sets top level bar width to maximum columns when exceeds terminal width\" do\n    allow(TTY::Screen).to receive(:width).and_return(15)\n\n    bars = TTY::ProgressBar::Multi.new(\"[:bar] main\", output: output)\n\n    bar1 = bars.register(\"[:bar] one\", total: 20)\n    bar2 = bars.register(\"[:bar] two\", total: 20)\n\n    bar1.advance(10)\n    bar2.advance(10)\n\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G#{top}[==    ] main\\n\",\n      \"\\e[1G#{bottom}[===  ] one\\n\",\n      save,\n      \"\\e[2A\",   # up 2 lines\n      \"\\e[1G#{top}[===   ] main\",\n      restore,\n      \"\\e[1G#{bottom}[===  ] two\\n\"\n    ].join)\n\n    bar1.advance(10)\n\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G#{top}[==    ] main\\n\",\n      \"\\e[1G#{bottom}[===  ] one\\n\",\n      save,\n      \"\\e[2A\",   # up 2 lines\n      \"\\e[1G#{top}[===   ] main\",\n      restore,\n      \"\\e[1G#{bottom}[===  ] two\\n\",\n      save,\n      \"\\e[3A\",   # up 3 lines\n      \"\\e[1G#{top}[===== ] main\",\n      restore,\n      save,\n      \"\\e[2A\",   # up 2 lines\n      \"\\e[1G#{middle}[=====] one\",\n      restore,\n      save,\n      \"\\e[2A\",   # up 2 lines\n      \"#{middle}\\n\", # bar finished\n      restore\n    ].join)\n\n    bar2.advance(10)\n\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G#{top}[==    ] main\\n\",\n      \"\\e[1G#{bottom}[===  ] one\\n\",\n      save,\n      \"\\e[2A\",   # up 2 lines\n      \"\\e[1G#{top}[===   ] main\",\n      restore,\n      \"\\e[1G#{bottom}[===  ] two\\n\",\n      save,\n      \"\\e[3A\",   # up 3 lines\n      \"\\e[1G#{top}[===== ] main\",\n      restore,\n      save,\n      \"\\e[2A\",   # up 2 lines\n      \"\\e[1G#{middle}[=====] one\",\n      restore,\n      save,\n      \"\\e[2A\",   # up 2 lines\n      \"#{middle}\\n\", # bar finished\n      restore,\n      save,\n      \"\\e[3A\", # up 3 lines\n      \"\\e[1G#{top}[======] main\",\n      restore,\n      save,\n      \"\\e[3A\",  # up 1 line\n      \"#{top}\\n\",\n      restore,\n      save,\n      \"\\e[1A\",  # up 1 line\n      \"\\e[1G#{bottom}[=====] two\",\n      restore,\n      save,\n      \"\\e[1A\",  # up 1 line\n      \"#{bottom}\\n\",\n      restore\n    ].join)\n  end\n\n  it \"sets top level bar width to a custom value\" do\n    bars = TTY::ProgressBar::Multi.new(\"[:bar] main\", output: output, width: 20)\n\n    bar1 = bars.register(\"[:bar] one\", total: 20)\n    bar2 = bars.register(\"[:bar] two\", total: 20)\n\n    bar1.advance(10)\n    bar2.advance(10)\n\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G#{top}[=====               ] main\\n\",\n      \"\\e[1G#{bottom}[==========          ] one\\n\",\n      save,\n      \"\\e[2A\",   # up 2 lines\n      \"\\e[1G#{top}[==========          ] main\",\n      restore,\n      \"\\e[1G#{bottom}[==========          ] two\\n\"\n    ].join)\n  end\n\n  it \"doesn't attempt setting top level bar width without the format\" do\n    expect {\n      TTY::ProgressBar::Multi.new(output: output, width: 20)\n    }.to_not raise_error\n  end\nend\n"
  },
  {
    "path": "spec/unit/new_spec.rb",
    "content": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \".new\" do\n  let(:output) { StringIO.new }\n\n  it \"fails to initialize without a bar formatting string\" do\n    expect {\n      TTY::ProgressBar.new(total: 10)\n    }.to raise_error(\n      ArgumentError,\n      /Expected bar formatting string, got `{:?total(=>|: )10}` instead\\./\n    )\n  end\n\n  it \"allows to change formatting string\" do\n    bar = TTY::ProgressBar.new(\"[:bar]\", output: output, total: 4)\n    bar.advance(2)\n    bar.format = \"(:bar)\"\n    bar.advance(2)\n    output.rewind\n\n    expect(output.read).to eq(\"\\e[1G[==  ]\\e[1G(====)\\n\")\n  end\n\n  it \"displays output where width == total\" do\n    progress = described_class.new(\"[:bar]\", output: output, total: 10)\n    progress.advance\n    output.rewind\n    expect(output.read).to eq(\"\\e[1G[=         ]\")\n  end\n\n  it \"yields configuration to block\" do\n    progress = described_class.new \"[:bar]\" do |config|\n      config.output = output\n      config.total = 10\n      config.width = 40\n      config.interval = 30\n      config.frequency = 1.5\n      config.head = \">>\"\n      config.complete = \"#\"\n      config.incomplete = \"-\"\n      config.unknown = \"?\"\n      config.clear = true\n      config.clear_head = true\n      config.hide_cursor = true\n      config.bar_format = :block\n    end\n\n    expect(progress.output).to eq(output)\n    expect(progress.total).to eq(10)\n    expect(progress.width).to eq(40)\n    expect(progress.clear).to eq(true)\n    expect(progress.interval).to eq(30)\n    expect(progress.frequency).to eq(1.5)\n    expect(progress.head).to eq(\">>\")\n    expect(progress.complete).to eq(\"#\")\n    expect(progress.incomplete).to eq(\"-\")\n    expect(progress.unknown).to eq(\"?\")\n    expect(progress.clear_head).to eq(true)\n    expect(progress.hide_cursor).to eq(true)\n    expect(progress.bar_format).to eq(:block)\n  end\n\n  it \"raises error when bar format is set to unsupported type\" do\n    expect {\n      described_class.new \"[:bar]\", bar_format: :unknown\n    }.to raise_error(\n      ArgumentError,\n      \"unsupported bar format: :unknown. Available formats are: \" \\\n      \":arrow, :asterisk, :blade, :block, :box, :bracket, \" \\\n      \":burger, :button, :chevron, :circle, :classic, :crate, :diamond, :dot, \" \\\n      \":heart, :rectangle, :square, :star, :track, :tread, :triangle, :wave\"\n    )\n  end\n\n  it \"overrides option configuration inside a block\" do\n    bar = described_class.new(\":bar\", complete: \"#\") do |config|\n      config.complete = \"x\"\n    end\n\n    expect(bar.complete).to eq(\"x\")\n  end\n\n  it \"displays output where width > total\" do\n    progress = TTY::ProgressBar.new(\"[:bar]\", output: output, total: 5, width: 10)\n    5.times { progress.advance }\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G[==        ]\",\n      \"\\e[1G[====      ]\",\n      \"\\e[1G[======    ]\",\n      \"\\e[1G[========  ]\",\n      \"\\e[1G[==========]\\n\"\n    ].join)\n  end\n\n  it \"displays output where width < total\" do\n    progress = TTY::ProgressBar.new(\"[:bar]\", output: output, total: 10, width: 5)\n    10.times { progress.advance }\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G[=    ]\",\n      \"\\e[1G[=    ]\",\n      \"\\e[1G[==   ]\",\n      \"\\e[1G[==   ]\",\n      \"\\e[1G[===  ]\",\n      \"\\e[1G[===  ]\",\n      \"\\e[1G[==== ]\",\n      \"\\e[1G[==== ]\",\n      \"\\e[1G[=====]\",\n      \"\\e[1G[=====]\\n\"\n    ].join)\n  end\n\n  it \"displays total value\" do\n    progress = TTY::ProgressBar.new(\"|:total|\", output: output, total: 10)\n    progress.advance(3)\n    output.rewind\n    expect(output.read).to eq(\"\\e[1G|10|\")\n  end\nend\n"
  },
  {
    "path": "spec/unit/pause_spec.rb",
    "content": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \"#pause\" do\n  let(:output) { StringIO.new }\n\n  it \"pauses progress without printing newline\" do\n    progress = TTY::ProgressBar.new(\"[:bar]\", output: output, total: 10)\n    5.times { |i|\n      progress.pause if i == 3\n      progress.advance\n    }\n    expect(progress.complete?).to be(false)\n    expect(progress.paused?).to be(true)\n\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G[=         ]\",\n      \"\\e[1G[==        ]\",\n      \"\\e[1G[===       ]\"\n    ].join)\n  end\nend\n"
  },
  {
    "path": "spec/unit/pipeline_spec.rb",
    "content": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar::Pipeline, \"#decorate\" do\n  subject(:pipeline) { described_class.new }\n\n  it \"decorates tokenized string with pipeline formatters\" do\n    progress_bar = double(current: \"3\", total: \"10\", indeterminate?: false)\n    pipeline.use TTY::ProgressBar::CurrentFormatter.new(progress_bar)\n    pipeline.use TTY::ProgressBar::TotalFormatter.new(progress_bar)\n    tokenized = \"[:current/:total]\"\n    expect(pipeline.decorate(tokenized)).to eq(\"[3/10]\")\n  end\n\n  it \"enumerates pipeline formatters\" do\n    progress_bar = double(current: \"3\", total: \"10\")\n    pipeline.use TTY::ProgressBar::CurrentFormatter.new(progress_bar)\n    pipeline.use TTY::ProgressBar::TotalFormatter.new(progress_bar)\n    yielded = []\n    pipeline.each { |formatter| yielded << formatter }\n    expect(yielded.size).to eq(2)\n  end\nend\n"
  },
  {
    "path": "spec/unit/ratio_spec.rb",
    "content": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \"#ratio=\" do\n  let(:output) { StringIO.new }\n\n  it \"allows to set ratio\" do\n    progress = TTY::ProgressBar.new(\"[:bar]\", output: output, total: 10)\n    progress.ratio = 0.7\n    expect(progress.current).to eq(7)\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G[=======   ]\"\n    ].join)\n  end\n\n  it \"finds closest available step from the ratio\" do\n    progress = TTY::ProgressBar.new(\"[:bar]\", output: output, total: 3)\n    progress.ratio = 0.5\n    expect(progress.current).to eq(1)\n  end\n\n  it \"doesn't allow to set wrong ratio\" do\n    progress = TTY::ProgressBar.new(\"[:bar]\", output: output, total: 3)\n    progress.ratio = 3.2\n    expect(progress.current).to eq(3)\n    expect(progress.complete?).to eq(true)\n  end\n\n  it \"avoids division by zero\" do\n    progress = TTY::ProgressBar.new(\"[:bar]\", output: output, total: 0)\n    expect(progress.ratio).to eq(0)\n  end\nend\n"
  },
  {
    "path": "spec/unit/render_spec.rb",
    "content": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \"#render\" do\n  let(:output) { StringIO.new }\n\n  it \"pads out longer previous lines\" do\n    progress = TTY::ProgressBar.new \":current_byte\" do |config|\n      config.output = output\n    end\n\n    progress.advance(1)\n    progress.advance(1_048_574)\n    progress.advance(1)\n    progress.advance(1)\n\n    output.rewind\n\n    expect(output.read).to eq([\n      \"\\e[1G1B\",\n      \"\\e[1G1024.00KB\", # must not pad, line is longer\n      \"\\e[1G1.00MB   \", # must pad out \"0KB\"\n      \"\\e[1G1.00MB\"    # must not pad, line is equal\n    ].join)\n  end\nend\n"
  },
  {
    "path": "spec/unit/reset_spec.rb",
    "content": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \"#reset\" do\n  let(:output) { StringIO.new }\n\n  it \"resets current progress\" do\n    progress = TTY::ProgressBar.new(\"[:bar]\", output: output, total: 10)\n    progress.advance(5)\n    progress.reset\n    2.times { progress.advance(3) }\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G[=====     ]\",\n      \"\\e[1G[===       ]\",\n      \"\\e[1G[======    ]\"\n    ].join)\n  end\n\n  it \"resets finished progress\" do\n    progress = TTY::ProgressBar.new(\"[:bar]\", output: output, total: 10)\n    progress.advance(10)\n    expect(progress.complete?).to be(true)\n    progress.reset\n    expect(progress.complete?).to be(false)\n    2.times { progress.advance(3) }\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G[==========]\\n\",\n      \"\\e[1G[===       ]\",\n      \"\\e[1G[======    ]\"\n    ].join)\n  end\nend\n"
  },
  {
    "path": "spec/unit/resize_spec.rb",
    "content": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \"#resize\" do\n  let(:output) { StringIO.new }\n\n  it \"resizes output down by x2\" do\n    progress = TTY::ProgressBar.new(\"[:bar]\", output: output, total: 5, width: 10)\n    2.times { progress.advance }\n    progress.resize(5)\n    3.times { progress.advance }\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G[==        ]\",\n      \"\\e[1G[====      ]\",\n      \"\\e[0m\\e[2K\\e[1G\",\n      \"\\e[1G[===  ]     \",\n      \"\\e[1G[==== ]\",\n      \"\\e[1G[=====]\\n\"\n    ].join)\n  end\n\n  it \"resizes output up by x2\" do\n    progress = TTY::ProgressBar.new(\"[:bar]\", output: output, total: 5, width: 10)\n    2.times { progress.advance }\n    progress.resize(20)\n    3.times { progress.advance }\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G[==        ]\",\n      \"\\e[1G[====      ]\",\n      \"\\e[0m\\e[2K\\e[1G\",\n      \"\\e[1G[============        ]\",\n      \"\\e[1G[================    ]\",\n      \"\\e[1G[====================]\\n\"\n    ].join)\n  end\nend\n"
  },
  {
    "path": "spec/unit/resume_spec.rb",
    "content": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \"#resume\" do\n  let(:output) { StringIO.new }\n\n  it \"resumes stopped progression\" do\n    progress = described_class.new(\"[:bar]\", output: output, total: 10)\n\n    3.times { progress.advance }\n    progress.stop\n    expect(progress.stopped?).to eq(true)\n\n    progress.resume\n    expect(progress.stopped?).to eq(false)\n    3.times { progress.advance }\n\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G[=         ]\",\n      \"\\e[1G[==        ]\",\n      \"\\e[1G[===       ]\",\n      \"\\e[1G[===       ]\\n\", # stop render\n      \"\\e[1G[====      ]\",\n      \"\\e[1G[=====     ]\",\n      \"\\e[1G[======    ]\"\n    ].join)\n  end\n\n  it \"resumes stopped progression with all the metrics\" do\n    Timecop.freeze(Time.local(2021, 1, 9, 12, 0, 0))\n    format = \"[:bar] :current/:total :percent \" \\\n             \":current_byte/:total_byte :byte_rate/s :mean_byte/s \" \\\n             \":rate/s :mean_rate/s :elapsed :eta\"\n\n    progress = described_class.new(format, output: output, total: 10)\n\n    3.times do |i|\n      Timecop.freeze(Time.local(2021, 1, 9, 12, 0, 1 + i))\n      progress.advance\n    end\n    progress.stop\n    expect(progress.stopped?).to eq(true)\n\n    progress.resume\n    expect(progress.stopped?).to eq(false)\n    3.times do |i|\n      Timecop.freeze(Time.local(2021, 1, 9, 12, 0, 4 + i))\n      progress.advance\n    end\n\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G[=         ] 1/10 10% 1B/10B 1B/s 1B/s  1.00/s  1.00/s  0s  0s\",\n      \"\\e[1G[==        ] 2/10 20% 2B/10B 1B/s 1B/s  1.00/s  1.00/s  1s  4s\",\n      \"\\e[1G[===       ] 3/10 30% 3B/10B 1B/s 1B/s  1.00/s  1.00/s  2s  4s\",\n      \"\\e[1G[===       ] 3/10 30% 3B/10B 1B/s 1B/s  1.00/s  1.00/s  2s  4s\\n\", # stop render\n      \"\\e[1G[====      ] 4/10 40% 4B/10B 1B/s 1B/s  1.00/s  1.00/s  2s  3s\",\n      \"\\e[1G[=====     ] 5/10 50% 5B/10B 1B/s 1B/s  1.00/s  1.00/s  3s  3s\",\n      \"\\e[1G[======    ] 6/10 60% 6B/10B 1B/s 1B/s  1.00/s  1.00/s  4s  2s\"\n    ].join)\n  end\n\n  it \"resumes finished progression with all the metrics\" do\n    time_now = Time.local(2021, 1, 10, 12, 0, 0)\n    Timecop.freeze(time_now)\n    format = \"[:bar] :current/:total :percent \" \\\n             \":current_byte/:total_byte :byte_rate/s :mean_byte/s \" \\\n             \":rate/s :mean_rate/s :elapsed :eta\"\n\n    progress = described_class.new(format, output: output, total: 10)\n\n    3.times do |i|\n      Timecop.freeze(Time.local(2021, 1, 10, 12, 0, 1 + i))\n      progress.advance\n    end\n    progress.finish\n    expect(progress.complete?).to eq(true)\n\n    progress.update(total: 13)\n    progress.resume\n    expect(progress.complete?).to eq(false)\n\n    3.times do |i|\n      Timecop.freeze(Time.local(2021, 1, 10, 12, 0, 4 + i))\n      progress.advance\n    end\n\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G[=         ] 1/10 10% 1B/10B 1B/s 1B/s  1.00/s  1.00/s  0s  0s\",\n      \"\\e[1G[==        ] 2/10 20% 2B/10B 1B/s 1B/s  1.00/s  1.00/s  1s  4s\",\n      \"\\e[1G[===       ] 3/10 30% 3B/10B 1B/s 1B/s  1.00/s  1.00/s  2s  4s\",\n      \"\\e[1G[==========] 10/10 100% 10B/10B 1B/s 1B/s  1.00/s  1.00/s  2s  0s\\n\", # finish render\n      \"\\e[1G[========  ] 11/13 84% 11B/13B 1B/s 1B/s  1.00/s  1.00/s  2s  0s \",\n      \"\\e[1G[========= ] 12/13 92% 12B/13B 1B/s 1B/s  1.00/s  1.00/s  3s  0s\",\n      \"\\e[1G[==========] 13/13 100% 13B/13B 1B/s 1B/s  1.00/s  1.00/s  4s  0s\\n\"\n    ].join)\n    Timecop.return\n  end\n\n  it \"resumes paused progression with all the metrics\" do\n    Timecop.freeze(Time.local(2021, 1, 11, 12, 0, 0))\n    format = \"[:bar] :current/:total :percent \" \\\n             \":current_byte/:total_byte :byte_rate/s :mean_byte/s \" \\\n             \":rate/s :mean_rate/s :elapsed :eta\"\n\n    progress = described_class.new(format, output: output, total: 10)\n\n    3.times do |i|\n      Timecop.freeze(Time.local(2021, 1, 11, 12, 0, 1 + i))\n      progress.advance\n    end\n    progress.pause\n    expect(progress.paused?).to eq(true)\n\n    progress.resume\n    expect(progress.paused?).to eq(false)\n\n    3.times do |i|\n      Timecop.freeze(Time.local(2021, 1, 11, 12, 0, 4 + i))\n      progress.advance\n    end\n\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G[=         ] 1/10 10% 1B/10B 1B/s 1B/s  1.00/s  1.00/s  0s  0s\",\n      \"\\e[1G[==        ] 2/10 20% 2B/10B 1B/s 1B/s  1.00/s  1.00/s  1s  4s\",\n      \"\\e[1G[===       ] 3/10 30% 3B/10B 1B/s 1B/s  1.00/s  1.00/s  2s  4s\", # pause render\n      \"\\e[1G[====      ] 4/10 40% 4B/10B 1B/s 1B/s  1.00/s  1.00/s  2s  3s\",\n      \"\\e[1G[=====     ] 5/10 50% 5B/10B 1B/s 1B/s  1.00/s  1.00/s  3s  3s\",\n      \"\\e[1G[======    ] 6/10 60% 6B/10B 1B/s 1B/s  1.00/s  1.00/s  4s  2s\"\n    ].join)\n    Timecop.return\n  end\nend\n"
  },
  {
    "path": "spec/unit/set_current_spec.rb",
    "content": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \"#current=\" do\n  let(:output) { StringIO.new }\n\n  it \"allows to go back\" do\n    progress = TTY::ProgressBar.new(\"[:bar]\", output: output, total: 10)\n    3.times { progress.advance }\n    progress.current = 5\n    expect(progress.current).to eq(5)\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G[=         ]\",\n      \"\\e[1G[==        ]\",\n      \"\\e[1G[===       ]\",\n      \"\\e[1G[=====     ]\"\n    ].join)\n    progress.current = 0\n    expect(progress.current).to eq(0)\n  end\n\n  it \"doesn't allow to go over total\" do\n    progress = TTY::ProgressBar.new(\"[:bar]\", output: output, total: 10)\n    progress.current = 12\n    expect(progress.current).to eq(10)\n    output.rewind\n    expect(output.read).to eq(\"\\e[1G[==========]\\n\")\n  end\n\n  it \"doesn't allow to go below 0\" do\n    progress = TTY::ProgressBar.new(\"[:bar]\", output: output, total: 10)\n    progress.current = -1\n    expect(progress.current).to eq(0)\n  end\n\n  it \"doesn't allow nil\" do\n    progress = TTY::ProgressBar.new(\"[:bar]\", output: output, total: 10)\n    expect {\n      progress.current = nil\n    }.to raise_error(\n      ArgumentError,\n      \"Expected a numeric value, got nil instead.\"\n    )\n    expect(progress.current).to eq(0)\n  end\n\n  it \"cannot backtrack on finished\" do\n    progress = TTY::ProgressBar.new(\"[:bar]\", output: output, total: 10)\n    progress.current = 10\n    expect(progress.current).to eq(10)\n    progress.current = 5\n    expect(progress.current).to eq(10)\n    output.rewind\n    expect(output.read).to eq(\"\\e[1G[==========]\\n\")\n  end\n\n  it \"allows setting progress when the total is unknown\" do\n    progress = TTY::ProgressBar.new(\"[:bar]\", output: output, total: nil)\n    progress.current = 5\n    expect(progress.current).to eq(5)\n  end\nend\n"
  },
  {
    "path": "spec/unit/start_spec.rb",
    "content": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \"#start\" do\n  let(:output) { StringIO.new }\n\n  it \"starts timer and draws initial progress\" do\n    progress = TTY::ProgressBar.new(\"[:bar]\", output: output, total: 10)\n\n    progress.start\n    progress.advance\n\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G[          ]\",\n      \"\\e[1G[=         ]\"\n    ].join)\n  end\n\n  it \"starts timer and displays time and rate metrics\" do\n    Timecop.freeze(Time.local(2021, 1, 23, 12, 0, 0))\n    format = \"[:bar] :percent :elapsed :eta :eta_time :rate/s\"\n    progress = TTY::ProgressBar.new(format, output: output, total: 10)\n\n    Timecop.freeze(Time.local(2021, 1, 23, 12, 0, 1)) do\n      progress.start\n    end\n\n    3.times do |sec|\n      Timecop.freeze(Time.local(2021, 1, 23, 12, 0, 3 + sec)) do\n        progress.advance\n      end\n    end\n\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G[          ] 0%  0s --s --:--:--  0.00/s\",\n      \"\\e[1G[=         ] 10%  2s 18s 12:00:21 0.50000/s\",\n      \"\\e[1G[==        ] 20%  3s 12s 12:00:16  1.00/s  \",\n      \"\\e[1G[===       ] 30%  4s  9s 12:00:14  1.00/s\"\n    ].join)\n  end\nend\n"
  },
  {
    "path": "spec/unit/stop_spec.rb",
    "content": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \"#stop\" do\n  let(:output) { StringIO.new }\n\n  it \"stops progress and prints newline\" do\n    progress = TTY::ProgressBar.new(\"[:bar]\", output: output, total: 10)\n    5.times { |i|\n      progress.stop if i == 3\n      progress.advance\n    }\n    expect(progress.complete?).to be(false)\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G[=         ]\",\n      \"\\e[1G[==        ]\",\n      \"\\e[1G[===       ]\",\n      \"\\e[1G[===       ]\\n\"\n    ].join)\n  end\n\n  it \"stops progress and clears the display\" do\n    progress = TTY::ProgressBar.new(\"[:bar]\", output: output, total: 10,\n                                              clear: true)\n    2.times { progress.advance }\n    progress.stop\n\n    expect(progress.complete?).to be(false)\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G[=         ]\",\n      \"\\e[1G[==        ]\",\n      \"\\e[1G[==        ]\",\n      \"\\e[0m\\e[2K\\e[1G\"\n    ].join)\n  end\n\n  it \"stops progress and restores hidden cursor\" do\n    progress = TTY::ProgressBar.new(\"[:bar]\", output: output, total: 10,\n                                              hide_cursor: true)\n    2.times { progress.advance }\n    progress.stop\n\n    expect(progress.complete?).to be(false)\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[?25l\",\n      \"\\e[1G[=         ]\",\n      \"\\e[1G[==        ]\",\n      \"\\e[1G[==        ]\\n\\e[?25h\"\n    ].join)\n  end\nend\n"
  },
  {
    "path": "spec/unit/timer_spec.rb",
    "content": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar::Timer do\n  before { Timecop.safe_mode = false }\n\n  after { Timecop.return }\n\n  it \"defaults elapsed time to zero\" do\n    timer = described_class.new\n\n    expect(timer.elapsed_time).to eq(0)\n  end\n\n  it \"doesn't run timer until started\" do\n    timer = described_class.new\n\n    expect(timer.running?).to eq(false)\n    expect(timer.start_time).to eq(nil)\n  end\n\n  it \"returns 0 when calling stop when not running\" do\n    timer = described_class.new\n    expect(timer.stop).to eq(0)\n  end\n\n  it \"measures elapsed time for a single interval when stopped\" do\n    timer = described_class.new\n    start_time = Time.at(10, 0)\n    Timecop.freeze(start_time)\n    expect(timer.start).to eq(start_time)\n    expect(timer.start_time).to eq(start_time)\n\n    Timecop.freeze(Time.at(10, 500_000))\n    expect(timer.stop).to eq(0.5)\n    expect(timer.elapsed_time).to eq(0.5)\n    expect(timer.running?).to eq(false)\n    expect(timer.start_time).to eq(nil)\n  end\n\n  it \"measures elapsed time for a single interval when still running\" do\n    timer = described_class.new\n    start_time = Time.at(10, 0)\n    Timecop.freeze(start_time)\n    expect(timer.start).to eq(start_time)\n\n    Timecop.freeze(Time.at(10, 500_000))\n    expect(timer.elapsed_time).to eq(0.5)\n    expect(timer.running?).to eq(true)\n    expect(timer.start_time).to eq(start_time)\n  end\n\n  it \"measures the total elapsed time for multiple intervals when stopped\" do\n    timer = described_class.new\n\n    Timecop.freeze(Time.at(10, 0))\n    timer.start\n\n    Timecop.freeze(Time.at(10, 500_000))\n    expect(timer.stop).to eq(0.5)\n    expect(timer.elapsed_time).to eq(0.5)\n\n    Timecop.freeze(Time.at(11, 000_000))\n    timer.start\n\n    Timecop.freeze(Time.at(11, 500_000))\n    expect(timer.stop).to eq(0.5)\n    expect(timer.elapsed_time).to eq(1)\n    expect(timer.running?).to eq(false)\n  end\n\n  it \"measures the total elapsed time for multiple intervals when still running\" do\n    timer = described_class.new\n\n    Timecop.freeze(Time.at(10, 0))\n    timer.start\n\n    Timecop.freeze(Time.at(10, 500_000))\n    expect(timer.stop).to eq(0.5)\n    expect(timer.elapsed_time).to eq(0.5)\n\n    Timecop.freeze(Time.at(11, 000_000))\n    timer.start\n\n    Timecop.freeze(Time.at(11, 500_000))\n    expect(timer.stop).to eq(0.5)\n    expect(timer.elapsed_time).to eq(1)\n\n    Timecop.freeze(Time.at(12, 000_000))\n    timer.start\n\n    Timecop.freeze(Time.at(12, 500_000))\n    expect(timer.elapsed_time).to eq(1.5)\n    expect(timer.running?).to eq(true)\n  end\n\n  it \"resets time measurements\" do\n    timer = described_class.new\n    start_time = Time.at(10, 0)\n    Timecop.freeze(start_time)\n    timer.start\n\n    Timecop.freeze(Time.at(12, 500_000))\n\n    expect(timer.elapsed_time).to eq(2.5)\n    expect(timer.running?).to eq(true)\n    expect(timer.start_time).to eq(start_time)\n\n    timer.reset\n\n    expect(timer.elapsed_time).to eq(0)\n    expect(timer.running?).to eq(false)\n    expect(timer.start_time).to eq(nil)\n  end\nend\n"
  },
  {
    "path": "spec/unit/update_spec.rb",
    "content": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \"#update\" do\n  let(:output) { StringIO.new }\n\n  it \"updates bar configuration options\" do\n    progress = TTY::ProgressBar.new \"[:bar]\" do |config|\n      config.output = output\n      config.total = 10\n    end\n    10.times { |i|\n      progress.update(complete: \"-\", head: \">\") if i == 2\n      progress.advance(2)\n    }\n    output.rewind\n    expect(output.read).to eq([\n      \"\\e[1G[==        ]\",\n      \"\\e[1G[====      ]\",\n      \"\\e[1G[----->    ]\",\n      \"\\e[1G[------->  ]\",\n      \"\\e[1G[--------->]\\n\"\n    ].join)\n  end\nend\n"
  },
  {
    "path": "spec/unit/width_spec.rb",
    "content": "# frozen_string_literal: true\n\nRSpec.describe TTY::ProgressBar, \"#width\" do\n  let(:output) { StringIO.new }\n\n  it \"handles width exceeding terminal width\" do\n    progress = TTY::ProgressBar.new \"[:bar]\" do |config|\n      config.output = output\n      config.total = 5\n      config.width = 1024\n    end\n    allow(TTY::Screen).to receive(:width).and_return(20)\n\n    5.times { progress.advance }\n    output.rewind\n\n    expect(output.read).to eq([\n      \"\\e[1G[====              ]\",\n      \"\\e[1G[=======           ]\",\n      \"\\e[1G[===========       ]\",\n      \"\\e[1G[==============    ]\",\n      \"\\e[1G[==================]\\n\"\n    ].join)\n  end\n\n  it \"handles unicode characters width in formatting string\" do\n    bar = TTY::ProgressBar.new(\"あめかんむり[:bar]\", output: output, total: 20)\n    allow(TTY::Screen).to receive(:width).and_return(20)\n\n    4.times { bar.advance(5) }\n    output.rewind\n\n    expect(output.read).to eq([\n      \"\\e[1Gあめかんむり[==    ]\",\n      \"\\e[1Gあめかんむり[===   ]\",\n      \"\\e[1Gあめかんむり[===== ]\",\n      \"\\e[1Gあめかんむり[======]\\n\"\n    ].join)\n  end\n\n  it \"handles unicodes characters within bar\" do\n    bar = TTY::ProgressBar.new(\"[:bar]\", output: output, total: 20,\n                                         complete: \"あ\", incomplete: \"め\")\n    allow(TTY::Screen).to receive(:width).and_return(20)\n\n    4.times { bar.advance(5) }\n    output.rewind\n\n    expect(output.read).to eq([\n      \"\\e[1G[ああめめめめめめめ]\",\n      \"\\e[1G[ああああめめめめめ]\",\n      \"\\e[1G[あああああああめめ]\",\n      \"\\e[1G[あああああああああ]\\n\"\n    ].join)\n  end\n\n  it \"handles unicodes characters within bar\" do\n    bar = TTY::ProgressBar.new(\"[:bar]\", output: output, total: 20,\n                                         complete: \"あ\", incomplete: \" \")\n    allow(TTY::Screen).to receive(:width).and_return(20)\n\n    4.times { bar.advance(5) }\n    output.rewind\n\n    expect(output.read).to eq([\n      \"\\e[1G[ああ              ]\",\n      \"\\e[1G[ああああ          ]\",\n      \"\\e[1G[あああああああ    ]\",\n      \"\\e[1G[あああああああああ]\\n\"\n    ].join)\n  end\n\n  it \"handles unicodes characters within bar\" do\n    bar = TTY::ProgressBar.new(\"[:bar]\", output: output, total: 20,\n                                         complete: \"x\", incomplete: \"め\")\n    allow(TTY::Screen).to receive(:width).and_return(20)\n\n    4.times { bar.advance(5) }\n    output.rewind\n\n    expect(output.read).to eq([\n      \"\\e[1G[xxxxめめめめめめめ]\",\n      \"\\e[1G[xxxxxxxxめめめめめ]\",\n      \"\\e[1G[xxxxxxxxxxxxxxめめ]\",\n      \"\\e[1G[xxxxxxxxxxxxxxxxxx]\\n\"\n    ].join)\n  end\nend\n"
  },
  {
    "path": "tasks/console.rake",
    "content": "# frozen_string_literal: true\n\ndesc \"Load gem inside irb console\"\ntask :console do\n  require \"irb\"\n  require \"irb/completion\"\n  require_relative \"../lib/tty-progressbar\"\n  ARGV.clear\n  IRB.start\nend\ntask :c => :console\n"
  },
  {
    "path": "tasks/coverage.rake",
    "content": "# frozen_string_literal: true\n\ndesc \"Measure code coverage\"\ntask :coverage do\n  begin\n    original, ENV[\"COVERAGE\"] = ENV[\"COVERAGE\"], \"true\"\n    Rake::Task[\"spec\"].invoke\n  ensure\n    ENV[\"COVERAGE\"] = original\n  end\nend\n"
  },
  {
    "path": "tasks/spec.rake",
    "content": "# frozen_string_literal: true\n\nbegin\n  require \"rspec/core/rake_task\"\n\n  desc \"Run all specs\"\n  RSpec::Core::RakeTask.new(:spec) do |task|\n    task.pattern = \"spec/{unit,integration}{,/*/**}/*_spec.rb\"\n  end\n\n  namespace :spec do\n    desc \"Run unit specs\"\n    RSpec::Core::RakeTask.new(:unit) do |task|\n      task.pattern = \"spec/unit{,/*/**}/*_spec.rb\"\n    end\n\n    desc \"Run integration specs\"\n    RSpec::Core::RakeTask.new(:integration) do |task|\n      task.pattern = \"spec/integration{,/*/**}/*_spec.rb\"\n    end\n\n    desc \"Run performance specs\"\n    RSpec::Core::RakeTask.new(:perf) do |task|\n      task.pattern = \"spec/perf{,/*/**}/*_spec.rb\"\n    end\n  end\n\nrescue LoadError\n  %w[spec spec:unit spec:integration].each do |name|\n    task name do\n      $stderr.puts \"In order to run #{name}, do `gem install rspec`\"\n    end\n  end\nend\n"
  },
  {
    "path": "tty-progressbar.gemspec",
    "content": "# frozen_string_literal: true\n\nrequire_relative \"lib/tty/progressbar/version\"\n\nGem::Specification.new do |spec|\n  spec.name = \"tty-progressbar\"\n  spec.version = TTY::ProgressBar::VERSION\n  spec.authors = [\"Piotr Murach\"]\n  spec.email = [\"piotr@piotrmurach.com\"]\n  spec.summary = \"A flexible and extensible progress bar for terminal applications.\"\n  spec.description = \"Display a single or multiple progress bars in the terminal. A progress bar can show determinate or indeterminate progress that can be paused and resumed at any time. A bar format supports many tokens for common information display like elapsed time, estimated time to completion, mean rate and more.\"\n  spec.homepage = \"https://ttytoolkit.org\"\n  spec.license = \"MIT\"\n  if spec.respond_to?(:metadata=)\n    spec.metadata = {\n      \"allowed_push_host\" => \"https://rubygems.org\",\n      \"bug_tracker_uri\" => \"https://github.com/piotrmurach/tty-progressbar/issues\",\n      \"changelog_uri\" => \"https://github.com/piotrmurach/tty-progressbar/blob/master/CHANGELOG.md\",\n      \"documentation_uri\" => \"https://www.rubydoc.info/gems/tty-progressbar\",\n      \"funding_uri\" => \"https://github.com/sponsors/piotrmurach\",\n      \"homepage_uri\" => spec.homepage,\n      \"rubygems_mfa_required\" => \"true\",\n      \"source_code_uri\" => \"https://github.com/piotrmurach/tty-progressbar\"\n    }\n  end\n  spec.files = Dir[\"lib/**/*\"]\n  spec.extra_rdoc_files = [\"README.md\", \"CHANGELOG.md\", \"LICENSE.txt\"]\n  spec.require_paths = [\"lib\"]\n  spec.required_ruby_version = \">= 2.0.0\"\n\n  spec.add_dependency \"strings-ansi\", \"~> 0.2\"\n  spec.add_dependency \"tty-cursor\", \"~> 0.7\"\n  spec.add_dependency \"tty-screen\", \"~> 0.8\"\n  spec.add_dependency \"unicode-display_width\", \">= 1.6\", \"< 3.0\"\n\n  spec.add_development_dependency \"rake\"\n  spec.add_development_dependency \"rspec\", \">= 3.0\"\n  spec.add_development_dependency \"timecop\", \"~> 0.9\"\nend\n"
  }
]