rpv 0.1.0 → 0.2.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/README.md +104 -3
- data/Rakefile +12 -0
- data/benchmark.rb +48 -0
- data/lib/rpv/extensions.rb +9 -16
- data/lib/rpv/options.rb +3 -3
- data/lib/rpv/version.rb +1 -1
- data/lib/rpv.rb +24 -4
- metadata +3 -3
- data/lib/rpv/global.rb +0 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b434261aec0276464e49a998634fc98fae8dfee68a98c24216ec77acff0f3ea6
|
4
|
+
data.tar.gz: 87fb6855cc287665162904f726cdf931c77d903c895709399510ce6c43d9a191
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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
|
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
|
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
|
data/lib/rpv/extensions.rb
CHANGED
@@ -1,21 +1,14 @@
|
|
1
1
|
require "rpv"
|
2
|
-
require "rpv/global"
|
3
2
|
|
4
3
|
module Enumerable
|
5
|
-
def with_rpv(
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
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
|
3
|
-
BOOLEAN_MAPPED_OPTIONS = %w[
|
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 --
|
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
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
|
-
|
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
|
-
|
10
|
-
|
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.
|
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
|
+
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
|