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 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