rpv 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fdb60cbf7a2013a1c3d8f226de95a1fe2504d5d783d475d693caec111100e58b
4
- data.tar.gz: 283247bce675713e7b752461fcf6ce458c4d9e9e3d70a63d633dd943b14a7c85
3
+ metadata.gz: b434261aec0276464e49a998634fc98fae8dfee68a98c24216ec77acff0f3ea6
4
+ data.tar.gz: 87fb6855cc287665162904f726cdf931c77d903c895709399510ce6c43d9a191
5
5
  SHA512:
6
- metadata.gz: 3374d40f52abb43edb38a6d27641e6b2d3d1e9080c705ece404a984b475a53257430c64361fb65f400d81f94985911eae3854dd185486e39018b686317c3723a
7
- data.tar.gz: b684b97e157f6a7314206d30bb6d2670c3702b9b288ac463e468da1d883cfbcdb76df1f53e15f22d878518c5b9b940640cb25df864723719fd6b9b73a079d58a
6
+ metadata.gz: 03eca1d617b473e9e7132369a47ca5f9c7849d83c4137496d324a754eaaf8e7d2f6c0177055155ff0dd16edf52e8da5f9e95d5e7ca0ad10795ac0a4a1210a980
7
+ data.tar.gz: dc2a27f8adb26aa807ae9f818a08afbe0c1232673165a46ea7b976526b3a9e4e0bc75325f8b4ea0c6ee55d95d29ce82cfcb710ddf698d9acb50e5958386c9846
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.2.0] - 2024-12-18
4
+
5
+ - Improve Abbreviated API.
6
+ - `increment` accepts an optional `amount` argument.
7
+ - More options supported: `format`, `delay_start`, `width`, `height`, `timer`, `eta`, and `rate`.
8
+ - Also added a `rate_limit` option that does work. It was there before but it really didn't do anything.
9
+ - Improved documentation.
10
+
3
11
  ## [0.1.0] - 2024-12-11
4
12
 
5
13
  - Initial release
data/README.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Rpv
2
2
 
3
+ Rpv provides progress bar and rate limiting features to be used in Ruby scripts and programs. It
4
+ supports multiple parallel bars. It wraps [pv](https://ivarch.com/programs/pv.shtml), which needs to
5
+ be installed on the system.
6
+
3
7
  ## Installation
4
8
 
5
9
  Install the gem and add to the application's Gemfile by executing:
@@ -10,15 +14,112 @@ If bundler is not being used to manage dependencies, install the gem by executin
10
14
 
11
15
  $ gem install rpv
12
16
 
17
+ You need to have pv installed. [It is available for most
18
+ systems](https://ivarch.com/programs/pv.shtml#packages).
19
+
13
20
  ## Usage
14
21
 
15
- TODO: Write usage instructions here
22
+ There are some [examples](examples/) available. Now for the details:
23
+
24
+ ### Generic API
25
+
26
+ You'll first need to require the library:
27
+
28
+ ```ruby
29
+ require "rpv"
30
+ ```
31
+
32
+ Rpv provides a very simple API: you instantiate the bar (with some options), then you call
33
+ `.increment` on it as many times as needed to make it advance, then `.finish` to finish the
34
+ proccess:
35
+
36
+ ```ruby
37
+ bar = Rpv.new
38
+ 10.times do
39
+ # do something
40
+ bar.increment
41
+ end
42
+ bar.finish
43
+ ```
44
+
45
+ `increment` takes an optional integer argument to make it advance more than one step.
46
+
47
+ #### Block syntax
48
+
49
+ Rpv also provides a block syntax, that saves you from assigning the variable and calling `.finish`:
50
+
51
+ ```ruby
52
+ Rpv.new do |bar|
53
+ 10.times do
54
+ # do something
55
+ bar.increment
56
+ end
57
+ end
58
+ ```
59
+
60
+ #### Options
61
+
62
+ Rpv supports some options, which can be passed to the initializer:
63
+
64
+ ```ruby
65
+ Rpv.new(name: "Processing items") do |rpv|
66
+ ...
67
+ ```
68
+
69
+ They all map to equally named options in `pv` (and the defaults are the same), so the best place to
70
+ look for details is [pv's manual page](https://ivarch.com/programs/quickref/pv.shtml). But here is a
71
+ summary of those that are supported:
72
+
73
+ | Option | What it does | Default |
74
+ |-----------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------|
75
+ | **Display options** | | If no display options are specified: `progress`, `timer`, `eta`, and `rate`. Otherwise, only those explicitly specified. |
76
+ | `progress` | Display a progress bar. If no size is specified, the bar just moves from left to right. | |
77
+ | `timer` | Display total elapsed time. | |
78
+ | `eta` | Display estimated time to completion based on the current progress and rate. | |
79
+ | `fineta` | Display estimated local time of completion based on the current progress and rate. | |
80
+ | `rate` | Display current rate of completion (steps per second). | |
81
+ | `average_rate` | Display average rate of completion over the last 30 seconds. | |
82
+ | `format` | Ignore all other display flags and use a custom format. See [_Formatting_ in `pv`'s manual](https://ivarch.com/programs/quickref/pv.shtml#formatting). | |
83
+ | `quiet` | No output. | |
84
+ | **Output modifiers** | | |
85
+ | `delay_start` | Don't show progress information until N seconds have passed. Useful if the task at hand could be so short that a progress bar is unnecesary. Only if the task takes more than N seconds will a progress bar be shown. | `0`. |
86
+ | `size` | Total amount of steps of the wrapped task. | `nil` (meaning unknown). |
87
+ | `interval` | Interval in seconds (decimals allowed) between updates of the display. | 1 second. |
88
+ | `width` | Width of the display in columns. | Width of the terminal (80 if it can't be determined). |
89
+ | `height` | Height of the display in rows. | Height of the terminal (25 if it can't be determined). |
90
+ | `name` | Title of the display (name of the process). | empty |
91
+ | `stats` | Show a summary at the end | `false` |
92
+ | **Data transfer modifiers** | | |
93
+ | `rate_limit` | Limit the maximum number of steps that can be completed per second (the call to `increment` blocks the thread for the necessary amount of time). | No limit. |
94
+
95
+ #### Multiple bars
96
+
97
+ You don't need to do anything special to have multiple bars progressing in parallel. See [examples/multibar.rb](examples/multibar.rb).
98
+
99
+ ### Abbreviated API
100
+
101
+ Finally, for the most typical case in scripts (iterate over something and do something with it), Rpv
102
+ provides an optional abbreviated API. You'll need to require it explicitly, and it'll add an extra
103
+ method on `Enumerable` that will let you do something like:
104
+
105
+ ```ruby
106
+ require "rpv/extensions"
107
+
108
+ big_list.with_rpv(<options>).each do |item|
109
+ do_something_with(item)
110
+ end
111
+ ```
16
112
 
17
113
  ## Development
18
114
 
19
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
115
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run
116
+ the tests. You can also run `bin/console` for an interactive prompt that will allow you to
117
+ experiment.
20
118
 
21
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
119
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new
120
+ version, update the version number in `version.rb`, and then run `bundle exec rake release`, which
121
+ will create a git tag for the version, push git commits and the created tag, and push the `.gem`
122
+ file to [rubygems.org](https://rubygems.org).
22
123
 
23
124
  ## Contributing
24
125
 
data/Rakefile CHANGED
@@ -8,3 +8,15 @@ RSpec::Core::RakeTask.new(:spec)
8
8
  require "standard/rake"
9
9
 
10
10
  task default: %i[spec standard]
11
+
12
+ desc "Run all the examples"
13
+ task :examples do
14
+ FileList["examples/*.rb"].each do |example|
15
+ ruby example
16
+ end
17
+ end
18
+
19
+ desc "Run the benchmark"
20
+ task :benchmark do
21
+ ruby "benchmark.rb"
22
+ end
data/benchmark.rb ADDED
@@ -0,0 +1,48 @@
1
+ # Performance is in general not very important for this kind of package. It is used to track tasks
2
+ # that _are_ slow. Yet, I want to keep an eye on it being reasonable, using other progress gems as a
3
+ # baseline. It was also useful to identify a significant bottleneck in rpv doing something that is
4
+ # only needed when applying a rate limit (a feature other progress bars don't have), and to decide
5
+ # to only do that when a rate limit option is passed (see Rpv#initialize).
6
+ #
7
+ # This script produces a lot of output in stderr (the bars). You usually want to run it redirecting
8
+ # stderr to /dev/null (otherwise you're mainly testing your terminal's performance), unless of
9
+ # course, you're testing that.
10
+
11
+ require "benchmark/ips"
12
+ require "fortschritt"
13
+ require "ruby-progressbar"
14
+ require "rpv"
15
+
16
+ # monkey-patch fortschritt to output to stderr
17
+ class Fortschritt::Meter
18
+ def print!
19
+ Fortschritt.printer.print(self, $stderr)
20
+ end
21
+ end
22
+
23
+ Benchmark.ips(warmup: 0.1, time: 2) do |x|
24
+ x.report "ruby-progressbar" do |times|
25
+ progressbar = ProgressBar.create(total: nil, output: $stderr)
26
+ times.times { progressbar.increment }
27
+ progressbar.finish
28
+ end
29
+
30
+ x.report "fortschritt" do |times|
31
+ Fortschritt.init(0)
32
+ times.times { Fortschritt.increment }
33
+ end
34
+
35
+ x.report "rpv" do |times|
36
+ Rpv.new do |rpv|
37
+ times.times { rpv.increment }
38
+ end
39
+ end
40
+
41
+ x.report "rpv with rate limit" do |times|
42
+ Rpv.new(rate_limit: 10**10) do |rpv|
43
+ times.times { rpv.increment }
44
+ end
45
+ end
46
+
47
+ x.compare!
48
+ end
@@ -1,21 +1,14 @@
1
1
  require "rpv"
2
- require "rpv/global"
3
2
 
4
3
  module Enumerable
5
- def with_rpv(**options)
6
- Rpv::Global.init(size: size, **options)
7
- self
8
- end
9
-
10
- def finish_rpv
11
- Rpv::Global.finish
12
- self
13
- end
14
- end
15
-
16
- class Object
17
- def rpv
18
- Rpv::Global.increment
19
- self
4
+ def with_rpv(options = {})
5
+ Enumerator.new do |e|
6
+ Rpv.new(size: size, **options) do |rpv|
7
+ each do |i|
8
+ e.yield i
9
+ rpv.increment
10
+ end
11
+ end
12
+ end
20
13
  end
21
14
  end
data/lib/rpv/options.rb CHANGED
@@ -1,10 +1,10 @@
1
1
  class Rpv::Options
2
- MAPPED_OPTIONS = %w[size name interval rate_limit]
3
- BOOLEAN_MAPPED_OPTIONS = %w[quiet progress fineta]
2
+ MAPPED_OPTIONS = %w[format delay_start size interval width height name rate_limit]
3
+ BOOLEAN_MAPPED_OPTIONS = %w[progress timer eta fineta rate average_rate quiet stats]
4
4
 
5
5
  def self.parse(options)
6
6
  options.transform_keys!(&:to_s)
7
- args = %w[--si --line-mode --discard --cursor]
7
+ args = %w[--si --line-mode --cursor]
8
8
  MAPPED_OPTIONS.each do |option|
9
9
  args << ["--#{option.tr("_", "-")}", options[option].to_s] if options[option]
10
10
  end
data/lib/rpv/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Rpv
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
data/lib/rpv.rb CHANGED
@@ -1,17 +1,37 @@
1
1
  require "rpv/options"
2
+ require "open3"
2
3
 
3
4
  class Rpv
4
5
  def initialize(options = {})
5
- @pv = IO.popen(["pv"] + Options.parse(options).to_a, "w+")
6
+ if options.key?(:rate_limit)
7
+ # This also works with rate limit, but brings a performance penalty so we only use it if
8
+ # necessary. Otherwise we fallback to a simpler mechanism (IO.popen and --discard).
9
+ @pv, @out, @thread = Open3.popen2("pv", *Options.parse(options).to_a)
10
+ else
11
+ @pv = IO.popen(["pv", "--discard"] + Options.parse(options).to_a, "w+")
12
+ end
13
+
14
+ if block_given?
15
+ yield self
16
+ finish
17
+ end
6
18
  end
7
19
 
8
- def increment
9
- @pv.puts
10
- @pv.flush
20
+ def increment(amount = 1)
21
+ amount.times do
22
+ @pv.puts
23
+ @pv.flush
24
+ @out&.gets
25
+ end
11
26
  end
12
27
 
13
28
  def finish
14
29
  @pv.flush
15
30
  @pv.close
31
+ @thread&.join
32
+ end
33
+
34
+ def finished?
35
+ @pv.closed?
16
36
  end
17
37
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rpv
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sergio Gil
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-12-11 00:00:00.000000000 Z
11
+ date: 2024-12-18 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email:
@@ -23,9 +23,9 @@ files:
23
23
  - LICENSE.txt
24
24
  - README.md
25
25
  - Rakefile
26
+ - benchmark.rb
26
27
  - lib/rpv.rb
27
28
  - lib/rpv/extensions.rb
28
- - lib/rpv/global.rb
29
29
  - lib/rpv/options.rb
30
30
  - lib/rpv/version.rb
31
31
  - sig/rpv.rbs
data/lib/rpv/global.rb DELETED
@@ -1,13 +0,0 @@
1
- module Rpv::Global
2
- def self.init(**options)
3
- @rpv = Rpv.new(**options)
4
- end
5
-
6
- def self.increment
7
- @rpv.increment
8
- end
9
-
10
- def self.finish
11
- @rpv.finish
12
- end
13
- end