console 1.29.2 → 1.36.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
- checksums.yaml.gz.sig +0 -0
- data/context/command-line.md +47 -0
- data/context/configuration.md +23 -0
- data/context/events.md +71 -0
- data/context/getting-started.md +170 -0
- data/context/index.yaml +28 -0
- data/context/integration.md +37 -0
- data/lib/console/capture.rb +29 -3
- data/lib/console/clock.rb +9 -3
- data/lib/console/compatible/logger.rb +34 -3
- data/lib/console/config.rb +96 -0
- data/lib/console/event/failure.rb +33 -4
- data/lib/console/event/generic.rb +22 -1
- data/lib/console/event/spawn.rb +39 -7
- data/lib/console/event.rb +7 -1
- data/lib/console/filter.rb +83 -7
- data/lib/console/format/safe.rb +205 -58
- data/lib/console/format.rb +5 -1
- data/lib/console/interface.rb +18 -10
- data/lib/console/logger.rb +25 -38
- data/lib/console/output/default.rb +31 -6
- data/lib/console/output/failure.rb +9 -2
- data/lib/console/output/null.rb +5 -2
- data/lib/console/output/sensitive.rb +42 -1
- data/lib/console/output/serialized.rb +32 -7
- data/lib/console/output/split.rb +12 -1
- data/lib/console/output/terminal.rb +68 -15
- data/lib/console/output/wrapper.rb +12 -1
- data/lib/console/output.rb +14 -3
- data/lib/console/progress.rb +56 -9
- data/lib/console/resolver.rb +19 -2
- data/lib/console/terminal/formatter/failure.rb +18 -6
- data/lib/console/terminal/formatter/progress.rb +16 -3
- data/lib/console/terminal/formatter/spawn.rb +16 -5
- data/lib/console/terminal/formatter.rb +16 -0
- data/lib/console/terminal/text.rb +66 -20
- data/lib/console/terminal/xterm.rb +18 -3
- data/lib/console/terminal.rb +7 -9
- data/lib/console/version.rb +2 -2
- data/lib/console/warn.rb +2 -2
- data/lib/console.rb +2 -1
- data/license.md +6 -3
- data/readme.md +60 -6
- data/releases.md +115 -1
- data.tar.gz.sig +0 -0
- metadata +20 -12
- metadata.gz.sig +0 -0
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# Released under the MIT License.
|
|
4
|
-
# Copyright, 2021-
|
|
4
|
+
# Copyright, 2021-2025, by Samuel Williams.
|
|
5
5
|
|
|
6
6
|
require_relative "wrapper"
|
|
7
7
|
|
|
8
8
|
module Console
|
|
9
9
|
module Output
|
|
10
|
+
# Redact sensitive information from output.
|
|
10
11
|
class Sensitive < Wrapper
|
|
12
|
+
# Default redaction pattern.
|
|
11
13
|
REDACT = /
|
|
12
14
|
phone
|
|
13
15
|
| email
|
|
@@ -34,28 +36,52 @@ module Console
|
|
|
34
36
|
| password
|
|
35
37
|
/xi
|
|
36
38
|
|
|
39
|
+
# Create a new sensitive output wrapper.
|
|
40
|
+
#
|
|
41
|
+
# @parameter output [Console::Output] The output to wrap.
|
|
42
|
+
# @parameter redact [Regexp] The pattern to redact.
|
|
43
|
+
# @parameter options [Hash] Additional options to pass to the output.
|
|
37
44
|
def initialize(output, redact: REDACT, **options)
|
|
38
45
|
super(output, **options)
|
|
39
46
|
|
|
40
47
|
@redact = redact
|
|
41
48
|
end
|
|
42
49
|
|
|
50
|
+
# Check if the given text should be redacted.
|
|
51
|
+
#
|
|
52
|
+
# @parameter text [String] The text to check.
|
|
53
|
+
# @returns [Boolean] Whether the text should be redacted.
|
|
43
54
|
def redact?(text)
|
|
44
55
|
text.match?(@redact)
|
|
45
56
|
end
|
|
46
57
|
|
|
58
|
+
# Redact sensitive information from a hash.
|
|
59
|
+
#
|
|
60
|
+
# @parameter arguments [Hash] The hash to redact.
|
|
61
|
+
# @parameter filter [Proc] An optional filter to apply to redacted text.
|
|
62
|
+
# @returns [Hash] The redacted hash.
|
|
47
63
|
def redact_hash(arguments, filter)
|
|
48
64
|
arguments.transform_values do |value|
|
|
49
65
|
redact(value, filter)
|
|
50
66
|
end
|
|
51
67
|
end
|
|
52
68
|
|
|
69
|
+
# Redact sensitive information from an array.
|
|
70
|
+
#
|
|
71
|
+
# @parameter array [Array] The array to redact.
|
|
72
|
+
# @parameter filter [Proc] An optional filter to apply to redacted text.
|
|
73
|
+
# @returns [Array] The redacted array.
|
|
53
74
|
def redact_array(array, filter)
|
|
54
75
|
array.map do |value|
|
|
55
76
|
redact(value, filter)
|
|
56
77
|
end
|
|
57
78
|
end
|
|
58
79
|
|
|
80
|
+
# Redact sensitive information from the given argument.
|
|
81
|
+
#
|
|
82
|
+
# @parameter argument [String | Array | Hash] The argument to redact.
|
|
83
|
+
# @parameter filter [Proc] An optional filter to apply to redacted text.
|
|
84
|
+
# @returns [String | Array | Hash] The redacted argument.
|
|
59
85
|
def redact(argument, filter)
|
|
60
86
|
case argument
|
|
61
87
|
when String
|
|
@@ -75,17 +101,32 @@ module Console
|
|
|
75
101
|
end
|
|
76
102
|
end
|
|
77
103
|
|
|
104
|
+
# A simple filter for redacting sensitive information.
|
|
78
105
|
class Filter
|
|
106
|
+
# Create a new filter.
|
|
107
|
+
#
|
|
108
|
+
# @parameter substitutions [Hash] The substitutions to apply.
|
|
79
109
|
def initialize(substitutions)
|
|
80
110
|
@substitutions = substitutions
|
|
81
111
|
@pattern = Regexp.union(substitutions.keys)
|
|
82
112
|
end
|
|
83
113
|
|
|
114
|
+
# Apply the filter to the given text. This will replace all occurrences of the pattern with the corresponding substitution.
|
|
115
|
+
#
|
|
116
|
+
# @parameter text [String] The text to filter.
|
|
117
|
+
# @returns [String] The filtered text.
|
|
84
118
|
def call(text)
|
|
85
119
|
text.gsub(@pattern, @substitutions)
|
|
86
120
|
end
|
|
87
121
|
end
|
|
88
122
|
|
|
123
|
+
# Write a message to the output, filtering sensitive information if necessary.
|
|
124
|
+
#
|
|
125
|
+
# @parameter subject [String] The subject of the message.
|
|
126
|
+
# @parameter arguments [Array] The arguments to output.
|
|
127
|
+
# @parameter sensitive [Boolean | Filter | Hash] Whether to filter sensitive information.
|
|
128
|
+
# @parameter options [Hash] Additional options to pass to the output.
|
|
129
|
+
# @parameter block [Proc] An optional block to pass to the output.
|
|
89
130
|
def call(subject = nil, *arguments, sensitive: true, **options, &block)
|
|
90
131
|
if sensitive
|
|
91
132
|
if sensitive.respond_to?(:call)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# Released under the MIT License.
|
|
4
|
-
# Copyright, 2019-
|
|
4
|
+
# Copyright, 2019-2025, by Samuel Williams.
|
|
5
5
|
|
|
6
6
|
require_relative "../format"
|
|
7
7
|
require "time"
|
|
@@ -9,9 +9,15 @@ require "fiber/annotation"
|
|
|
9
9
|
|
|
10
10
|
module Console
|
|
11
11
|
module Output
|
|
12
|
+
# Serialize log messages in a structured format.
|
|
12
13
|
class Serialized
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
# Create a new serialized output.
|
|
15
|
+
#
|
|
16
|
+
# @parameter io [IO] The output stream.
|
|
17
|
+
# @parameter format [Console::Format] The format to use for serializing log messages.
|
|
18
|
+
# @parameter options [Hash] Additional options to customize the output.
|
|
19
|
+
def initialize(stream, format: Format.default, **options)
|
|
20
|
+
@stream = stream
|
|
15
21
|
@format = format
|
|
16
22
|
end
|
|
17
23
|
|
|
@@ -20,21 +26,38 @@ module Console
|
|
|
20
26
|
self
|
|
21
27
|
end
|
|
22
28
|
|
|
23
|
-
|
|
29
|
+
# @attribute [IO] The output stream.
|
|
30
|
+
attr :stream
|
|
31
|
+
|
|
32
|
+
# @attribute [Console::Format] The format to use for serializing log messages.
|
|
24
33
|
attr :format
|
|
25
34
|
|
|
35
|
+
# Serialize the given record.
|
|
36
|
+
#
|
|
37
|
+
# @parameter record [Hash] The record to serialize.
|
|
38
|
+
# @returns [String] The serialized record.
|
|
26
39
|
def dump(record)
|
|
27
40
|
@format.dump(record)
|
|
28
41
|
end
|
|
29
42
|
|
|
43
|
+
# Output the given log message.
|
|
44
|
+
#
|
|
45
|
+
# @parameter subject [String] The subject of the log message.
|
|
46
|
+
# @parameter arguments [Array] The arguments to log.
|
|
47
|
+
# @parameter severity [Symbol] The severity of the log message.
|
|
48
|
+
# @parameter options [Hash] Additional options.
|
|
49
|
+
# @parameter block [Proc] An optional block used to generate the log message.
|
|
30
50
|
def call(subject = nil, *arguments, severity: UNKNOWN, **options, &block)
|
|
31
51
|
record = {
|
|
32
52
|
time: Time.now.iso8601,
|
|
33
53
|
severity: severity,
|
|
34
|
-
|
|
35
|
-
|
|
54
|
+
process_id: Process.pid,
|
|
55
|
+
fiber_id: Fiber.current.object_id,
|
|
36
56
|
}
|
|
37
57
|
|
|
58
|
+
# For backwards compatibility:
|
|
59
|
+
record[:pid] = record[:process_id]
|
|
60
|
+
|
|
38
61
|
# We want to log just a brief subject:
|
|
39
62
|
if subject.is_a?(String)
|
|
40
63
|
record[:subject] = subject
|
|
@@ -42,6 +65,7 @@ module Console
|
|
|
42
65
|
record[:subject] = subject.name
|
|
43
66
|
else
|
|
44
67
|
record[:subject] = subject.class.name
|
|
68
|
+
record[:object_id] = subject.object_id
|
|
45
69
|
end
|
|
46
70
|
|
|
47
71
|
if annotation = Fiber.current.annotation
|
|
@@ -68,10 +92,11 @@ module Console
|
|
|
68
92
|
|
|
69
93
|
record.update(options)
|
|
70
94
|
|
|
71
|
-
@
|
|
95
|
+
@stream.write(self.dump(record) << "\n")
|
|
72
96
|
end
|
|
73
97
|
end
|
|
74
98
|
|
|
99
|
+
# @deprecated This is a legacy constant, please use `Serialized` instead.
|
|
75
100
|
JSON = Serialized
|
|
76
101
|
end
|
|
77
102
|
end
|
data/lib/console/output/split.rb
CHANGED
|
@@ -1,23 +1,34 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# Released under the MIT License.
|
|
4
|
-
# Copyright, 2022-
|
|
4
|
+
# Copyright, 2022-2025, by Samuel Williams.
|
|
5
5
|
|
|
6
6
|
module Console
|
|
7
7
|
module Output
|
|
8
|
+
# Split output into multiple outputs.
|
|
8
9
|
class Split
|
|
10
|
+
# Create a new split output.
|
|
11
|
+
#
|
|
12
|
+
# @parameter outputs [Array(Console::Output)] The outputs to split into.
|
|
9
13
|
def self.[](*outputs)
|
|
10
14
|
self.new(outputs)
|
|
11
15
|
end
|
|
12
16
|
|
|
17
|
+
# Create a new split output.
|
|
18
|
+
#
|
|
19
|
+
# @parameter outputs [Array(Console::Output)] The outputs to split into.
|
|
13
20
|
def initialize(outputs)
|
|
14
21
|
@outputs = outputs
|
|
15
22
|
end
|
|
16
23
|
|
|
24
|
+
# Set the verbose flag for all outputs.
|
|
25
|
+
#
|
|
26
|
+
# @parameter value [Boolean] The new value.
|
|
17
27
|
def verbose!(value = true)
|
|
18
28
|
@outputs.each{|output| output.verbose!(value)}
|
|
19
29
|
end
|
|
20
30
|
|
|
31
|
+
# Invoke the outputs. If a block is used, it may be invoked multiple times.
|
|
21
32
|
def call(...)
|
|
22
33
|
@outputs.each do |output|
|
|
23
34
|
output.call(...)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# Released under the MIT License.
|
|
4
|
-
# Copyright, 2019-
|
|
4
|
+
# Copyright, 2019-2025, by Samuel Williams.
|
|
5
5
|
# Copyright, 2021, by Robert Schulze.
|
|
6
6
|
|
|
7
7
|
require_relative "../clock"
|
|
@@ -14,48 +14,66 @@ require "stringio"
|
|
|
14
14
|
|
|
15
15
|
module Console
|
|
16
16
|
module Output
|
|
17
|
+
# Represents a terminal output, and formats log messages for display.
|
|
17
18
|
class Terminal
|
|
19
|
+
# Represents an output buffer that formats lines with a prefix.
|
|
18
20
|
class Buffer < StringIO
|
|
21
|
+
# Create a new buffer with the given prefix.
|
|
22
|
+
#
|
|
23
|
+
# @parameter prefix [String] The prefix to use for each line.
|
|
19
24
|
def initialize(prefix = nil)
|
|
20
25
|
@prefix = prefix
|
|
21
26
|
|
|
22
27
|
super()
|
|
23
28
|
end
|
|
24
29
|
|
|
30
|
+
# @attribute [String] The prefix to use for each line.
|
|
25
31
|
attr :prefix
|
|
26
32
|
|
|
27
|
-
|
|
28
|
-
|
|
33
|
+
# Write lines using the given prefix.
|
|
34
|
+
#
|
|
35
|
+
# @parameter lines [Array] The lines to write.
|
|
36
|
+
# @parameter prefix [String] The prefix to use for each line.
|
|
37
|
+
def puts(*lines, prefix: @prefix)
|
|
38
|
+
lines.each do |line|
|
|
29
39
|
self.write(prefix) if prefix
|
|
30
|
-
super(
|
|
40
|
+
super(line)
|
|
31
41
|
end
|
|
32
42
|
end
|
|
33
43
|
|
|
44
|
+
# Write a line to the buffer.
|
|
34
45
|
alias << puts
|
|
35
46
|
end
|
|
36
47
|
|
|
37
|
-
#
|
|
48
|
+
# The environment variable used to store the start time of the console terminal output.
|
|
38
49
|
CONSOLE_START_AT = "CONSOLE_START_AT"
|
|
39
50
|
|
|
40
|
-
# Exports
|
|
41
|
-
def self.start_at!(
|
|
42
|
-
if time_string =
|
|
51
|
+
# Exports CONSOLE_START_AT which can be used to synchronize the start times of all child processes when they log using delta time.
|
|
52
|
+
def self.start_at!(env = ENV)
|
|
53
|
+
if time_string = env[CONSOLE_START_AT]
|
|
43
54
|
start_at = Time.parse(time_string) rescue nil
|
|
44
55
|
end
|
|
45
56
|
|
|
46
57
|
unless start_at
|
|
47
58
|
start_at = Time.now
|
|
48
|
-
|
|
59
|
+
env[CONSOLE_START_AT] = start_at.to_s
|
|
49
60
|
end
|
|
50
61
|
|
|
51
62
|
return start_at
|
|
52
63
|
end
|
|
53
64
|
|
|
54
|
-
|
|
55
|
-
|
|
65
|
+
# Create a new terminal output.
|
|
66
|
+
#
|
|
67
|
+
# @parameter stream [IO] The output stream.
|
|
68
|
+
# @parameter verbose [Boolean] Whether to print verbose output.
|
|
69
|
+
# @parameter start_at [Time] The start time of the terminal output.
|
|
70
|
+
# @parameter format [Console::Terminal::Format] The format to use for terminal output.
|
|
71
|
+
# @parameter options [Hash] Additional options to customize the output.
|
|
72
|
+
def initialize(stream, verbose: nil, start_at: Terminal.start_at!, format: nil, **options)
|
|
73
|
+
@stream = stream
|
|
56
74
|
@start_at = start_at
|
|
57
75
|
|
|
58
|
-
@terminal = format.nil? ? Console::Terminal.for(@
|
|
76
|
+
@terminal = format.nil? ? Console::Terminal.for(@stream) : format.new(@stream)
|
|
59
77
|
|
|
60
78
|
if verbose.nil?
|
|
61
79
|
@verbose = !@terminal.colors?
|
|
@@ -78,22 +96,31 @@ module Console
|
|
|
78
96
|
self.register_formatters
|
|
79
97
|
end
|
|
80
98
|
|
|
81
|
-
# This a final output
|
|
99
|
+
# This a final output.
|
|
82
100
|
def last_output
|
|
83
101
|
self
|
|
84
102
|
end
|
|
85
103
|
|
|
86
|
-
|
|
104
|
+
# @attribute [IO] The output stream.
|
|
105
|
+
attr :stream
|
|
87
106
|
|
|
107
|
+
# @attribute [Boolean] Whether to print verbose output.
|
|
88
108
|
attr_accessor :verbose
|
|
89
109
|
|
|
110
|
+
# @attribute [Time] The start time of the terminal output.
|
|
90
111
|
attr :start
|
|
112
|
+
|
|
113
|
+
# @attribute [Console::Terminal::Format] The format to use for terminal output.
|
|
91
114
|
attr :terminal
|
|
92
115
|
|
|
116
|
+
# Set the verbose output.
|
|
117
|
+
#
|
|
118
|
+
# @parameter value [Boolean] Whether to print verbose output.
|
|
93
119
|
def verbose!(value = true)
|
|
94
120
|
@verbose = value
|
|
95
121
|
end
|
|
96
122
|
|
|
123
|
+
# Register all formatters in the given namespace.
|
|
97
124
|
def register_formatters(namespace = Console::Terminal::Formatter)
|
|
98
125
|
namespace.constants.each do |name|
|
|
99
126
|
formatter = namespace.const_get(name)
|
|
@@ -101,8 +128,20 @@ module Console
|
|
|
101
128
|
end
|
|
102
129
|
end
|
|
103
130
|
|
|
131
|
+
# The default severity for log messages, if not specified.
|
|
104
132
|
UNKNOWN = :unknown
|
|
105
133
|
|
|
134
|
+
# Log a message with the given severity.
|
|
135
|
+
#
|
|
136
|
+
# @parameter subject [String] The subject of the log message.
|
|
137
|
+
# @parameter arguments [Array] The arguments to log.
|
|
138
|
+
# @parameter name [String | Nil] The optional name of the log message, used as a prefix, otherwise defaults to the severity name.
|
|
139
|
+
# @parameter severity [Symbol] The severity of the log message.
|
|
140
|
+
# @parameter event [Hash] The event to log.
|
|
141
|
+
# @parameter options [Hash] Additional options.
|
|
142
|
+
# @yields {|buffer, terminal| ...} An optional block used to generate the log message.
|
|
143
|
+
# @parameter buffer [Console::Output::Terminal::Buffer] The output buffer.
|
|
144
|
+
# @parameter terminal [Console::Terminal] The terminal instance.
|
|
106
145
|
def call(subject = nil, *arguments, name: nil, severity: UNKNOWN, event: nil, **options, &block)
|
|
107
146
|
width = @terminal.width
|
|
108
147
|
|
|
@@ -134,7 +173,7 @@ module Console
|
|
|
134
173
|
format_options(options, buffer)
|
|
135
174
|
end
|
|
136
175
|
|
|
137
|
-
@
|
|
176
|
+
@stream.write buffer.string
|
|
138
177
|
end
|
|
139
178
|
|
|
140
179
|
protected
|
|
@@ -242,13 +281,27 @@ module Console
|
|
|
242
281
|
end
|
|
243
282
|
end
|
|
244
283
|
|
|
284
|
+
# Terminal text output.
|
|
245
285
|
module Text
|
|
286
|
+
# Create a new terminal output.
|
|
287
|
+
#
|
|
288
|
+
# @parameter output [IO] The output stream.
|
|
289
|
+
# @parameter options [Hash] Additional options to customize the output.
|
|
290
|
+
# @returns [Console::Output::Terminal] The terminal output instance.
|
|
246
291
|
def self.new(output, **options)
|
|
247
292
|
Terminal.new(output, format: Console::Terminal::Text, **options)
|
|
248
293
|
end
|
|
249
294
|
end
|
|
250
295
|
|
|
296
|
+
# Terminal XTerm output.
|
|
251
297
|
module XTerm
|
|
298
|
+
# Create a new terminal output.
|
|
299
|
+
#
|
|
300
|
+
# You can use this to force XTerm output on a non-TTY output streams that support XTerm escape codes.
|
|
301
|
+
#
|
|
302
|
+
# @parameter output [IO] The output stream.
|
|
303
|
+
# @parameter options [Hash] Additional options to customize the output.
|
|
304
|
+
# @returns [Console::Output::Terminal] The terminal output instance.
|
|
252
305
|
def self.new(output, **options)
|
|
253
306
|
Terminal.new(output, format: Console::Terminal::XTerm, **options)
|
|
254
307
|
end
|
|
@@ -1,25 +1,36 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# Released under the MIT License.
|
|
4
|
-
# Copyright, 2021-
|
|
4
|
+
# Copyright, 2021-2025, by Samuel Williams.
|
|
5
5
|
|
|
6
6
|
module Console
|
|
7
7
|
module Output
|
|
8
|
+
# A generic wrapper for output handling.
|
|
8
9
|
class Wrapper
|
|
10
|
+
# Create a new wrapper output.
|
|
11
|
+
#
|
|
12
|
+
# @parameter delegate [Console::Output] The output to delegate to.
|
|
13
|
+
# @parameter options [Hash] Additional options to customize the output.
|
|
9
14
|
def initialize(delegate, **options)
|
|
10
15
|
@delegate = delegate
|
|
11
16
|
end
|
|
12
17
|
|
|
18
|
+
# @attribute [Console::Output] The output to delegate to.
|
|
13
19
|
attr :delegate
|
|
14
20
|
|
|
21
|
+
# The last output is the last output of the delegate.
|
|
15
22
|
def last_output
|
|
16
23
|
@delegate.last_output
|
|
17
24
|
end
|
|
18
25
|
|
|
26
|
+
# Set the verbose flag for the delegate.
|
|
27
|
+
#
|
|
28
|
+
# @parameter value [Boolean] The new value.
|
|
19
29
|
def verbose!(value = true)
|
|
20
30
|
@delegate.verbose!(value)
|
|
21
31
|
end
|
|
22
32
|
|
|
33
|
+
# Invoke the delegate.
|
|
23
34
|
def call(...)
|
|
24
35
|
@delegate.call(...)
|
|
25
36
|
end
|
data/lib/console/output.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# Released under the MIT License.
|
|
4
|
-
# Copyright, 2021-
|
|
4
|
+
# Copyright, 2021-2025, by Samuel Williams.
|
|
5
5
|
|
|
6
6
|
require_relative "output/default"
|
|
7
7
|
require_relative "output/serialized"
|
|
@@ -9,16 +9,27 @@ require_relative "output/terminal"
|
|
|
9
9
|
require_relative "output/null"
|
|
10
10
|
|
|
11
11
|
module Console
|
|
12
|
+
# Output handling.
|
|
12
13
|
module Output
|
|
14
|
+
# Create a new output based on the environment.
|
|
15
|
+
#
|
|
16
|
+
# The environment variable `CONSOLE_OUTPUT` can be used to specify a list of output classes to wrap around the output. If not specified the {Default} output is used.
|
|
17
|
+
#
|
|
18
|
+
# The output argument is deliberately unders-specified but can be an IO object or an instance of {Output}.
|
|
19
|
+
#
|
|
20
|
+
# @parameter output [Console::Output] The output to wrap OR an IO object.
|
|
21
|
+
# @parameter env [Hash] The environment to read configuration from.
|
|
22
|
+
# @parameter options [Hash] Additional options to customize the output.
|
|
23
|
+
# @returns [Console::Output] The output instance.
|
|
13
24
|
def self.new(output = nil, env = ENV, **options)
|
|
14
25
|
if names = env["CONSOLE_OUTPUT"]
|
|
15
26
|
names = names.split(",").reverse
|
|
16
27
|
|
|
17
28
|
names.inject(output) do |output, name|
|
|
18
|
-
Output.const_get(name).new(output, **options)
|
|
29
|
+
Output.const_get(name).new(output, env: env, **options)
|
|
19
30
|
end
|
|
20
31
|
else
|
|
21
|
-
return Output::Default.new(output, **options)
|
|
32
|
+
return Output::Default.new(output, env: env, **options)
|
|
22
33
|
end
|
|
23
34
|
end
|
|
24
35
|
end
|
data/lib/console/progress.rb
CHANGED
|
@@ -1,22 +1,30 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# Released under the MIT License.
|
|
4
|
-
# Copyright, 2020-
|
|
4
|
+
# Copyright, 2020-2025, by Samuel Williams.
|
|
5
5
|
# Copyright, 2022, by Anton Sozontov.
|
|
6
6
|
|
|
7
7
|
require_relative "clock"
|
|
8
8
|
|
|
9
9
|
module Console
|
|
10
|
+
# A simple progress indicator
|
|
10
11
|
class Progress
|
|
12
|
+
# @deprecated Use {Clock.now} instead.
|
|
11
13
|
def self.now
|
|
12
|
-
|
|
14
|
+
Clock.now
|
|
13
15
|
end
|
|
14
16
|
|
|
17
|
+
# Create a new progress indicator.
|
|
18
|
+
#
|
|
19
|
+
# @parameter subject [Object] The subject of the progress indicator.
|
|
20
|
+
# @parameter total [Integer] The total number of steps.
|
|
21
|
+
# @parameter minimum_output_duration [Numeric] The minimum duration between outputs.
|
|
22
|
+
# @parameter options [Hash] Additional options to customize the output.
|
|
15
23
|
def initialize(subject, total = 0, minimum_output_duration: 0.1, **options)
|
|
16
24
|
@subject = subject
|
|
17
25
|
@options = options
|
|
18
26
|
|
|
19
|
-
@start_time =
|
|
27
|
+
@start_time = Clock.now
|
|
20
28
|
|
|
21
29
|
@last_output_time = nil
|
|
22
30
|
@minimum_output_duration = minimum_output_duration
|
|
@@ -25,34 +33,53 @@ module Console
|
|
|
25
33
|
@total = total
|
|
26
34
|
end
|
|
27
35
|
|
|
36
|
+
# @attribute [Object] The subject of the progress indicator.
|
|
28
37
|
attr :subject
|
|
38
|
+
|
|
39
|
+
# @attribute [Numeric] The minimum duration between outputs.
|
|
40
|
+
attr :minimum_output_duration
|
|
41
|
+
|
|
42
|
+
# @attribute [Time] The time the progress indicator was started.
|
|
43
|
+
attr :start_time
|
|
44
|
+
|
|
45
|
+
# @attribute [Numeric] The current number of steps completed.
|
|
29
46
|
attr :current
|
|
47
|
+
|
|
48
|
+
# @attribute [Numeric] The total number of steps.
|
|
30
49
|
attr :total
|
|
31
50
|
|
|
51
|
+
# @returns [Numeric] The duration since the progress indicator was started.
|
|
32
52
|
def duration
|
|
33
|
-
|
|
53
|
+
Clock.now - @start_time
|
|
34
54
|
end
|
|
35
55
|
|
|
56
|
+
# @returns [Rational] The ratio of steps completed to total steps.
|
|
36
57
|
def ratio
|
|
37
58
|
Rational(@current.to_f, @total.to_f)
|
|
38
59
|
end
|
|
39
60
|
|
|
61
|
+
# @returns [Numeric] The number of steps remaining.
|
|
40
62
|
def remaining
|
|
41
63
|
@total - @current
|
|
42
64
|
end
|
|
43
65
|
|
|
66
|
+
# @returns [Numeric | Nil] The average duration per step, or `nil` if no steps have been completed.
|
|
44
67
|
def average_duration
|
|
45
68
|
if @current > 0
|
|
46
69
|
duration / @current
|
|
47
70
|
end
|
|
48
71
|
end
|
|
49
72
|
|
|
73
|
+
# @returns [Numeric | Nil] The estimated remaining time, or `nil` if no steps have been completed.
|
|
50
74
|
def estimated_remaining_time
|
|
51
75
|
if average_duration = self.average_duration
|
|
52
76
|
average_duration * remaining
|
|
53
77
|
end
|
|
54
78
|
end
|
|
55
79
|
|
|
80
|
+
# Generate an appropriate event for the progress indicator.
|
|
81
|
+
#
|
|
82
|
+
# @returns [Hash] The progress indicator as a hash.
|
|
56
83
|
def to_hash
|
|
57
84
|
Hash.new.tap do |hash|
|
|
58
85
|
hash[:type] = :progress
|
|
@@ -64,30 +91,44 @@ module Console
|
|
|
64
91
|
end
|
|
65
92
|
end
|
|
66
93
|
|
|
94
|
+
# Increment the progress indicator by the given amount.
|
|
95
|
+
#
|
|
96
|
+
# @parameter amount [Numeric] The amount to increment by.
|
|
97
|
+
# @returns [Progress] The progress indicator itself.
|
|
67
98
|
def increment(amount = 1)
|
|
68
99
|
@current += amount
|
|
69
100
|
|
|
70
101
|
if output?
|
|
71
102
|
Console.call(@subject, self.to_s, event: self.to_hash, **@options)
|
|
72
|
-
@last_output_time =
|
|
103
|
+
@last_output_time = Clock.now
|
|
73
104
|
end
|
|
74
105
|
|
|
75
106
|
return self
|
|
76
107
|
end
|
|
77
108
|
|
|
109
|
+
# Resize the progress indicator to the given total.
|
|
110
|
+
#
|
|
111
|
+
# @parameter total [Numeric] The new total number of steps.
|
|
112
|
+
# @returns [Progress] The progress indicator itself.
|
|
78
113
|
def resize(total)
|
|
79
114
|
@total = total
|
|
80
115
|
|
|
81
116
|
Console.call(@subject, self.to_s, event: self.to_hash, **@options)
|
|
82
|
-
@last_output_time =
|
|
117
|
+
@last_output_time = Clock.now
|
|
83
118
|
|
|
84
119
|
return self
|
|
85
120
|
end
|
|
86
121
|
|
|
87
|
-
|
|
88
|
-
|
|
122
|
+
# Augment the progress indicator with additional information.
|
|
123
|
+
#
|
|
124
|
+
# @parameter *arguments [Array] The arguments to log.
|
|
125
|
+
# @parameter **options [Hash] Additional options to log.
|
|
126
|
+
# @parameter &block [Proc] An optional block used to generate the log message.
|
|
127
|
+
def mark(*arguments, **options, &block)
|
|
128
|
+
Console.call(@subject, *arguments, **options, **@options, &block)
|
|
89
129
|
end
|
|
90
130
|
|
|
131
|
+
# @returns [String] A human-readable representation of the progress indicator.
|
|
91
132
|
def to_s
|
|
92
133
|
if estimated_remaining_time = self.estimated_remaining_time
|
|
93
134
|
"#{@current}/#{@total} completed in #{Clock.formatted_duration(self.duration)}, #{Clock.formatted_duration(estimated_remaining_time)} remaining."
|
|
@@ -98,12 +139,18 @@ module Console
|
|
|
98
139
|
|
|
99
140
|
private
|
|
100
141
|
|
|
142
|
+
# Compute a time delta since the last output, used for rate limiting the output.
|
|
143
|
+
#
|
|
144
|
+
# @returns [Numeric | Nil] The duration since the last output.
|
|
101
145
|
def duration_since_last_output
|
|
102
146
|
if @last_output_time
|
|
103
|
-
|
|
147
|
+
Clock.now - @last_output_time
|
|
104
148
|
end
|
|
105
149
|
end
|
|
106
150
|
|
|
151
|
+
# Whether an output should be generated at this time, taking into account the remaining steps, and the duration since the last output.
|
|
152
|
+
#
|
|
153
|
+
# @returns [Boolean] Whether an output should be generated.
|
|
107
154
|
def output?
|
|
108
155
|
if remaining.zero?
|
|
109
156
|
return true
|