├── .ruby-version ├── Gemfile ├── .markdownlintignore ├── Gemfile.lock ├── .github ├── FUNDING.yml ├── workflows │ ├── labels.yml │ ├── testing.yml │ ├── locking.yml │ └── linting.yml ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug.md ├── contributing.md ├── labels.yml └── pull_request_template.md ├── Rakefile ├── checksum ├── progressbar-1.10.0.gem.md5 ├── progressbar-1.10.1.gem.md5 ├── progressbar-1.11.0.gem.md5 ├── progressbar-1.12.0.gem.md5 ├── progressbar-1.13.0.gem.md5 ├── progressbar-1.8.1.gem.md5 ├── progressbar-1.8.2.gem.md5 ├── progressbar-1.9.0.gem.md5 ├── ruby-progressbar-1.10.0.gem.md5 ├── ruby-progressbar-1.10.1.gem.md5 ├── ruby-progressbar-1.11.0.gem.md5 ├── ruby-progressbar-1.12.0.gem.md5 ├── ruby-progressbar-1.13.0.gem.md5 ├── ruby-progressbar-1.8.0.gem.md5 ├── ruby-progressbar-1.8.1.gem.md5 ├── ruby-progressbar-1.8.3.gem.md5 ├── ruby-progressbar-1.9.0.gem.md5 ├── progressbar-1.10.0.gem.sha256 ├── progressbar-1.10.1.gem.sha256 ├── progressbar-1.11.0.gem.sha256 ├── progressbar-1.12.0.gem.sha256 ├── progressbar-1.13.0.gem.sha256 ├── progressbar-1.8.1.gem.sha256 ├── progressbar-1.8.2.gem.sha256 ├── progressbar-1.9.0.gem.sha256 ├── ruby-progressbar-1.10.0.gem.sha256 ├── ruby-progressbar-1.10.1.gem.sha256 ├── ruby-progressbar-1.11.0.gem.sha256 ├── ruby-progressbar-1.12.0.gem.sha256 ├── ruby-progressbar-1.13.0.gem.sha256 ├── ruby-progressbar-1.8.0.gem.sha256 ├── ruby-progressbar-1.8.1.gem.sha256 ├── ruby-progressbar-1.8.3.gem.sha256 ├── ruby-progressbar-1.9.0.gem.sha256 ├── progressbar-1.10.0.gem.sha512 ├── progressbar-1.10.1.gem.sha512 ├── progressbar-1.11.0.gem.sha512 ├── progressbar-1.12.0.gem.sha512 ├── progressbar-1.13.0.gem.sha512 ├── progressbar-1.8.1.gem.sha512 ├── progressbar-1.8.2.gem.sha512 ├── progressbar-1.9.0.gem.sha512 ├── ruby-progressbar-1.10.0.gem.sha512 ├── ruby-progressbar-1.10.1.gem.sha512 ├── ruby-progressbar-1.11.0.gem.sha512 ├── ruby-progressbar-1.12.0.gem.sha512 ├── ruby-progressbar-1.13.0.gem.sha512 ├── ruby-progressbar-1.8.0.gem.sha512 ├── ruby-progressbar-1.8.1.gem.sha512 ├── ruby-progressbar-1.8.3.gem.sha512 └── ruby-progressbar-1.9.0.gem.sha512 ├── lib ├── ruby-progressbar │ ├── version.rb │ ├── refinements.rb │ ├── errors │ │ └── invalid_progress_error.rb │ ├── components │ │ ├── title.rb │ │ ├── percentage.rb │ │ ├── rate.rb │ │ ├── bar.rb │ │ └── time.rb │ ├── projector.rb │ ├── outputs │ │ ├── null.rb │ │ ├── tty.rb │ │ └── non_tty.rb │ ├── throttle.rb │ ├── refinements │ │ └── progress_enumerator.rb │ ├── format │ │ ├── formatter.rb │ │ ├── string.rb │ │ └── molecule.rb │ ├── time.rb │ ├── timer.rb │ ├── projectors │ │ └── smoothed_average.rb │ ├── output.rb │ ├── calculators │ │ └── length.rb │ ├── progress.rb │ └── base.rb ├── progressbar.rb └── ruby-progressbar.rb ├── bin ├── console └── setup ├── spec ├── errors │ └── invalid_progress_error_spec.rb ├── spec_helper.rb ├── lib │ └── ruby-progressbar │ │ ├── outputs │ │ ├── tty_spec.rb │ │ ├── non_tty_spec.rb │ │ └── null_spec.rb │ │ ├── format │ │ ├── string_spec.rb │ │ └── molecule_spec.rb │ │ ├── timer_spec.rb │ │ ├── components │ │ ├── title_spec.rb │ │ ├── percentage_spec.rb │ │ ├── rate_spec.rb │ │ ├── throttle_spec.rb │ │ ├── bar_spec.rb │ │ └── time_spec.rb │ │ ├── projector_spec.rb │ │ ├── time_spec.rb │ │ ├── output_spec.rb │ │ ├── projector │ │ └── smoothed_average_spec.rb │ │ ├── refinements │ │ └── progress_enumerator_spec.rb │ │ ├── progress_spec.rb │ │ └── calculators │ │ └── length_spec.rb ├── support │ ├── time.rb │ └── warning_filter.rb └── fixtures │ └── benchmark.rb ├── .rubocop.yml ├── gemfiles ├── v2 │ ├── Gemfile │ ├── Gemfile.lock │ └── ruby-progressbar.gemspec └── v1 │ ├── Gemfile │ ├── Gemfile.lock │ └── ruby-progressbar.gemspec ├── settings └── rubygems.yml ├── .chamber.pub.pem ├── LICENSE.txt ├── .rubocop_local.yml ├── certs └── jfelchner.pem ├── progressbar.gemspec ├── .yamllint ├── ruby-progressbar.gemspec ├── CODE-OF-CONDUCT.md ├── .remarkrc ├── .gitignore ├── README.md ├── .rubocop_performance.yml ├── .rubocop_rspec.yml └── .overcommit.yml /.ruby-version: -------------------------------------------------------------------------------- 1 | 2.7.6 2 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | ./gemfiles/v2/Gemfile -------------------------------------------------------------------------------- /.markdownlintignore: -------------------------------------------------------------------------------- 1 | CHANGELOG.md 2 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | ./gemfiles/v2/Gemfile.lock -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | github: jfelchner 4 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env rake 2 | require 'bundler/gem_tasks' 3 | -------------------------------------------------------------------------------- /checksum/progressbar-1.10.0.gem.md5: -------------------------------------------------------------------------------- 1 | 42fa7971f60a90e1876dfb9cca884b3d 2 | -------------------------------------------------------------------------------- /checksum/progressbar-1.10.1.gem.md5: -------------------------------------------------------------------------------- 1 | f6f047234e63c58e45b91c53bf77f079 2 | -------------------------------------------------------------------------------- /checksum/progressbar-1.11.0.gem.md5: -------------------------------------------------------------------------------- 1 | 77faf50172610ec7753649b98572b205 2 | -------------------------------------------------------------------------------- /checksum/progressbar-1.12.0.gem.md5: -------------------------------------------------------------------------------- 1 | f0ff5c96affc5fbd163428535eed122f 2 | -------------------------------------------------------------------------------- /checksum/progressbar-1.13.0.gem.md5: -------------------------------------------------------------------------------- 1 | 26581f1d89f9c701d6ca06654c7ab52c 2 | -------------------------------------------------------------------------------- /checksum/progressbar-1.8.1.gem.md5: -------------------------------------------------------------------------------- 1 | f70f124c2d85b7923760de5b27220966 2 | -------------------------------------------------------------------------------- /checksum/progressbar-1.8.2.gem.md5: -------------------------------------------------------------------------------- 1 | 5daea819b84c3b702d09d4abd9a6f57c 2 | -------------------------------------------------------------------------------- /checksum/progressbar-1.9.0.gem.md5: -------------------------------------------------------------------------------- 1 | 0cbd6df79d4fa4955da1ea738951e4df 2 | -------------------------------------------------------------------------------- /checksum/ruby-progressbar-1.10.0.gem.md5: -------------------------------------------------------------------------------- 1 | 10511dedc1b37b3ae1060f4e4d4edd7c 2 | -------------------------------------------------------------------------------- /checksum/ruby-progressbar-1.10.1.gem.md5: -------------------------------------------------------------------------------- 1 | 7fe5834abce78f9ffcc2340c495e684c 2 | -------------------------------------------------------------------------------- /checksum/ruby-progressbar-1.11.0.gem.md5: -------------------------------------------------------------------------------- 1 | ab2638003880a34034136b7dcc9080ef 2 | -------------------------------------------------------------------------------- /checksum/ruby-progressbar-1.12.0.gem.md5: -------------------------------------------------------------------------------- 1 | 3dd3c94ee81398e25f204e0368c4abee 2 | -------------------------------------------------------------------------------- /checksum/ruby-progressbar-1.13.0.gem.md5: -------------------------------------------------------------------------------- 1 | 3e515f9efab3583fa5ef6009480ef8e7 2 | -------------------------------------------------------------------------------- /checksum/ruby-progressbar-1.8.0.gem.md5: -------------------------------------------------------------------------------- 1 | c2fb5a016efd590284cb06858621ad0e 2 | -------------------------------------------------------------------------------- /checksum/ruby-progressbar-1.8.1.gem.md5: -------------------------------------------------------------------------------- 1 | 07f7eddceb272e9b0c8ea123c489f0e8 2 | -------------------------------------------------------------------------------- /checksum/ruby-progressbar-1.8.3.gem.md5: -------------------------------------------------------------------------------- 1 | 74a2da533496f8e0c018f25caec7fcc0 2 | -------------------------------------------------------------------------------- /checksum/ruby-progressbar-1.9.0.gem.md5: -------------------------------------------------------------------------------- 1 | fd9767c49846e37c4a8e81ed7b951d51 2 | -------------------------------------------------------------------------------- /lib/ruby-progressbar/version.rb: -------------------------------------------------------------------------------- 1 | class ProgressBar 2 | VERSION = '1.13.0'.freeze 3 | end 4 | -------------------------------------------------------------------------------- /lib/ruby-progressbar/refinements.rb: -------------------------------------------------------------------------------- 1 | require 'ruby-progressbar/refinements/progress_enumerator' 2 | -------------------------------------------------------------------------------- /checksum/progressbar-1.10.0.gem.sha256: -------------------------------------------------------------------------------- 1 | 2de99e75d62346a6061a049d04df5e6d00ac456f504926bdae5fe2406a666799 2 | -------------------------------------------------------------------------------- /checksum/progressbar-1.10.1.gem.sha256: -------------------------------------------------------------------------------- 1 | aa1c5b88074abced1a510dcc1a6d58a313b48b800f0e1f997ae4aacbb4ace823 2 | -------------------------------------------------------------------------------- /checksum/progressbar-1.11.0.gem.sha256: -------------------------------------------------------------------------------- 1 | 7064ecc6b46ccfe1457a76754e72fa5a452024bc5eab8f289e0a3d4ff1a0548d 2 | -------------------------------------------------------------------------------- /checksum/progressbar-1.12.0.gem.sha256: -------------------------------------------------------------------------------- 1 | 7836482aeee8e5f6e8d0f6500de96cb6b455168b4aafd2f6304eecad23235f50 2 | -------------------------------------------------------------------------------- /checksum/progressbar-1.13.0.gem.sha256: -------------------------------------------------------------------------------- 1 | 18c921d1c527dbe7eba3c5a07a1e3bcdd5e42fc18276f5711187a7489448be78 2 | -------------------------------------------------------------------------------- /checksum/progressbar-1.8.1.gem.sha256: -------------------------------------------------------------------------------- 1 | 2dbac4b02919b215acb2d2251d75dbcf3c4320803c384d854b691a65f4aa074b 2 | -------------------------------------------------------------------------------- /checksum/progressbar-1.8.2.gem.sha256: -------------------------------------------------------------------------------- 1 | 51394d27a4f84bc8d81666128e0d72f3b286da9234ffbe8c47e2978d8212fa97 2 | -------------------------------------------------------------------------------- /checksum/progressbar-1.9.0.gem.sha256: -------------------------------------------------------------------------------- 1 | dbb8497223e4bef55cf763acd435dfa723238c9e4fdf25c375fbad72b5065c1b 2 | -------------------------------------------------------------------------------- /checksum/ruby-progressbar-1.10.0.gem.sha256: -------------------------------------------------------------------------------- 1 | 933e58652a6e7eac5426310d5dc96a629dadd631de029210df3a019a47f562b3 2 | -------------------------------------------------------------------------------- /checksum/ruby-progressbar-1.10.1.gem.sha256: -------------------------------------------------------------------------------- 1 | ee23400615f91c2ce6bc1e3bfa98392302ef38cbba8ad6de1bc26a4e1a88e7cc 2 | -------------------------------------------------------------------------------- /checksum/ruby-progressbar-1.11.0.gem.sha256: -------------------------------------------------------------------------------- 1 | cc127db3866dc414ffccbf92928a241e585b3aa2b758a5563e74a6ee0f57d50a 2 | -------------------------------------------------------------------------------- /checksum/ruby-progressbar-1.12.0.gem.sha256: -------------------------------------------------------------------------------- 1 | 433242aef7e13ab37d1e9cbd0d8d76f7d125f1a4e0222a7df7f19f8f13b5afe4 2 | -------------------------------------------------------------------------------- /checksum/ruby-progressbar-1.13.0.gem.sha256: -------------------------------------------------------------------------------- 1 | 80fc9c47a9b640d6834e0dc7b3c94c9df37f08cb072b7761e4a71e22cff29b33 2 | -------------------------------------------------------------------------------- /checksum/ruby-progressbar-1.8.0.gem.sha256: -------------------------------------------------------------------------------- 1 | 78020988fc8603843747e316edb8fc50481c6842583eab5749e29fbbc9660f54 2 | -------------------------------------------------------------------------------- /checksum/ruby-progressbar-1.8.1.gem.sha256: -------------------------------------------------------------------------------- 1 | 95ded755295440de814704970d7ccaf3cb259854534f03a03a6d05918f3eece3 2 | -------------------------------------------------------------------------------- /checksum/ruby-progressbar-1.8.3.gem.sha256: -------------------------------------------------------------------------------- 1 | 0e47b5ce47c71e74ca25bb3aea20f9889a148ebd9e4ce5651e05bb3930d83309 2 | -------------------------------------------------------------------------------- /checksum/ruby-progressbar-1.9.0.gem.sha256: -------------------------------------------------------------------------------- 1 | d32d1b046400e58007e7043e3b07c9e2c32a248964a55afc780516b7630ff0c5 2 | -------------------------------------------------------------------------------- /lib/ruby-progressbar/errors/invalid_progress_error.rb: -------------------------------------------------------------------------------- 1 | class ProgressBar 2 | class InvalidProgressError < RuntimeError 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /checksum/progressbar-1.10.0.gem.sha512: -------------------------------------------------------------------------------- 1 | cc91568aa0c7d40612fac118c00ba2b5b76f56e6fffcbcc1324639dff4281916530010366c4beab3a64d5e16f272e236a18cb3350c5caa23a6c5f3fde65a667d 2 | -------------------------------------------------------------------------------- /checksum/progressbar-1.10.1.gem.sha512: -------------------------------------------------------------------------------- 1 | 081e95c9e1fe722a0aa7696f3b4b76e5de9bdac2618bc625edff05e100bc69abcf335da1473e267cbdb637f5af6f3ed3bf9c1127b712e86350ad2b131dc2ddf2 2 | -------------------------------------------------------------------------------- /checksum/progressbar-1.11.0.gem.sha512: -------------------------------------------------------------------------------- 1 | 2a98301e99b0c8a5ab396b91afc50a5ea9400c476e1050e37f64158cda59b6651552ed5b29905afd0764212e6e0cd7cb1f8d6c38636c6b700426b2e16041a5ed 2 | -------------------------------------------------------------------------------- /checksum/progressbar-1.12.0.gem.sha512: -------------------------------------------------------------------------------- 1 | 5949db57810e6316cf34fa110012a9c47cf6a1c5518d29527329552f370a03437838144be814b9b3d5edb703249d85b6822eb67d5e3c51414b92791593c4f8c1 2 | -------------------------------------------------------------------------------- /checksum/progressbar-1.13.0.gem.sha512: -------------------------------------------------------------------------------- 1 | 29835e3dd5b4f4f1fb6f62e2faead89abbbe4b969c77089d00bacdea25f8f093ff771f101490bc3e897f8e8bdce677b52c1f4d8bbc5ed79294699a4601063fec 2 | -------------------------------------------------------------------------------- /checksum/progressbar-1.8.1.gem.sha512: -------------------------------------------------------------------------------- 1 | c4a223ac27aa72c0b546baf31a9a8b2338fdfaf36deb3b273de191818ba3921230448b82ca438900e239ef36567e6ae5c98cff2a9a67fb03ce1f6fbad73f9afb 2 | -------------------------------------------------------------------------------- /checksum/progressbar-1.8.2.gem.sha512: -------------------------------------------------------------------------------- 1 | 9bc8a304420d0faa0c8fde669ff9d06ea60d937b6c68d5c198b195d38cb4a5c39e9b3c1068305800ecff2cada52328093d814aec039dd83700d74544ed86fd04 2 | -------------------------------------------------------------------------------- /checksum/progressbar-1.9.0.gem.sha512: -------------------------------------------------------------------------------- 1 | 8b860e7e6d00ceb4c235073482797cc41c0a724fcd2e6cb6c24592d78feccc007524e761bb8bf24d8b5f8d24ed153089ebfec8dcbdc06188ff607294ca828084 2 | -------------------------------------------------------------------------------- /checksum/ruby-progressbar-1.10.0.gem.sha512: -------------------------------------------------------------------------------- 1 | 7214a228cd153e817e7f89bfb763db4bc67c06c41162c91422e0f4e2d7bd02a19b2bf56281552029e87d3cffbd60d1dc8eb36c67aeea39be9fe6819c90c85130 2 | -------------------------------------------------------------------------------- /checksum/ruby-progressbar-1.10.1.gem.sha512: -------------------------------------------------------------------------------- 1 | 1a474e697d1e9849cc67842a756bad605aa8341fe8451a984f115f1c64754f522b4496d09ef8b47d16135fc165f4d02ea8944f99767573c699ca694ec3205f52 2 | -------------------------------------------------------------------------------- /checksum/ruby-progressbar-1.11.0.gem.sha512: -------------------------------------------------------------------------------- 1 | 4c1b20c1a7a982a8da9cd85ba53f043dd72aa7c4d98d47013d311d305d30461d760300ae0d60cdd2767c73d99c55fe07607b7086803f90ebf261549111524f27 2 | -------------------------------------------------------------------------------- /checksum/ruby-progressbar-1.12.0.gem.sha512: -------------------------------------------------------------------------------- 1 | ef650167b3c0a48ffa29b47f55fffa66220e5b2f42d86c0647f776afbcb518a009e88e9da3134eaa91c7fbc90ab794f2d23ac640c650953101bd7cdd65c45eb0 2 | -------------------------------------------------------------------------------- /checksum/ruby-progressbar-1.13.0.gem.sha512: -------------------------------------------------------------------------------- 1 | 4e651cfa75dfed3467017583886d0e0eedb46c13c7f8faa67fae06f169983fbf6a4525799cb45411b9c9c9f536786a66ba71764e760261c0e02ff7487bf4c464 2 | -------------------------------------------------------------------------------- /checksum/ruby-progressbar-1.8.0.gem.sha512: -------------------------------------------------------------------------------- 1 | ecb44206390ad277908cb89fc247ea249c1c81b6aa18e941679dcfaea14e27160cafb2e06abc9ef42512d6a88f9d33096bc58c6d70f93ccb03801f80c4c4f79c 2 | -------------------------------------------------------------------------------- /checksum/ruby-progressbar-1.8.1.gem.sha512: -------------------------------------------------------------------------------- 1 | bded41f29ed3386b83b0c4fe208813de330cb6be527c4b91cbf436f44d0efe58cb717a783d104f32b697fe1d9b71eeec500c8a72a9a5b34fc416c86e7565bc01 2 | -------------------------------------------------------------------------------- /checksum/ruby-progressbar-1.8.3.gem.sha512: -------------------------------------------------------------------------------- 1 | b6959c91e612e1cf2c806a8c81372e22a251431973abe133068be60cfa4f7fd0cc03dcc88e3b9fbd7b6559e5eb447e24013bf6cdd79cf393b2d6a5a16801443f 2 | -------------------------------------------------------------------------------- /checksum/ruby-progressbar-1.9.0.gem.sha512: -------------------------------------------------------------------------------- 1 | c95bc065c96a2f11cd4d77a351dad0d3bd9928a1c8463107984bb3b100b7916bd744a33d856e91039cecee6a216dbcf7e122a3709dc332cb41cbc70e96dfd097 2 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | require 'bundler/setup' 5 | 6 | Bundler.require(:default, :console) 7 | 8 | require 'irb' 9 | IRB.start 10 | -------------------------------------------------------------------------------- /spec/errors/invalid_progress_error_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'ruby-progressbar/errors/invalid_progress_error' 3 | 4 | class ProgressBar 5 | describe InvalidProgressError do 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'rspectacular' 2 | require "#{Dir.pwd}/spec/support/warning_filter" 3 | require "#{Dir.pwd}/spec/support/time" 4 | 5 | RSpec.configure do |config| 6 | config.warnings = true 7 | end 8 | -------------------------------------------------------------------------------- /spec/lib/ruby-progressbar/outputs/tty_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'ruby-progressbar/outputs/tty' 3 | 4 | class ProgressBar 5 | module Outputs 6 | describe Tty do 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/lib/ruby-progressbar/format/string_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'ruby-progressbar/format/string' 3 | 4 | class ProgressBar 5 | module Format 6 | describe String do 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/lib/ruby-progressbar/outputs/non_tty_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'ruby-progressbar/outputs/non_tty' 3 | 4 | class ProgressBar 5 | module Outputs 6 | describe NonTty do 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | inherit_from: 4 | - '.rubocop_core.yml' 5 | - '.rubocop_performance.yml' 6 | - '.rubocop_rspec.yml' 7 | - '.rubocop_local.yml' 8 | 9 | # Put your project-specific Rubocop configuration in .rubocop_local.yml 10 | -------------------------------------------------------------------------------- /lib/ruby-progressbar/components/title.rb: -------------------------------------------------------------------------------- 1 | class ProgressBar 2 | module Components 3 | class Title 4 | DEFAULT_TITLE = 'Progress'.freeze 5 | 6 | attr_accessor :title 7 | 8 | def initialize(options = {}) 9 | self.title = options[:title] || DEFAULT_TITLE 10 | end 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /gemfiles/v2/Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source 'https://rubygems.org' 4 | 5 | group :console do 6 | gem 'awesome_print', '~> 1.6' 7 | end 8 | 9 | gem 'ruby-prof', '~> 1.6', :platforms => :mri_20, 10 | :group => :development 11 | 12 | gemspec :name => 'ruby-progressbar' 13 | -------------------------------------------------------------------------------- /gemfiles/v1/Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source 'https://rubygems.org' 4 | 5 | group :console do 6 | gem 'awesome_print', '~> 1.6' 7 | end 8 | 9 | gem 'ruby-prof', '~> 0.15.8', :platforms => :mri_20, 10 | :group => :development 11 | 12 | gemspec :name => 'ruby-progressbar' 13 | -------------------------------------------------------------------------------- /spec/lib/ruby-progressbar/timer_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'ruby-progressbar/timer' 3 | 4 | class ProgressBar 5 | describe Timer do 6 | it 'can be reset and queried' do 7 | timer = Timer.new 8 | 9 | timer.start 10 | timer.reset 11 | 12 | expect(timer.elapsed_seconds).to be 0 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /spec/lib/ruby-progressbar/components/title_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'ruby-progressbar/components/title' 3 | 4 | class ProgressBar 5 | module Components 6 | describe Title do 7 | it 'can use the default title if none is specified' do 8 | expect(Title.new.title).to eql Title::DEFAULT_TITLE 9 | end 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /spec/support/time.rb: -------------------------------------------------------------------------------- 1 | ### 2 | # In our specs, I want to make sure time gets mocked so I can accurately test 3 | # times displayed to the user. 4 | # 5 | class PBTimeTester 6 | def self.now 7 | ::Time.now.utc 8 | end 9 | end 10 | 11 | class ProgressBar 12 | class Time 13 | def initialize(time = ::PBTimeTester) 14 | self.time = time 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/progressbar.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'ruby-progressbar/base' 4 | require 'ruby-progressbar/refinements' if Module. 5 | private_instance_methods. 6 | include?(:using) 7 | 8 | class ProgressBar 9 | def self.create(*args) 10 | ProgressBar::Base.new(*args) 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/ruby-progressbar.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'ruby-progressbar/base' 4 | require 'ruby-progressbar/refinements' if Module. 5 | private_instance_methods. 6 | include?(:using) 7 | 8 | class ProgressBar 9 | def self.create(*args) 10 | ProgressBar::Base.new(*args) 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /settings/rubygems.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | rubygems: 4 | _secure_api_key: ebkL9zPPh6WsxKbUTq9769eqjSdVYEeFwCMkDGSZ7kd+F/P+uj5XX0iCAyoOOG9KmB5JIfsnGlUDrlYqkYxRic37IhHYJyAr8PWntPuNasljAnYQFdRK93KzH2Ce0MFPVlhsKuGTx3dTd9a2TWgksglghWLk0zSXt+LpgsyVaWChbGUuC/Tga6SLK6CKL8qbh0lF78laMsGvGQ5apOgXVhg3i8UGEVzTS3fE2NSy85CQdhBQDyJd595T26tlUd57J4tUNkIcgYBaCsFYSjVIqz5ZMF5NKc7PtfkAe3Wlg9fRPLWBCWxS9P6BTmpV4gYm+iusRsJbcdIKWfqV356iZg== 5 | -------------------------------------------------------------------------------- /lib/ruby-progressbar/projector.rb: -------------------------------------------------------------------------------- 1 | require 'ruby-progressbar/projectors/smoothed_average' 2 | 3 | class ProgressBar 4 | class Projector 5 | DEFAULT_PROJECTOR = ProgressBar::Projectors::SmoothedAverage 6 | NAME_TO_PROJECTOR_MAP = { 7 | 'smoothed' => ProgressBar::Projectors::SmoothedAverage 8 | }.freeze 9 | 10 | def self.from_type(name) 11 | NAME_TO_PROJECTOR_MAP.fetch(name, DEFAULT_PROJECTOR) 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /.chamber.pub.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvUvrgcqkZWuE0m6qRkDx 3 | tBtnUTIX7JzDfN1byCUrWUT2af635KxaA2TNTEW1nhhMFZIvZc+UCI+2nNWx2lZo 4 | jyyt4AQ83F0JX4sqJQxtdlXym5g/Vp5etv4OBHADhZvvrzJCOCO8p25OOvwTfyq6 5 | 65YshBNQRVoB0oXHPTz5SGvtrzKNX2EulZGESPAIhFtNMBjmtOnWAtWoseOGxNGy 6 | oDSElZzYi3DEfNpUMQr49K9JZTpz+kIAeXUSpkdtTQ9PYY/jcyka1XZ/RkKbGK1V 7 | VtKQ2WCcB5R3jnAThverEVv28rLwn4dZeySd1lYJ4+1oZMWbGskR03zFoyMYGMmi 8 | ZwIDAQAB 9 | -----END PUBLIC KEY----- 10 | -------------------------------------------------------------------------------- /lib/ruby-progressbar/outputs/null.rb: -------------------------------------------------------------------------------- 1 | require 'ruby-progressbar/output' 2 | 3 | class ProgressBar 4 | module Outputs 5 | class Null < Output 6 | alias refresh_with_format_change with_refresh 7 | 8 | def clear; end 9 | def log(_string); end 10 | def refresh(*); end 11 | 12 | def clear_string 13 | '' 14 | end 15 | 16 | def bar_update_string 17 | '' 18 | end 19 | 20 | def default_format 21 | '' 22 | end 23 | 24 | def resolve_format(_format) 25 | '' 26 | end 27 | 28 | def eol 29 | '' 30 | end 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /spec/lib/ruby-progressbar/projector_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'ruby-progressbar/projector' 3 | 4 | class ProgressBar 5 | describe Projector do 6 | describe '.from_type' do 7 | it 'has a default projector' do 8 | projector = Projector.from_type(nil) 9 | 10 | expect(projector).to be ProgressBar::Projectors::SmoothedAverage 11 | end 12 | 13 | it 'can return a specific projector' do 14 | projector = Projector.from_type('smoothed') 15 | 16 | expect(projector).to be ProgressBar::Projectors::SmoothedAverage 17 | end 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/ruby-progressbar/components/percentage.rb: -------------------------------------------------------------------------------- 1 | class ProgressBar 2 | module Components 3 | class Percentage 4 | attr_accessor :progress 5 | 6 | def initialize(options = {}) 7 | self.progress = options[:progress] 8 | end 9 | 10 | def percentage 11 | progress.percentage_completed.to_s 12 | end 13 | 14 | def justified_percentage 15 | progress.percentage_completed.to_s.rjust(3) 16 | end 17 | 18 | def percentage_with_precision 19 | progress.percentage_completed_with_precision 20 | end 21 | 22 | def justified_percentage_with_precision 23 | progress.percentage_completed_with_precision.to_s.rjust(6) 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /spec/support/warning_filter.rb: -------------------------------------------------------------------------------- 1 | module WarningFilter 2 | class Filter < ::IO 3 | def initialize(io) 4 | @io = io 5 | 6 | super(File::RDONLY | File::WRONLY) 7 | end 8 | 9 | def write(line) 10 | return if warning_from_gem?(line) 11 | 12 | @io.write(line) 13 | end 14 | 15 | protected 16 | 17 | def warning_from_gem?(line) 18 | warning?(line) && from_gem?(line) 19 | end 20 | 21 | def warning?(line) 22 | line.include?('warning') 23 | end 24 | 25 | def from_gem?(line) 26 | Gem.path.any? { |path| line.include?(path) } 27 | end 28 | end 29 | end 30 | 31 | $stderr = WarningFilter::Filter.new($stderr) 32 | -------------------------------------------------------------------------------- /lib/ruby-progressbar/outputs/tty.rb: -------------------------------------------------------------------------------- 1 | require 'ruby-progressbar/output' 2 | 3 | class ProgressBar 4 | module Outputs 5 | class Tty < Output 6 | DEFAULT_FORMAT_STRING = '%t: |%B|'.freeze 7 | 8 | alias refresh_with_format_change with_refresh 9 | 10 | def clear 11 | stream.print clear_string 12 | stream.print "\r" 13 | end 14 | 15 | def bar_update_string 16 | bar.to_s 17 | end 18 | 19 | def default_format 20 | ENV['RUBY_PROGRESS_BAR_FORMAT'] || DEFAULT_FORMAT_STRING 21 | end 22 | 23 | def resolve_format(other_format) 24 | other_format || default_format 25 | end 26 | 27 | def eol 28 | bar.stopped? ? "\n" : "\r" 29 | end 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /lib/ruby-progressbar/throttle.rb: -------------------------------------------------------------------------------- 1 | class ProgressBar 2 | class Throttle 3 | attr_accessor :rate, 4 | :started_at, 5 | :stopped_at, 6 | :timer 7 | 8 | def initialize(options = {}) 9 | self.rate = options[:throttle_rate] || 0.01 10 | self.started_at = nil 11 | self.stopped_at = nil 12 | self.timer = options.fetch(:throttle_timer, Timer.new) 13 | end 14 | 15 | def choke(options = {}) 16 | return unless !timer.started? || 17 | options.fetch(:force_update_if, false) || 18 | timer.elapsed_seconds >= rate 19 | 20 | timer.restart 21 | 22 | yield 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /spec/lib/ruby-progressbar/outputs/null_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'ruby-progressbar/outputs/null' 3 | 4 | class ProgressBar 5 | module Outputs 6 | describe Null do 7 | let(:output_io) { StringIO.new } 8 | 9 | it 'does not output anything ever' do 10 | allow_any_instance_of(Null).to receive(:stream).and_return(output_io) # rubocop:disable RSpec/AnyInstance 11 | 12 | progressbar = ProgressBar::Base.new(:length => 20, :output => Null) 13 | 14 | progressbar.increment 15 | progressbar.log('hello') 16 | progressbar.progress += 20 17 | progressbar.reset 18 | progressbar.finish 19 | 20 | output_io.rewind 21 | 22 | expect(output_io.read).to be_empty 23 | end 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /spec/fixtures/benchmark.rb: -------------------------------------------------------------------------------- 1 | # bundle exec ruby-prof --printer=graph_html 2 | # --file=../results.html 3 | # --require 'ruby-progressbar' 4 | # --sort=total ./spec/fixtures/benchmark.rb 5 | 6 | total = 100_000 7 | # output = File.open('/Users/jfelchner/Downloads/benchmark.txt', 'w+') 8 | output = $stdout 9 | 10 | # Progressbar gem 11 | # bar = ProgressBar.new('Progress', total) 12 | # 13 | # total.times do |i| 14 | # bar.inc 15 | # end 16 | # 17 | # bar.finish 18 | 19 | # Ruby/ProgressBar 20 | bar = ProgressBar.create(:output => output, 21 | :length => 80, 22 | :start => 0, 23 | :total => total) 24 | 25 | total.times do |_i| 26 | # bar.log i 27 | bar.increment 28 | end 29 | -------------------------------------------------------------------------------- /spec/lib/ruby-progressbar/format/molecule_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'ruby-progressbar/format/molecule' 3 | 4 | class ProgressBar 5 | module Format 6 | describe Molecule do 7 | it 'sets the key when initialized' do 8 | molecule = Molecule.new('t') 9 | 10 | expect(molecule.key).to eql 't' 11 | end 12 | 13 | it 'sets the method name when initialized' do 14 | molecule = Molecule.new('t') 15 | 16 | expect(molecule.method_name).to eql [:title_component, :title] 17 | end 18 | 19 | it 'can retrieve the full key for itself' do 20 | molecule = Molecule.new('t') 21 | 22 | expect(molecule.full_key).to eql '%t' 23 | end 24 | 25 | it 'can determine if it is a bar molecule' do 26 | expect(Molecule.new('B')).to be_bar_molecule 27 | end 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /.github/workflows/labels.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | on: 4 | - "issues" 5 | - "label" 6 | - "workflow_dispatch" 7 | 8 | name: "Labels" 9 | jobs: 10 | synchronize-labels: 11 | name: "Synchronize Labels" 12 | runs-on: "ubuntu-latest" 13 | steps: 14 | - name: "Checkout Code" 15 | uses: "actions/checkout@v3" 16 | timeout-minutes: 5 17 | with: 18 | fetch-depth: 0 19 | 20 | - name: "Synchronize Labels" 21 | uses: "micnncim/action-label-syncer@v1" 22 | with: 23 | manifest: ".github/labels.yml" 24 | env: 25 | GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" 26 | -------------------------------------------------------------------------------- /lib/ruby-progressbar/refinements/progress_enumerator.rb: -------------------------------------------------------------------------------- 1 | class ProgressBar 2 | module Refinements 3 | module Enumerator 4 | ARITY_ERROR_MESSAGE = 'Only two arguments allowed to be passed to ' \ 5 | 'with_progressbar (item, progress_bar)'.freeze 6 | 7 | refine ::Enumerator do 8 | def with_progressbar(options = {}, &block) 9 | progress_bar = ProgressBar.create(options.merge(:starting_at => 0, :total => size)) 10 | 11 | each do |item| 12 | progress_bar.increment 13 | 14 | next unless block 15 | 16 | yielded_args = [] 17 | yielded_args << item if block.arity > 0 18 | yielded_args << progress_bar if block.arity > 1 19 | 20 | fail ::ArgumentError, ARITY_ERROR_MESSAGE if block.arity > 2 21 | 22 | yield(*yielded_args) 23 | end 24 | end 25 | end 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/ruby-progressbar/format/formatter.rb: -------------------------------------------------------------------------------- 1 | class ProgressBar 2 | module Format 3 | class Formatter 4 | def self.process(format_string, max_length, bar) 5 | processed_string = format_string.dup 6 | 7 | format_string.non_bar_molecules.each do |molecule| 8 | processed_string.gsub!(molecule.full_key, molecule.lookup_value(bar, nil)) 9 | end 10 | 11 | processed_string.gsub!(/%%/, '%') 12 | 13 | bar_length = max_length - 14 | processed_string.displayable_length + 15 | format_string.bar_molecule_placeholder_length 16 | bar_length = 0 if bar_length < 0 17 | 18 | format_string.bar_molecules.each do |molecule| 19 | processed_string.gsub!(molecule.full_key, 20 | molecule.lookup_value(bar, bar_length)) 21 | end 22 | 23 | processed_string 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/ruby-progressbar/components/rate.rb: -------------------------------------------------------------------------------- 1 | class ProgressBar 2 | module Components 3 | class Rate 4 | attr_accessor :rate_scale, 5 | :timer, 6 | :progress 7 | 8 | def initialize(options = {}) 9 | self.rate_scale = options[:rate_scale] || lambda { |x| x } 10 | self.timer = options[:timer] 11 | self.progress = options[:progress] 12 | end 13 | 14 | def rate_of_change(format_string = '%i') 15 | return '0' if elapsed_seconds <= 0 16 | 17 | format_string % scaled_rate 18 | end 19 | 20 | def rate_of_change_with_precision 21 | rate_of_change('%.2f') 22 | end 23 | 24 | private 25 | 26 | def scaled_rate 27 | rate_scale.call(base_rate) 28 | end 29 | 30 | def base_rate 31 | progress.absolute / elapsed_seconds 32 | end 33 | 34 | def elapsed_seconds 35 | timer.elapsed_whole_seconds.to_f 36 | end 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /lib/ruby-progressbar/time.rb: -------------------------------------------------------------------------------- 1 | # rubocop:disable Style/InlineComment 2 | class ProgressBar 3 | class Time 4 | TIME_MOCKING_LIBRARY_METHODS = [ 5 | :__simple_stub__now, # ActiveSupport 6 | :now_without_mock_time, # Timecop 7 | :now_without_delorean, # Delorean 8 | :now # Unmocked 9 | ].freeze 10 | 11 | def initialize(time = ::Time) 12 | self.time = time 13 | end 14 | 15 | def now 16 | time.__send__(unmocked_time_method) 17 | end 18 | 19 | def unmocked_time_method 20 | @unmocked_time_method ||= TIME_MOCKING_LIBRARY_METHODS.find do |method| 21 | time.respond_to? method 22 | end 23 | end 24 | 25 | protected 26 | 27 | attr_accessor :time 28 | end 29 | end 30 | # rubocop:enable Style/InlineComment 31 | -------------------------------------------------------------------------------- /lib/ruby-progressbar/format/string.rb: -------------------------------------------------------------------------------- 1 | require 'ruby-progressbar/format/molecule' 2 | 3 | class ProgressBar 4 | module Format 5 | class String < ::String 6 | MOLECULE_PATTERN = /%[a-zA-Z]/.freeze 7 | ANSI_SGR_PATTERN = /\e\[[\d;]+m/.freeze 8 | 9 | def displayable_length 10 | gsub(ANSI_SGR_PATTERN, '').length 11 | end 12 | 13 | def bar_molecule_placeholder_length 14 | @bar_molecule_placeholder_length ||= bar_molecules.size * 2 15 | end 16 | 17 | def non_bar_molecules 18 | @non_bar_molecules ||= molecules.select(&:non_bar_molecule?) 19 | end 20 | 21 | def bar_molecules 22 | @bar_molecules ||= molecules.select(&:bar_molecule?) 23 | end 24 | 25 | def molecules 26 | @molecules ||= begin 27 | molecules = [] 28 | 29 | scan(MOLECULE_PATTERN) do |match| 30 | molecules << Molecule.new(match[1, 1]) 31 | end 32 | 33 | molecules 34 | end 35 | end 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /lib/ruby-progressbar/outputs/non_tty.rb: -------------------------------------------------------------------------------- 1 | require 'ruby-progressbar/output' 2 | 3 | class ProgressBar 4 | module Outputs 5 | class NonTty < Output 6 | DEFAULT_FORMAT_STRING = '%t: |%b|'.freeze 7 | 8 | def clear 9 | self.last_update_length = 0 10 | 11 | stream.print "\n" 12 | end 13 | 14 | def last_update_length 15 | @last_update_length ||= 0 16 | end 17 | 18 | def bar_update_string 19 | formatted_string = bar.to_s 20 | formatted_string = formatted_string[0...-1] unless bar.finished? 21 | 22 | output_string = formatted_string[last_update_length..-1] 23 | self.last_update_length = formatted_string.length 24 | 25 | output_string.to_s 26 | end 27 | 28 | def default_format 29 | DEFAULT_FORMAT_STRING 30 | end 31 | 32 | def resolve_format(*) 33 | default_format 34 | end 35 | 36 | def refresh_with_format_change(*); end 37 | 38 | def eol 39 | bar.stopped? ? "\n" : '' 40 | end 41 | 42 | protected 43 | 44 | attr_writer :last_update_length 45 | end 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010-2019 The Kompanee, Ltd 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /gemfiles/v1/Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | ruby-progressbar (1.13.0) 5 | 6 | GEM 7 | remote: https://rubygems.org/ 8 | specs: 9 | awesome_print (1.8.0) 10 | diff-lcs (1.4.4) 11 | fuubar (2.5.0) 12 | rspec-core (~> 3.0) 13 | ruby-progressbar (~> 1.4) 14 | rspec (3.10.0) 15 | rspec-core (~> 3.10.0) 16 | rspec-expectations (~> 3.10.0) 17 | rspec-mocks (~> 3.10.0) 18 | rspec-core (3.10.0) 19 | rspec-support (~> 3.10.0) 20 | rspec-expectations (3.10.0) 21 | diff-lcs (>= 1.2.0, < 2.0) 22 | rspec-support (~> 3.10.0) 23 | rspec-mocks (3.10.0) 24 | diff-lcs (>= 1.2.0, < 2.0) 25 | rspec-support (~> 3.10.0) 26 | rspec-support (3.10.0) 27 | rspectacular (0.70.8) 28 | fuubar (~> 2.0) 29 | rspec (~> 3.1) 30 | ruby-prof (0.15.9) 31 | timecop (0.9.6) 32 | 33 | PLATFORMS 34 | ruby 35 | x86_64-linux 36 | 37 | DEPENDENCIES 38 | awesome_print (~> 1.6) 39 | fuubar (~> 2.3) 40 | rspec (~> 3.7) 41 | rspectacular (~> 0.70.6) 42 | ruby-prof (~> 0.15.8) 43 | ruby-progressbar! 44 | timecop (~> 0.9) 45 | 46 | BUNDLED WITH 47 | 1.17.3 48 | -------------------------------------------------------------------------------- /gemfiles/v2/Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | ruby-progressbar (1.13.0) 5 | 6 | GEM 7 | remote: https://rubygems.org/ 8 | specs: 9 | awesome_print (1.9.2) 10 | diff-lcs (1.5.0) 11 | fuubar (2.5.1) 12 | rspec-core (~> 3.0) 13 | ruby-progressbar (~> 1.4) 14 | rspec (3.12.0) 15 | rspec-core (~> 3.12.0) 16 | rspec-expectations (~> 3.12.0) 17 | rspec-mocks (~> 3.12.0) 18 | rspec-core (3.12.1) 19 | rspec-support (~> 3.12.0) 20 | rspec-expectations (3.12.2) 21 | diff-lcs (>= 1.2.0, < 2.0) 22 | rspec-support (~> 3.12.0) 23 | rspec-mocks (3.12.3) 24 | diff-lcs (>= 1.2.0, < 2.0) 25 | rspec-support (~> 3.12.0) 26 | rspec-support (3.12.0) 27 | rspectacular (0.70.8) 28 | fuubar (~> 2.0) 29 | rspec (~> 3.1) 30 | ruby-prof (1.6.1) 31 | timecop (0.9.6) 32 | 33 | PLATFORMS 34 | x86_64-darwin-19 35 | x86_64-linux 36 | 37 | DEPENDENCIES 38 | awesome_print (~> 1.6) 39 | fuubar (~> 2.3) 40 | rspec (~> 3.7) 41 | rspectacular (~> 0.70.6) 42 | ruby-prof (~> 1.6) 43 | ruby-progressbar! 44 | timecop (~> 0.9) 45 | 46 | BUNDLED WITH 47 | 2.3.24 48 | -------------------------------------------------------------------------------- /.rubocop_local.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | Layout/ClassStructure: 4 | Enabled: false 5 | 6 | Layout/ClosingParenthesisIndentation: 7 | Enabled: false 8 | 9 | Layout/DotPosition: 10 | EnforcedStyle: 'trailing' 11 | 12 | Layout/ExtraSpacing: 13 | Enabled: false 14 | 15 | Layout/IndentationWidth: 16 | Enabled: false 17 | 18 | Layout/MultilineMethodCallIndentation: 19 | Enabled: false 20 | 21 | Lint/NumberConversion: 22 | Enabled: false 23 | 24 | Naming/FileName: 25 | Exclude: 26 | - 'lib/ruby-progressbar.rb' 27 | 28 | RSpec/MultipleMemoizedHelpers: 29 | Enabled: false 30 | 31 | Style/ConstantVisibility: 32 | Enabled: false 33 | 34 | Style/FrozenStringLiteralComment: 35 | Enabled: false 36 | 37 | Style/HashSyntax: 38 | EnforcedStyle: hash_rockets 39 | 40 | Style/Lambda: 41 | Enabled: false 42 | 43 | Style/NumericPredicate: 44 | Enabled: false 45 | 46 | Style/OptionHash: 47 | Enabled: false 48 | 49 | Style/SafeNavigation: 50 | Enabled: false 51 | 52 | Style/SlicingWithRange: 53 | Enabled: false 54 | 55 | Style/SymbolArray: 56 | Enabled: false 57 | 58 | Style/TrailingCommaInArguments: 59 | EnforcedStyleForMultiline: 'no_comma' 60 | 61 | Style/TrailingCommaInArrayLiteral: 62 | EnforcedStyleForMultiline: 'no_comma' 63 | 64 | Style/TrailingCommaInHashLiteral: 65 | EnforcedStyleForMultiline: 'no_comma' 66 | -------------------------------------------------------------------------------- /lib/ruby-progressbar/timer.rb: -------------------------------------------------------------------------------- 1 | require 'ruby-progressbar/time' 2 | 3 | class ProgressBar 4 | class Timer 5 | attr_accessor :started_at, 6 | :stopped_at 7 | 8 | def initialize(options = {}) 9 | self.time = options[:time] || ::ProgressBar::Time.new 10 | end 11 | 12 | def start 13 | self.started_at = stopped? ? time.now - (stopped_at - started_at) : time.now 14 | self.stopped_at = nil 15 | end 16 | 17 | def stop 18 | return unless started? 19 | 20 | self.stopped_at = time.now 21 | end 22 | 23 | def pause 24 | stop 25 | end 26 | 27 | def resume 28 | start 29 | end 30 | 31 | def now 32 | time.now 33 | end 34 | 35 | def started? 36 | started_at 37 | end 38 | 39 | def stopped? 40 | stopped_at 41 | end 42 | 43 | def reset 44 | self.started_at = nil 45 | self.stopped_at = nil 46 | end 47 | 48 | def reset? 49 | !started_at 50 | end 51 | 52 | def restart 53 | reset 54 | start 55 | end 56 | 57 | def elapsed_seconds 58 | return 0 unless started? 59 | 60 | ((stopped_at || time.now) - started_at) 61 | end 62 | 63 | def elapsed_whole_seconds 64 | elapsed_seconds.floor 65 | end 66 | 67 | def divide_seconds(seconds) 68 | hours, seconds = seconds.divmod(3600) 69 | minutes, seconds = seconds.divmod(60) 70 | 71 | [hours, minutes, seconds] 72 | end 73 | 74 | protected 75 | 76 | attr_accessor :time 77 | end 78 | end 79 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature Request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | Current Problem 8 | -------------------------------------------------------------------------------- 9 | 10 | 16 | 17 | Potential Solution or New Behavior 18 | -------------------------------------------------------------------------------- 19 | 20 | 21 | 22 | ### Mockups 23 | 24 | 28 | 29 | 51 | -------------------------------------------------------------------------------- /lib/ruby-progressbar/projectors/smoothed_average.rb: -------------------------------------------------------------------------------- 1 | class ProgressBar 2 | module Projectors 3 | class SmoothedAverage 4 | DEFAULT_STRENGTH = 0.1 5 | DEFAULT_BEGINNING_POSITION = 0 6 | 7 | attr_accessor :samples, 8 | :strength 9 | attr_reader :projection 10 | 11 | def initialize(options = {}) 12 | self.samples = [] 13 | self.projection = 0.0 14 | self.strength = options[:strength] || DEFAULT_STRENGTH 15 | 16 | start(:at => DEFAULT_BEGINNING_POSITION) 17 | end 18 | 19 | def start(options = {}) 20 | self.projection = 0.0 21 | self.progress = samples[0] = (options[:at] || progress) 22 | end 23 | 24 | def decrement 25 | self.progress -= 1 26 | end 27 | 28 | def increment 29 | self.progress += 1 30 | end 31 | 32 | def progress 33 | samples[1] 34 | end 35 | 36 | def total=(_new_total); end 37 | 38 | def reset 39 | start(:at => samples[0]) 40 | end 41 | 42 | def progress=(new_progress) 43 | samples[1] = new_progress 44 | self.projection = \ 45 | self.class.calculate( 46 | @projection, 47 | absolute, 48 | strength 49 | ) 50 | end 51 | 52 | def none? 53 | projection.zero? 54 | end 55 | 56 | def self.calculate(current_projection, new_value, rate) 57 | (new_value * (1.0 - rate)) + (current_projection * rate) 58 | end 59 | 60 | protected 61 | 62 | attr_writer :projection 63 | 64 | private 65 | 66 | def absolute 67 | samples[1] - samples[0] 68 | end 69 | end 70 | end 71 | end 72 | -------------------------------------------------------------------------------- /spec/lib/ruby-progressbar/components/percentage_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'ruby-progressbar/components/percentage' 3 | 4 | class ProgressBar 5 | module Components 6 | describe Percentage do 7 | describe '#percentage' do 8 | it 'returns the percentage' do 9 | progress = Progress.new(:total => 10) 10 | percentage = Percentage.new(:progress => progress) 11 | 12 | progress.progress = 5 13 | 14 | expect(percentage.percentage).to eql '50' 15 | end 16 | end 17 | 18 | describe '#percentage_with_precision' do 19 | it 'returns the percentage' do 20 | progress = Progress.new(:total => 10) 21 | percentage = Percentage.new(:progress => progress) 22 | 23 | progress.progress = 5 24 | 25 | expect(percentage.percentage_with_precision).to eql '50.00' 26 | end 27 | end 28 | 29 | describe '#justified_percentage' do 30 | it 'returns the percentage' do 31 | progress = Progress.new(:total => 10) 32 | percentage = Percentage.new(:progress => progress) 33 | 34 | progress.progress = 5 35 | 36 | expect(percentage.justified_percentage).to eql ' 50' 37 | end 38 | end 39 | 40 | describe '#justified_percentage_with_precision' do 41 | it 'returns the percentage' do 42 | progress = Progress.new(:total => 10) 43 | percentage = Percentage.new(:progress => progress) 44 | 45 | progress.progress = 5 46 | 47 | expect(percentage.justified_percentage_with_precision).to eql ' 50.00' 48 | end 49 | end 50 | end 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /.github/workflows/testing.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | name: "Build" 4 | 5 | on: 6 | - "push" 7 | - "pull_request" 8 | 9 | jobs: 10 | test: 11 | name: "Testing" 12 | runs-on: "ubuntu-latest" 13 | 14 | strategy: 15 | matrix: 16 | ruby: 17 | - "2.1" 18 | - "2.2" 19 | - "2.3" 20 | - "2.4" 21 | - "2.5" 22 | - "2.6" 23 | - "2.7" 24 | - "3.0" 25 | - "3.1" 26 | - "3.2" 27 | - "ruby-head" 28 | include: 29 | - ruby: "2.1" 30 | bundler: "1" 31 | 32 | - ruby: "2.2" 33 | bundler: "1" 34 | 35 | continue-on-error: ${{ endsWith(matrix.ruby, 'head') }} 36 | 37 | env: 38 | BUNDLE_GEMFILE: "./gemfiles/v${{ matrix.bundler || '2' }}/Gemfile" 39 | 40 | steps: 41 | - name: "Checkout Code" 42 | uses: "actions/checkout@v3" 43 | timeout-minutes: 5 44 | with: 45 | fetch-depth: 0 46 | 47 | - name: "Build Ruby" 48 | uses: "ruby/setup-ruby@v1" 49 | with: 50 | ruby-version: "${{ matrix.ruby }}" 51 | bundler: "${{ matrix.bundler || 2 }}" 52 | bundler-cache: true 53 | 54 | - name: "Run RSpec" 55 | run: | 56 | bundle exec rspec 57 | -------------------------------------------------------------------------------- /spec/lib/ruby-progressbar/time_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | class TimeMockedWithTimecop 4 | def self.now_without_mock_time; end 5 | end 6 | 7 | class TimeMockedWithDelorean 8 | def self.now_without_delorean; end 9 | end 10 | 11 | class TimeMockedWithActiveSupport 12 | def self.__simple_stub__now; end 13 | end 14 | 15 | class UnmockedTime 16 | def self.now; end 17 | end 18 | 19 | class ProgressBar 20 | describe Time do 21 | it 'when Time is being mocked by Timecop retrieves the unmocked Timecop time' do 22 | allow(TimeMockedWithTimecop).to receive(:now_without_mock_time) 23 | 24 | Time.new(TimeMockedWithTimecop).now 25 | 26 | expect(TimeMockedWithTimecop).to have_received(:now_without_mock_time).once 27 | end 28 | 29 | it 'when Time is being mocked by Delorean retrieves the unmocked Delorean time' do 30 | allow(TimeMockedWithDelorean).to receive(:now_without_delorean) 31 | 32 | Time.new(TimeMockedWithDelorean).now 33 | 34 | expect(TimeMockedWithDelorean).to have_received(:now_without_delorean).once 35 | end 36 | 37 | it 'when Time is being mocked by ActiveSupport retrieves the unmocked time' do 38 | allow(TimeMockedWithActiveSupport).to receive(:__simple_stub__now) 39 | 40 | Time.new(TimeMockedWithActiveSupport).now 41 | 42 | expect(TimeMockedWithActiveSupport).to have_received(:__simple_stub__now).once 43 | end 44 | 45 | it 'when Time is not being mocked will return the actual time' do 46 | allow(UnmockedTime).to receive(:now) 47 | 48 | Time.new(UnmockedTime).now 49 | 50 | expect(UnmockedTime).to have_received(:now).once 51 | end 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /certs/jfelchner.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIEdjCCAt6gAwIBAgIBATANBgkqhkiG9w0BAQsFADAyMTAwLgYDVQQDDCdhY2Nv 3 | dW50c19ydWJ5Z2Vtcy9EQz10aGVrb21wYW5lZS9EQz1jb20wHhcNMjMwMjI2MTcx 4 | MDI1WhcNMjYwMjI1MTcxMDI1WjAyMTAwLgYDVQQDDCdhY2NvdW50c19ydWJ5Z2Vt 5 | cy9EQz10aGVrb21wYW5lZS9EQz1jb20wggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAw 6 | ggGKAoIBgQCqhYn5ODEoLvuBIF2M1GzoaZU28+ntP5QApvDE0Te04n0JbBC1cNYH 7 | mr71neeSx7tlZ9w9kJ/8GNcY5bm7pNJqhyhfc+uG9M7FttcxM8AYXogjcdUDP234 8 | +TdmZIz20JxtWBgAZK2I3ktlgLFLC3Pxq63yzhJ75Xok07Wh+ypwjGzDNofPhz+y 9 | XR+UeUTp2UGe7kDVoqu/AwwPVhk1qUIRFLfC8SLDTD0CuNW3/AnkwQrKSm8vkiIn 10 | q9GCnOq0+jQly0b6a1Gi3ZDYEEswnTzziw2gotUZnQkF5bcOcxK1CB/Okk2jtG7i 11 | ztMEU785tERbOSszZrz9rBS/+GnMxlD0pxy50zFfHX3jY1hwnwGjE8Gg+0iYr/tm 12 | eysjhcbZfKrMynoqAioCSwstIwtYYYYpYzCPZzwaIBaBqQmUTkuMeiGbAdOdFOrR 13 | lOgl5jxCYbNOOTaXbm0nGBFaTucB88+JLbsNAuoNGUf/ybDcZ1zKRkMr2vtb+OtL 14 | GoP81fN6l88CAwEAAaOBljCBkzAJBgNVHRMEAjAAMAsGA1UdDwQEAwIEsDAdBgNV 15 | HQ4EFgQUL4eV4OM9h7fkM27qf9p4ragHi6AwLAYDVR0RBCUwI4EhYWNjb3VudHMr 16 | cnVieWdlbXNAdGhla29tcGFuZWUuY29tMCwGA1UdEgQlMCOBIWFjY291bnRzK3J1 17 | YnlnZW1zQHRoZWtvbXBhbmVlLmNvbTANBgkqhkiG9w0BAQsFAAOCAYEAD/tBN1cM 18 | 8Qu6u+rPM3SEtlEK/ZdVY0IowXtXMskkderNBJ4HY+xBfIWyAXLTr3Fy6xscVZ95 19 | raFCiWHqvR577u3/BsVZ5BoQ0g25oY2bwoamQSdx71ygs25Q+UFbg6lHq6olszj0 20 | HqKXUy/iPFb+OzGq7NOtKOD5pHl3ew8H7U5tfh0kx6B5TdL9BZLurjskW0n2G+kY 21 | NSGCTGYU8wY4Bsk/AmfoFT/ATwmrf68CTD+IBY5yvt2DGvcyuSrX1RQP8Vk//0EP 22 | J2ezTNGIBeQFcyyo09gMfy1yxv9XAvwmy6pAx7/m/F2XzTiXuzmJ7zJ6J0OaHUG4 23 | svJgf3o9Eor2okQND60Qdpdl4qdSy3KaNqvQQbTRB96e/+K8ksz4rras5jPaAs0p 24 | DV37k4cni6c/jUm2CqepsJ/dbzeWdkhcuO6hwEQV0jvFky5C6d5hHcrbJwxl1sTL 25 | V+pWW6L9MSZzKkjWVJXD43B3tWBjIDthQVTzS4j90PUkUXgBXjS7Jxj/ 26 | -----END CERTIFICATE----- 27 | -------------------------------------------------------------------------------- /lib/ruby-progressbar/output.rb: -------------------------------------------------------------------------------- 1 | require 'ruby-progressbar/calculators/length' 2 | require 'ruby-progressbar/throttle' 3 | 4 | class ProgressBar 5 | class Output 6 | DEFAULT_OUTPUT_STREAM = $stdout 7 | 8 | attr_accessor :stream 9 | 10 | def initialize(options = {}) 11 | self.bar = options[:bar] 12 | self.stream = options[:output] || DEFAULT_OUTPUT_STREAM 13 | self.throttle = Throttle.new(options) 14 | self.length_calculator = Calculators::Length.new( 15 | :length => options[:length], 16 | :output => stream 17 | ) 18 | end 19 | 20 | def self.detect(options = {}) 21 | if options[:output].is_a?(Class) && options[:output] <= ProgressBar::Output 22 | options[:output].new(options) 23 | elsif (options[:output] || DEFAULT_OUTPUT_STREAM).tty? 24 | Outputs::Tty.new(options) 25 | else 26 | Outputs::NonTty.new(options) 27 | end 28 | end 29 | 30 | def log(string) 31 | clear 32 | stream.puts string 33 | 34 | refresh(:force => true) unless bar.stopped? 35 | end 36 | 37 | def clear_string 38 | ' ' * length_calculator.length 39 | end 40 | 41 | def length 42 | length_calculator.length 43 | end 44 | 45 | def with_refresh 46 | yield 47 | refresh 48 | end 49 | 50 | def refresh(options = {}) 51 | throttle.choke(:force_update_if => (bar.stopped? || options[:force])) do 52 | clear if length_calculator.length_changed? 53 | 54 | print_and_flush 55 | end 56 | end 57 | 58 | protected 59 | 60 | attr_accessor :length_calculator, 61 | :throttle, 62 | :bar 63 | 64 | private 65 | 66 | def print_and_flush 67 | stream.print bar_update_string + eol 68 | stream.flush 69 | end 70 | end 71 | end 72 | -------------------------------------------------------------------------------- /spec/lib/ruby-progressbar/output_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'ruby-progressbar/output' 3 | 4 | class MyTestOutput < ProgressBar::Output 5 | def clear; end 6 | 7 | def bar_update_string 8 | bar.to_s 9 | end 10 | 11 | def log(_string) 12 | stream.puts 'We All Float' 13 | end 14 | 15 | def resolve_format(*) 16 | '%t |%B|' 17 | end 18 | 19 | def eol 20 | bar.stopped? ? "\n" : "\r" 21 | end 22 | end 23 | 24 | class ProgressBar 25 | describe Output do 26 | let(:output_io) { StringIO.new } 27 | 28 | it 'uses the passed in output class if it is a ProgressBar::Output' do 29 | allow_any_instance_of(MyTestOutput).to receive(:stream).and_return(output_io) # rubocop:disable RSpec/AnyInstance 30 | 31 | progressbar = ProgressBar::Base.new(:length => 20, :output => MyTestOutput) 32 | 33 | progressbar.log('hello') 34 | 35 | output_io.rewind 36 | 37 | expect(output_io.read).to eql "Progress | |\r" \ 38 | "We All Float\n" 39 | end 40 | 41 | it 'passes the output stream to the length calculator' do 42 | allow(Calculators::Length).to receive(:new).and_call_original 43 | 44 | ProgressBar::Output.new(:output => output_io) 45 | 46 | expect(Calculators::Length).to have_received(:new). 47 | with( 48 | :length => nil, 49 | :output => output_io 50 | ) 51 | end 52 | 53 | it 'passes stdout to the length calculator if output is not specified' do 54 | allow(Calculators::Length).to receive(:new).and_call_original 55 | 56 | ProgressBar::Output.new 57 | 58 | expect(Calculators::Length).to have_received(:new). 59 | with( 60 | :length => nil, 61 | :output => $stdout 62 | ) 63 | end 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /.github/contributing.md: -------------------------------------------------------------------------------- 1 | Contributing 2 | ================================================================================ 3 | 4 | We love pull requests from everyone. By participating in this project, you 5 | agree to abide by the code of conduct. 6 | 7 | Here are some ways **you** can contribute: 8 | 9 | * by using alpha, beta, and prerelease versions 10 | * by reporting bugs 11 | * by suggesting new features 12 | * by writing or editing documentation 13 | * by writing specifications 14 | * by writing code 15 | * by fixing styling inconsistencies 16 | * by refactoring code 17 | * by closing issues 18 | * by reviewing patches 19 | 20 | Submitting an Issue 21 | -------------------------------------------------------------------------------- 22 | 23 | We use the GitHub issue tracker to track bugs and features. Before submitting 24 | a bug report or feature request, check to make sure it hasn't already been 25 | submitted. When submitting a bug report, please include a [Gist][gist] that 26 | includes a stack trace and any details that may be necessary to reproduce the 27 | bug. Ideally, a bug report should include a pull request with failing specs. 28 | 29 | Submitting a Pull Request 30 | -------------------------------------------------------------------------------- 31 | 32 | 1. [Fork][fork] the official repository. 33 | 2. [Create a topic branch.][branch] 34 | 3. Implement your feature or bug fix. 35 | 4. Add, commit, and push your changes. 36 | 5. [Submit a pull request.][pr] 37 | 38 | Notes 39 | -------------------------------------------------------------------------------- 40 | 41 | * Please add tests if you changed code. Contributions without tests won't be 42 | accepted. 43 | 44 | Inspired by 45 | 46 | [branch]: https://help.github.com/articles/creating-and-deleting-branches-within-your-repository/ 47 | [fork]: https://help.github.com/articles/fork-a-repo/ 48 | [gist]: https://gist.github.com/ 49 | [pr]: https://help.github.com/articles/using-pull-requests/ 50 | -------------------------------------------------------------------------------- /.github/workflows/locking.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | on: 4 | schedule: 5 | - cron: "0 0 * * *" 6 | workflow_dispatch: {} 7 | 8 | name: "Issue Locking" 9 | jobs: 10 | lock-issues: 11 | runs-on: "ubuntu-latest" 12 | steps: 13 | - name: "Lock Issues" 14 | uses: "dessant/lock-threads@v4" 15 | with: 16 | github-token: "${{ secrets.GITHUB_TOKEN }}" 17 | issue-lock-inactive-days: "180" 18 | issue-exclude-created-before: "" 19 | issue-exclude-labels: "outdated,on-hold,in-progress,watchlist" 20 | issue-lock-labels: "outdated" 21 | issue-lock-comment: > 22 | This issue has been automatically locked since there has not been 23 | any recent activity after it was closed. Please open a new issue 24 | for related bugs. 25 | issue-lock-reason: "resolved" 26 | process-only: "issues" 27 | 28 | lock-pull-requests: 29 | runs-on: "ubuntu-latest" 30 | steps: 31 | - name: "Lock Pull Requests" 32 | uses: "dessant/lock-threads@v4" 33 | with: 34 | github-token: "${{ secrets.GITHUB_TOKEN }}" 35 | pr-lock-inactive-days: "180" 36 | pr-exclude-created-before: "" 37 | pr-exclude-labels: "outdated,on-hold,in-progress,watchlist" 38 | pr-lock-labels: "outdated" 39 | pr-lock-comment: > 40 | This issue has been automatically locked since there has not been 41 | any recent activity after it was closed. Please open a new issue 42 | for related bugs. 43 | pr-lock-reason: "resolved" 44 | process-only: "prs" 45 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # shellcheck disable=SC2037 3 | 4 | ### 5 | # Install Bundler Source Credentials 6 | # 7 | declare private_url="$(command chamber show --as-env --files="config/settings/**/*.yml" | grep 'GEMFURY_URL' | perl -p -e 's/GEMFURY_URL="(.*)"/\1/')" 8 | declare private_token="$(command chamber show --as-env --files="config/settings/**/*.yml" | grep 'GEMFURY_TOKEN' | perl -p -e 's/GEMFURY_TOKEN="(.*)"/\1/')" 9 | 10 | if [ -n "${private_url}" ] && [ -n "${private_token}" ]; then 11 | bundle config --local "${private_url}" "${private_token}" 12 | else 13 | bundle config --delete "${private_url}" 14 | fi 15 | 16 | ### 17 | # Install Dependencies 18 | # 19 | echo 'Installing Ruby Packages...' 20 | gem install bundler --conservative 21 | bundle check || bundle install 22 | 23 | gem install rubocop --conservative 24 | 25 | echo 'Installing NPM Packages...' 26 | command npm install -g "jsonlint" 2> /dev/null 27 | command npm update -g "jsonlint" 2> /dev/null 28 | 29 | nodenv rehash 2> /dev/null 30 | 31 | ### 32 | # Install Project bin Executables 33 | # 34 | echo 'Installing project binaries...' 35 | if ! [ -d ".git/shellwreck-verification-dir" ]; then 36 | mkdir .git/shellwreck-verification-dir 37 | fi 38 | 39 | ### 40 | # Add Project Information to the Git Repository 41 | # 42 | echo 'Installing project configuration into git repo...' 43 | command git config --replace-all --local project.application-name 'ruby-progressbar' 44 | 45 | command git config --replace-all --local deployment.pipeline 'rubygems' 46 | command git config --replace-all --local deployment.profile 'jfelchner' 47 | 48 | command git config --replace-all --local workflow.issue-tracker 'github' 49 | 50 | ### 51 | # Setup Git Hook Templates 52 | # 53 | gem install overcommit --conservative 54 | 55 | if [ -d "${HOME}/.shellwreck/plugins/git/symlinks/hooks" ]; then 56 | rm --recursive --force .git/hooks 57 | ln -s "${HOME}/.shellwreck/plugins/git/symlinks/hooks" .git/hooks 58 | fi 59 | -------------------------------------------------------------------------------- /progressbar.gemspec: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'ruby-progressbar/version' 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = 'progressbar' 8 | spec.version = ProgressBar::VERSION 9 | spec.authors = ['thekompanee', 'jfelchner'] 10 | spec.email = ['support@thekompanee.com'] 11 | spec.summary = %q{Ruby/ProgressBar is a flexible text progress bar library for Ruby.} 12 | spec.description = %q{Ruby/ProgressBar is an extremely flexible text progress bar library for Ruby. The output can be customized with a flexible formatting system including: percentage, bars of various formats, elapsed time and estimated time remaining.} 13 | spec.homepage = 'https://github.com/jfelchner/ruby-progressbar' 14 | spec.licenses = ['MIT'] 15 | 16 | spec.cert_chain = ['certs/jfelchner.pem'] 17 | spec.signing_key = File.expand_path('~/.gem/certs/jfelchner-private_key.pem') if $0 =~ /gem\z/ 18 | 19 | spec.executables = [] 20 | spec.files = Dir['{app,config,db,lib/ruby-progressbar}/**/*'] + %w{lib/progressbar.rb Rakefile README.md LICENSE.txt} 21 | 22 | spec.metadata = { 23 | 'bug_tracker_uri' => 'https://github.com/jfelchner/ruby-progressbar/issues', 24 | 'changelog_uri' => 'https://github.com/jfelchner/ruby-progressbar/blob/master/CHANGELOG.md', 25 | 'documentation_uri' => "https://github.com/jfelchner/ruby-progressbar/tree/releases/v#{ProgressBar::VERSION}", 26 | 'homepage_uri' => 'https://github.com/jfelchner/ruby-progressbar', 27 | 'source_code_uri' => 'https://github.com/jfelchner/ruby-progressbar', 28 | 'wiki_uri' => 'https://github.com/jfelchner/ruby-progressbar/wiki', 29 | } 30 | 31 | spec.add_development_dependency 'rspec', ["~> 3.7"] 32 | spec.add_development_dependency 'rspectacular', ["~> 0.70.6"] 33 | spec.add_development_dependency 'fuubar', ["~> 2.3"] 34 | spec.add_development_dependency 'timecop', ["~> 0.9"] 35 | end 36 | -------------------------------------------------------------------------------- /.yamllint: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | rules: 4 | braces: 5 | min-spaces-inside: 1 6 | max-spaces-inside: 1 7 | min-spaces-inside-empty: 0 8 | max-spaces-inside-empty: 0 9 | brackets: 10 | min-spaces-inside: 0 11 | max-spaces-inside: 0 12 | min-spaces-inside-empty: 0 13 | max-spaces-inside-empty: 0 14 | colons: 15 | max-spaces-before: 0 16 | max-spaces-after: -1 17 | commas: 18 | max-spaces-before: 0 19 | min-spaces-after: 1 20 | max-spaces-after: 1 21 | comments: 22 | require-starting-space: true 23 | min-spaces-from-content: 1 24 | comments-indentation: "enable" 25 | document-end: 26 | present: false 27 | document-start: 28 | present: true 29 | empty-lines: 30 | max: 1 31 | max-start: 0 32 | max-end: 0 33 | empty-values: 34 | forbid-in-block-mappings: true 35 | forbid-in-flow-mappings: true 36 | hyphens: 37 | max-spaces-after: 1 38 | indentation: 39 | spaces: 2 40 | indent-sequences: true 41 | check-multi-line-strings: false 42 | key-duplicates: "enable" 43 | key-ordering: "disable" 44 | line-length: 45 | max: 90 46 | allow-non-breakable-words: true 47 | allow-non-breakable-inline-mappings: true 48 | new-line-at-end-of-file: "enable" 49 | new-lines: 50 | type: "unix" 51 | octal-values: 52 | forbid-implicit-octal: true 53 | forbid-explicit-octal: true 54 | trailing-spaces: "enable" 55 | -------------------------------------------------------------------------------- /ruby-progressbar.gemspec: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'ruby-progressbar/version' 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = 'ruby-progressbar' 8 | spec.version = ProgressBar::VERSION 9 | spec.authors = ['thekompanee', 'jfelchner'] 10 | spec.email = ['support@thekompanee.com'] 11 | spec.summary = %q{Ruby/ProgressBar is a flexible text progress bar library for Ruby.} 12 | spec.description = %q{Ruby/ProgressBar is an extremely flexible text progress bar library for Ruby. The output can be customized with a flexible formatting system including: percentage, bars of various formats, elapsed time and estimated time remaining.} 13 | spec.homepage = 'https://github.com/jfelchner/ruby-progressbar' 14 | spec.licenses = ['MIT'] 15 | 16 | spec.cert_chain = ['certs/jfelchner.pem'] 17 | spec.signing_key = File.expand_path('~/.gem/certs/jfelchner-private_key.pem') if $0 =~ /gem\z/ 18 | 19 | spec.executables = [] 20 | spec.files = Dir['{app,config,db,lib/ruby-progressbar}/**/*'] + %w{lib/ruby-progressbar.rb Rakefile README.md LICENSE.txt} 21 | 22 | spec.metadata = { 23 | 'bug_tracker_uri' => 'https://github.com/jfelchner/ruby-progressbar/issues', 24 | 'changelog_uri' => 'https://github.com/jfelchner/ruby-progressbar/blob/master/CHANGELOG.md', 25 | 'documentation_uri' => "https://github.com/jfelchner/ruby-progressbar/tree/releases/v#{ProgressBar::VERSION}", 26 | 'homepage_uri' => 'https://github.com/jfelchner/ruby-progressbar', 27 | 'source_code_uri' => 'https://github.com/jfelchner/ruby-progressbar', 28 | 'wiki_uri' => 'https://github.com/jfelchner/ruby-progressbar/wiki', 29 | } 30 | 31 | spec.add_development_dependency 'rspec', ["~> 3.7"] 32 | spec.add_development_dependency 'rspectacular', ["~> 0.70.6"] 33 | spec.add_development_dependency 'fuubar', ["~> 2.3"] 34 | spec.add_development_dependency 'timecop', ["~> 0.9"] 35 | end 36 | -------------------------------------------------------------------------------- /gemfiles/v1/ruby-progressbar.gemspec: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | lib = File.expand_path('../../../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'ruby-progressbar/version' 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = 'ruby-progressbar' 8 | spec.version = ProgressBar::VERSION 9 | spec.authors = ['thekompanee', 'jfelchner'] 10 | spec.email = ['support@thekompanee.com'] 11 | spec.summary = %q{Ruby/ProgressBar is a flexible text progress bar library for Ruby.} 12 | spec.description = %q{Ruby/ProgressBar is an extremely flexible text progress bar library for Ruby. The output can be customized with a flexible formatting system including: percentage, bars of various formats, elapsed time and estimated time remaining.} 13 | spec.homepage = 'https://github.com/jfelchner/ruby-progressbar' 14 | spec.licenses = ['MIT'] 15 | 16 | spec.cert_chain = ['certs/jfelchner.pem'] 17 | spec.signing_key = File.expand_path('~/.gem/certs/jfelchner-private_key.pem') if $0 =~ /gem\z/ 18 | 19 | spec.executables = [] 20 | spec.files = Dir['{app,config,db,lib/ruby-progressbar}/**/*'] + %w{lib/ruby-progressbar.rb Rakefile README.md LICENSE.txt} 21 | 22 | spec.metadata = { 23 | 'bug_tracker_uri' => 'https://github.com/jfelchner/ruby-progressbar/issues', 24 | 'changelog_uri' => 'https://github.com/jfelchner/ruby-progressbar/blob/master/CHANGELOG.md', 25 | 'documentation_uri' => "https://github.com/jfelchner/ruby-progressbar/tree/releases/v#{ProgressBar::VERSION}", 26 | 'homepage_uri' => 'https://github.com/jfelchner/ruby-progressbar', 27 | 'source_code_uri' => 'https://github.com/jfelchner/ruby-progressbar', 28 | 'wiki_uri' => 'https://github.com/jfelchner/ruby-progressbar/wiki', 29 | } 30 | 31 | spec.add_development_dependency 'rspec', ["~> 3.7"] 32 | spec.add_development_dependency 'rspectacular', ["~> 0.70.6"] 33 | spec.add_development_dependency 'fuubar', ["~> 2.3"] 34 | spec.add_development_dependency 'timecop', ["~> 0.9"] 35 | end 36 | -------------------------------------------------------------------------------- /gemfiles/v2/ruby-progressbar.gemspec: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | lib = File.expand_path('../../../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'ruby-progressbar/version' 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = 'ruby-progressbar' 8 | spec.version = ProgressBar::VERSION 9 | spec.authors = ['thekompanee', 'jfelchner'] 10 | spec.email = ['support@thekompanee.com'] 11 | spec.summary = %q{Ruby/ProgressBar is a flexible text progress bar library for Ruby.} 12 | spec.description = %q{Ruby/ProgressBar is an extremely flexible text progress bar library for Ruby. The output can be customized with a flexible formatting system including: percentage, bars of various formats, elapsed time and estimated time remaining.} 13 | spec.homepage = 'https://github.com/jfelchner/ruby-progressbar' 14 | spec.licenses = ['MIT'] 15 | 16 | spec.cert_chain = ['certs/jfelchner.pem'] 17 | spec.signing_key = File.expand_path('~/.gem/certs/jfelchner-private_key.pem') if $0 =~ /gem\z/ 18 | 19 | spec.executables = [] 20 | spec.files = Dir['{app,config,db,lib/ruby-progressbar}/**/*'] + %w{lib/ruby-progressbar.rb Rakefile README.md LICENSE.txt} 21 | 22 | spec.metadata = { 23 | 'bug_tracker_uri' => 'https://github.com/jfelchner/ruby-progressbar/issues', 24 | 'changelog_uri' => 'https://github.com/jfelchner/ruby-progressbar/blob/master/CHANGELOG.md', 25 | 'documentation_uri' => "https://github.com/jfelchner/ruby-progressbar/tree/releases/v#{ProgressBar::VERSION}", 26 | 'homepage_uri' => 'https://github.com/jfelchner/ruby-progressbar', 27 | 'source_code_uri' => 'https://github.com/jfelchner/ruby-progressbar', 28 | 'wiki_uri' => 'https://github.com/jfelchner/ruby-progressbar/wiki', 29 | } 30 | 31 | spec.add_development_dependency 'rspec', ["~> 3.7"] 32 | spec.add_development_dependency 'rspectacular', ["~> 0.70.6"] 33 | spec.add_development_dependency 'fuubar', ["~> 2.3"] 34 | spec.add_development_dependency 'timecop', ["~> 0.9"] 35 | end 36 | -------------------------------------------------------------------------------- /lib/ruby-progressbar/format/molecule.rb: -------------------------------------------------------------------------------- 1 | class ProgressBar 2 | module Format 3 | class Molecule 4 | MOLECULES = { 5 | :t => [:title_component, :title], 6 | :T => [:title_component, :title], 7 | :c => [:progressable, :progress], 8 | :C => [:progressable, :total], 9 | :u => [:progressable, :total_with_unknown_indicator], 10 | :p => [:percentage_component, :percentage], 11 | :P => [:percentage_component, :percentage_with_precision], 12 | :j => [:percentage_component, :justified_percentage], 13 | :J => [:percentage_component, :justified_percentage_with_precision], 14 | :a => [:time_component, :elapsed_with_label], 15 | :e => [:time_component, :estimated_with_unknown_oob], 16 | :E => [:time_component, :estimated_with_friendly_oob], 17 | :f => [:time_component, :estimated_with_no_oob], 18 | :l => [:time_component, :estimated_wall_clock], 19 | :B => [:bar_component, :complete_bar], 20 | :b => [:bar_component, :bar], 21 | :W => [:bar_component, :complete_bar_with_percentage], 22 | :w => [:bar_component, :bar_with_percentage], 23 | :i => [:bar_component, :incomplete_space], 24 | :r => [:rate_component, :rate_of_change], 25 | :R => [:rate_component, :rate_of_change_with_precision] 26 | }.freeze 27 | 28 | BAR_MOLECULES = %w{W w B b i}.freeze 29 | 30 | attr_accessor :key, 31 | :method_name 32 | 33 | def initialize(letter) 34 | self.key = letter 35 | self.method_name = MOLECULES.fetch(key.to_sym) 36 | end 37 | 38 | def bar_molecule? 39 | BAR_MOLECULES.include? key 40 | end 41 | 42 | def non_bar_molecule? 43 | !bar_molecule? 44 | end 45 | 46 | def full_key 47 | "%#{key}" 48 | end 49 | 50 | def lookup_value(environment, length = 0) 51 | component = environment.__send__(method_name[0]) 52 | 53 | if bar_molecule? 54 | component.__send__(method_name[1], length).to_s 55 | else 56 | component.__send__(method_name[1]).to_s 57 | end 58 | end 59 | end 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | Current Behavior 8 | -------------------------------------------------------------------------------- 9 | 10 | 11 | ### Steps to Reproduce 12 | 13 | 14 | 1. Sign in as "marty@hillvalley.net" 15 | 2. Click "Current Orders" 16 | 3. Try to remove "Flux Capacitor" from the cart 17 | 18 | ### Screenshot 19 | 20 | 24 | 25 | Expected Behavior 26 | -------------------------------------------------------------------------------- 27 | 28 | 29 | Environment 30 | -------------------------------------------------------------------------------- 31 | 32 | * Device: 33 | 34 | * OS: 35 | 36 | * [ ] Windows 37 | * [ ] macOS 38 | * [ ] iOS 39 | * [ ] Android 40 | * [ ] Linux 41 | * [ ] Other 42 | 43 | * OS Version: 44 | 45 | * Browser: 46 | 47 | * [ ] Chrome 48 | * [ ] Safari 49 | * [ ] Firefox 50 | * [ ] Internet Explorer 51 | * [ ] Edge 52 | * [ ] Opera 53 | 54 | * Browser Version: 55 | 56 | 78 | -------------------------------------------------------------------------------- /CODE-OF-CONDUCT.md: -------------------------------------------------------------------------------- 1 | Contributor Code of Conduct 2 | ================================================================================ 3 | 4 | As contributors and maintainers of this project, and in the interest of 5 | fostering an open and welcoming community, we pledge to respect all people who 6 | contribute through reporting issues, posting feature requests, updating 7 | documentation, submitting pull requests or patches, and other activities. 8 | 9 | We are committed to making participation in this project a harassment-free 10 | experience for everyone, regardless of level of experience, gender, gender 11 | identity and expression, sexual orientation, disability, personal appearance, 12 | body size, race, ethnicity, age, religion, or nationality. 13 | 14 | Examples of unacceptable behavior by participants include: 15 | 16 | * The use of sexualized language or imagery 17 | 18 | * Personal attacks 19 | 20 | * Trolling or insulting/derogatory comments 21 | 22 | * Public or private harassment 23 | 24 | * Publishing other's private information, such as physical or electronic 25 | addresses, without explicit permission 26 | 27 | * Other unethical or unprofessional conduct. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject 30 | comments, commits, code, wiki edits, issues, and other contributions that are 31 | not aligned to this Code of Conduct. By adopting this Code of Conduct, project 32 | maintainers commit themselves to fairly and consistently applying these 33 | principles to every aspect of managing this project. Project maintainers who do 34 | not follow or enforce the Code of Conduct may be permanently removed from the 35 | project team. 36 | 37 | This code of conduct applies both within project spaces and in public spaces 38 | when an individual is representing the project or its community. 39 | 40 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 41 | reported by opening an issue or contacting one or more of the project 42 | maintainers. 43 | 44 | This Code of Conduct is adapted from the [Contributor Covenant][1], version 45 | 1.2.0, available at [here][2]. 46 | 47 | [1]: http://contributor-covenant.org 48 | [2]: http://contributor-covenant.org/version/1/2/0/ 49 | -------------------------------------------------------------------------------- /.github/labels.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | # Problem 4 | 5 | - name: "bug" 6 | color: "cb2431" 7 | description: "Problem - Bug" 8 | 9 | - name: "security" 10 | color: "86181d" 11 | description: "Problem - Security" 12 | 13 | # Miscellaneous 14 | 15 | - name: "chore" 16 | color: "fff5b1" 17 | description: "Miscellaneous - Chore" 18 | 19 | - name: "legal" 20 | color: "fff5b1" 21 | description: "Miscellaneous - Legal" 22 | 23 | # Experience 24 | 25 | - name: "copy" 26 | color: "ffab70" 27 | description: "Experience - Copy" 28 | 29 | - name: "design" 30 | color: "fb8532" 31 | description: "Experience - Design" 32 | 33 | - name: "ux" 34 | color: "f66a0a" 35 | description: "Experience - UX" 36 | 37 | # Environment 38 | 39 | - name: "test" 40 | color: "d1bcf9" 41 | description: "Environment - Test" 42 | 43 | - name: "staging" 44 | color: "8a63d2" 45 | description: "Environment - Staging" 46 | 47 | - name: "production" 48 | color: "4c2889" 49 | description: "Environment - Production" 50 | 51 | # Improvements 52 | 53 | - name: "enhancement" 54 | color: "bef5cb" 55 | description: "Improvements - Enhancement" 56 | 57 | - name: "optimization" 58 | color: "34d058" 59 | description: "Improvements - Optimization" 60 | 61 | - name: "feature" 62 | color: "176f2c" 63 | description: "Improvements - Feature" 64 | 65 | # Pending 66 | 67 | - name: "in-progress" 68 | color: "79b8ff" 69 | description: "Pending - In-Progress" 70 | 71 | - name: "watchlist" 72 | color: "0366d6" 73 | description: "Pending - Watchlist" 74 | 75 | # Inactive 76 | 77 | - name: "invalid" 78 | color: "f6f8fa" 79 | description: "Inactive - Invalid" 80 | 81 | - name: "wontfix" 82 | color: "e1e4e8" 83 | description: "Inactive - Won't Fix" 84 | 85 | - name: "duplicate" 86 | color: "959da5" 87 | description: "Inactive - Duplicate" 88 | 89 | - name: "on-hold" 90 | color: "586069" 91 | description: "Inactive - On Hold" 92 | 93 | - name: "outdated" 94 | color: "2f363d" 95 | description: "Inactive - Outdated" 96 | -------------------------------------------------------------------------------- /spec/lib/ruby-progressbar/components/rate_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'ruby-progressbar/components/rate' 3 | 4 | class ProgressBar 5 | module Components 6 | describe Rate do 7 | describe '#rate_of_change' do 8 | it 'returns the rate as a formatted integer' do 9 | Timecop.freeze(::Time.utc(2020, 1, 1, 0, 0, 0)) 10 | 11 | projector = Projectors::SmoothedAverage.new 12 | progress = Progress.new(:projector => projector, :total => 100) 13 | timer = Timer.new 14 | rate = Rate.new(:progress => progress, 15 | :timer => timer) 16 | 17 | progress.start 18 | timer.start 19 | 20 | progress.progress = 50 21 | 22 | Timecop.freeze(::Time.utc(2020, 1, 1, 0, 0, 10)) 23 | 24 | expect(rate.rate_of_change).to eql '5' 25 | 26 | Timecop.return 27 | end 28 | 29 | it 'can scale the rate' do 30 | Timecop.freeze(::Time.utc(2020, 1, 1, 0, 0, 0)) 31 | 32 | projector = Projectors::SmoothedAverage.new 33 | progress = Progress.new(:projector => projector, :total => 100) 34 | timer = Timer.new 35 | rate = Rate.new(:progress => progress, 36 | :timer => timer, 37 | :rate_scale => lambda { |x| x * 2 }) 38 | 39 | progress.start 40 | timer.start 41 | 42 | progress.progress = 50 43 | 44 | Timecop.freeze(::Time.utc(2020, 1, 1, 0, 0, 10)) 45 | 46 | expect(rate.rate_of_change).to eql '10' 47 | 48 | Timecop.return 49 | end 50 | end 51 | 52 | describe '#rate_of_change_with_precision' do 53 | it 'returns the rate as a formatted integer' do 54 | Timecop.freeze(::Time.utc(2020, 1, 1, 0, 0, 0)) 55 | 56 | projector = Projectors::SmoothedAverage.new 57 | progress = Progress.new(:projector => projector, :total => 100) 58 | timer = Timer.new 59 | rate = Rate.new(:progress => progress, 60 | :timer => timer) 61 | 62 | progress.start 63 | timer.start 64 | 65 | progress.progress = 50 66 | 67 | Timecop.freeze(::Time.utc(2020, 1, 1, 0, 0, 10)) 68 | 69 | expect(rate.rate_of_change_with_precision).to eql '5.00' 70 | 71 | Timecop.return 72 | end 73 | end 74 | end 75 | end 76 | end 77 | -------------------------------------------------------------------------------- /lib/ruby-progressbar/calculators/length.rb: -------------------------------------------------------------------------------- 1 | class ProgressBar 2 | module Calculators 3 | class Length 4 | attr_reader :length_override 5 | attr_accessor :current_length, 6 | :output 7 | 8 | def initialize(options = {}) 9 | self.length_override = options[:length] 10 | self.output = options[:output] 11 | self.current_length = nil 12 | end 13 | 14 | def length 15 | current_length || reset_length 16 | end 17 | 18 | def length_changed? 19 | previous_length = current_length 20 | self.current_length = calculate_length 21 | 22 | previous_length != current_length 23 | end 24 | 25 | def calculate_length 26 | length_override || terminal_width || 80 27 | end 28 | 29 | def reset_length 30 | self.current_length = calculate_length 31 | end 32 | 33 | def length_override=(other) 34 | @length_override ||= ENV['RUBY_PROGRESS_BAR_LENGTH'] || other 35 | @length_override = @length_override.to_i if @length_override 36 | end 37 | 38 | private 39 | 40 | # This code was copied and modified from Rake, available under MIT-LICENSE 41 | # Copyright (c) 2003, 2004 Jim Weirich 42 | # rubocop:disable Style/RescueStandardError 43 | def terminal_width 44 | return 80 unless unix? 45 | 46 | result = dynamic_width 47 | (result < 20) ? 80 : result 48 | rescue 49 | 80 50 | end 51 | # rubocop:enable Style/RescueStandardError 52 | 53 | begin 54 | require 'io/console' 55 | 56 | def dynamic_width 57 | if output && output.tty? && output.respond_to?(:winsize) 58 | dynamic_width_via_output_stream_object 59 | elsif IO.console 60 | dynamic_width_via_io_object 61 | else 62 | dynamic_width_via_system_calls 63 | end 64 | end 65 | rescue LoadError 66 | def dynamic_width 67 | dynamic_width_via_system_calls 68 | end 69 | end 70 | 71 | def dynamic_width_via_output_stream_object 72 | _rows, columns = output.winsize 73 | columns 74 | end 75 | 76 | def dynamic_width_via_io_object 77 | _rows, columns = IO.console.winsize 78 | columns 79 | end 80 | 81 | def dynamic_width_via_system_calls 82 | dynamic_width_stty.nonzero? || dynamic_width_tput 83 | end 84 | 85 | def dynamic_width_stty 86 | `stty size 2>/dev/null`.split[1].to_i 87 | end 88 | 89 | def dynamic_width_tput 90 | `tput cols 2>/dev/null`.to_i 91 | end 92 | 93 | def unix? 94 | RUBY_PLATFORM =~ /(aix|darwin|linux|(net|free|open)bsd|cygwin|solaris|irix|hpux)/i 95 | end 96 | end 97 | end 98 | end 99 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | name: Pull Request 4 | about: Describe the changes you would like to make 5 | 6 | --- 7 | 8 | 9 | 10 | Why This Change Is Necessary 11 | -------------------------------------------------------------------------------- 12 | 13 | 14 | 15 | * [] Bug Fix 16 | * [] New Feature 17 | 18 | 22 | 23 | How These Changes Address the Issue 24 | -------------------------------------------------------------------------------- 25 | 26 | 30 | 31 | Side Effects Caused By This Change 32 | -------------------------------------------------------------------------------- 33 | 34 | * [] This Causes a Breaking Change 35 | * [] This Does Not Cause Any Known Side Effects 36 | 37 | 43 | 44 | Screenshots 45 | -------------------------------------------------------------------------------- 46 | 47 | 51 | 52 | Checklist 53 | -------------------------------------------------------------------------------- 54 | 55 | 58 | 59 | * [] I have run `rubocop` against the codebase 60 | * [] I have added tests to cover my changes 61 | * [] All new and existing tests passed 62 | 63 | 85 | -------------------------------------------------------------------------------- /lib/ruby-progressbar/components/bar.rb: -------------------------------------------------------------------------------- 1 | ### 2 | # UPA = Unknown Progress Animation 3 | # 4 | class ProgressBar 5 | module Components 6 | class Bar 7 | DEFAULT_PROGRESS_MARK = '='.freeze 8 | DEFAULT_REMAINDER_MARK = ' '.freeze 9 | DEFAULT_UPA_STEPS = ['=---', '-=--', '--=-', '---='].freeze 10 | 11 | attr_accessor :progress_mark, 12 | :remainder_mark, 13 | :length, 14 | :progress, 15 | :upa_steps 16 | 17 | def initialize(options = {}) 18 | self.upa_steps = options[:unknown_progress_animation_steps] || DEFAULT_UPA_STEPS 19 | self.progress_mark = options[:progress_mark] || DEFAULT_PROGRESS_MARK 20 | self.remainder_mark = options[:remainder_mark] || DEFAULT_REMAINDER_MARK 21 | self.progress = options[:progress] 22 | self.length = options[:length] 23 | end 24 | 25 | def to_s(options = { :format => :standard }) 26 | if progress.unknown? 27 | unknown_string 28 | elsif options[:format] == :standard 29 | "#{standard_complete_string}#{incomplete_string}" 30 | elsif options[:format] == :integrated_percentage 31 | "#{integrated_percentage_complete_string}#{incomplete_string}" 32 | end 33 | end 34 | 35 | def bar(length) 36 | self.length = length 37 | 38 | standard_complete_string 39 | end 40 | 41 | def complete_bar(length) 42 | self.length = length 43 | 44 | to_s(:format => :standard) 45 | end 46 | 47 | def complete_bar_with_percentage(length) 48 | self.length = length 49 | 50 | to_s(:format => :integrated_percentage) 51 | end 52 | 53 | def incomplete_space(length) 54 | self.length = length 55 | 56 | if progress.unknown? 57 | unknown_string 58 | else 59 | incomplete_string 60 | end 61 | end 62 | 63 | def bar_with_percentage(length) 64 | self.length = length 65 | 66 | integrated_percentage_complete_string 67 | end 68 | 69 | private 70 | 71 | def integrated_percentage_complete_string 72 | return standard_complete_string if completed_length < 5 73 | 74 | " #{progress.percentage_completed} ".to_s.center(completed_length, progress_mark) 75 | end 76 | 77 | def standard_complete_string 78 | progress_mark * completed_length 79 | end 80 | 81 | def incomplete_string 82 | remainder_mark * (length - completed_length) 83 | end 84 | 85 | def unknown_string 86 | unknown_frame_string = unknown_progress_frame * ((length / upa_steps.size) + 2) 87 | 88 | unknown_frame_string[0, length] 89 | end 90 | 91 | def completed_length 92 | (length * progress.percentage_completed / 100).floor 93 | end 94 | 95 | def unknown_progress_frame 96 | current_animation_step = progress.progress % upa_steps.size 97 | 98 | upa_steps[current_animation_step] 99 | end 100 | end 101 | end 102 | end 103 | -------------------------------------------------------------------------------- /lib/ruby-progressbar/progress.rb: -------------------------------------------------------------------------------- 1 | require 'ruby-progressbar/errors/invalid_progress_error' 2 | 3 | class ProgressBar 4 | class Progress 5 | DEFAULT_TOTAL = 100 6 | DEFAULT_BEGINNING_POSITION = 0 7 | 8 | attr_reader :total, 9 | :progress 10 | attr_accessor :starting_position 11 | 12 | def initialize(options = {}) 13 | self.total = options.fetch(:total, DEFAULT_TOTAL) 14 | 15 | start(:at => DEFAULT_BEGINNING_POSITION) 16 | end 17 | 18 | def start(options = {}) 19 | self.progress = \ 20 | self.starting_position = options[:at] || progress 21 | end 22 | 23 | def finish 24 | self.progress = total unless unknown? 25 | end 26 | 27 | def finished? 28 | @progress == @total 29 | end 30 | 31 | def increment 32 | if progress == total 33 | warn "WARNING: Your progress bar is currently at #{progress} out of #{total} " \ 34 | "and cannot be incremented. In v2.0.0 this will become a " \ 35 | "ProgressBar::InvalidProgressError." 36 | end 37 | 38 | self.progress += 1 unless progress == total 39 | end 40 | 41 | def decrement 42 | if progress == 0 43 | warn "WARNING: Your progress bar is currently at #{progress} out of #{total} " \ 44 | "and cannot be decremented. In v2.0.0 this will become a " \ 45 | "ProgressBar::InvalidProgressError." 46 | end 47 | 48 | self.progress -= 1 unless progress == 0 49 | end 50 | 51 | def reset 52 | start(:at => starting_position) 53 | end 54 | 55 | def progress=(new_progress) 56 | if total && new_progress > total 57 | fail ProgressBar::InvalidProgressError, 58 | "You can't set the item's current value to be greater than the total." 59 | end 60 | 61 | @progress = new_progress 62 | end 63 | 64 | def total=(new_total) 65 | unless progress.nil? || new_total.nil? || new_total >= progress 66 | fail ProgressBar::InvalidProgressError, 67 | "You can't set the item's total value to less than the current progress." 68 | end 69 | 70 | @total = new_total 71 | end 72 | 73 | def percentage_completed 74 | return 0 if total.nil? 75 | return 100 if total == 0 76 | 77 | # progress / total * 100 78 | # 79 | # Doing this way so we can avoid converting each 80 | # number to a float and then back to an integer. 81 | # 82 | (progress * 100 / total).to_i 83 | end 84 | 85 | def none? 86 | progress.zero? 87 | end 88 | 89 | def unknown? 90 | progress.nil? || total.nil? 91 | end 92 | 93 | def total_with_unknown_indicator 94 | total || '??' 95 | end 96 | 97 | def percentage_completed_with_precision 98 | return 100.0 if total == 0 99 | return 0.0 if total.nil? 100 | 101 | '%5.2f' % [(progress * 100 / total.to_f * 100).floor / 100.0] 102 | end 103 | 104 | def absolute 105 | progress - starting_position 106 | end 107 | end 108 | end 109 | -------------------------------------------------------------------------------- /lib/ruby-progressbar/components/time.rb: -------------------------------------------------------------------------------- 1 | ### 2 | # OOB = 'Out of Bounds' 3 | # 4 | class ProgressBar 5 | module Components 6 | class Time 7 | TIME_FORMAT = '%02d:%02d:%02d'.freeze 8 | OOB_TIME_FORMATS = [:unknown, :friendly, nil].freeze 9 | OOB_LIMIT_IN_HOURS = 99 10 | OOB_UNKNOWN_TIME_TEXT = '??:??:??'.freeze 11 | OOB_FRIENDLY_TIME_TEXT = '> 4 Days'.freeze 12 | NO_TIME_ELAPSED_TEXT = '--:--:--'.freeze 13 | ESTIMATED_LABEL = ' ETA'.freeze 14 | ELAPSED_LABEL = 'Time'.freeze 15 | WALL_CLOCK_FORMAT = '%H:%M:%S'.freeze 16 | OOB_TEXT_TO_FORMAT = { 17 | :unknown => OOB_UNKNOWN_TIME_TEXT, 18 | :friendly => OOB_FRIENDLY_TIME_TEXT 19 | }.freeze 20 | 21 | def initialize(options = {}) 22 | self.timer = options[:timer] 23 | self.progress = options[:progress] 24 | self.projector = options[:projector] 25 | end 26 | 27 | def estimated_with_label(out_of_bounds_time_format = nil) 28 | "#{ESTIMATED_LABEL}: #{estimated(out_of_bounds_time_format)}" 29 | end 30 | 31 | def elapsed_with_label 32 | "#{ELAPSED_LABEL}: #{elapsed}" 33 | end 34 | 35 | def estimated_with_no_oob 36 | estimated_with_elapsed_fallback(nil) 37 | end 38 | 39 | def estimated_with_unknown_oob 40 | estimated_with_elapsed_fallback(:unknown) 41 | end 42 | 43 | def estimated_with_friendly_oob 44 | estimated_with_elapsed_fallback(:friendly) 45 | end 46 | 47 | def estimated_wall_clock 48 | return timer.stopped_at.strftime(WALL_CLOCK_FORMAT) if progress.finished? 49 | return NO_TIME_ELAPSED_TEXT unless timer.started? 50 | 51 | memo_estimated_seconds_remaining = estimated_seconds_remaining 52 | return NO_TIME_ELAPSED_TEXT unless memo_estimated_seconds_remaining 53 | 54 | (timer.now + memo_estimated_seconds_remaining). 55 | strftime(WALL_CLOCK_FORMAT) 56 | end 57 | 58 | protected 59 | 60 | attr_accessor :timer, 61 | :progress, 62 | :projector 63 | 64 | private 65 | 66 | def estimated(out_of_bounds_time_format) 67 | memo_estimated_seconds_remaining = estimated_seconds_remaining 68 | 69 | return OOB_UNKNOWN_TIME_TEXT unless memo_estimated_seconds_remaining 70 | 71 | hours, minutes, seconds = timer.divide_seconds(memo_estimated_seconds_remaining) 72 | 73 | if hours > OOB_LIMIT_IN_HOURS && out_of_bounds_time_format 74 | OOB_TEXT_TO_FORMAT.fetch(out_of_bounds_time_format) 75 | else 76 | TIME_FORMAT % [hours, minutes, seconds] 77 | end 78 | end 79 | 80 | def elapsed 81 | return NO_TIME_ELAPSED_TEXT unless timer.started? 82 | 83 | hours, minutes, seconds = timer.divide_seconds(timer.elapsed_whole_seconds) 84 | 85 | TIME_FORMAT % [hours, minutes, seconds] 86 | end 87 | 88 | def estimated_with_elapsed_fallback(out_of_bounds_time_format) 89 | return elapsed_with_label if progress.finished? 90 | 91 | estimated_with_label(out_of_bounds_time_format) 92 | end 93 | 94 | def estimated_seconds_remaining 95 | return if progress.unknown? || projector.none? || progress.none? || timer.stopped? || timer.reset? 96 | 97 | (timer.elapsed_seconds * ((progress.total / projector.projection) - 1)).round 98 | end 99 | end 100 | end 101 | end 102 | -------------------------------------------------------------------------------- /spec/lib/ruby-progressbar/projector/smoothed_average_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'ruby-progressbar/projectors/smoothed_average' 3 | 4 | class ProgressBar 5 | module Projectors 6 | describe SmoothedAverage do 7 | describe '.calculate' do 8 | it 'can properly calculate a projection' do 9 | first_projection = SmoothedAverage.calculate(4.5, 12, 0.1) 10 | expect(first_projection).to be_within(0.001).of 11.25 11 | 12 | second_projection = SmoothedAverage.calculate(8.2, 51, 0.7) 13 | expect(second_projection).to be_within(0.001).of 21.04 14 | 15 | third_projection = SmoothedAverage.calculate(41.8, 100, 0.59) 16 | expect(third_projection).to be_within(0.001).of 65.662 17 | end 18 | end 19 | 20 | describe '#projection' do 21 | it 'can properly calculate a running average' do 22 | projector = SmoothedAverage.new(:strength => 0.1) 23 | projector.start 24 | projector.progress = 5 25 | projector.progress = 12 26 | 27 | expect(projector.projection).to be_within(0.001).of 11.25 28 | end 29 | 30 | it 'knows the running average even when progress has been made' do 31 | projector = SmoothedAverage.new(:total => 50) 32 | 33 | projector.instance_variable_set(:@projection, 10) 34 | projector.start :at => 0 35 | 36 | expect(projector.projection).to be_zero 37 | 38 | projector.progress += 40 39 | 40 | expect(projector.projection).to be 36.0 41 | end 42 | 43 | it 'knows the running average is reset even after progress is started' do 44 | projector = SmoothedAverage.new(:total => 50) 45 | 46 | projector.instance_variable_set(:@projection, 10) 47 | projector.start :at => 0 48 | 49 | expect(projector.projection).to be_zero 50 | 51 | projector.start :at => 40 52 | 53 | expect(projector.projection).to be 0.0 54 | end 55 | end 56 | 57 | describe '#start' do 58 | it 'resets the projection' do 59 | projector = SmoothedAverage.new 60 | projector.start 61 | projector.progress = 10 62 | 63 | expect(projector.projection).not_to be_zero 64 | 65 | projector.start 66 | 67 | expect(projector.projection).to be 0.0 68 | end 69 | end 70 | 71 | describe '#reset' do 72 | it 'resets the projection' do 73 | projector = SmoothedAverage.new 74 | projector.start 75 | projector.progress = 10 76 | 77 | expect(projector.projection).not_to be_zero 78 | 79 | projector.reset 80 | 81 | expect(projector.projection).to be 0.0 82 | end 83 | 84 | it 'resets based on the starting position' do 85 | projector = SmoothedAverage.new(:strength => 0.1) 86 | projector.start(:at => 10) 87 | projector.progress = 20 88 | 89 | expect(projector.projection).not_to be_zero 90 | 91 | projector.reset 92 | projector.progress = 20 93 | 94 | expect(projector.projection).to be 9.0 95 | end 96 | end 97 | 98 | describe '#strength' do 99 | it 'allows the default strength to be overridden' do 100 | projector = SmoothedAverage.new(:strength => 0.3) 101 | 102 | expect(projector.strength).to be 0.3 103 | end 104 | 105 | it 'has a default strength' do 106 | expect(SmoothedAverage.new.strength).to be 0.1 107 | end 108 | end 109 | end 110 | end 111 | end 112 | -------------------------------------------------------------------------------- /spec/lib/ruby-progressbar/refinements/progress_enumerator_spec.rb: -------------------------------------------------------------------------------- 1 | if Module.private_instance_methods.include?(:using) 2 | 3 | require 'spec_helper' 4 | require 'ruby-progressbar/refinements/progress_enumerator' 5 | 6 | class ProgressBar 7 | module Refinements 8 | describe Enumerator do 9 | using ProgressBar::Refinements::Enumerator 10 | 11 | it 'creates a progress bar with the Enumerable size' do 12 | allow(ProgressBar).to receive(:create). 13 | and_call_original 14 | 15 | (0...10).each.with_progressbar { |_x| nil } 16 | 17 | expect(ProgressBar).to have_received(:create). 18 | with(hash_including(:total => 10)) 19 | end 20 | 21 | it 'does not allow the user to override the progress bar total' do 22 | allow(ProgressBar).to receive(:create). 23 | and_call_original 24 | 25 | (0...10).each.with_progressbar(:total => 20) { |_x| nil } 26 | 27 | expect(ProgressBar).to have_received(:create). 28 | with(hash_including(:total => 10)) 29 | end 30 | 31 | it 'does not allow the user to override the progress bar starting position' do 32 | allow(ProgressBar).to receive(:create). 33 | and_call_original 34 | 35 | (0...10).each.with_progressbar(:starting_at => 20) { |_x| nil } 36 | 37 | expect(ProgressBar).to have_received(:create). 38 | with(hash_including(:starting_at => 0)) 39 | end 40 | 41 | it 'passes arguments to create' do 42 | allow(ProgressBar).to receive(:create). 43 | and_call_original 44 | 45 | (0...10).each.with_progressbar(:title => 'We All Float') { |_x| nil } 46 | 47 | expect(ProgressBar).to have_received(:create). 48 | with(hash_including(:title => 'We All Float')) 49 | end 50 | 51 | it 'calls progressbar.increment the right number of times' do 52 | mock = instance_double('ProgressBar::Progress') 53 | 54 | allow(ProgressBar).to receive(:create).and_return(mock) 55 | allow(mock).to receive(:increment) 56 | 57 | (0...10).each.with_progressbar { |_x| nil } 58 | 59 | expect(ProgressBar).to have_received(:create) 60 | expect(mock).to have_received(:increment).exactly(10).times 61 | end 62 | 63 | it 'chains return values properly' do 64 | transform = lambda { |i| 10 * i } 65 | 66 | chained_progress_transform = (0...10).map.with_progressbar(&transform) 67 | direct_transform = (0...10).map(&transform) 68 | 69 | expect(chained_progress_transform).to eql direct_transform 70 | end 71 | 72 | it 'chains properly in the middle' do 73 | transform = lambda { |i| 10 * i } 74 | 75 | chained_progress_transform = (0...10).each.with_progressbar.map(&transform) 76 | direct_transform = (0...10).each.map(&transform) 77 | 78 | expect(chained_progress_transform).to eql direct_transform 79 | end 80 | 81 | it 'returns an enumerator' do 82 | transform = lambda { |i| 10 * i } 83 | enumerator = (0...10).each.with_progressbar 84 | 85 | expect(enumerator.map(&transform)).to eql((0...10).map(&transform)) 86 | end 87 | 88 | it 'passes the progressbar instance to the block when two arguments are requested for the block' do 89 | progress = 0 90 | current_item = -1 91 | 92 | (0...10).each.with_progressbar do |item, progress_bar| 93 | expect(progress_bar.progress).to be(progress += 1) 94 | expect(item).to be(current_item += 1) 95 | end 96 | end 97 | end 98 | end 99 | end 100 | end 101 | -------------------------------------------------------------------------------- /spec/lib/ruby-progressbar/progress_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'ruby-progressbar/progress' 3 | 4 | class ProgressBar 5 | describe Progress do 6 | it 'knows the default total when no parameters are passed' do 7 | progress = Progress.new 8 | 9 | expect(progress.total).to eql Progress::DEFAULT_TOTAL 10 | end 11 | 12 | it 'knows the default beginning progress when no parameters are passed and ' \ 13 | 'the progress has not been started' do 14 | 15 | progress = Progress.new 16 | 17 | expect(progress.progress).to be_zero 18 | end 19 | 20 | it 'knows the default starting value when no parameters are passed and the ' \ 21 | 'progress has been started' do 22 | 23 | progress = Progress.new 24 | 25 | progress.start 26 | 27 | expect(progress.progress).to eql Progress::DEFAULT_BEGINNING_POSITION 28 | end 29 | 30 | it 'knows the given starting value when no parameters are passed and the ' \ 31 | 'progress is started with a starting value' do 32 | 33 | progress = Progress.new 34 | 35 | progress.start :at => 10 36 | 37 | expect(progress.progress).to be 10 38 | end 39 | 40 | it 'knows how to finish itself even if the total is unknown' do 41 | progress = Progress.new :total => nil 42 | 43 | expect(progress.finish).to be(nil) 44 | end 45 | 46 | it 'knows the overridden total when the total is passed in' do 47 | progress = Progress.new(:total => 12, 48 | :progress_mark => 'x', 49 | :remainder_mark => '.') 50 | 51 | expect(progress.total).to be 12 52 | end 53 | 54 | it 'knows the percentage completed when begun with no progress' do 55 | progress = Progress.new 56 | 57 | progress.start 58 | 59 | expect(progress.percentage_completed).to be 0 60 | end 61 | 62 | it 'knows the progress after it has been incremented' do 63 | progress = Progress.new 64 | 65 | progress.start 66 | progress.increment 67 | 68 | expect(progress.progress).to be 1 69 | end 70 | 71 | it 'knows the percentage completed after it has been incremented' do 72 | progress = Progress.new(:total => 50) 73 | 74 | progress.start 75 | progress.increment 76 | 77 | expect(progress.percentage_completed).to be 2 78 | end 79 | 80 | it 'knows to always round down the percentage completed' do 81 | progress = Progress.new(:total => 200) 82 | 83 | progress.start :at => 1 84 | 85 | expect(progress.percentage_completed).to be 0 86 | end 87 | 88 | it 'cannot increment past the total' do 89 | progress = Progress.new(:total => 50) 90 | 91 | progress.start :at => 50 92 | progress.increment 93 | 94 | expect(progress.progress).to be 50 95 | expect(progress.percentage_completed).to be 100 96 | end 97 | 98 | it 'allow progress to be decremented once it is finished' do 99 | progress = Progress.new(:total => 50) 100 | 101 | progress.start :at => 50 102 | progress.decrement 103 | 104 | expect(progress.progress).to be 49 105 | expect(progress.percentage_completed).to be 98 106 | end 107 | 108 | it 'knows the percentage completed is 100% if the total is zero' do 109 | progress = Progress.new(:total => 0) 110 | 111 | expect(progress.percentage_completed).to be 100 112 | end 113 | 114 | it 'raises an error when passed a number larger than the total' do 115 | progress = Progress.new(:total => 100) 116 | 117 | expect { progress.progress = 101 }. 118 | to \ 119 | raise_error(InvalidProgressError, 120 | "You can't set the item's current value to be greater " \ 121 | "than the total.") 122 | end 123 | end 124 | end 125 | -------------------------------------------------------------------------------- /spec/lib/ruby-progressbar/components/throttle_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'ruby-progressbar/progress' 3 | 4 | class ProgressBar 5 | describe Throttle do 6 | let(:timer) { ProgressBar::Timer.new(:time => ::Time) } 7 | 8 | it 'yields the first time if the throttle rate is given and the timer is not started' do 9 | throttle = ProgressBar::Throttle.new(:throttle_rate => 10, 10 | :throttle_timer => timer) 11 | 12 | yielded = false 13 | 14 | throttle.choke do 15 | yielded = true 16 | end 17 | 18 | expect(yielded).to be_a TrueClass 19 | end 20 | 21 | it 'does not yield after the initial yield if the period has not passed yet' do 22 | throttle = ProgressBar::Throttle.new(:throttle_rate => 10, 23 | :throttle_timer => timer) 24 | timer.start 25 | 26 | throttle.choke {} 27 | 28 | yielded = false 29 | 30 | 9.times do 31 | Timecop.freeze(1) 32 | 33 | throttle.choke do 34 | yielded = true 35 | end 36 | end 37 | 38 | Timecop.return 39 | 40 | expect(yielded).to be_a FalseClass 41 | end 42 | 43 | it 'always yields if forced to, even after the initial yield or if the period ' \ 44 | 'has not passed' do 45 | 46 | throttle = ProgressBar::Throttle.new(:throttle_rate => 10, 47 | :throttle_timer => timer) 48 | timer.start 49 | 50 | throttle.choke {} 51 | 52 | yielded = 0 53 | 54 | 25.times do 55 | Timecop.freeze(1) 56 | 57 | throttle.choke(:force_update_if => true) do 58 | yielded += 1 59 | end 60 | end 61 | 62 | Timecop.return 63 | 64 | expect(yielded).to be 25 65 | end 66 | 67 | it 'yields if the period has passed, even after the initial yield' do 68 | throttle = ProgressBar::Throttle.new(:throttle_rate => 10, 69 | :throttle_timer => timer) 70 | timer.start 71 | 72 | throttle.choke {} 73 | 74 | yielded = false 75 | 76 | Timecop.freeze(11) 77 | 78 | throttle.choke do 79 | yielded = true 80 | end 81 | 82 | Timecop.return 83 | 84 | expect(yielded).to be true 85 | end 86 | 87 | it 'does not yield after a previous yield if the period has not passed yet' do 88 | throttle = ProgressBar::Throttle.new(:throttle_rate => 10, 89 | :throttle_timer => timer) 90 | 91 | Timecop.freeze(0) 92 | 93 | timer.start 94 | 95 | Timecop.freeze(15) 96 | 97 | throttle.choke {} 98 | 99 | yielded = false 100 | 101 | 9.times do 102 | Timecop.freeze(1) 103 | 104 | throttle.choke do 105 | yielded = true 106 | end 107 | 108 | expect(yielded).to be false 109 | end 110 | 111 | Timecop.return 112 | end 113 | 114 | it 'yields after the period has passed, even after a previous yield' do 115 | throttle = ProgressBar::Throttle.new(:throttle_rate => 10, 116 | :throttle_timer => timer) 117 | 118 | Timecop.freeze(0) 119 | 120 | timer.start 121 | 122 | Timecop.freeze(15) 123 | 124 | throttle.choke {} 125 | 126 | yielded = false 127 | 128 | Timecop.freeze(10) 129 | 130 | throttle.choke do 131 | yielded = true 132 | end 133 | 134 | Timecop.return 135 | 136 | expect(yielded).to be true 137 | end 138 | 139 | it 'does not throttle if no throttle rate is given' do 140 | throttle = Throttle.new(:throttle_timer => timer, 141 | :throttle_rate => nil) 142 | yield_count = 0 143 | 144 | 25.times do 145 | Timecop.freeze(1) 146 | 147 | throttle.choke do 148 | yield_count += 1 149 | end 150 | end 151 | 152 | Timecop.return 153 | 154 | expect(yield_count).to be 25 155 | end 156 | end 157 | end 158 | -------------------------------------------------------------------------------- /spec/lib/ruby-progressbar/calculators/length_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'ruby-progressbar/calculators/length' 3 | 4 | class ProgressBar 5 | module Calculators 6 | describe Length do 7 | let(:tty_output) do 8 | IO.new(IO.sysopen('/dev/null', 'w')).tap do |io| 9 | allow(io).to receive(:tty?).and_return true 10 | end 11 | end 12 | 13 | let(:non_tty_output) do 14 | IO.new(IO.sysopen('/dev/null', 'w')).tap do |io| 15 | allow(io).to receive(:tty?).and_return false 16 | end 17 | end 18 | 19 | context 'when the RUBY_PROGRESS_BAR_LENGTH environment variable exists' do 20 | before(:each) { ENV['RUBY_PROGRESS_BAR_LENGTH'] = '44' } 21 | 22 | after(:each) { ENV['RUBY_PROGRESS_BAR_LENGTH'] = nil } 23 | 24 | it 'calculates the length as the value of the environment variable as an integer' do 25 | length_calculator = Calculators::Length.new 26 | 27 | expect(length_calculator.length).to be 44 28 | end 29 | end 30 | 31 | if RUBY_PLATFORM != 'java' && 32 | Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('1.9.3') 33 | 34 | it 'can properly calculate the length even if IO.console is nil' do 35 | calculator = Length.new 36 | 37 | allow(IO).to receive(:console).and_return nil 38 | allow(calculator).to receive(:dynamic_width_via_system_calls).and_return 123_456 39 | 40 | expect(calculator.calculate_length).to be 123_456 41 | end 42 | end 43 | 44 | it 'allows the length to be overridden on creation' do 45 | length_calculator = Calculators::Length.new(:length => 88) 46 | 47 | expect(length_calculator.length).to be 88 48 | end 49 | 50 | it 'can calculate the width of the terminal in Unix environments' do 51 | length_calculator = Calculators::Length.new 52 | 53 | allow(length_calculator).to receive(:unix?).and_return(true) 54 | allow(length_calculator).to receive(:dynamic_width).and_return(99) 55 | 56 | expect(length_calculator.length).to be 99 57 | end 58 | 59 | unless RUBY_VERSION.start_with?('1.') 60 | it 'asks stream for length if it is a TTY' do 61 | allow(tty_output).to receive(:winsize).and_return [123, 456] 62 | allow(IO).to receive(:console).and_call_original 63 | 64 | length_calculator = Calculators::Length.new(:output => tty_output) 65 | 66 | expect(IO).not_to have_received :console 67 | expect(length_calculator.length).to be 456 68 | end 69 | 70 | it 'asks IO.console to calculate length if the output is null' do 71 | allow(tty_output).to receive(:winsize).and_return [123, 456] 72 | allow(IO).to receive(:console).and_return(tty_output) 73 | 74 | length_calculator = Calculators::Length.new 75 | 76 | expect(length_calculator.length).to be 456 77 | expect(IO).to have_received(:console). 78 | at_least(:once) 79 | end 80 | 81 | it 'asks IO.console to calculate length if the output is not a TTY' do 82 | allow(non_tty_output).to receive(:winsize).and_return [654, 321] 83 | allow(tty_output).to receive(:winsize).and_return [123, 456] 84 | allow(IO).to receive(:console).and_return(tty_output) 85 | 86 | length_calculator = Calculators::Length.new(:output => non_tty_output) 87 | 88 | expect(length_calculator.length).to be 456 89 | end 90 | end 91 | 92 | it 'defaults to 80 if it is not a Unix environment' do 93 | length_calculator = Calculators::Length.new 94 | 95 | allow(length_calculator).to receive(:unix?).and_return(false) 96 | 97 | expect(length_calculator.length).to be 80 98 | end 99 | 100 | it 'defaults to 80 if the width is less than 20' do 101 | length_calculator = Calculators::Length.new 102 | 103 | allow(length_calculator).to receive(:unix?).and_return(true) 104 | allow(length_calculator).to receive(:dynamic_width).and_return(19) 105 | 106 | expect(length_calculator.length).to be 80 107 | end 108 | end 109 | end 110 | end 111 | -------------------------------------------------------------------------------- /.remarkrc: -------------------------------------------------------------------------------- 1 | { 2 | "settings": {}, 3 | "plugins": { 4 | "lint-blockquote-indentation": ["error", 2], 5 | "lint-checkbox-character-style": ["error", { 6 | "checked": "x", 7 | "unchecked": " " 8 | }], 9 | "lint-code-block-style": ["error", "fenced"], 10 | "lint-definition-case": ["error", true], 11 | "lint-definition-spacing": ["error", true], 12 | "lint-emphasis-marker": ["error", "_"], 13 | "lint-fenced-code-flag": ["error", true], 14 | "lint-fenced-code-marker": ["error", "`"], 15 | "lint-file-extension": ["error", "md"], 16 | "lint-final-definition": ["error", true], 17 | "lint-final-newline": ["error", true], 18 | "lint-first-heading-level": ["warn", 1], 19 | "lint-hard-break-spaces": ["error", true], 20 | "lint-heading-increment": ["error", true], 21 | "lint-heading-style": ["error", "setext"], 22 | "lint-heading-whitespace": ["error", true], 23 | "lint-linebreak-style": ["error", "unix"], 24 | "lint-link-title-style": ["error", "\""], 25 | "lint-list-item-bullet-indent": ["error", true], 26 | "lint-list-item-content-indent": ["error", true], 27 | "lint-list-item-indent": ["error", "space"], 28 | "lint-list-item-spacing": ["error", true], 29 | "lint-maximum-heading-length": false, 30 | "lint-maximum-line-length": ["error", 80], 31 | "lint-no-auto-link-without-protocol": ["error", true], 32 | "lint-no-blockquote-without-marker": ["error", true], 33 | "lint-no-consecutive-blank-lines": ["error", true], 34 | "lint-no-duplicate-definitions": ["error", true], 35 | "lint-no-duplicate-headings": false, 36 | "lint-no-duplicate-headings-in-section": ["error", true], 37 | "lint-no-emphasis-as-heading": ["error", true], 38 | "lint-no-empty-sections": ["error", true], 39 | "lint-no-empty-url": ["error", true], 40 | "lint-no-file-name-articles": ["error", true], 41 | "lint-no-file-name-consecutive-dashes": ["error", true], 42 | "lint-no-file-name-irregular-characters": ["error", "\\.a-zA-Z0-9-_"], 43 | "lint-no-file-name-mixed-case": ["error", true], 44 | "lint-no-file-name-outer-dashes": ["error", true], 45 | "lint-no-heading-content-indent": ["error", true], 46 | "lint-no-heading-indent": ["error", true], 47 | "lint-no-heading-like-paragraph": ["error", true], 48 | "lint-no-heading-punctuation": ["error", ".,;:!?"], 49 | "lint-no-html": false, 50 | "lint-no-inline-padding": ["error", true], 51 | "lint-no-literal-urls": ["error", true], 52 | "lint-no-missing-blank-lines": ["error", { 53 | "exceptTightLists": true 54 | }], 55 | "lint-no-multiple-toplevel-headings": false, 56 | "lint-no-paragraph-content-indent": ["error", true], 57 | "lint-no-reference-like-url": ["error", true], 58 | "lint-no-shell-dollars": ["error", true], 59 | "lint-no-shortcut-reference-image": ["error", true], 60 | "lint-no-shortcut-reference-link": ["error", true], 61 | "lint-no-table-indentation": ["error", true], 62 | "lint-no-tabs": ["error", true], 63 | "lint-no-undefined-references": ["error", true], 64 | "lint-no-unused-definitions": ["error", true], 65 | "lint-no-url-trailing-slash": ["error", true], 66 | "lint-ordered-list-marker-style": ["error", "."], 67 | "lint-ordered-list-marker-value": ["error", "ordered"], 68 | "lint-strong-marker": ["error", "*"], 69 | "lint-table-cell-padding": ["error", "padded"], 70 | "lint-table-pipe-alignment": ["error", true], 71 | "lint-table-pipes": ["error", true], 72 | "lint-unordered-list-marker-style": ["error", "*"] 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Vendored Dependencies 2 | Pods 3 | bower_components 4 | node_modules 5 | web/app/mu-plugins 6 | web/app/plugins 7 | web/app/upgrade 8 | web/app/uploads 9 | web/wp 10 | vendor 11 | 12 | # OS Files 13 | .AppleDB 14 | .AppleDesktop 15 | .AppleDouble 16 | .DS_Store 17 | .LSOverride 18 | .apdisk 19 | Thumbs.db 20 | 21 | # Testing Files 22 | *.gemfile.lock 23 | capybara* 24 | pickle-email-*.html 25 | public/uploads 26 | rerun.txt 27 | site/web/app/uploads 28 | test/data 29 | 30 | # Cache Files 31 | *.stTheme.cache 32 | *.tfstate 33 | *.tmPreferences.cache 34 | *.tmlanguage.cache 35 | .TAGS 36 | .byebug_history 37 | .divshot-cache 38 | .dropbox 39 | .dropbox.attr 40 | .dropbox.cache 41 | .rake_tasks 42 | .repl_history 43 | .sass-cache 44 | .tags 45 | .yardoc 46 | /variables.tf.json 47 | TAGS 48 | _yardoc 49 | disk_image_for_*.json 50 | disk_image_settings.json 51 | lambdas/*.js 52 | lambdas/*.py 53 | lambdas/*.zip 54 | packer_cache 55 | public/packs* 56 | public/system 57 | site/web/app/upgrade 58 | spec/reports 59 | tags 60 | terraform.tfvars 61 | test/tmp 62 | test/version_tmp 63 | tmtags 64 | variables.sls 65 | 66 | !TAGS/ 67 | !tags/ 68 | 69 | # Local Compiled Files 70 | **/cordova/platforms/android/CordovaLib/ant-build 71 | **/cordova/platforms/android/CordovaLib/ant-gen 72 | **/cordova/platforms/android/CordovaLib/bin 73 | **/cordova/platforms/android/CordovaLib/gen 74 | **/cordova/platforms/android/ant-build 75 | **/cordova/platforms/android/ant-gen 76 | **/cordova/platforms/android/assets/www 77 | **/cordova/platforms/android/bin 78 | **/cordova/platforms/android/gen 79 | **/cordova/platforms/browser/build 80 | **/cordova/platforms/browser/www 81 | **/cordova/platforms/ios/build/ 82 | **/cordova/platforms/ios/www/ 83 | **/cordova/platforms/wp8/*.csproj.user 84 | **/cordova/platforms/wp8/*.suo 85 | **/cordova/platforms/wp8/.staging 86 | **/cordova/platforms/wp8/bin 87 | **/cordova/platforms/wp8/obj 88 | **/cordova/platforms/wp8/www 89 | **/public/stylesheets/*.css 90 | *.a 91 | *.css.map 92 | *.elc 93 | *.gem 94 | *.hmap 95 | *.ipa 96 | *.o 97 | *.rbc 98 | *.sass.map 99 | *.scss.map 100 | *.so 101 | *.xccheckout 102 | *.xcuserdatad 103 | DerivedData 104 | build 105 | build-iPhoneOS 106 | build-iPhoneSimulator 107 | coverage 108 | coverage.data 109 | dist 110 | files/scripts/**/*.js 111 | files/scripts/**/*.py 112 | files/scripts/**/*.zip 113 | out 114 | pillars/files 115 | pkg 116 | profile 117 | rdoc 118 | target 119 | 120 | # Local Configuration Files 121 | **/cordova/platforms/android/CordovaLib/local.properties 122 | **/cordova/platforms/android/local.properties 123 | **/settings-local.yml 124 | **/settings/*-local.yml 125 | *.bundle 126 | *.iml 127 | *.ipr 128 | *.iws 129 | *.mode1v3 130 | *.mode2v3 131 | *.pbxuser 132 | *.perspectivev3 133 | *.project 134 | *.sublime-project 135 | *.sublime-settings 136 | *.sublime-workspace 137 | *.tmproj 138 | .Rhistory 139 | .build 140 | .bundle 141 | .chamber*.enc 142 | .chamber*.enc.pass 143 | .chamber*.pem 144 | .emacs.desktop 145 | .emacs.desktop.lock 146 | .env 147 | .env.* 148 | .env.*.local 149 | .env.local 150 | .env.yml 151 | .foreman 152 | .htaccess 153 | .idea 154 | .idea_modules 155 | .powenv 156 | .powrc 157 | .pryrc 158 | .railsrc 159 | .rspec 160 | .rvmrc 161 | .secrets 162 | .shellwreck.sh 163 | .tern-port 164 | .terraform 165 | Gemfile.local 166 | Procfile.local 167 | atlassian-ide-plugin.xml 168 | com_crashlytics_export_strings.xml 169 | config.yml 170 | config/application.yml 171 | config/database.yml 172 | config/initializers/secret_token.rb 173 | config/master.key 174 | config/secrets.yml 175 | crashlytics-build.properties 176 | crashlytics.properties 177 | nbproject 178 | xcuserdata 179 | 180 | !.circleci/config.yml 181 | !.env.example 182 | !default.mode1v3 183 | !default.mode2v3 184 | !default.pbxuser 185 | !default.perspectivev3 186 | 187 | # Temporary files 188 | *.backup 189 | *.bak 190 | *.gho 191 | *.lck 192 | *.lock 193 | *.log* 194 | *.moved-aside 195 | *.old 196 | *.ori 197 | *.orig 198 | *.rs.bk 199 | *.sqlite3 200 | *.sqlite3-journal 201 | *.swo 202 | *.swp 203 | *.tmp 204 | *~ 205 | .\#* 206 | .netrwhist 207 | \#* 208 | db-sync 209 | dump.rdb 210 | log 211 | solr/* 212 | spec/examples.txt 213 | sql-dump-*.sql 214 | tmp 215 | 216 | # Vagrant 217 | .vagrant 218 | 219 | # Always keep chamber public keys 220 | !.chamber*.pub.pem 221 | 222 | # Always keep lock files 223 | !yarn.lock 224 | !Gemfile.lock 225 | !Cargo.lock 226 | !composer.lock 227 | 228 | # Always Version .keep and .gitkeep files 229 | !.gitkeep 230 | !.keep 231 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Ruby/ProgressBar 2 | ================================================================================ 3 | 4 |
5 | 6 | RubyGems Version 7 | 8 | 9 | 10 | RubyGems Rank Overall 11 | 12 | 13 | 14 | RubyGems Rank Daily 15 | 16 | 17 | 18 | RubyGems Downloads 19 | 20 | 21 | 22 | Build Status 23 | 24 | 25 | 26 | Maintainability 27 | 28 |
29 | 30 |
31 | 32 | 33 | 34 | The **ultimate** text progress bar library for Ruby! It'll **SMASH YOU OVER THE 35 | HEAD** with a **PURE RUSH** of progress bar excitement! 36 | 37 | Don't miss out on what all the kids are talking about! If you want everyone to 38 | know that your gem or app can survive _in the cage_ then YOU WANT 39 | **RUBY-PROGRESSBAR**! 40 | 41 |
42 | 43 | It's Better Than The Other 186,312 Progress Bar Libraries Because 44 | -------------------------------------------------------------------------------- 45 | 46 | * It has [stood the test of time][history] (2008-today) 47 | 48 | * Full test suite 49 | 50 | * [_**ZERO**_ dependencies][gemspec] 51 | 52 | * Used by [tons of other open source projects][dependencies] (which means we 53 | find out about bugs quickly) 54 | 55 | * It's pretty [freakin' sweet](https://www.youtube.com/watch?v=On3IoVhf_GM) 56 | 57 | * And most importantly... our awesome [contributors][contributors] 58 | 59 | Basic Usage 60 | -------------------------------------------------------------------------------- 61 | 62 | ### Creation 63 | 64 | It's simple to get started: 65 | 66 | ```ruby 67 | progressbar = ProgressBar.create 68 | ``` 69 | 70 | Creates a basic progress bar beginning at `0`, a maximum capacity of `100` and 71 | tells it to start. 72 | 73 | ```text 74 | Progress: | | 75 | ``` 76 | 77 | ### Marking Progress 78 | 79 | Every call to `#increment` will advance the bar by `1`. Therefore: 80 | 81 | ```ruby 82 | 50.times { progressbar.increment } 83 | ``` 84 | 85 | Would output an advancing line which would end up here: 86 | 87 | ```text 88 | Progress: |=================================== | 89 | ``` 90 | 91 | ### Animation 92 | 93 | ![Basic Usage Marking Progress](http://kompanee-public-assets.s3.amazonaws.com/readmes/ruby-progressbar-basic-usage-marking-progress-2.gif) 94 | 95 | Full Reference 96 | -------------------------------------------------------------------------------- 97 | 98 | There's gotten to be too much awesome to pack into one page. Visit the 99 | [wiki][wiki] for the full documentation. 100 | 101 | Issues 102 | -------------------------------------------------------------------------------- 103 | 104 | If you have problems, please create a [Github issue][issues]. 105 | 106 | Credits 107 | -------------------------------------------------------------------------------- 108 | 109 | ![The Kompanee][kompanee-logo] 110 | 111 | ruby-progressbar is maintained by [The Kompanee, Ltd.][kompanee-site] 112 | 113 | The names and logos for The Kompanee are trademarks of The Kompanee, Ltd. 114 | 115 | License 116 | -------------------------------------------------------------------------------- 117 | 118 | ruby-progressbar 1.0 is Copyright © 2011-2021 The Kompanee. It is free 119 | software, and may be redistributed under the terms specified in the LICENSE 120 | file. 121 | ruby-progressbar 0.9.0 is Copyright © 2008 [Satoru Takabayashi][satoru] 122 | 123 | [contributors]: https://github.com/jfelchner/ruby-progressbar/graphs/contributors 124 | [dependencies]: https://github.com/jfelchner/ruby-progressbar/network/dependents 125 | [gemspec]: https://github.com/jfelchner/ruby-progressbar/blob/master/ruby-progressbar.gemspec 126 | [history]: https://github.com/jfelchner/ruby-progressbar/wiki/History 127 | [issues]: https://github.com/jfelchner/ruby-progressbar/issues 128 | [kompanee-logo]: https://kompanee-public-assets.s3.amazonaws.com/readmes/kompanee-horizontal-black.png 129 | [kompanee-site]: http://www.thekompanee.com 130 | [satoru]: http://0xcc.net 131 | [wiki]: https://github.com/jfelchner/ruby-progressbar/wiki 132 | -------------------------------------------------------------------------------- /.rubocop_performance.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | # Last updated to 1.16.0 4 | 5 | require: 6 | - 'rubocop-performance' 7 | 8 | ################################################################################ 9 | # PERFORMANCE 10 | ################################################################################ 11 | 12 | Performance/AncestorsInclude: 13 | Enabled: true 14 | 15 | # Mistakenly created: https://github.com/rubocop-hq/rubocop-performance/pull/199/files 16 | Performance/ArraySemiInfiniteRangeSlice: 17 | Enabled: false 18 | 19 | Performance/BigDecimalWithNumericArgument: 20 | Enabled: true 21 | 22 | Performance/BindCall: 23 | Enabled: true 24 | 25 | Performance/BlockGivenWithExplicitBlock: 26 | Enabled: true 27 | 28 | Performance/Caller: 29 | Enabled: true 30 | 31 | Performance/Casecmp: 32 | Enabled: true 33 | 34 | Performance/CaseWhenSplat: 35 | Enabled: true 36 | 37 | Performance/ChainArrayAllocation: 38 | # Disallows: Most compact chaining where performance impact is minimal 39 | Enabled: false 40 | 41 | Performance/CollectionLiteralInLoop: 42 | Enabled: true 43 | MinSize: 1 44 | Exclude: 45 | - '**/spec/**/*.rb' 46 | 47 | Performance/CompareWithBlock: 48 | Enabled: true 49 | 50 | Performance/ConcurrentMonotonicTime: 51 | Enabled: true 52 | 53 | Performance/ConstantRegexp: 54 | Enabled: true 55 | 56 | Performance/Count: 57 | Enabled: true 58 | 59 | Performance/DeletePrefix: 60 | Enabled: true 61 | 62 | Performance/DeleteSuffix: 63 | Enabled: true 64 | 65 | Performance/Detect: 66 | Enabled: true 67 | 68 | Performance/DoubleStartEndWith: 69 | Enabled: true 70 | IncludeActiveSupportAliases: true 71 | 72 | Performance/EndWith: 73 | Enabled: true 74 | 75 | Performance/FixedSize: 76 | Enabled: true 77 | 78 | Performance/FlatMap: 79 | Enabled: true 80 | 81 | Performance/InefficientHashSearch: 82 | Enabled: true 83 | 84 | Performance/IoReadlines: 85 | Enabled: true 86 | 87 | Performance/MapCompact: 88 | Enabled: true 89 | 90 | Performance/MethodObjectAsBlock: 91 | Enabled: true 92 | 93 | Performance/OpenStruct: 94 | Enabled: true 95 | Exclude: 96 | - '**/spec/**/*.rb' 97 | 98 | Performance/RangeInclude: 99 | Enabled: true 100 | 101 | Performance/RedundantBlockCall: 102 | Enabled: true 103 | 104 | Performance/RedundantEqualityComparisonBlock: 105 | Enabled: true 106 | 107 | Performance/RedundantMatch: 108 | Enabled: true 109 | 110 | Performance/RedundantMerge: 111 | Enabled: true 112 | 113 | Performance/RedundantSortBlock: 114 | Enabled: true 115 | 116 | Performance/RedundantSplitRegexpArgument: 117 | Enabled: true 118 | 119 | Performance/RedundantStringChars: 120 | Enabled: true 121 | 122 | Performance/RegexpMatch: 123 | Enabled: true 124 | 125 | Performance/ReverseEach: 126 | Enabled: true 127 | 128 | Performance/ReverseFirst: 129 | Enabled: true 130 | 131 | # filter_map excludes `false` unlike `compact` which does not. 132 | Performance/SelectMap: 133 | Enabled: false 134 | 135 | Performance/Size: 136 | Enabled: true 137 | 138 | Performance/SortReverse: 139 | Enabled: true 140 | 141 | Performance/Squeeze: 142 | Enabled: true 143 | 144 | Performance/StartWith: 145 | Enabled: true 146 | 147 | Performance/StringIdentifierArgument: 148 | Enabled: true 149 | 150 | Performance/StringInclude: 151 | Enabled: true 152 | 153 | Performance/StringReplacement: 154 | Enabled: true 155 | 156 | Performance/Sum: 157 | Enabled: true 158 | OnlySumOrWithInitialValue: false 159 | 160 | Performance/TimesMap: 161 | Enabled: true 162 | AutoCorrect: true 163 | 164 | Performance/UnfreezeString: 165 | Enabled: true 166 | 167 | Performance/UriDefaultParser: 168 | Enabled: true 169 | -------------------------------------------------------------------------------- /.github/workflows/linting.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | name: "Linting" 4 | 5 | on: 6 | - "push" 7 | 8 | jobs: 9 | lint: 10 | name: "Linting" 11 | runs-on: "ubuntu-latest" 12 | 13 | steps: 14 | - name: "Cache Ruby" 15 | uses: "actions/cache@v3" 16 | with: 17 | path: "vendor/bundle" 18 | key: | 19 | ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }} 20 | restore-keys: | 21 | ${{ runner.os }}-gems- 22 | 23 | - name: "Cache NodeJS" 24 | uses: "actions/cache@v3" 25 | with: 26 | path: "~/.npm" 27 | key: | 28 | ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} 29 | restore-keys: | 30 | ${{ runner.os }}-node- 31 | 32 | - name: "Cache Python" 33 | uses: "actions/cache@v3" 34 | with: 35 | path: "~/.cache/pip" 36 | key: | 37 | ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} 38 | restore-keys: | 39 | ${{ runner.os }}-pip- 40 | 41 | - name: "Checkout Code" 42 | uses: "actions/checkout@v3" 43 | timeout-minutes: 5 44 | with: 45 | fetch-depth: 0 46 | 47 | - name: "Build Ruby" 48 | uses: "ruby/setup-ruby@v1" 49 | with: 50 | ruby-version: 2.7 51 | 52 | - name: "Build Node" 53 | 54 | uses: "actions/setup-node@v3" 55 | with: 56 | node-version: 14 57 | 58 | - name: "Install Ruby Gems" 59 | run: > 60 | gem install --no-document 61 | rubocop 62 | rubocop-rspec 63 | rubocop-rails 64 | rubocop-performance 65 | 66 | - name: "Install Node Modules" 67 | run: > 68 | npm install -g 69 | @prantlf/jsonlint 70 | babel-eslint 71 | eslint 72 | htmlhint 73 | remark-cli 74 | remark-lint-blockquote-indentation 75 | remark-lint-checkbox-character-style 76 | remark-lint-code-block-style 77 | remark-lint-definition-case 78 | remark-lint-definition-spacing 79 | remark-lint-emphasis-marker 80 | remark-lint-fenced-code-flag 81 | remark-lint-fenced-code-marker 82 | remark-lint-file-extension 83 | remark-lint-final-definition 84 | remark-lint-final-newline 85 | remark-lint-first-heading-level 86 | remark-lint-hard-break-spaces 87 | remark-lint-heading-increment 88 | remark-lint-heading-style 89 | remark-lint-heading-whitespace 90 | remark-lint-linebreak-style 91 | remark-lint-link-title-style 92 | remark-lint-list-item-bullet-indent 93 | remark-lint-list-item-content-indent 94 | remark-lint-list-item-indent 95 | remark-lint-list-item-spacing 96 | remark-lint-maximum-line-length 97 | remark-lint-no-auto-link-without-protocol 98 | remark-lint-no-blockquote-without-marker 99 | remark-lint-no-blockquote-without-marker 100 | remark-lint-no-consecutive-blank-lines 101 | remark-lint-no-duplicate-definitions 102 | remark-lint-no-duplicate-headings 103 | remark-lint-no-duplicate-headings-in-section 104 | remark-lint-no-duplicate-headings-in-section 105 | remark-lint-no-emphasis-as-heading 106 | remark-lint-no-empty-sections 107 | remark-lint-no-empty-sections 108 | remark-lint-no-empty-url 109 | remark-lint-no-empty-url 110 | remark-lint-no-file-name-articles 111 | remark-lint-no-file-name-consecutive-dashes 112 | remark-lint-no-file-name-irregular-characters 113 | remark-lint-no-file-name-mixed-case 114 | remark-lint-no-file-name-outer-dashes 115 | remark-lint-no-heading-content-indent 116 | remark-lint-no-heading-indent 117 | remark-lint-no-heading-indent 118 | remark-lint-no-heading-like-paragraph 119 | remark-lint-no-heading-like-paragraph 120 | remark-lint-no-heading-punctuation 121 | remark-lint-no-html 122 | remark-lint-no-inline-padding 123 | remark-lint-no-literal-urls 124 | remark-lint-no-missing-blank-lines 125 | remark-lint-no-missing-blank-lines 126 | remark-lint-no-multiple-toplevel-headings 127 | remark-lint-no-paragraph-content-indent 128 | remark-lint-no-paragraph-content-indent 129 | remark-lint-no-reference-like-url 130 | remark-lint-no-reference-like-url 131 | remark-lint-no-shell-dollars 132 | remark-lint-no-shortcut-reference-image 133 | remark-lint-no-shortcut-reference-link 134 | remark-lint-no-table-indentation 135 | remark-lint-no-tabs 136 | remark-lint-no-tabs 137 | remark-lint-no-undefined-references 138 | remark-lint-no-unused-definitions 139 | remark-lint-no-url-trailing-slash 140 | remark-lint-no-url-trailing-slash 141 | remark-lint-ordered-list-marker-style 142 | remark-lint-ordered-list-marker-value 143 | remark-lint-strong-marker 144 | remark-lint-table-cell-padding 145 | remark-lint-table-pipe-alignment 146 | remark-lint-table-pipes 147 | remark-lint-unordered-list-marker-style 148 | remark-message-control 149 | remark-preset-lint-consistent 150 | remark-preset-lint-markdown-style-guide 151 | remark-preset-lint-recommended 152 | stylelint 153 | stylelint-order 154 | stylelint-scss 155 | 156 | - name: "Install Python Eggs" 157 | run: > 158 | pip install --user 159 | vim-vint 160 | 161 | - name: "Install Packages" 162 | run: > 163 | sudo apt install -y 164 | shellcheck 165 | tidy 166 | yamllint 167 | 168 | - name: "Run Rubocop" 169 | run: | 170 | rubocop --force-exclusion 171 | 172 | - name: "Run YAMLLint" 173 | run: | 174 | yamllint -f "github" -d ".yamllint" . 175 | 176 | - name: "Run Remark" 177 | run: | 178 | remark --ignore-path .markdownlintignore --silently-ignore **/*.md .**/*.md 179 | -------------------------------------------------------------------------------- /lib/ruby-progressbar/base.rb: -------------------------------------------------------------------------------- 1 | require 'forwardable' 2 | 3 | require 'ruby-progressbar/components/bar' 4 | require 'ruby-progressbar/components/percentage' 5 | require 'ruby-progressbar/components/rate' 6 | require 'ruby-progressbar/components/time' 7 | require 'ruby-progressbar/components/title' 8 | require 'ruby-progressbar/format/formatter' 9 | require 'ruby-progressbar/format/string' 10 | require 'ruby-progressbar/outputs/non_tty' 11 | require 'ruby-progressbar/outputs/tty' 12 | require 'ruby-progressbar/progress' 13 | require 'ruby-progressbar/projector' 14 | require 'ruby-progressbar/timer' 15 | 16 | class ProgressBar 17 | class Base 18 | extend Forwardable 19 | 20 | # rubocop:disable Layout/HeredocIndentation 21 | SMOOTHING_DEPRECATION_WARNING = <<-HEREDOC.tr("\n", ' ') 22 | WARNING: Passing the 'smoothing' option is deprecated and will be removed 23 | in version 2.0. Please pass { projector: { type: 'smoothing', strength: 0.x }}. 24 | For more information on why this change is happening, visit 25 | https://github.com/jfelchner/ruby-progressbar/wiki/Upgrading 26 | HEREDOC 27 | 28 | RUNNING_AVERAGE_RATE_DEPRECATION_WARNING = <<-HEREDOC.tr("\n", ' ') 29 | WARNING: Passing the 'running_average_rate' option is deprecated and will be removed 30 | in version 2.0. Please pass { projector: { type: 'smoothing', strength: 0.x }}. 31 | For more information on why this change is happening, visit 32 | https://github.com/jfelchner/ruby-progressbar/wiki/Upgrading 33 | HEREDOC 34 | # rubocop:enable Layout/HeredocIndentation 35 | 36 | def_delegators :output, 37 | :clear, 38 | :log, 39 | :refresh 40 | 41 | def_delegators :progressable, 42 | :progress, 43 | :total 44 | 45 | def initialize(options = {}) # rubocop:disable Metrics/AbcSize 46 | options[:projector] ||= {} 47 | 48 | self.autostart = options.fetch(:autostart, true) 49 | self.autofinish = options.fetch(:autofinish, true) 50 | self.finished = false 51 | 52 | self.timer = Timer.new(options) 53 | projector_opts = if options[:projector].any? 54 | options[:projector] 55 | elsif options[:smoothing] 56 | warn SMOOTHING_DEPRECATION_WARNING 57 | 58 | { :strength => options[:smoothing] } 59 | elsif options[:running_average_rate] 60 | warn RUNNING_AVERAGE_RATE_DEPRECATION_WARNING 61 | 62 | { :strength => options[:smoothing] } 63 | else 64 | {} 65 | end 66 | self.projector = Projector. 67 | from_type(options[:projector][:type]). 68 | new(projector_opts) 69 | self.progressable = Progress.new(options) 70 | 71 | options = options.merge(:progress => progressable, 72 | :projector => projector, 73 | :timer => timer) 74 | 75 | self.title_component = Components::Title.new(options) 76 | self.bar_component = Components::Bar.new(options) 77 | self.percentage_component = Components::Percentage.new(options) 78 | self.rate_component = Components::Rate.new(options) 79 | self.time_component = Components::Time.new(options) 80 | 81 | self.output = Output.detect(options.merge(:bar => self)) 82 | @format = Format::String.new(output.resolve_format(options[:format])) 83 | 84 | start :at => options[:starting_at] if autostart 85 | end 86 | 87 | def start(options = {}) 88 | timer.start 89 | update_progress(:start, options) 90 | end 91 | 92 | def finish 93 | return if finished? 94 | 95 | output.with_refresh do 96 | self.finished = true 97 | progressable.finish 98 | timer.stop 99 | end 100 | end 101 | 102 | def pause 103 | output.with_refresh { timer.pause } unless paused? 104 | end 105 | 106 | def stop 107 | output.with_refresh { timer.stop } unless stopped? 108 | end 109 | 110 | def resume 111 | output.with_refresh { timer.resume } if stopped? 112 | end 113 | 114 | def reset 115 | output.with_refresh do 116 | self.finished = false 117 | progressable.reset 118 | projector.reset 119 | timer.reset 120 | end 121 | end 122 | 123 | def stopped? 124 | timer.stopped? || finished? 125 | end 126 | 127 | alias paused? stopped? 128 | 129 | def finished? 130 | finished || (autofinish && progressable.finished?) 131 | end 132 | 133 | def started? 134 | timer.started? 135 | end 136 | 137 | def decrement 138 | update_progress(:decrement) 139 | end 140 | 141 | def increment 142 | update_progress(:increment) 143 | end 144 | 145 | def progress=(new_progress) 146 | update_progress(:progress=, new_progress) 147 | end 148 | 149 | def total=(new_total) 150 | update_progress(:total=, new_total) 151 | end 152 | 153 | def progress_mark=(mark) 154 | output.refresh_with_format_change { bar_component.progress_mark = mark } 155 | end 156 | 157 | def remainder_mark=(mark) 158 | output.refresh_with_format_change { bar_component.remainder_mark = mark } 159 | end 160 | 161 | def title 162 | title_component.title 163 | end 164 | 165 | def title=(title) 166 | output.refresh_with_format_change { title_component.title = title } 167 | end 168 | 169 | def to_s(new_format = nil) 170 | self.format = new_format if new_format 171 | 172 | Format::Formatter.process(@format, output.length, self) 173 | end 174 | 175 | # rubocop:disable Metrics/AbcSize, Layout/LineLength 176 | def to_h 177 | { 178 | 'output_stream' => output.__send__(:stream), 179 | 'length' => output.length, 180 | 'title' => title_component.title, 181 | 'progress_mark' => bar_component.progress_mark, 182 | 'remainder_mark' => bar_component.remainder_mark, 183 | 'progress' => progressable.progress, 184 | 'total' => progressable.total, 185 | 'percentage' => progressable.percentage_completed_with_precision.to_f, 186 | 'elapsed_time_in_seconds' => time_component.__send__(:timer).elapsed_seconds, 187 | 'estimated_time_remaining_in_seconds' => time_component.__send__(:estimated_seconds_remaining), 188 | 'base_rate_of_change' => rate_component.__send__(:base_rate), 189 | 'scaled_rate_of_change' => rate_component.__send__(:scaled_rate), 190 | 'unknown_progress_animation_steps' => bar_component.upa_steps, 191 | 'throttle_rate' => output.__send__(:throttle).rate, 192 | 'started?' => started?, 193 | 'stopped?' => stopped?, 194 | 'finished?' => finished? 195 | } 196 | end 197 | # rubocop:enable Metrics/AbcSize, Layout/LineLength 198 | 199 | def inspect 200 | "#" 201 | end 202 | 203 | def format=(other) 204 | output.refresh_with_format_change do 205 | @format = Format::String.new(other || output.default_format) 206 | end 207 | end 208 | 209 | alias format format= 210 | 211 | protected 212 | 213 | attr_accessor :output, 214 | :projector, 215 | :timer, 216 | :progressable, 217 | :title_component, 218 | :bar_component, 219 | :percentage_component, 220 | :rate_component, 221 | :time_component, 222 | :autostart, 223 | :autofinish, 224 | :finished 225 | 226 | def update_progress(*args) 227 | output.with_refresh do 228 | progressable.__send__(*args) 229 | projector.__send__(*args) 230 | timer.stop if finished? 231 | end 232 | end 233 | end 234 | end 235 | -------------------------------------------------------------------------------- /spec/lib/ruby-progressbar/components/bar_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'ruby-progressbar/components/bar' 3 | 4 | class ProgressBar 5 | module Components 6 | describe Bar do 7 | describe '#initialize' do 8 | it 'has a default mark when a new bar is created and no parameters are passed' do 9 | expect(Bar.new.progress_mark).to eql Bar::DEFAULT_PROGRESS_MARK 10 | end 11 | 12 | it 'has a default remainder mark when a new bar is created and no parameters ' \ 13 | 'are passed' do 14 | expect(Bar.new.remainder_mark).to eql Bar::DEFAULT_REMAINDER_MARK 15 | end 16 | 17 | it 'returns the overridden mark when a new bar is created and options are passed' do 18 | progressbar = Bar.new(:progress_mark => 'x') 19 | 20 | expect(progressbar.progress_mark).to eql 'x' 21 | end 22 | 23 | it 'returns the overridden remainder mark when a new bar is created and options ' \ 24 | 'are passed' do 25 | progressbar = Bar.new(:remainder_mark => 'o') 26 | 27 | expect(progressbar.remainder_mark).to eql 'o' 28 | end 29 | end 30 | 31 | describe '#bar' do 32 | it 'displays the bar with no indication of progress when just begun' do 33 | progress = Progress.new(:total => 50) 34 | progressbar = Bar.new(:progress => progress, 35 | :length => 100) 36 | 37 | bar_text = progressbar.bar(100) 38 | expect(bar_text).to eql '' 39 | end 40 | 41 | it 'displays the bar with an indication of progress when nothing has been ' \ 42 | 'completed and the bar is incremented' do 43 | progress = Progress.new(:total => 50) 44 | progressbar = Bar.new(:progress => progress, 45 | :length => 100) 46 | progress.increment 47 | 48 | bar_text = progressbar.bar(100) 49 | expect(bar_text).to eql '==' 50 | end 51 | 52 | it 'displays the bar with no indication of progress when a fraction of a percentage ' \ 53 | 'has been completed' do 54 | progress = Progress.new(:total => 200) 55 | progressbar = Bar.new(:progress => progress, 56 | :length => 100) 57 | progress.start :at => 1 58 | 59 | bar_text = progressbar.bar(100) 60 | expect(bar_text).to eql '' 61 | end 62 | 63 | it 'displays the bar as 100% complete when completed' do 64 | progress = Progress.new(:total => 50) 65 | progressbar = Bar.new(:progress => progress, 66 | :length => 100) 67 | progress.start :at => 50 68 | progress.increment 69 | 70 | bar_text = progressbar.bar(100) 71 | expect(bar_text).to eql('=' * 100) 72 | end 73 | 74 | it 'displays the bar as 98% complete when completed and the bar is decremented' do 75 | progress = Progress.new(:total => 50) 76 | progressbar = Bar.new(:progress => progress, 77 | :length => 100) 78 | progress.start :at => 50 79 | progress.decrement 80 | 81 | bar_text = progressbar.bar(100) 82 | expect(bar_text).to eql('=' * 98) 83 | end 84 | end 85 | 86 | describe '#bar_with_percentage' do 87 | it 'displays the bar with an integrated percentage properly when empty' do 88 | progress = Progress.new(:total => 100) 89 | progressbar = Bar.new(:progress => progress, 90 | :length => 100) 91 | 92 | bar_text = progressbar.bar_with_percentage(100) 93 | expect(bar_text).to eql '' 94 | end 95 | 96 | it 'displays the bar with an integrated percentage properly just before' \ 97 | 'the percentage is displayed' do 98 | progress = Progress.new(:total => 100) 99 | progressbar = Bar.new(:progress => progress, 100 | :length => 100) 101 | 102 | 4.times { progress.increment } 103 | 104 | bar_text = progressbar.bar_with_percentage(100) 105 | expect(bar_text).to eql '====' 106 | end 107 | 108 | it 'displays the bar with an integrated percentage properly immediately after' \ 109 | 'the percentage is displayed' do 110 | progress = Progress.new(:total => 100) 111 | progressbar = Bar.new(:progress => progress, 112 | :length => 100) 113 | 114 | 5.times { progress.increment } 115 | 116 | bar_text = progressbar.bar_with_percentage(100) 117 | expect(bar_text).to eql '= 5 =' 118 | end 119 | 120 | it 'displays the bar with an integrated percentage properly on double digit' \ 121 | 'percentage' do 122 | progress = Progress.new(:total => 100) 123 | progressbar = Bar.new(:progress => progress, 124 | :length => 100) 125 | 126 | 10.times { progress.increment } 127 | 128 | bar_text = progressbar.bar_with_percentage(100) 129 | expect(bar_text).to eql '=== 10 ===' 130 | end 131 | 132 | it 'displays the bar with an integrated percentage properly when finished' do 133 | progress = Progress.new(:total => 100) 134 | progressbar = Bar.new(:progress => progress, 135 | :length => 100) 136 | progress.finish 137 | 138 | bar_text = progressbar.bar_with_percentage(100) 139 | expect(bar_text).to eql("#{'=' * 47} 100 #{'=' * 48}") 140 | end 141 | 142 | it 'calculates the remaining negative space properly with an integrated percentage ' \ 143 | 'bar of 0 percent' do 144 | progress = Progress.new(:total => 200) 145 | progressbar = Bar.new(:progress => progress, 146 | :length => 100) 147 | 148 | bar_text = progressbar.bar_with_percentage(100) 149 | expect(bar_text).to eql '' 150 | 151 | 9.times { progress.increment } 152 | 153 | bar_text = progressbar.bar_with_percentage(100) 154 | expect(bar_text).to eql '====' 155 | 156 | progress.increment 157 | 158 | bar_text = progressbar.bar_with_percentage(100) 159 | expect(bar_text).to eql '= 5 =' 160 | end 161 | end 162 | 163 | describe '#incomplete_space' do 164 | it 'displays the bar with an integrated percentage properly when empty' do 165 | progress = Progress.new(:total => 100) 166 | progressbar = Bar.new(:progress => progress, 167 | :length => 100) 168 | 169 | bar_text = progressbar.incomplete_space(100) 170 | expect(bar_text).to eql ' ' * 100 171 | end 172 | 173 | it 'displays the bar with an integrated percentage properly just before' \ 174 | 'the percentage is displayed' do 175 | progress = Progress.new(:total => 100) 176 | progressbar = Bar.new(:progress => progress, 177 | :length => 100) 178 | 179 | 4.times { progress.increment } 180 | 181 | bar_text = progressbar.incomplete_space(100) 182 | expect(bar_text).to eql(' ' * 96) 183 | end 184 | 185 | it 'displays the bar with an integrated percentage properly immediately after' \ 186 | 'the percentage is displayed' do 187 | progress = Progress.new(:total => 100) 188 | progressbar = Bar.new(:progress => progress, 189 | :length => 100) 190 | 191 | 5.times { progress.increment } 192 | 193 | bar_text = progressbar.incomplete_space(100) 194 | expect(bar_text).to eql(' ' * 95) 195 | end 196 | 197 | it 'displays the bar with an integrated percentage properly on double digit' \ 198 | 'percentage' do 199 | progress = Progress.new(:total => 100) 200 | progressbar = Bar.new(:progress => progress, 201 | :length => 100) 202 | 203 | 10.times { progress.increment } 204 | 205 | bar_text = progressbar.incomplete_space(100) 206 | expect(bar_text).to eql(' ' * 90) 207 | end 208 | 209 | it 'displays the bar with an integrated percentage properly when finished' do 210 | progress = Progress.new(:total => 100) 211 | progressbar = Bar.new(:progress => progress, 212 | :length => 100) 213 | progress.finish 214 | 215 | bar_text = progressbar.incomplete_space(100) 216 | expect(bar_text).to eql '' 217 | end 218 | 219 | it 'calculates the remaining negative space properly with an integrated percentage ' \ 220 | 'bar of 0 percent' do 221 | progress = Progress.new(:total => 200) 222 | progressbar = Bar.new(:progress => progress, 223 | :length => 100) 224 | 225 | bar_text = progressbar.incomplete_space(100) 226 | expect(bar_text).to eql ' ' * 100 227 | 228 | 9.times { progress.increment } 229 | 230 | bar_text = progressbar.incomplete_space(100) 231 | expect(bar_text).to eql(' ' * 96) 232 | 233 | progress.increment 234 | 235 | bar_text = progressbar.incomplete_space(100) 236 | expect(bar_text).to eql(' ' * 95) 237 | end 238 | 239 | it 'is represented correctly when a bar has an unknown amount to completion' do 240 | progress = Progress.new(:total => nil) 241 | progressbar = Bar.new(:progress => progress, 242 | :length => 80) 243 | 244 | bar_text = progressbar.incomplete_space(80) 245 | expect(bar_text).to eql('=---' * 20) 246 | end 247 | 248 | it 'is represented after being incremented once when a bar has an unknown amount ' \ 249 | 'to completion' do 250 | progress = Progress.new(:total => nil) 251 | progressbar = Bar.new(:progress => progress, 252 | :length => 80) 253 | 254 | progress.increment 255 | 256 | bar_text = progressbar.incomplete_space(80) 257 | expect(bar_text).to eql('-=--' * 20) 258 | end 259 | 260 | it 'is represented after being incremented twice when a bar has an unknown amount ' \ 261 | 'to completion' do 262 | progress = Progress.new(:total => nil) 263 | progressbar = Bar.new(:progress => progress, 264 | :length => 80) 265 | 266 | 2.times { progress.increment } 267 | 268 | bar_text = progressbar.incomplete_space(80) 269 | expect(bar_text).to eql('--=-' * 20) 270 | end 271 | 272 | it 'is represented correctly when a bar has a customized unknown animation' do 273 | progress = Progress.new(:total => nil) 274 | progressbar = Bar.new(:progress => progress, 275 | :unknown_progress_animation_steps => [ 276 | '*--', 277 | '-*-', 278 | '--*' 279 | ], 280 | :length => 80) 281 | 282 | bar_text = progressbar.incomplete_space(80) 283 | expect(bar_text).to eql("#{'*--' * 26}*-") 284 | end 285 | end 286 | 287 | it 'raises an error when attempting to set the current value of the bar to be ' \ 288 | 'greater than the total' do 289 | progress = Progress.new(:total => 10) 290 | _progressbar = Bar.new(:progress => progress) 291 | 292 | expect { progress.start :at => 11 }. 293 | to \ 294 | raise_error(InvalidProgressError, 295 | "You can't set the item's current value to be greater " \ 296 | "than the total.") 297 | end 298 | end 299 | end 300 | end 301 | -------------------------------------------------------------------------------- /.rubocop_rspec.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | # Last updated to 2.18.1 4 | 5 | require: 6 | - 'rubocop-rspec' 7 | 8 | ################################################################################ 9 | # CAPYBARA 10 | ################################################################################ 11 | 12 | Capybara/CurrentPathExpectation: 13 | Enabled: true 14 | 15 | Capybara/MatchStyle: 16 | Enabled: true 17 | 18 | Capybara/NegationMatcher: 19 | Enabled: true 20 | 21 | Capybara/SpecificActions: 22 | Enabled: true 23 | 24 | Capybara/SpecificFinders: 25 | Enabled: true 26 | 27 | Capybara/SpecificMatcher: 28 | Enabled: true 29 | 30 | Capybara/VisibilityMatcher: 31 | Enabled: true 32 | 33 | ################################################################################ 34 | # RSPEC 35 | ################################################################################ 36 | 37 | RSpec/AlignLeftLetBrace: 38 | Enabled: true 39 | 40 | RSpec/AlignRightLetBrace: 41 | Enabled: false 42 | 43 | RSpec/AnyInstance: 44 | Enabled: true 45 | 46 | RSpec/AroundBlock: 47 | Enabled: true 48 | 49 | RSpec/Be: 50 | Enabled: true 51 | 52 | RSpec/BeEq: 53 | Enabled: true 54 | 55 | RSpec/BeEql: 56 | Enabled: true 57 | 58 | RSpec/BeNil: 59 | Enabled: true 60 | EnforcedStyle: 'be' 61 | 62 | RSpec/BeforeAfterAll: 63 | Enabled: true 64 | Exclude: 65 | - 'spec/spec_helper.rb' 66 | - 'spec/rails_helper.rb' 67 | - 'spec/support/**/*.rb' 68 | 69 | RSpec/Capybara/FeatureMethods: 70 | Enabled: true 71 | 72 | RSpec/ChangeByZero: 73 | Enabled: true 74 | NegatedMatcher: ~ 75 | 76 | RSpec/ClassCheck: 77 | Enabled: true 78 | 79 | RSpec/ContextMethod: 80 | Enabled: true 81 | 82 | RSpec/ContextWording: 83 | Enabled: true 84 | AllowedPatterns: [] 85 | Prefixes: 86 | - 'administrators' 87 | - 'and' 88 | - 'anyone' 89 | - 'but' 90 | - 'or' 91 | - 'regular users' 92 | - 'when' 93 | - 'with' 94 | - 'without' 95 | 96 | RSpec/DescribeClass: 97 | Enabled: true 98 | 99 | # Doesn't allow: 100 | # 101 | # module Foo 102 | # describe Bar do 103 | # end 104 | # end 105 | RSpec/DescribedClassModuleWrapping: 106 | Enabled: false 107 | 108 | RSpec/DescribeMethod: 109 | Enabled: true 110 | 111 | RSpec/DescribeSymbol: 112 | Enabled: true 113 | 114 | RSpec/DescribedClass: 115 | Enabled: true 116 | SkipBlocks: false 117 | EnforcedStyle: 'explicit' 118 | 119 | RSpec/Dialect: 120 | Enabled: true 121 | PreferredMethods: {} 122 | 123 | RSpec/DuplicatedMetadata: 124 | Enabled: true 125 | 126 | RSpec/EmptyExampleGroup: 127 | Enabled: false 128 | 129 | RSpec/EmptyHook: 130 | Enabled: true 131 | 132 | RSpec/EmptyLineAfterExample: 133 | Enabled: true 134 | AllowConsecutiveOneLiners: true 135 | 136 | RSpec/EmptyLineAfterExampleGroup: 137 | Enabled: true 138 | 139 | RSpec/EmptyLineAfterFinalLet: 140 | Enabled: true 141 | 142 | RSpec/EmptyLineAfterHook: 143 | Enabled: true 144 | AllowConsecutiveOneLiners: true 145 | 146 | RSpec/EmptyLineAfterSubject: 147 | Enabled: true 148 | Exclude: 149 | - '**/factories/*.rb' 150 | 151 | RSpec/ExampleLength: 152 | Enabled: true 153 | CountAsOne: 154 | - 'array' 155 | - 'hash' 156 | - 'heredoc' 157 | Max: 25 158 | 159 | RSpec/ExampleWithoutDescription: 160 | Enabled: true 161 | EnforcedStyle: 'single_line_only' 162 | 163 | RSpec/ExampleWording: 164 | Enabled: true 165 | IgnoredWords: [] 166 | 167 | RSpec/ExcessiveDocstringSpacing: 168 | Enabled: true 169 | 170 | RSpec/ExpectActual: 171 | Enabled: true 172 | Exclude: 173 | - 'spec/routing/**/*' 174 | 175 | RSpec/ExpectChange: 176 | Enabled: true 177 | EnforcedStyle: 'method_call' 178 | 179 | RSpec/ExpectInHook: 180 | Enabled: true 181 | 182 | RSpec/ExpectOutput: 183 | Enabled: true 184 | 185 | RSpec/FactoryBot/AttributeDefinedStatically: 186 | Enabled: true 187 | 188 | RSpec/FactoryBot/ConsistentParenthesesStyle: 189 | Enabled: true 190 | 191 | RSpec/FactoryBot/CreateList: 192 | Enabled: true 193 | 194 | RSpec/FactoryBot/FactoryClassName: 195 | Enabled: true 196 | 197 | RSpec/FactoryBot/FactoryNameStyle: 198 | Enabled: true 199 | 200 | RSpec/FactoryBot/SyntaxMethods: 201 | Enabled: true 202 | 203 | RSpec/FilePath: 204 | Enabled: true 205 | IgnoreMethods: false 206 | SpecSuffixOnly: true 207 | CustomTransform: 208 | RuboCop: 'rubocop' 209 | RSpec: 'rspec' 210 | 211 | RSpec/Focus: 212 | Enabled: true 213 | 214 | RSpec/HookArgument: 215 | Enabled: true 216 | EnforcedStyle: 'each' 217 | 218 | RSpec/HooksBeforeExamples: 219 | Enabled: true 220 | 221 | RSpec/IdenticalEqualityAssertion: 222 | Enabled: true 223 | 224 | RSpec/ImplicitBlockExpectation: 225 | Enabled: true 226 | 227 | RSpec/ImplicitExpect: 228 | Enabled: true 229 | EnforcedStyle: 'is_expected' 230 | 231 | RSpec/ImplicitSubject: 232 | Enabled: true 233 | EnforcedStyle: 'single_statement_only' 234 | 235 | RSpec/InstanceSpy: 236 | Enabled: true 237 | 238 | RSpec/InstanceVariable: 239 | Enabled: true 240 | 241 | RSpec/ItBehavesLike: 242 | Enabled: true 243 | EnforcedStyle: 'it_behaves_like' 244 | 245 | RSpec/IteratedExpectation: 246 | Enabled: true 247 | 248 | RSpec/LeadingSubject: 249 | Enabled: true 250 | 251 | RSpec/LeakyConstantDeclaration: 252 | Enabled: true 253 | 254 | RSpec/LetBeforeExamples: 255 | Enabled: true 256 | 257 | RSpec/LetSetup: 258 | Enabled: true 259 | 260 | RSpec/MessageChain: 261 | Enabled: true 262 | 263 | RSpec/MessageExpectation: 264 | Enabled: true 265 | EnforcedStyle: 'allow' 266 | 267 | RSpec/MessageSpies: 268 | Enabled: true 269 | EnforcedStyle: 'have_received' 270 | 271 | RSpec/MissingExampleGroupArgument: 272 | Enabled: true 273 | 274 | RSpec/MultipleDescribes: 275 | Enabled: true 276 | 277 | RSpec/MultipleExpectations: 278 | Enabled: false 279 | Max: 5 280 | 281 | RSpec/MultipleMemoizedHelpers: 282 | Enabled: true 283 | AllowSubject: false 284 | Max: 2 285 | 286 | RSpec/MultipleSubjects: 287 | Enabled: true 288 | 289 | RSpec/NamedSubject: 290 | Enabled: true 291 | IgnoreSharedExamples: true 292 | EnforcedStyle: "always" 293 | 294 | RSpec/NestedGroups: 295 | Enabled: true 296 | Max: 2 297 | AllowedGroups: [] 298 | 299 | RSpec/NoExpectationExample: 300 | Enabled: true 301 | AllowedPatterns: [] 302 | 303 | RSpec/NotToNot: 304 | Enabled: true 305 | EnforcedStyle: 'not_to' 306 | 307 | RSpec/OverwritingSetup: 308 | Enabled: true 309 | 310 | RSpec/Pending: 311 | Enabled: false 312 | 313 | RSpec/PendingWithoutReason: 314 | Enabled: true 315 | 316 | RSpec/PredicateMatcher: 317 | Enabled: true 318 | Strict: true 319 | EnforcedStyle: 'inflected' 320 | AllowedExplicitMatchers: [] 321 | 322 | RSpec/Rails/AvoidSetupHook: 323 | Enabled: true 324 | 325 | RSpec/Rails/HaveHttpStatus: 326 | Enabled: true 327 | 328 | RSpec/Rails/HttpStatus: 329 | Enabled: true 330 | EnforcedStyle: 'numeric' 331 | 332 | RSpec/Rails/InferredSpecType: 333 | Enabled: true 334 | 335 | RSpec/Rails/MinitestAssertions: 336 | Enabled: true 337 | 338 | RSpec/ReceiveCounts: 339 | Enabled: true 340 | 341 | RSpec/ReceiveNever: 342 | Enabled: true 343 | 344 | RSpec/RepeatedDescription: 345 | Enabled: true 346 | 347 | RSpec/RepeatedExample: 348 | Enabled: true 349 | 350 | RSpec/RepeatedExampleGroupBody: 351 | Enabled: true 352 | 353 | RSpec/RepeatedExampleGroupDescription: 354 | Enabled: true 355 | 356 | RSpec/RepeatedIncludeExample: 357 | Enabled: true 358 | 359 | RSpec/ReturnFromStub: 360 | Enabled: true 361 | EnforcedStyle: 'and_return' 362 | 363 | RSpec/ScatteredLet: 364 | Enabled: true 365 | 366 | RSpec/ScatteredSetup: 367 | Enabled: true 368 | 369 | RSpec/SharedContext: 370 | Enabled: true 371 | 372 | RSpec/SharedExamples: 373 | Enabled: true 374 | 375 | RSpec/SingleArgumentMessageChain: 376 | Enabled: true 377 | 378 | RSpec/SortMetadata: 379 | Enabled: true 380 | 381 | RSpec/StubbedMock: 382 | Enabled: true 383 | 384 | RSpec/SubjectDeclaration: 385 | Enabled: true 386 | 387 | RSpec/SubjectStub: 388 | Enabled: true 389 | 390 | RSpec/UnspecifiedException: 391 | Enabled: true 392 | 393 | RSpec/VariableDefinition: 394 | Enabled: true 395 | EnforcedStyle: 'symbols' 396 | 397 | RSpec/VariableName: 398 | Enabled: true 399 | EnforcedStyle: 'snake_case' 400 | AllowedPatterns: [] 401 | 402 | RSpec/VerifiedDoubleReference: 403 | Enabled: true 404 | EnforcedStyle: 'string' 405 | 406 | RSpec/VerifiedDoubles: 407 | Enabled: true 408 | IgnoreSymbolicNames: false 409 | IgnoreNameless: false 410 | 411 | RSpec/VoidExpect: 412 | Enabled: true 413 | 414 | RSpec/Yield: 415 | Enabled: true 416 | -------------------------------------------------------------------------------- /.overcommit.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | # This is an opinionated list of which hooks are valuable to run and what their 4 | # out-of-the-box settings should be. 5 | # ------------------------------------------------------------------------------- 6 | 7 | # Loads Bundler context from a Gemfile. If false, does nothing (default). 8 | # 9 | # Specifying a Gemfile for Bundler to load allows you to control which gems are 10 | # available in the load path (i.e. loadable via `require`) within your hook 11 | # runs. Note that having a Gemfile requires you to include `overcommit` itself 12 | # in your Gemfile (otherwise Overcommit can't load itself!). 13 | # 14 | # This is useful if you want to: 15 | # 16 | # - Enforce a specific version of Overcommit to use for all hook runs 17 | # (or to use a version from the master branch that has not been released yet) 18 | # - Enforce a specific version or unreleased branch is used for a gem you want 19 | # to use in your git hooks 20 | # 21 | # WARNING: This makes your hook runs slower, but you can work around this! 22 | # 23 | # Loading a Bundler context necessarily adds a startup delay to your hook runs 24 | # as Bundler parses the Gemfile and checks that the dependencies are satisfied. 25 | # Thus for projects with many gems this can introduce a noticeable delay. 26 | # 27 | # The recommended workaround is to create a separate Gemfile in the root of your 28 | # repository (call it `.overcommit_gems.rb`), and include only the gems that 29 | # your Overcommit hooks need in order to run. This significantly reduces the 30 | # startup delay in your hook runs. Make sure to commit both 31 | # `.overcommit_gems.rb` and the resulting `.overcommit_gems.rb.lock` file to 32 | # your repository, and then set the `gemfile` option below to the name you gave 33 | # the file. 34 | # (Generate lock file by running `bundle install --gemfile=.overcommit_gems.rb`) 35 | gemfile: false 36 | 37 | # Where to store hook plugins specific to a repository. These are loaded in 38 | # addition to the default hooks Overcommit comes with. The location is relative 39 | # to the root of the repository. 40 | plugin_directory: '.git-hooks' 41 | 42 | # Whether to hide hook output by default. This results in completely silent hook 43 | # runs except in the case of warning or failure. 44 | quiet: false 45 | 46 | # Number of hooks that can be run concurrently. Typically this won't need to be 47 | # adjusted, but if you know that some of your hooks themselves use multiple 48 | # processors you can lower this value accordingly. You can define 49 | # single-operator mathematical expressions, e.g. '%{processors} * 2', or 50 | # '%{processors} / 2'. 51 | concurrency: '%{processors}' 52 | 53 | # Whether to check if a hook plugin has changed since Overcommit last ran it. 54 | # This is a defense mechanism when working with repositories which can contain 55 | # untrusted code (e.g. when you fetch a pull request from a third party). 56 | # See https://github.com/brigade/overcommit#security for more information. 57 | verify_signatures: true 58 | 59 | PreCommit: 60 | ALL: 61 | problem_on_unmodified_line: 'report' 62 | requires_files: true 63 | required: false 64 | exclude: 65 | - 'bower_components/**/*' 66 | - 'node_modules/**/*' 67 | - 'vendor/**/*' 68 | 69 | AuthorEmail: 70 | enabled: true 71 | 72 | AuthorName: 73 | enabled: true 74 | 75 | BerksfileCheck: 76 | enabled: true 77 | 78 | Brakeman: 79 | enabled: false 80 | 81 | BrokenSymlinks: 82 | enabled: true 83 | 84 | BundleAudit: 85 | enabled: true 86 | include: 87 | - 'Gemfile' 88 | - 'Gemfile.lock' 89 | - '*.gemspec' 90 | 91 | BundleCheck: 92 | enabled: true 93 | include: 94 | - 'Gemfile' 95 | - 'Gemfile.lock' 96 | - '*.gemspec' 97 | 98 | BundleOutdated: 99 | enabled: true 100 | include: 101 | - 'Gemfile' 102 | - 'Gemfile.lock' 103 | - '*.gemspec' 104 | 105 | CaseConflicts: 106 | enabled: true 107 | 108 | ChamberSecurity: 109 | enabled: true 110 | 111 | CoffeeLint: 112 | enabled: true 113 | 114 | CookStyle: 115 | enabled: false 116 | 117 | Credo: 118 | enabled: true 119 | include: 120 | - '**/*.ex' 121 | - '**/*.exs' 122 | 123 | CssLint: 124 | enabled: false 125 | 126 | Dogma: 127 | enabled: true 128 | 129 | EsLint: 130 | enabled: true 131 | 132 | ExecutePermissions: 133 | enabled: true 134 | exclude: 135 | - 'Rakefile' 136 | - 'bin/**/*' 137 | - 'scripts/**/*' 138 | 139 | Fasterer: 140 | enabled: false 141 | include: 142 | - '**/*.rb' 143 | 144 | FileSize: 145 | enabled: true 146 | size_limit_bytes: 1_000_000 147 | exclude: 148 | - 'db/structure.sql' 149 | - 'db/schema.rb' 150 | 151 | FixMe: 152 | enabled: true 153 | exclude: 154 | - '.overcommit.yml' 155 | - '.rubocop_core.yml' 156 | - 'bin/deploy' 157 | keywords: 158 | - 'BROKEN' 159 | - 'BUG' 160 | - 'ERROR' 161 | - 'FIXME' 162 | - 'HACK' 163 | - 'NOTE' 164 | - 'OPTIMIZE' 165 | - 'REVIEW' 166 | - 'TODO' 167 | - 'WTF' 168 | - 'XXX' 169 | 170 | Flay: 171 | enabled: false 172 | 173 | Foodcritic: 174 | enabled: false 175 | 176 | ForbiddenBranches: 177 | enabled: false 178 | 179 | GoLint: 180 | enabled: true 181 | 182 | GoVet: 183 | enabled: true 184 | 185 | Hadolint: 186 | enabled: false 187 | 188 | HamlLint: 189 | enabled: false 190 | 191 | HardTabs: 192 | enabled: true 193 | exclude: 194 | - '**/*.svg' 195 | - '**/Info.plist' 196 | - '**/project.pbxproj' 197 | - '**/structure.sql' 198 | - '**/xcschememanagement.plist' 199 | 200 | Hlint: 201 | enabled: true 202 | 203 | HtmlHint: 204 | enabled: true 205 | 206 | HtmlTidy: 207 | enabled: false 208 | 209 | ImageOptim: 210 | enabled: false 211 | 212 | JavaCheckstyle: 213 | enabled: true 214 | 215 | Jscs: 216 | enabled: false 217 | 218 | JsHint: 219 | enabled: false 220 | 221 | JsLint: 222 | enabled: false 223 | 224 | Jsl: 225 | enabled: false 226 | 227 | JsonSyntax: 228 | enabled: true 229 | 230 | KtLint: 231 | enabled: false 232 | 233 | LicenseFinder: 234 | enabled: false 235 | 236 | LicenseHeader: 237 | enabled: false 238 | licence_file: 'LICENSE.txt' 239 | 240 | LineEndings: 241 | enabled: true 242 | 243 | LocalPathsInGemfile: 244 | enabled: true 245 | 246 | Mdl: 247 | enabled: false 248 | include: 249 | - '**/*.md' 250 | 251 | MergeConflicts: 252 | enabled: true 253 | 254 | NginxTest: 255 | enabled: true 256 | 257 | Pep257: 258 | enabled: false 259 | 260 | Pep8: 261 | enabled: false 262 | 263 | PhpCs: 264 | enabled: true 265 | 266 | PhpLint: 267 | enabled: false 268 | 269 | PhpStan: 270 | enabled: true 271 | 272 | Pronto: 273 | enabled: false 274 | 275 | PuppetLint: 276 | enabled: true 277 | 278 | PuppetMetadataJsonLint: 279 | enabled: true 280 | 281 | Pyflakes: 282 | enabled: false 283 | 284 | Pylint: 285 | enabled: false 286 | 287 | PythonFlake8: 288 | enabled: false 289 | 290 | RailsBestPractices: 291 | enabled: false 292 | 293 | RailsSchemaUpToDate: 294 | enabled: true 295 | include: 296 | - '**/db/migrate/*.rb' 297 | - '**/db/schema.rb' 298 | - '**/db/structure.sql' 299 | 300 | RakeTarget: 301 | enabled: false 302 | 303 | Reek: 304 | enabled: false 305 | 306 | RstLint: 307 | enabled: false 308 | 309 | RuboCop: 310 | enabled: true 311 | 312 | RubyLint: 313 | enabled: false 314 | 315 | Scalariform: 316 | enabled: false 317 | 318 | Scalastyle: 319 | enabled: false 320 | 321 | ScssLint: 322 | enabled: false 323 | 324 | SemiStandard: 325 | enabled: false 326 | 327 | ShellCheck: 328 | enabled: true 329 | flags: 330 | - '--format=gcc' 331 | - '-e SC1090' 332 | - '-e SC1117' 333 | - '-e SC2148' 334 | - '-e SC2155' 335 | - '-e SC2181' 336 | include: 337 | - '**/*.sh' 338 | - '**/*.bash' 339 | - '**/*.zsh' 340 | - '**/bash_profile' 341 | - '**/bashrc' 342 | - '**/zshrc' 343 | 344 | SlimLint: 345 | enabled: false 346 | 347 | Sqlint: 348 | enabled: true 349 | 350 | Standard: 351 | enabled: false 352 | 353 | Stylelint: 354 | enabled: true 355 | 356 | SwiftLint: 357 | enabled: true 358 | 359 | TerraformFormat: 360 | enabled: true 361 | 362 | TrailingWhitespace: 363 | enabled: true 364 | exclude: '**/structure.sql' 365 | 366 | TravisLint: 367 | enabled: true 368 | 369 | TsLint: 370 | enabled: true 371 | include: 372 | - '**/*.ts' 373 | 374 | Vint: 375 | enabled: true 376 | 377 | W3cCss: 378 | enabled: false 379 | 380 | W3cHtml: 381 | enabled: false 382 | 383 | XmlLint: 384 | enabled: true 385 | 386 | XmlSyntax: 387 | enabled: true 388 | 389 | YamlLint: 390 | enabled: true 391 | include: 392 | - '**/*.yaml' 393 | - '**/*.yml' 394 | on_warn: 'fail' 395 | 396 | YamlSyntax: 397 | enabled: true 398 | 399 | YardCoverage: 400 | enabled: false 401 | 402 | YarnCheck: 403 | enabled: true 404 | 405 | PrePush: 406 | ALL: 407 | requires_files: false 408 | required: false 409 | 410 | CargoTest: 411 | enabled: false 412 | 413 | GitLfs: 414 | enabled: true 415 | flags: 416 | - 'pre-push' 417 | 418 | Minitest: 419 | enabled: false 420 | 421 | ProtectedBranches: 422 | enabled: false 423 | 424 | RSpec: 425 | enabled: false 426 | 427 | PreRebase: 428 | ALL: 429 | requires_files: false 430 | required: false 431 | 432 | MergedCommits: 433 | enabled: false 434 | 435 | CommitMsg: 436 | ALL: 437 | requires_files: false 438 | 439 | CapitalizedSubject: 440 | enabled: true 441 | 442 | Commitplease: 443 | enabled: false 444 | 445 | EmptyMessage: 446 | enabled: false 447 | 448 | GerritChangeId: 449 | enabled: false 450 | 451 | HardTabs: 452 | enabled: true 453 | 454 | MessageFormat: 455 | enabled: true 456 | pattern: '(Add|Fix|Change|Remove|Deprecate|Security|Docs|Style|Tests|Chore|WIP):\s' 457 | expected_pattern_message: ': ' 458 | sample_message: 'Chore: Refactor onboarding flow' 459 | 460 | RussianNovel: 461 | enabled: false 462 | 463 | SingleLineSubject: 464 | enabled: true 465 | 466 | SpellCheck: 467 | enabled: false 468 | 469 | TextWidth: 470 | enabled: true 471 | max_subject_width: 72 472 | max_body_width: 80 473 | 474 | TrailingPeriod: 475 | enabled: true 476 | 477 | PostCheckout: 478 | ALL: 479 | required: false 480 | skip_file_checkout: true 481 | 482 | BowerInstall: 483 | enabled: false 484 | 485 | BundleCheck: 486 | enabled: true 487 | 488 | GitLfs: 489 | enabled: false 490 | flags: 491 | - 'post-checkout' 492 | 493 | IndexTags: 494 | enabled: false 495 | 496 | NpmInstall: 497 | enabled: false 498 | 499 | SubmoduleStatus: 500 | enabled: true 501 | 502 | PostCommit: 503 | ALL: 504 | requires_files: false 505 | required: false 506 | 507 | BowerInstall: 508 | enabled: false 509 | 510 | BundleInstall: 511 | enabled: false 512 | 513 | GitGuilt: 514 | enabled: false 515 | 516 | GitLfs: 517 | enabled: false 518 | flags: 519 | - 'post-commit' 520 | 521 | IndexTags: 522 | enabled: false 523 | 524 | NpmInstall: 525 | enabled: false 526 | 527 | SubmoduleStatus: 528 | enabled: true 529 | 530 | YarnInstall: 531 | enabled: false 532 | 533 | PostMerge: 534 | ALL: 535 | enabled: false 536 | requires_files: false 537 | 538 | BowerInstall: 539 | enabled: false 540 | 541 | BundleInstall: 542 | enabled: false 543 | 544 | GitLfs: 545 | enabled: false 546 | flags: 547 | - 'post-merge' 548 | 549 | IndexTags: 550 | enabled: false 551 | 552 | NpmInstall: 553 | enabled: false 554 | 555 | SubmoduleStatus: 556 | enabled: false 557 | 558 | YarnInstall: 559 | enabled: false 560 | 561 | PostRewrite: 562 | ALL: 563 | requires_files: false 564 | 565 | BowerInstall: 566 | enabled: false 567 | 568 | BundleInstall: 569 | enabled: false 570 | 571 | IndexTags: 572 | enabled: false 573 | 574 | NpmInstall: 575 | enabled: false 576 | 577 | SubmoduleStatus: 578 | enabled: false 579 | 580 | YarnInstall: 581 | enabled: false 582 | -------------------------------------------------------------------------------- /spec/lib/ruby-progressbar/components/time_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'ruby-progressbar/components/time' 3 | 4 | class ProgressBar 5 | module Components 6 | describe Time do 7 | describe '#elapsed_with_label' do 8 | it 'displays unknown elapsed time when the timer has not been started' do 9 | timer = Timer.new 10 | projector = Projectors::SmoothedAverage.new 11 | progress = Progress.new 12 | time = Time.new(:timer => timer, 13 | :progress => progress, 14 | :projector => projector) 15 | 16 | expect(time.elapsed_with_label).to eql 'Time: --:--:--' 17 | end 18 | 19 | it 'displays elapsed time when the timer has just been started' do 20 | timer = Timer.new 21 | projector = Projectors::SmoothedAverage.new 22 | progress = Progress.new 23 | time = Time.new(:timer => timer, 24 | :progress => progress, 25 | :projector => projector) 26 | 27 | timer.start 28 | projector.start 29 | 30 | expect(time.elapsed_with_label).to eql 'Time: 00:00:00' 31 | end 32 | 33 | it 'displays elapsed time if it was previously started' do 34 | timer = Timer.new 35 | projector = Projectors::SmoothedAverage.new 36 | progress = Progress.new 37 | time = Time.new(:timer => timer, 38 | :progress => progress, 39 | :projector => projector) 40 | 41 | Timecop.freeze(-16_093) 42 | 43 | timer.start 44 | projector.start 45 | 46 | Timecop.return 47 | 48 | expect(time.elapsed_with_label).to eql 'Time: 04:28:13' 49 | end 50 | 51 | it 'displays elapsed time frozen to a specific time if it was previously stopped' do 52 | timer = Timer.new 53 | projector = Projectors::SmoothedAverage.new 54 | progress = Progress.new 55 | time = Time.new(:timer => timer, 56 | :progress => progress, 57 | :projector => projector) 58 | 59 | Timecop.freeze(-16_093) 60 | 61 | timer.start 62 | projector.start 63 | 64 | Timecop.return 65 | Timecop.freeze(-32) 66 | 67 | timer.stop 68 | 69 | Timecop.return 70 | 71 | expect(time.elapsed_with_label).to eql 'Time: 04:27:41' 72 | end 73 | 74 | it 'displays unknown elapsed time after reset has been called' do 75 | timer = Timer.new 76 | projector = Projectors::SmoothedAverage.new 77 | progress = Progress.new 78 | time = Time.new(:timer => timer, 79 | :progress => progress, 80 | :projector => projector) 81 | 82 | Timecop.freeze(-16_093) 83 | 84 | timer.start 85 | projector.start 86 | 87 | Timecop.return 88 | 89 | timer.reset 90 | 91 | expect(time.elapsed_with_label).to eql 'Time: --:--:--' 92 | end 93 | end 94 | 95 | describe '#estimated_with_unknown_oob' do 96 | it 'displays estimated time if it is known' do 97 | timer = Timer.new 98 | projector = Projectors::SmoothedAverage.new(:strength => 0.0) 99 | progress = Progress.new(:total => 100) 100 | time = Time.new(:timer => timer, 101 | :progress => progress, 102 | :projector => projector) 103 | 104 | Timecop.freeze(-13_332) 105 | 106 | timer.start 107 | projector.start 108 | 50.times do 109 | progress.increment 110 | projector.increment 111 | end 112 | 113 | Timecop.return 114 | 115 | expect(time.estimated_with_unknown_oob).to eql ' ETA: 03:42:12' 116 | end 117 | 118 | it 'displays an unknown estimated time remaining when the timer has been started ' \ 119 | 'but no progress has been made' do 120 | timer = Timer.new 121 | projector = Projectors::SmoothedAverage.new 122 | progress = Progress.new(:total => 100) 123 | time = Time.new(:timer => timer, 124 | :progress => progress, 125 | :projector => projector) 126 | 127 | timer.start 128 | projector.start 129 | 130 | expect(time.estimated_with_unknown_oob).to eql ' ETA: ??:??:??' 131 | end 132 | 133 | it 'displays unknown time remaining when progress has been made and then progress ' \ 134 | 'is reset' do 135 | timer = Timer.new 136 | projector = Projectors::SmoothedAverage.new 137 | progress = Progress.new(:total => 100) 138 | time = Time.new(:timer => timer, 139 | :progress => progress, 140 | :projector => projector) 141 | 142 | Timecop.freeze(-13_332) 143 | 144 | timer.start 145 | projector.start 146 | 50.times do 147 | progress.increment 148 | projector.increment 149 | end 150 | 151 | Timecop.return 152 | 153 | progress.reset 154 | 155 | expect(time.estimated_with_unknown_oob).to eql ' ETA: ??:??:??' 156 | end 157 | 158 | it 'displays unknown time remaining when progress has been made and then rate ' \ 159 | 'is reset' do 160 | timer = Timer.new 161 | projector = Projectors::SmoothedAverage.new 162 | progress = Progress.new(:total => 100) 163 | time = Time.new(:timer => timer, 164 | :progress => progress, 165 | :projector => projector) 166 | 167 | Timecop.freeze(-13_332) 168 | 169 | timer.start 170 | projector.start 171 | 50.times do 172 | progress.increment 173 | projector.increment 174 | end 175 | 176 | Timecop.return 177 | 178 | projector.reset 179 | 180 | expect(time.estimated_with_unknown_oob).to eql ' ETA: ??:??:??' 181 | end 182 | 183 | it 'displays estimated time of "??:??:??" when estimated time is out of bounds ' \ 184 | 'and the out of bounds format is set to "unknown"' do 185 | timer = Timer.new 186 | projector = Projectors::SmoothedAverage.new(:strength => 0.0) 187 | progress = Progress.new(:total => 100) 188 | time = Time.new(:out_of_bounds_time_format => :unknown, 189 | :timer => timer, 190 | :progress => progress, 191 | :projector => projector) 192 | 193 | Timecop.freeze(-120_000) 194 | 195 | timer.start 196 | projector.start 197 | 25.times do 198 | progress.increment 199 | projector.increment 200 | end 201 | 202 | Timecop.return 203 | 204 | expect(time.estimated_with_unknown_oob).to eql ' ETA: ??:??:??' 205 | end 206 | 207 | it 'displays smoothed unknown estimated time when reset is called after progress ' \ 208 | 'is made' do 209 | timer = Timer.new 210 | projector = Projectors::SmoothedAverage.new(:strength => 0.5) 211 | progress = Progress.new(:total => 100) 212 | time = Time.new(:timer => timer, 213 | :progress => progress, 214 | :projector => projector) 215 | 216 | Timecop.freeze(-13_332) 217 | 218 | timer.start 219 | projector.start 220 | 50.times do 221 | progress.increment 222 | projector.increment 223 | end 224 | 225 | Timecop.return 226 | 227 | progress.reset 228 | 229 | expect(time.estimated_with_unknown_oob).to eql ' ETA: ??:??:??' 230 | end 231 | end 232 | 233 | describe '#estimated_with_friendly_oob' do 234 | it 'displays estimated time if it is known' do 235 | timer = Timer.new 236 | projector = Projectors::SmoothedAverage.new(:strength => 0.0) 237 | progress = Progress.new(:total => 100) 238 | time = Time.new(:timer => timer, 239 | :progress => progress, 240 | :projector => projector) 241 | 242 | Timecop.freeze(-13_332) 243 | 244 | timer.start 245 | projector.start 246 | 50.times do 247 | progress.increment 248 | projector.increment 249 | end 250 | 251 | Timecop.return 252 | 253 | expect(time.estimated_with_friendly_oob).to eql ' ETA: 03:42:12' 254 | end 255 | 256 | it 'displays estimated time of "> 4 Days" when estimated time is out of bounds ' \ 257 | 'and the out of bounds format is set to "friendly"' do 258 | timer = Timer.new 259 | projector = Projectors::SmoothedAverage.new(:strength => 0.0) 260 | progress = Progress.new(:total => 100) 261 | time = Time.new(:out_of_bounds_time_format => :friendly, 262 | :timer => timer, 263 | :progress => progress, 264 | :projector => projector) 265 | 266 | Timecop.freeze(-120_000) 267 | 268 | timer.start 269 | projector.start 270 | 25.times do 271 | progress.increment 272 | projector.increment 273 | end 274 | 275 | Timecop.return 276 | 277 | expect(time.estimated_with_friendly_oob).to eql ' ETA: > 4 Days' 278 | end 279 | end 280 | 281 | describe '#estimated_with_no_oob' do 282 | it 'displays estimated time if it is known' do 283 | timer = Timer.new 284 | projector = Projectors::SmoothedAverage.new(:strength => 0.0) 285 | progress = Progress.new(:total => 100) 286 | time = Time.new(:timer => timer, 287 | :progress => progress, 288 | :projector => projector) 289 | 290 | Timecop.freeze(-13_332) 291 | 292 | timer.start 293 | projector.start 294 | 50.times do 295 | progress.increment 296 | projector.increment 297 | end 298 | 299 | Timecop.return 300 | 301 | expect(time.estimated_with_no_oob).to eql ' ETA: 03:42:12' 302 | end 303 | 304 | it 'displays actual estimated time when estimated time is out of bounds and the ' \ 305 | 'out of bounds format is unset' do 306 | timer = Timer.new 307 | projector = Projectors::SmoothedAverage.new(:strength => 0.0) 308 | progress = Progress.new(:total => 100) 309 | time = Time.new(:out_of_bounds_time_format => nil, 310 | :timer => timer, 311 | :progress => progress, 312 | :projector => projector) 313 | 314 | Timecop.freeze(-120_000) 315 | 316 | timer.start 317 | projector.start 318 | 25.times do 319 | progress.increment 320 | projector.increment 321 | end 322 | 323 | Timecop.return 324 | 325 | expect(time.estimated_with_no_oob).to eql ' ETA: 100:00:00' 326 | end 327 | end 328 | 329 | describe '#estimated_with_label' do 330 | it 'does not display unknown time remaining when the timer has been started and ' \ 331 | 'it is incremented' do 332 | timer = Timer.new 333 | projector = Projectors::SmoothedAverage.new 334 | progress = Progress.new(:total => 100) 335 | time = Time.new(:timer => timer, 336 | :progress => progress, 337 | :projector => projector) 338 | 339 | timer.start 340 | projector.start 341 | progress.increment 342 | projector.increment 343 | 344 | expect(time.estimated_with_label).to eql ' ETA: 00:00:00' 345 | end 346 | 347 | it 'displays unsmoothed time remaining when progress has been made' do 348 | timer = Timer.new 349 | projector = Projectors::SmoothedAverage.new(:strength => 0.0) 350 | progress = Progress.new(:total => 100) 351 | time = Time.new(:timer => timer, 352 | :progress => progress, 353 | :projector => projector) 354 | 355 | Timecop.freeze(-13_332) 356 | 357 | timer.start 358 | projector.start 359 | 50.times do 360 | progress.increment 361 | projector.increment 362 | end 363 | 364 | Timecop.return 365 | 366 | expect(time.estimated_with_label).to eql ' ETA: 03:42:12' 367 | end 368 | 369 | it 'displays unsmoothed time remaining when progress has been made even after the ' \ 370 | 'bar is decremented' do 371 | timer = Timer.new 372 | projector = Projectors::SmoothedAverage.new(:strength => 0.0) 373 | progress = Progress.new(:total => 100) 374 | time = Time.new(:timer => timer, 375 | :progress => progress, 376 | :projector => projector) 377 | 378 | Timecop.freeze(-13_332) 379 | 380 | timer.start 381 | projector.start 382 | 50.times do 383 | progress.increment 384 | projector.increment 385 | end 386 | 387 | Timecop.return 388 | 389 | 20.times do 390 | progress.decrement 391 | projector.decrement 392 | end 393 | 394 | expect(time.estimated_with_label).to eql ' ETA: 08:38:28' 395 | end 396 | 397 | it 'displays smoothed estimated time properly even when taking decrements into ' \ 398 | 'account' do 399 | timer = Timer.new 400 | projector = Projectors::SmoothedAverage.new(:strength => 0.5) 401 | progress = Progress.new(:total => 100) 402 | time = Time.new(:timer => timer, 403 | :progress => progress, 404 | :projector => projector) 405 | 406 | Timecop.freeze(-13_332) 407 | 408 | timer.start 409 | projector.start 410 | 50.times do 411 | progress.increment 412 | projector.increment 413 | end 414 | 415 | Timecop.return 416 | 417 | 20.times do 418 | progress.decrement 419 | projector.decrement 420 | end 421 | 422 | expect(time.estimated_with_label).to eql ' ETA: 08:14:34' 423 | end 424 | 425 | it 'displays smoothed estimated time after progress has been made' do 426 | timer = Timer.new 427 | projector = Projectors::SmoothedAverage.new(:strength => 0.5) 428 | progress = Progress.new(:total => 100) 429 | time = Time.new(:timer => timer, 430 | :progress => progress, 431 | :projector => projector) 432 | 433 | Timecop.freeze(-13_332) 434 | 435 | timer.start 436 | projector.start 437 | 50.times do 438 | progress.increment 439 | projector.increment 440 | end 441 | 442 | Timecop.return 443 | 444 | expect(time.estimated_with_label).to eql ' ETA: 03:51:16' 445 | end 446 | 447 | it 'displays the estimated time remaining properly even for progress increments ' \ 448 | 'very short intervals' do 449 | timer = Timer.new 450 | projector = Projectors::SmoothedAverage.new(:strength => 0.1) 451 | progress = Progress.new(:total => 10) 452 | time = Time.new(:timer => timer, 453 | :progress => progress, 454 | :projector => projector) 455 | 456 | estimated_time_results = [] 457 | now = ::Time.now 458 | 459 | Timecop.freeze(now) 460 | 461 | timer.start 462 | projector.start 463 | 464 | 10.times do 465 | Timecop.freeze(now += 0.5) 466 | progress.increment 467 | projector.increment 468 | 469 | estimated_time_results << time.estimated_with_label 470 | end 471 | 472 | Timecop.return 473 | 474 | expect(estimated_time_results).to \ 475 | eql( 476 | [ 477 | ' ETA: 00:00:05', 478 | ' ETA: 00:00:04', 479 | ' ETA: 00:00:04', 480 | ' ETA: 00:00:03', 481 | ' ETA: 00:00:03', 482 | ' ETA: 00:00:02', 483 | ' ETA: 00:00:02', 484 | ' ETA: 00:00:01', 485 | ' ETA: 00:00:01', 486 | ' ETA: 00:00:00' 487 | ] 488 | ) 489 | end 490 | end 491 | 492 | describe '#estimated_wall_clock' do 493 | it 'displays the wall clock time as unknown when the timer has been reset' do 494 | timer = Timer.new 495 | projector = Projectors::SmoothedAverage.new 496 | progress = Progress.new 497 | time = Time.new(:timer => timer, 498 | :progress => progress, 499 | :projector => projector) 500 | 501 | Timecop.freeze(-16_093) 502 | 503 | timer.start 504 | projector.start 505 | 506 | Timecop.return 507 | 508 | timer.reset 509 | 510 | expect(time.estimated_wall_clock).to eql '--:--:--' 511 | end 512 | 513 | it 'displays the wall clock time as unknown when the progress has not begun' do 514 | timer = Timer.new 515 | projector = Projectors::SmoothedAverage.new 516 | progress = Progress.new 517 | time = Time.new(:timer => timer, 518 | :progress => progress, 519 | :projector => projector) 520 | 521 | Timecop.freeze(-16_093) 522 | 523 | timer.start 524 | projector.start 525 | 526 | Timecop.return 527 | 528 | expect(time.estimated_wall_clock).to eql '--:--:--' 529 | end 530 | 531 | it 'displays the completed wall clock time if the progress is finished' do 532 | timer = Timer.new 533 | projector = Projectors::SmoothedAverage.new 534 | progress = Progress.new 535 | time = Time.new(:timer => timer, 536 | :progress => progress, 537 | :projector => projector) 538 | 539 | Timecop.freeze(::Time.utc(2020, 1, 1, 0, 0, 0)) 540 | 541 | timer.start 542 | projector.start 543 | 544 | Timecop.freeze(::Time.utc(2020, 1, 1, 0, 30, 0)) 545 | 546 | timer.stop 547 | progress.finish 548 | 549 | Timecop.return 550 | 551 | expect(time.estimated_wall_clock).to eql '00:30:00' 552 | end 553 | 554 | it 'displays the estimated wall clock time if the progress is ongoing' do 555 | timer = Timer.new 556 | projector = Projectors::SmoothedAverage.new(:strength => 0.0) 557 | progress = Progress.new 558 | time = Time.new(:timer => timer, 559 | :progress => progress, 560 | :projector => projector) 561 | 562 | Timecop.freeze(::Time.utc(2020, 1, 1, 0, 0, 0)) 563 | 564 | timer.start 565 | projector.start 566 | progress.progress = 50 567 | projector.progress = 50 568 | 569 | Timecop.freeze(::Time.utc(2020, 1, 1, 0, 15, 0)) 570 | 571 | wall_clock = time.estimated_wall_clock 572 | 573 | Timecop.return 574 | 575 | expect(wall_clock).to eql '00:30:00' 576 | end 577 | end 578 | end 579 | end 580 | end 581 | --------------------------------------------------------------------------------