ruby-pv 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (11) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +2 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +28 -0
  5. data/Gemfile +3 -0
  6. data/README.md +44 -0
  7. data/Rakefile +38 -0
  8. data/UNLICENSE +24 -0
  9. data/lib/pv.rb +146 -0
  10. data/ruby-pv.gemspec +20 -0
  11. metadata +108 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0e80904a80987fd2c763dfe3ec6a1b434562307a
4
+ data.tar.gz: 12d06c90fd7f31236f7e2052c019a706b0df78f3
5
+ SHA512:
6
+ metadata.gz: 450dd4d904a0221d4624b6491360b46935b3bc6951ab454b2b316e839b554e6cb4580c0e41e07707bd32347604ed07b0a9c3b358f62cf7a9da654b23b41f5eaf
7
+ data.tar.gz: c3b70140eb65d20d1034f7a27b0c92728d66ea26119499307faba04b60f296a19ceb831c0d33da7a899afed00679fea41337dc760c2234e8f2dd14e23d3a6b91
@@ -0,0 +1,2 @@
1
+ /Gemfile.lock
2
+ /pkg/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
@@ -0,0 +1,28 @@
1
+ # This is the configuration used to check the rubocop source code.
2
+
3
+ Metrics/AbcSize:
4
+ Enabled: false
5
+
6
+ # TODO: shorten long methods.
7
+ Metrics/MethodLength:
8
+ Max: 13
9
+
10
+ Style/BlockDelimiters:
11
+ EnforcedStyle: semantic
12
+
13
+ Style/Documentation:
14
+ Exclude:
15
+ - 'spec/**/*'
16
+ - 'lib/pv.rb' # TODO: add docs.
17
+
18
+ Style/Semicolon:
19
+ AllowAsExpressionSeparator: true
20
+
21
+ Style/SignalException:
22
+ Enabled: false
23
+
24
+ Style/SpaceInsideHashLiteralBraces:
25
+ EnforcedStyle: no_space
26
+
27
+ Style/TrailingComma:
28
+ EnforcedStyleForMultiline: consistent_comma
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
@@ -0,0 +1,44 @@
1
+ # ruby-pv
2
+
3
+ :construction: **WORK IN PROGRESS** :construction:
4
+
5
+ A handy progress monitor for long-running tasks. Like [`pv`](http://linux.die.net/man/1/pv), but for Ruby.
6
+
7
+
8
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ ```ruby
13
+ gem 'ruby-pv'
14
+ ```
15
+
16
+ And then execute:
17
+
18
+ bundle
19
+
20
+ Or install it yourself as:
21
+
22
+ gem install ruby-pv
23
+
24
+ ## Usage
25
+
26
+ TODO: Write usage instructions here
27
+
28
+ ### Printing to stdout while `pv`ing
29
+
30
+ It just works. Seamlessly. {insert Steve Jobs picture}
31
+
32
+ ## Development
33
+
34
+ After checking out the repo, run `bundle` to install dependencies. Then, run `rake spec` to run the tests.
35
+
36
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in the `.gemspec` file, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and pudmsh the `.gem` file to [rubygems.org](https://rubygems.org).
37
+
38
+ ## Contributing
39
+
40
+ Bug reports and pull requests are welcome on GitHub at https://github.com/epidemian/ruby-pv.
41
+
42
+
43
+ TODO: Mention [tqdm](https://github.com/tqdm/tqdm) as an inspiration
44
+
@@ -0,0 +1,38 @@
1
+ $LOAD_PATH << File.join(__dir__, 'lib')
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: :spec
9
+
10
+ task :example do
11
+ require 'pv'
12
+ 500.times.pv do |n|
13
+ puts 'Some output' if n % 123 == 0
14
+ sleep rand / 50
15
+ end
16
+ end
17
+
18
+ task :slow_example do
19
+ require 'pv'
20
+ 8.times.pv do |n|
21
+ puts 'Some output' if n == 3 || n == 7
22
+ sleep 0.5
23
+ end
24
+ end
25
+
26
+ task :unsized_example do
27
+ require 'pv'
28
+ 125.times.to_enum.pv do |n|
29
+ sleep n < 25 ? 0.1 : 0.025
30
+ end
31
+ end
32
+
33
+ task :unsized_slow_example do
34
+ require 'pv'
35
+ 50.times.to_enum.pv do
36
+ sleep 0.5
37
+ end
38
+ end
@@ -0,0 +1,24 @@
1
+ This is free and unencumbered software released into the public domain.
2
+
3
+ Anyone is free to copy, modify, publish, use, compile, sell, or
4
+ distribute this software, either in source code form or as a compiled
5
+ binary, for any purpose, commercial or non-commercial, and by any
6
+ means.
7
+
8
+ In jurisdictions that recognize copyright laws, the author or authors
9
+ of this software dedicate any and all copyright interest in the
10
+ software to the public domain. We make this dedication for the benefit
11
+ of the public at large and to the detriment of our heirs and
12
+ successors. We intend this dedication to be an overt act of
13
+ relinquishment in perpetuity of all present and future rights to this
14
+ software under copyright law.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
+ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20
+ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ For more information, please refer to <http://unlicense.org/>
@@ -0,0 +1,146 @@
1
+ require 'io/console'
2
+
3
+ class Pv
4
+ include Enumerable
5
+
6
+ def initialize(enum)
7
+ @enum = enum
8
+ end
9
+
10
+ def each
11
+ return to_enum unless block_given?
12
+ # TODO: don't display progress unless $stdout.tty?
13
+
14
+ self.progress = 0
15
+
16
+ hijack_stdout
17
+
18
+ display_progress
19
+
20
+ @enum.each do |item|
21
+ val = yield item
22
+ self.progress += 1
23
+ display_progress
24
+ val
25
+ end
26
+ ensure
27
+ # Run on ensure block to raised StopIterations don't mess up the display.
28
+ clear_progress
29
+ restore_stdout
30
+ end
31
+
32
+ private
33
+
34
+ attr_accessor :progress
35
+
36
+ def total
37
+ @enum.size
38
+ end
39
+
40
+ def unknown_total?
41
+ total.nil? || total == Float::INFINITY
42
+ end
43
+
44
+ def display_progress
45
+ l_bar = "Progress: #{formatted_numeric_progress} ▕"
46
+ r_bar = '▏'
47
+ bar_size = term_width - l_bar.size - r_bar.size
48
+ bar = draw_progress_bar(bar_size)
49
+ line = "#{l_bar}#{bar}#{r_bar}\r"
50
+ @original_stdout.print line
51
+ @progress_displayed = true
52
+ end
53
+
54
+ def clear_progress
55
+ return unless @progress_displayed
56
+ @original_stdout.print(' ' * term_width + "\r")
57
+ @progress_displayed = false
58
+ end
59
+
60
+ def term_width
61
+ IO.console.winsize[1]
62
+ end
63
+
64
+ def formatted_numeric_progress
65
+ if unknown_total?
66
+ total_text = 'unknown'
67
+ progress_text = progress.to_s
68
+ else
69
+ total_text = total.to_s
70
+ progress_text = progress.to_s.rjust(total_text.size)
71
+ end
72
+ "#{progress_text}/#{total_text}"
73
+ end
74
+
75
+ def draw_progress_bar(bar_size)
76
+ if unknown_total?
77
+ draw_unknown_progress_bar(bar_size)
78
+ else
79
+ draw_known_progress_bar(bar_size)
80
+ end
81
+ end
82
+
83
+ FRAC_CHARS = ' ▏▎▍▌▋▊▉'
84
+
85
+ def draw_known_progress_bar(bar_size)
86
+ completed_size, remainder = (progress.to_f / total * bar_size).divmod(1)
87
+
88
+ rem_char =
89
+ progress == total ? '' : FRAC_CHARS[(FRAC_CHARS.size * remainder).floor]
90
+
91
+ completed = '█' * completed_size
92
+ uncompleted = ' ' * (bar_size - completed_size - rem_char.size)
93
+
94
+ "#{completed}#{rem_char}#{uncompleted}"
95
+ end
96
+
97
+ UNKNOWN_PROGRESS_FRAMES = [
98
+ '▐ ▌ ',
99
+ '▖▘▗▝ ',
100
+ ' ▝▖ ▚',
101
+ ' ▗▘ ▞',
102
+ '▘▖▝▗ ',
103
+ ].map(&:chars)
104
+
105
+ def draw_unknown_progress_bar(bar_size)
106
+ frame = UNKNOWN_PROGRESS_FRAMES[progress % UNKNOWN_PROGRESS_FRAMES.size]
107
+ frame.cycle.take(bar_size).join
108
+ end
109
+
110
+ # Hijacks $stdout so user can still print stuff while showing the progressbar.
111
+ def hijack_stdout
112
+ @original_stdout = $stdout
113
+ $stdout = PvAwareStdout.new do |data|
114
+ clear_progress
115
+ @original_stdout.write(data)
116
+ display_progress if data.end_with?("\n")
117
+ end
118
+ end
119
+
120
+ def restore_stdout
121
+ $stdout = @original_stdout
122
+ end
123
+
124
+ # TODO: make this respond to everything STDOUT responds to.
125
+ class PvAwareStdout
126
+ def initialize(&writer)
127
+ @writer = writer
128
+ end
129
+
130
+ def write(data)
131
+ @writer.call(data)
132
+ end
133
+ end
134
+ end
135
+
136
+ Enumerable.module_eval do
137
+ # TODO: Add non-monkey-patching alternative (refinements maybe?)
138
+ def pv(&blk)
139
+ pv = Pv.new(self)
140
+ if blk
141
+ pv.each(&blk)
142
+ else
143
+ pv
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,20 @@
1
+ $LOAD_PATH << File.join(__dir__, 'lib')
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = 'ruby-pv'
5
+ spec.version = '0.0.1'
6
+ spec.license = 'Public Domain'
7
+ spec.authors = ['Demian Ferreiro']
8
+ spec.email = 'epidemian@gmail.com'
9
+
10
+ spec.summary = 'A handy progress monitor for long-running tasks'
11
+ spec.homepage = 'https://github.com/epidemian/ruby-pv'
12
+
13
+ spec.files = `git ls-files`.split("\n").reject { |f| f =~ %r{^spec/} }
14
+ spec.require_paths = ['lib']
15
+
16
+ spec.add_development_dependency 'bundler', '~> 1.10'
17
+ spec.add_development_dependency 'rake', '~> 10.0'
18
+ spec.add_development_dependency 'rspec'
19
+ spec.add_development_dependency 'rubocop'
20
+ end
metadata ADDED
@@ -0,0 +1,108 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ruby-pv
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Demian Ferreiro
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-01-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.10'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.10'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubocop
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description:
70
+ email: epidemian@gmail.com
71
+ executables: []
72
+ extensions: []
73
+ extra_rdoc_files: []
74
+ files:
75
+ - ".gitignore"
76
+ - ".rspec"
77
+ - ".rubocop.yml"
78
+ - Gemfile
79
+ - README.md
80
+ - Rakefile
81
+ - UNLICENSE
82
+ - lib/pv.rb
83
+ - ruby-pv.gemspec
84
+ homepage: https://github.com/epidemian/ruby-pv
85
+ licenses:
86
+ - Public Domain
87
+ metadata: {}
88
+ post_install_message:
89
+ rdoc_options: []
90
+ require_paths:
91
+ - lib
92
+ required_ruby_version: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ required_rubygems_version: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ requirements: []
103
+ rubyforge_project:
104
+ rubygems_version: 2.4.5.1
105
+ signing_key:
106
+ specification_version: 4
107
+ summary: A handy progress monitor for long-running tasks
108
+ test_files: []