popro 0.0.1 → 0.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 23e8aecacb06e0bfe98467ac74b74f0b5e90ddc65f57069ae4bf29bdc7073b8a
4
- data.tar.gz: 6a220ecf6de03a768aeed0d4998983ada21d037859a9deafe6263b4604b201d7
3
+ metadata.gz: 28d2afab9d53f8fa8ebf80ec36ee2d0e1529d150a6ffd2993409ba845a3f4c79
4
+ data.tar.gz: 858dbf273be5fe62af6f0c74fbcfe51fd34f6491b344381f40fab71a053c50a1
5
5
  SHA512:
6
- metadata.gz: cb458d4d2a5169342ede984048be23c86d101db7546585e408e7470fe799f3344df4d2c691e42e43ff5ddd915e9af284a44ae59a7780890b06340a70a5e56419
7
- data.tar.gz: 6a8fc7aef7feec2b0c39af0f1478f1cebaf4ccb8667edcc0842d119019dec709998e2d0a3930869646b51a210f4758eaa2b721894b7a1b79d554f44294996ce3
6
+ metadata.gz: 337b2b031a41c3ba78e438b906fac942b98eaacff1e0c489d01e4811b5f676760cc8e0b9f2d6fbab27c1ec344c5f42d6c677546ce7fb43d52a17f89d0d38ac98
7
+ data.tar.gz: 5d3b42ff7fb6ee26873afc35217daeb43b9f431f05e251c59e3b112ab13f14481fc0f0d108d3f3cb53f3c458a531af5ca55c5821f058d5941949bc59a0eeae51
data/CHANGELOG CHANGED
@@ -1 +1,3 @@
1
+ 0.2.1 add support for estimation and re-use of default indicator with added concatted formatters
2
+ 0.2.0 various fixes and added unit tests
1
3
  0.0.1 initial version
data/README.md CHANGED
@@ -1,13 +1,20 @@
1
1
  # Po'Pro: The Poor-Man's Progress Indicator
2
2
 
3
+ [![Version](https://badge.fury.io/rb/popro.svg)](https://badge.fury.io/rb/popro)
4
+ [![Tests](https://github.com/MikeSmithEU/PoPro/workflows/Code%20quality%20&%20unit%20tests/badge.svg)](https://github.com/MikeSmithEU/PoPro/actions?query=workflow%3A%22Code+quality+%26+unit+tests%22)
5
+
6
+ ## Current TODOs
7
+
8
+ - [ ] properly update documentation
9
+ - [ ] simplify some stuff (each0?)
10
+ - [ ] 100% code coverage
11
+
3
12
  ## Why?
4
13
 
5
14
  Easier and cleaner progress indication.
6
15
 
7
16
  ## How?
8
17
 
9
- TODO: properly update documentation, simplify some stuff. 100% code coverage
10
-
11
18
  ### Basic usage
12
19
 
13
20
  The invocation `Popro#did(yielded)` is used to signify that one step in progress has been finished.
@@ -263,11 +270,11 @@ end
263
270
  ## Formatters
264
271
 
265
272
  You can set your own formatters using `Popro#formatter(&block)`. Each formatter can be a `Proc`, `block` or
266
- class implementing the `call` method (e.g. the `PoproFormatter::*` classes).
273
+ class implementing the `call` method (e.g. the `Popro::Formatter::*` classes).
267
274
 
268
275
  The formatter will be invoked with 2 arguments:
269
276
 
270
- 1. `info`, an instance of `PoproInfo` containing e.g. `current` and `total`.
277
+ 1. `info`, an instance of `Popro::Info` containing e.g. `current` and `total`.
271
278
  2. `yielded`, whatever was passed to the `Popro#did` method.
272
279
 
273
280
  It can also be used inside blocks.
@@ -315,7 +322,7 @@ would output:
315
322
 
316
323
  Indicator classes are responsible for communicating the progress (or not, as the case may be).
317
324
 
318
- It is possible to provide your own "indicators" or use one of the provided ones (see `PoproIndicator` module).
325
+ It is possible to provide your own "indicators" or use one of the provided ones (see `Popro::Indicator` module).
319
326
 
320
- The default `PoproIndicator` class is `PoproIndicator.default` (which returns an instance of `PoproIndicator::Stream`), which outputs the progress to STDOUT each time `Popro#did` or `Popro#will` is called.
327
+ The default `Popro::Indicator` class is `Popro::Indicator.default` (which returns an instance of `Popro::Indicator::Stream`), which outputs the progress to STDOUT each time `Popro#did` or `Popro#will` is called.
321
328
 
@@ -7,22 +7,19 @@ module Popro
7
7
 
8
8
  require_relative 'popro/progress'
9
9
 
10
- def self.new(total, **options, &block)
10
+ def self.new(total = 0, **options, &block)
11
11
  raise ConfigError, 'using :total is not supported in new' if options.key?(:total) && (options[:total] != total)
12
12
 
13
13
  options[:total] = total
14
14
  Progress.new(**options, &block)
15
15
  end
16
16
 
17
- def self.each(obj, _total = nil, **options, &block)
17
+ def self.each(obj, total = nil, **options, &block)
18
18
  new(0, **options).each(obj, total, &block).done
19
19
  end
20
20
 
21
- def self.each0(obj, **options, &block)
22
- raise ConfigError, 'using :step is not supported in each0' if options.key?(:step)
23
-
24
- options[:step] = 0
25
- each(obj, 0, **options, &block)
21
+ def self.each_will(obj, titler, total = nil, **options, &block)
22
+ new(0, **options).each_will(obj, titler, total, &block).done
26
23
  end
27
24
 
28
25
  def self.command_line(*_args)
@@ -3,7 +3,7 @@
3
3
  module Popro
4
4
  # the progress context passed as first argument to blocks (or named argument for `Progress#each` and `Popro.each`)
5
5
 
6
- WILL_CHECK_MARKS ||= '☐☑'
6
+ WILL_CHECK_MARKS ||= ''
7
7
 
8
8
  class Context
9
9
  def initialize(progress:, info:, indicator:, step: 1)
@@ -14,25 +14,17 @@ module Popro
14
14
  end
15
15
 
16
16
  def each(obj, total = nil, &block)
17
- total = obj.size if total.nil?
18
- @info.total += total if total.positive?
19
- block = proc { |d| d } unless block_given?
20
-
21
- obj.each do |*args|
22
- did block.call(*args, progress: self)
17
+ _each(obj, total) do |*args|
18
+ did block.call(*args, progress: @info)
23
19
  end
24
-
25
- self
26
20
  end
27
21
 
28
- def each0(obj, &block)
29
- each(obj, 0, &block)
30
- end
31
-
32
- def each_will(obj, titler, total = nil)
33
- each(obj, total) do |*args, **kwargs|
34
- kwargs[:progress].will titler.call(*args) do
35
- yield(*args, **kwargs)
22
+ def each_will(obj, titler, total = nil, &block)
23
+ _each(obj, total) do |*args|
24
+ title = titler.call(*args)
25
+ will(title) do
26
+ block.call(*args, progress: @info)
27
+ nil
36
28
  end
37
29
  end
38
30
  end
@@ -59,25 +51,25 @@ module Popro
59
51
  block
60
52
  end
61
53
 
62
- def did(yielded = nil, amount = 1)
54
+ def did(yielded = nil, amount = nil)
63
55
  @info.start unless @info.running?
64
- inc amount, yielded
56
+ amount = @step if amount.nil?
57
+ raise TypeError, "amount: expected an integer, got #{amount.class}" unless amount.is_a? Integer
58
+
59
+ @info.current += amount unless amount.zero?
60
+ @indicator.call(@info, yielded)
65
61
 
66
62
  self
67
63
  end
68
64
 
69
- def will(title = nil, use_block_result_as_title = nil, step = nil)
70
- @info.start unless @info.running?
71
- inc 0, "#{WILL_CHECK_MARKS[0]} #{title}"
65
+ def will(title = nil, step = nil, &block)
66
+ did "#{WILL_CHECK_MARKS[0]} #{title}", 0
72
67
 
73
68
  return self unless block_given?
74
69
 
75
- step = @step if step.nil?
76
- yielded = yield @context
77
- yielded = "#{WILL_CHECK_MARKS[1]} #{title}" unless title.nil? || use_block_result_as_title
78
-
79
- # no need to communicate to Indicator if we are not advancing (avoid double calls)
80
- did yielded, step unless step.zero?
70
+ block.call
71
+ yielded = "#{WILL_CHECK_MARKS[1]} #{title}"
72
+ did(yielded, step || @step)
81
73
  yielded
82
74
  end
83
75
 
@@ -87,11 +79,18 @@ module Popro
87
79
 
88
80
  private
89
81
 
90
- def inc(amount, yielded = nil)
91
- raise TypeError('expected an integer') unless amount.is_a? Integer
82
+ def _each(obj, total = nil, &block)
83
+ total = obj.size if total.nil?
84
+ raise TypeError, "total: expected an integer got #{total.class}" unless total.is_a?(Integer) || total.nil?
92
85
 
93
- @info.current += amount unless amount.zero?
94
- @indicator.call(@info, yielded)
86
+ @info.total += total if total.positive?
87
+ # block = proc { |d| d } unless block_given?
88
+
89
+ obj.each do |*args, **kwargs|
90
+ block.call(*args, **kwargs)
91
+ end
92
+
93
+ self
95
94
  end
96
95
  end
97
96
  end
@@ -30,7 +30,7 @@ module Popro
30
30
  def call(info, *args)
31
31
  result = @formatter.call(info, *args)
32
32
  @longest = [@longest, result.size].max
33
- "\r" + result.ljust(@longest, ' ')
33
+ "\r#{result.ljust(@longest, ' ')}"
34
34
  end
35
35
  end
36
36
 
@@ -43,6 +43,53 @@ module Popro
43
43
  end
44
44
  end
45
45
 
46
+ class Estimate
47
+ # TODO: cleaner implementation/formatstring
48
+ attr_reader :info
49
+
50
+ def initialize
51
+ @start_time = current_time
52
+ @info = nil
53
+ end
54
+
55
+ def call(info, *_args)
56
+ @info = info
57
+
58
+ [
59
+ "estimated time left: #{format_duration(estimated_left)}",
60
+ "[#{format_duration(elapsed)}/#{format_duration(estimated_total)}]"
61
+ ].join(', ')
62
+ end
63
+
64
+ private
65
+
66
+ def elapsed
67
+ current_time - @start_time
68
+ end
69
+
70
+ def estimated_total
71
+ return nil if info.current.zero? || info.total.zero?
72
+
73
+ elapsed + (info.total / info.current) * elapsed
74
+ end
75
+
76
+ def estimated_left
77
+ return nil if info.current.zero? || info.total.zero?
78
+
79
+ (info.total / info.current) * elapsed
80
+ end
81
+
82
+ def format_duration(secs)
83
+ return '??d??h??m??s' if secs.nil?
84
+
85
+ format('%02dd%02dh%02dm%02ds', secs / 86_400, secs / 3600 % 24, secs / 60 % 60, secs % 60)
86
+ end
87
+
88
+ def current_time
89
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
90
+ end
91
+ end
92
+
46
93
  class Sprintf
47
94
  def initialize(format_string = nil)
48
95
  @format_string = format_string
@@ -27,7 +27,7 @@ module Popro
27
27
  formatter = self.class.default_formatter(formatter) if formatter.nil? || formatter.is_a?(String)
28
28
 
29
29
  @formatter = formatter
30
- @stream = stream || STDOUT
30
+ @stream = stream || $stdout
31
31
  end
32
32
 
33
33
  def call(*args)
@@ -45,11 +45,12 @@ module Popro
45
45
  end
46
46
  end
47
47
 
48
- def self.default_formatter
48
+ def self.default_formatter(*extra_formatters)
49
49
  ::Popro::Formatter::RewriteLine.new(
50
50
  ::Popro::Formatter::Concat.new(
51
51
  ::Popro::Formatter::Spinner.new(:dots, bounce: true),
52
52
  ::Popro::Formatter::Sprintf.new,
53
+ *extra_formatters,
53
54
  (proc do |_, yielded = nil|
54
55
  yielded if yielded.is_a?(String) || yielded.is_a?(Numeric)
55
56
  end),
@@ -58,8 +59,8 @@ module Popro
58
59
  )
59
60
  end
60
61
 
61
- def self.default
62
- Stream.new(formatter: default_formatter)
62
+ def self.default(*extra_formatters)
63
+ Stream.new(formatter: default_formatter(*extra_formatters))
63
64
  end
64
65
  end
65
66
  end
@@ -57,7 +57,6 @@ module Popro
57
57
 
58
58
  def to_h
59
59
  {
60
- started: @started,
61
60
  pct: pct,
62
61
  pct_formatted: pct_formatted,
63
62
  current: @current,
@@ -6,17 +6,24 @@ module Popro
6
6
  require_relative 'info'
7
7
  require_relative 'indicator'
8
8
 
9
+ DEFAULT_OPTIONS ||= {
10
+ total: 0,
11
+ current: 0,
12
+ indicator: Indicator.default
13
+ }.freeze
14
+
9
15
  attr_reader :context
10
16
 
11
17
  def initialize(**options)
12
18
  @started = false
13
- @info = Info.new(total: options.delete(:total), current: options.delete(:current))
14
19
 
15
- options[:step] ||= (block_given? ? 0 : 1)
16
- options[:progress] = self
17
- options[:info] = @info
18
- options[:indicator] = Indicator.default unless options.key? :indicator
20
+ options = DEFAULT_OPTIONS
21
+ .merge(step: block_given? ? 0 : 1)
22
+ .merge(options)
19
23
 
24
+ @info = Info.new(total: options.delete(:total), current: options.delete(:current))
25
+
26
+ options.merge!(progress: self, info: @info)
20
27
  @context = Context.new(**options)
21
28
 
22
29
  register_aliases
@@ -26,15 +33,27 @@ module Popro
26
33
  done
27
34
  end
28
35
 
36
+ # increase the total
37
+ def add(amount)
38
+ @info.total += amount
39
+ self
40
+ end
41
+
29
42
  private
30
43
 
31
44
  def register_aliases
32
45
  class << self
33
- %i[each each0 each_will to_proc did will formatter start done].each do |method_name|
46
+ %i[each each_will to_proc did will formatter start done].each do |method_name|
34
47
  define_method method_name do |*args, &block|
35
48
  @context.public_send(method_name, *args, &block)
36
49
  end
37
50
  end
51
+
52
+ %i[current total].each do |method_name|
53
+ define_method method_name do
54
+ @info.public_send(method_name)
55
+ end
56
+ end
38
57
  end
39
58
  end
40
59
  end
metadata CHANGED
@@ -1,17 +1,17 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: popro
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - MikeSmithEU
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-06-01 00:00:00.000000000 Z
11
+ date: 2020-09-16 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: The Poor-Man's Progress Indicator
14
- email:
14
+ email:
15
15
  executables: []
16
16
  extensions: []
17
17
  extra_rdoc_files: []
@@ -31,7 +31,7 @@ homepage: https://rubygems.org/gems/popro
31
31
  licenses:
32
32
  - MIT
33
33
  metadata: {}
34
- post_install_message:
34
+ post_install_message:
35
35
  rdoc_options: []
36
36
  require_paths:
37
37
  - lib
@@ -39,7 +39,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
39
39
  requirements:
40
40
  - - ">="
41
41
  - !ruby/object:Gem::Version
42
- version: '0'
42
+ version: '2.7'
43
43
  required_rubygems_version: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - ">="
@@ -47,7 +47,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
47
47
  version: '0'
48
48
  requirements: []
49
49
  rubygems_version: 3.1.2
50
- signing_key:
50
+ signing_key:
51
51
  specification_version: 4
52
52
  summary: Po'Pro
53
53
  test_files: []