ruby-pv 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []