popro 0.1.3 → 0.2.4

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: c44ac97a28d694e15a696c5f17bd6c3b4ae7198f04390f32119f53e02aab48b1
4
- data.tar.gz: 6aa1744397c4b03126ef3f945acaa6c57ad55bc17c152483f514f190f6616b48
3
+ metadata.gz: 8c3d8c8dba5a532d23b95582322983b844243a72679bff15acce69933c106ea2
4
+ data.tar.gz: ea5170d62aa990167c89d0fee9d7ee07d1cb1902f0a8e64f1920abbb523ed446
5
5
  SHA512:
6
- metadata.gz: '09c839770f6b8c02f1f2456a0f41cd61cadb70da8dbd2cc8a7f4aef4eab578e6a918115770137d3a29d05bf6829ef45219d5ea28e37329da70d49dec8c581aef'
7
- data.tar.gz: 74689916c7ad0d6484e4371506578275a93c3d9f0a2f6e49b6761141dcf2b5fd8acedbe5d97ec21395893d4aa44310b639e4e97022b9de3dee09a5eea56938b2
6
+ metadata.gz: 9023ad0fbf774e02fab5dc99df4720365a742c651469342bed04326fa88a34dd7e91d04830f50a4d28ac28a677e660a93715b6b51688b17668933cf0bdc3276e
7
+ data.tar.gz: b79e9a485d34aaab14bd7674a3adfad80305ba9c87b0ff8a7cd1981ec5dd6afeaef263efab503cef06065662c5d1ad296e7bec3c49c56e21117028fb691a5d7e
data/CHANGELOG CHANGED
@@ -1 +1,6 @@
1
+ 0.2.4 add support for `Popro.(un)silence!` atomic silencing, better duration formatting
2
+ 0.2.3 add support for `Popro.silenced`
3
+ 0.2.2 add gonna and each_gonna, add Callback Indicator
4
+ 0.2.1 add support for estimation and re-use of default indicator with added concatted formatters
5
+ 0.2.0 various fixes and added unit tests
1
6
  0.0.1 initial version
@@ -7,7 +7,9 @@ module Popro
7
7
 
8
8
  require_relative 'popro/progress'
9
9
 
10
- def self.new(total, **options, &block)
10
+ @_is_silenced = false
11
+
12
+ def self.new(total = 0, **options, &block)
11
13
  raise ConfigError, 'using :total is not supported in new' if options.key?(:total) && (options[:total] != total)
12
14
 
13
15
  options[:total] = total
@@ -15,7 +17,6 @@ module Popro
15
17
  end
16
18
 
17
19
  def self.each(obj, total = nil, **options, &block)
18
- options[:step] = 0 unless options.key? :step
19
20
  new(0, **options).each(obj, total, &block).done
20
21
  end
21
22
 
@@ -23,6 +24,31 @@ module Popro
23
24
  new(0, **options).each_will(obj, titler, total, &block).done
24
25
  end
25
26
 
27
+ def self.each_gonna(obj, titler, total = nil, **options, &block)
28
+ new(0, **options).each_gonna(obj, titler, total, &block).done
29
+ end
30
+
31
+ def self.silenced
32
+ prev_silenced = @_is_silenced
33
+ @_is_silenced = true
34
+ yield
35
+ @_is_silenced = prev_silenced
36
+ end
37
+
38
+ def self.silence!
39
+ @_is_silenced = true
40
+ self
41
+ end
42
+
43
+ def self.unsilence!
44
+ @_is_silenced = false
45
+ self
46
+ end
47
+
48
+ def self.silenced?
49
+ @_is_silenced
50
+ end
51
+
26
52
  def self.command_line(*_args)
27
53
  raise 'TODO: implement a `ps` style progress indicator for command line'
28
54
  end
@@ -6,11 +6,13 @@ module Popro
6
6
  WILL_CHECK_MARKS ||= ' ✔'
7
7
 
8
8
  class Context
9
+ require_relative 'indicator'
10
+
9
11
  def initialize(progress:, info:, indicator:, step: 1)
10
12
  @progress = progress
11
- @indicator = indicator
12
13
  @info = info
13
14
  @step = step
15
+ @_indicator = indicator
14
16
  end
15
17
 
16
18
  def each(obj, total = nil, &block)
@@ -39,31 +41,44 @@ module Popro
39
41
  raise OutOfSyncError, 'done while not started' unless @info.running?
40
42
 
41
43
  @info.finish
42
- @indicator.finish if @indicator.respond_to? :finish
44
+ indicator.finish if indicator.respond_to? :finish
43
45
  end
44
46
 
45
47
  def formatter(&block)
46
- unless @indicator.respond_to?(:formatter=)
47
- raise ConfigError, "seems formatter is not available for #{@indicator.class}"
48
+ unless @_indicator.respond_to?(:formatter=)
49
+ raise ConfigError, "seems formatter is not available for #{@_indicator.class}"
48
50
  end
49
51
 
50
- @indicator.formatter = block
52
+ @_indicator.formatter = block
51
53
  block
52
54
  end
53
55
 
54
56
  def did(yielded = nil, amount = nil)
55
57
  @info.start unless @info.running?
56
58
  amount = @step if amount.nil?
57
- raise TypeError('amount: expected an integer') unless amount.is_a? Integer
59
+ raise TypeError, "amount: expected an integer, got #{amount.class}" unless amount.is_a? Integer
58
60
 
59
61
  @info.current += amount unless amount.zero?
60
- @indicator.call(@info, yielded)
62
+ indicator.call(@info, yielded)
63
+
64
+ self
65
+ end
61
66
 
67
+ def gonna(title)
68
+ @info.start unless @info.running?
69
+ indicator.call(@info, title)
62
70
  self
63
71
  end
64
72
 
73
+ def each_gonna(obj, titler, total = nil, &block)
74
+ _each(obj, total) do |*args|
75
+ gonna(titler.call(*args))
76
+ block.call(*args, progress: @info)
77
+ end
78
+ end
79
+
65
80
  def will(title = nil, step = nil, &block)
66
- did "#{WILL_CHECK_MARKS[0]} #{title}", 0
81
+ gonna "#{WILL_CHECK_MARKS[0]} #{title}"
67
82
 
68
83
  return self unless block_given?
69
84
 
@@ -79,9 +94,15 @@ module Popro
79
94
 
80
95
  private
81
96
 
97
+ def indicator
98
+ return Indicator::Null if Popro.silenced?
99
+
100
+ @_indicator
101
+ end
102
+
82
103
  def _each(obj, total = nil, &block)
83
104
  total = obj.size if total.nil?
84
- raise TypeError('total: expected an integer') unless total.is_a?(Integer) || total.nil?
105
+ raise TypeError, "total: expected an integer got #{total.class}" unless total.is_a?(Integer) || total.nil?
85
106
 
86
107
  @info.total += total if total.positive?
87
108
  # block = proc { |d| d } unless block_given?
@@ -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,77 @@ 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
+ UNITS = [
67
+ [60, '%02ds'],
68
+ [60, '%02dm'],
69
+ [24, '%02dh'],
70
+ [7, '%d days, '],
71
+ [52, '%d weeks, '],
72
+ [nil, '%d years, ']
73
+ ].freeze
74
+
75
+ def elapsed
76
+ current_time - @start_time
77
+ end
78
+
79
+ def estimated_total
80
+ return nil if info.current.zero? || info.total.zero?
81
+
82
+ elapsed + (info.total / info.current) * elapsed
83
+ end
84
+
85
+ def estimated_left
86
+ return nil if info.current.zero? || info.total.zero?
87
+
88
+ (info.total / info.current) * elapsed
89
+ end
90
+
91
+ def format_duration(secs)
92
+ return '?' if secs.nil?
93
+
94
+ return format('%.3fs', secs) if secs < 10
95
+
96
+ format_duration_long(secs)
97
+ end
98
+
99
+ def format_duration_long(secs)
100
+ UNITS.map do |(divisor, format_str)|
101
+ next if secs < 1
102
+
103
+ if divisor
104
+ amount = secs % divisor
105
+ secs /= divisor
106
+ end
107
+
108
+ format(format_str, divisor ? amount : secs)
109
+ end.take_while(&:itself).reverse!.join
110
+ end
111
+
112
+ def current_time
113
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
114
+ end
115
+ end
116
+
46
117
  class Sprintf
47
118
  def initialize(format_string = nil)
48
119
  @format_string = format_string
@@ -1,9 +1,25 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'popro'
4
+
3
5
  module Popro
4
6
  module Indicator
5
7
  require_relative 'formatter'
6
8
 
9
+ class Null
10
+ def self.new
11
+ self
12
+ end
13
+
14
+ def self.initialize
15
+ self
16
+ end
17
+
18
+ def self.call(*_args); end
19
+
20
+ def self.finish; end
21
+ end
22
+
7
23
  class Aggregate
8
24
  def initialize(*indicators)
9
25
  @indicators = indicators
@@ -27,7 +43,7 @@ module Popro
27
43
  formatter = self.class.default_formatter(formatter) if formatter.nil? || formatter.is_a?(String)
28
44
 
29
45
  @formatter = formatter
30
- @stream = stream || STDOUT
46
+ @stream = stream || $stdout
31
47
  end
32
48
 
33
49
  def call(*args)
@@ -45,11 +61,27 @@ module Popro
45
61
  end
46
62
  end
47
63
 
48
- def self.default_formatter
64
+ class Callback
65
+ def initialize(finish = nil, &block)
66
+ @finish = finish
67
+ @callback = block
68
+ end
69
+
70
+ def call(*args)
71
+ @callback.call(*args)
72
+ end
73
+
74
+ def finish
75
+ @finish&.call
76
+ end
77
+ end
78
+
79
+ def self.default_formatter(*extra_formatters)
49
80
  ::Popro::Formatter::RewriteLine.new(
50
81
  ::Popro::Formatter::Concat.new(
51
82
  ::Popro::Formatter::Spinner.new(:dots, bounce: true),
52
83
  ::Popro::Formatter::Sprintf.new,
84
+ *extra_formatters,
53
85
  (proc do |_, yielded = nil|
54
86
  yielded if yielded.is_a?(String) || yielded.is_a?(Numeric)
55
87
  end),
@@ -58,8 +90,8 @@ module Popro
58
90
  )
59
91
  end
60
92
 
61
- def self.default
62
- Stream.new(formatter: default_formatter)
93
+ def self.default(*extra_formatters)
94
+ Stream.new(formatter: default_formatter(*extra_formatters))
63
95
  end
64
96
  end
65
97
  end
@@ -9,20 +9,19 @@ module Popro
9
9
  DEFAULT_OPTIONS ||= {
10
10
  total: 0,
11
11
  current: 0,
12
+ indicator: Indicator.default
12
13
  }.freeze
13
14
 
14
15
  attr_reader :context
15
16
 
16
17
  def initialize(**options)
17
- options.merge!(DEFAULT_OPTIONS)
18
-
19
18
  @started = false
20
19
 
21
- @info = Info.new(total: options.delete(:total), current: options.delete(:current))
22
-
23
- options[:step] ||= (block_given? ? 0 : 1)
20
+ options = DEFAULT_OPTIONS
21
+ .merge(step: block_given? ? 0 : 1)
22
+ .merge(options)
24
23
 
25
- options[:indicator] = Indicator.default unless options.key? :indicator
24
+ @info = Info.new(total: options.delete(:total), current: options.delete(:current))
26
25
 
27
26
  options.merge!(progress: self, info: @info)
28
27
  @context = Context.new(**options)
@@ -44,7 +43,7 @@ module Popro
44
43
 
45
44
  def register_aliases
46
45
  class << self
47
- %i[each each_will to_proc did will formatter start done].each do |method_name|
46
+ %i[each each_will each_gonna to_proc gonna will did formatter start done].each do |method_name|
48
47
  define_method method_name do |*args, &block|
49
48
  @context.public_send(method_name, *args, &block)
50
49
  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.1.3
4
+ version: 0.2.4
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-20 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: []