popro 0.1.3 → 0.2.4

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: 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: []