console 1.24.0 → 1.25.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.
@@ -1,53 +1,61 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2019-2023, by Samuel Williams.
4
+ # Copyright, 2019-2024, by Samuel Williams.
5
5
  # Copyright, 2021, by Robert Schulze.
6
6
 
7
- require_relative '../buffer'
8
- require_relative '../event'
9
7
  require_relative '../clock'
10
-
11
- require_relative 'text'
12
- require_relative 'xterm'
8
+ require_relative '../terminal'
13
9
 
14
10
  require 'json'
15
11
  require 'fiber'
16
12
  require 'fiber/annotation'
13
+ require 'stringio'
17
14
 
18
15
  module Console
19
- module Terminal
20
- # This, and all related methods, is considered private.
21
- CONSOLE_START_AT = 'CONSOLE_START_AT'
22
-
23
- # Exports CONSOLE_START which can be used to synchronize the start times of all child processes when they log using delta time.
24
- def self.start_at!(environment = ENV)
25
- if time_string = environment[CONSOLE_START_AT]
26
- start_at = Time.parse(time_string) rescue nil
16
+ module Output
17
+ class Terminal
18
+ class Buffer < StringIO
19
+ def initialize(prefix = nil)
20
+ @prefix = prefix
21
+
22
+ super()
23
+ end
24
+
25
+ attr :prefix
26
+
27
+ def puts(*args, prefix: @prefix)
28
+ args.each do |arg|
29
+ self.write(prefix) if prefix
30
+ super(arg)
31
+ end
32
+ end
33
+
34
+ alias << puts
27
35
  end
28
36
 
29
- unless start_at
30
- start_at = Time.now
31
- environment[CONSOLE_START_AT] = start_at.to_s
32
- end
37
+ # This, and all related methods, is considered private.
38
+ CONSOLE_START_AT = 'CONSOLE_START_AT'
33
39
 
34
- return start_at
35
- end
36
-
37
- def self.for(io)
38
- if io.isatty
39
- XTerm.new(io)
40
- else
41
- Text.new(io)
40
+ # Exports CONSOLE_START which can be used to synchronize the start times of all child processes when they log using delta time.
41
+ def self.start_at!(environment = ENV)
42
+ if time_string = environment[CONSOLE_START_AT]
43
+ start_at = Time.parse(time_string) rescue nil
44
+ end
45
+
46
+ unless start_at
47
+ start_at = Time.now
48
+ environment[CONSOLE_START_AT] = start_at.to_s
49
+ end
50
+
51
+ return start_at
42
52
  end
43
- end
44
-
45
- class Logger
46
- def initialize(io = $stderr, verbose: nil, start_at: Terminal.start_at!, format: nil, **options)
47
- @io = io
53
+
54
+ def initialize(output, verbose: nil, start_at: Terminal.start_at!, format: nil, **options)
55
+ @io = output
48
56
  @start_at = start_at
49
57
 
50
- @terminal = format.nil? ? Terminal.for(io) : format.new(io)
58
+ @terminal = format.nil? ? Console::Terminal.for(@io) : format.new(@io)
51
59
 
52
60
  if verbose.nil?
53
61
  @verbose = !@terminal.colors?
@@ -66,7 +74,8 @@ module Console
66
74
  @terminal[:annotation] = @terminal.reset
67
75
  @terminal[:value] = @terminal.style(:blue)
68
76
 
69
- self.register_defaults(@terminal)
77
+ @formatters = {}
78
+ self.register_formatters
70
79
  end
71
80
 
72
81
  attr :io
@@ -80,20 +89,23 @@ module Console
80
89
  @verbose = value
81
90
  end
82
91
 
83
- def register_defaults(terminal)
84
- Event.constants.each do |constant|
85
- klass = Event.const_get(constant)
86
- klass.register(terminal)
92
+ def register_formatters(namespace = Console::Terminal::Formatter)
93
+ namespace.constants.each do |name|
94
+ formatter = namespace.const_get(name)
95
+ @formatters[formatter::KEY] = formatter.new(@terminal)
87
96
  end
88
97
  end
89
98
 
90
99
  UNKNOWN = :unknown
91
100
 
92
- def call(subject = nil, *arguments, name: nil, severity: UNKNOWN, **options, &block)
101
+ def call(subject = nil, *arguments, name: nil, severity: UNKNOWN, event: nil, **options, &block)
102
+ width = @terminal.width
103
+
93
104
  prefix = build_prefix(name || severity.to_s)
94
105
  indent = " " * prefix.size
95
106
 
96
107
  buffer = Buffer.new("#{indent}| ")
108
+ indent_size = buffer.prefix.size
97
109
 
98
110
  format_subject(severity, prefix, subject, buffer)
99
111
 
@@ -109,6 +121,10 @@ module Console
109
121
  end
110
122
  end
111
123
 
124
+ if event
125
+ format_event(event, buffer, width - indent_size)
126
+ end
127
+
112
128
  if options&.any?
113
129
  format_options(options, buffer)
114
130
  end
@@ -118,20 +134,24 @@ module Console
118
134
 
119
135
  protected
120
136
 
137
+ def format_event(event, buffer, width)
138
+ event = event.to_hash
139
+ type = event[:type]
140
+
141
+ if formatter = @formatters[type]
142
+ formatter.format(event, buffer, verbose: @verbose, width: width)
143
+ else
144
+ format_value(::JSON.pretty_generate(event), buffer)
145
+ end
146
+ end
147
+
121
148
  def format_options(options, output)
122
149
  format_value(::JSON.pretty_generate(options), output)
123
150
  end
124
151
 
125
152
  def format_argument(argument, output)
126
- case argument
127
- when Exception
128
- Event::Failure.for(argument).format(output, @terminal, @verbose)
129
- when Event::Generic
130
- argument.format(output, @terminal, @verbose)
131
- else
132
- argument.to_s.each_line do |line|
133
- output.puts line
134
- end
153
+ argument.to_s.each_line do |line|
154
+ output.puts line
135
155
  end
136
156
  end
137
157
 
@@ -216,5 +236,17 @@ module Console
216
236
  end
217
237
  end
218
238
  end
239
+
240
+ module Text
241
+ def self.new(output, **options)
242
+ Terminal.new(output, format: Console::Terminal::Text, **options)
243
+ end
244
+ end
245
+
246
+ module XTerm
247
+ def self.new(output, **options)
248
+ Terminal.new(output, format: Console::Terminal::XTerm, **options)
249
+ end
250
+ end
219
251
  end
220
252
  end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2021-2024, by Samuel Williams.
5
+
6
+ module Console
7
+ module Output
8
+ class Wrapper
9
+ def initialize(delegate, **options)
10
+ @delegate = delegate
11
+ end
12
+
13
+ def verbose!(value = true)
14
+ @delegate.verbose!(value)
15
+ end
16
+
17
+ def call(...)
18
+ @delegate.call(...)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -1,12 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2021-2023, by Samuel Williams.
4
+ # Copyright, 2021-2024, by Samuel Williams.
5
5
 
6
6
  require_relative 'output/default'
7
- require_relative 'output/json'
8
- require_relative 'output/text'
9
- require_relative 'output/xterm'
7
+ require_relative 'output/serialized'
8
+ require_relative 'output/terminal'
10
9
  require_relative 'output/null'
11
10
 
12
11
  module Console
@@ -1,10 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2020-2023, by Samuel Williams.
4
+ # Copyright, 2020-2024, by Samuel Williams.
5
5
  # Copyright, 2022, by Anton Sozontov.
6
6
 
7
- require_relative 'event/progress'
8
7
  require_relative 'clock'
9
8
 
10
9
  module Console
@@ -13,9 +12,9 @@ module Console
13
12
  Process.clock_gettime(Process::CLOCK_MONOTONIC)
14
13
  end
15
14
 
16
- def initialize(output, subject, total = 0, minimum_output_duration: 0.1)
17
- @output = output
15
+ def initialize(subject, total = 0, minimum_output_duration: 0.1, **options)
18
16
  @subject = subject
17
+ @options = options
19
18
 
20
19
  @start_time = Progress.now
21
20
 
@@ -54,11 +53,22 @@ module Console
54
53
  end
55
54
  end
56
55
 
56
+ def to_hash
57
+ Hash.new.tap do |hash|
58
+ hash[:type] = :progress
59
+ hash[:current] = @current
60
+ hash[:total] = @total
61
+
62
+ hash[:duration] = self.duration
63
+ hash[:estimated_remaining_time] = self.estimated_remaining_time
64
+ end
65
+ end
66
+
57
67
  def increment(amount = 1)
58
68
  @current += amount
59
69
 
60
70
  if output?
61
- @output.info(@subject, self) {Event::Progress.new(@current, @total)}
71
+ Console.call(@subject, self.to_s, event: self.to_hash, **@options)
62
72
  @last_output_time = Progress.now
63
73
  end
64
74
 
@@ -68,14 +78,14 @@ module Console
68
78
  def resize(total)
69
79
  @total = total
70
80
 
71
- @output.info(@subject, self) {Event::Progress.new(@current, @total)}
81
+ Console.call(@subject, self.to_s, event: self.to_hash, **@options)
72
82
  @last_output_time = Progress.now
73
83
 
74
84
  return self
75
85
  end
76
86
 
77
- def mark(...)
78
- @output.info(@subject, ...)
87
+ def mark(*arguments, **options)
88
+ Console.call(@subject, *arguments, **options, **@options)
79
89
  end
80
90
 
81
91
  def to_s
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2024, by Samuel Williams.
5
+
6
+ module Console
7
+ module Terminal
8
+ module Formatter
9
+ class Failure
10
+ KEY = :failure
11
+
12
+ def initialize(terminal)
13
+ @terminal = terminal
14
+
15
+ @terminal[:exception_title] ||= @terminal.style(:red, nil, :bold)
16
+ @terminal[:exception_detail] ||= @terminal.style(:yellow)
17
+ @terminal[:exception_backtrace] ||= @terminal.style(:red)
18
+ @terminal[:exception_backtrace_other] ||= @terminal.style(:red, nil, :faint)
19
+ @terminal[:exception_message] ||= @terminal.style(:default)
20
+ end
21
+
22
+ def format(event, output, prefix: nil, verbose: false, width: 80)
23
+ title = event[:class]
24
+ message = event[:message]
25
+ backtrace = event[:backtrace]
26
+ root = event[:root]
27
+
28
+ lines = message.lines.map(&:chomp)
29
+
30
+ output.puts " #{prefix}#{@terminal[:exception_title]}#{title}#{@terminal.reset}: #{lines.shift}"
31
+
32
+ lines.each do |line|
33
+ output.puts " #{@terminal[:exception_detail]}#{line}#{@terminal.reset}"
34
+ end
35
+
36
+ root_pattern = /^#{root}\// if root
37
+
38
+ backtrace&.each_with_index do |line, index|
39
+ path, offset, message = line.split(":", 3)
40
+ style = :exception_backtrace
41
+
42
+ # Make the path a bit more readable:
43
+ if root_pattern and path.sub!(root_pattern, "").nil?
44
+ style = :exception_backtrace_other
45
+ end
46
+
47
+ output.puts " #{index == 0 ? "→" : " "} #{@terminal[style]}#{path}:#{offset}#{@terminal[:exception_message]} #{message}#{@terminal.reset}"
48
+ end
49
+
50
+ if cause = event[:cause]
51
+ format(cause, output, prefix: "Caused by ", verbose: verbose, width: width)
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2024, by Samuel Williams.
5
+
6
+ module Console
7
+ module Terminal
8
+ module Formatter
9
+ class Progress
10
+ KEY = :progress
11
+
12
+ BLOCK = [
13
+ " ",
14
+ "▏",
15
+ "▎",
16
+ "▍",
17
+ "▌",
18
+ "▋",
19
+ "▊",
20
+ "▉",
21
+ "█",
22
+ ]
23
+
24
+ def initialize(terminal)
25
+ @terminal = terminal
26
+ @terminal[:progress_bar] ||= terminal.style(:blue, :white)
27
+ end
28
+
29
+ def format(event, output, verbose: false, width: 80)
30
+ current = event[:current].to_f
31
+ total = event[:total].to_f
32
+ value = current / total
33
+
34
+ # Clamp value to 1.0 to avoid rendering issues:
35
+ if value > 1.0
36
+ value = 1.0
37
+ end
38
+
39
+ output.puts "#{@terminal[:progress_bar]}#{self.bar(value, width-10)}#{@terminal.reset} #{sprintf('%6.2f', value * 100)}%"
40
+ end
41
+
42
+ private
43
+
44
+ def bar(value, width)
45
+ blocks = width * value
46
+ full_blocks = blocks.floor
47
+ partial_block = ((blocks - full_blocks) * BLOCK.size).floor
48
+
49
+ if partial_block.zero?
50
+ BLOCK.last * full_blocks
51
+ else
52
+ "#{BLOCK.last * full_blocks}#{BLOCK[partial_block]}"
53
+ end.ljust(width)
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2019-2024, by Samuel Williams.
5
+
6
+ module Console
7
+ module Terminal
8
+ module Formatter
9
+ # Format spawn events.
10
+ class Spawn
11
+ KEY = :spawn
12
+
13
+ def initialize(terminal)
14
+ @terminal = terminal
15
+ @terminal[:spawn_command] ||= @terminal.style(:blue, nil, :bold)
16
+ end
17
+
18
+ def format(event, output, verbose: false, width: 80)
19
+ environment, arguments, options = event.values_at(:environment, :arguments, :options)
20
+
21
+ arguments = arguments.flatten.collect(&:to_s)
22
+
23
+ output.puts "#{@terminal[:spawn_command]}#{arguments.join(' ')}#{@terminal.reset}#{chdir_string(options)}"
24
+
25
+ if verbose and environment
26
+ environment.each do |key, value|
27
+ output.puts "export #{key}=#{value}"
28
+ end
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ def chdir_string(options)
35
+ if options and chdir = options[:chdir]
36
+ " in #{chdir}"
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2019-2022, by Samuel Williams.
4
+ # Copyright, 2019-2024, by Samuel Williams.
5
5
 
6
6
  require 'io/console'
7
7
 
@@ -26,6 +26,10 @@ module Console
26
26
  false
27
27
  end
28
28
 
29
+ def width
30
+ 80
31
+ end
32
+
29
33
  def style(foreground, background = nil, *attributes)
30
34
  end
31
35
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2019-2023, by Samuel Williams.
4
+ # Copyright, 2019-2024, by Samuel Williams.
5
5
 
6
6
  require 'io/console'
7
7
 
@@ -41,6 +41,13 @@ module Console
41
41
 
42
42
  def size
43
43
  @output.winsize
44
+ rescue Errno::ENOTTY
45
+ # Fake it...
46
+ [24, 80]
47
+ end
48
+
49
+ def width
50
+ size.last
44
51
  end
45
52
 
46
53
  def style(foreground, background = nil, *attributes)
@@ -1,6 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2019-2022, by Samuel Williams.
4
+ # Copyright, 2019-2024, by Samuel Williams.
5
5
 
6
- require_relative 'terminal/logger'
6
+ require_relative 'terminal/text'
7
+ require_relative 'terminal/xterm'
8
+
9
+ require_relative 'terminal/formatter/progress'
10
+ require_relative 'terminal/formatter/failure'
11
+ require_relative 'terminal/formatter/spawn'
12
+
13
+ module Console
14
+ module Terminal
15
+ def self.for(io)
16
+ if io.tty?
17
+ XTerm.new(io)
18
+ else
19
+ Text.new(io)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2019-2023, by Samuel Williams.
4
+ # Copyright, 2019-2024, by Samuel Williams.
5
5
 
6
6
  module Console
7
- VERSION = "1.24.0"
7
+ VERSION = "1.25.0"
8
8
  end
data/lib/console.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2019-2023, by Samuel Williams.
4
+ # Copyright, 2019-2024, by Samuel Williams.
5
5
  # Copyright, 2019, by Bryan Powell.
6
6
  # Copyright, 2020, by Michael Adams.
7
7
  # Copyright, 2021, by Cédric Boutillier.
@@ -43,12 +43,4 @@ module Console
43
43
  Logger.instance.call(...)
44
44
  end
45
45
  end
46
-
47
- def logger= logger
48
- warn "Setting logger on #{self} is deprecated. Use Console.logger= instead.", uplevel: 1
49
- end
50
-
51
- def logger
52
- Logger.instance
53
- end
54
46
  end
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: console
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.24.0
4
+ version: 1.25.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
@@ -46,7 +46,7 @@ cert_chain:
46
46
  Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
47
47
  voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
48
48
  -----END CERTIFICATE-----
49
- date: 2024-04-22 00:00:00.000000000 Z
49
+ date: 2024-05-03 00:00:00.000000000 Z
50
50
  dependencies:
51
51
  - !ruby/object:Gem::Dependency
52
52
  name: fiber-annotation
@@ -66,16 +66,16 @@ dependencies:
66
66
  name: fiber-local
67
67
  requirement: !ruby/object:Gem::Requirement
68
68
  requirements:
69
- - - ">="
69
+ - - "~>"
70
70
  - !ruby/object:Gem::Version
71
- version: '0'
71
+ version: '1.1'
72
72
  type: :runtime
73
73
  prerelease: false
74
74
  version_requirements: !ruby/object:Gem::Requirement
75
75
  requirements:
76
- - - ">="
76
+ - - "~>"
77
77
  - !ruby/object:Gem::Version
78
- version: '0'
78
+ version: '1.1'
79
79
  - !ruby/object:Gem::Dependency
80
80
  name: json
81
81
  requirement: !ruby/object:Gem::Requirement
@@ -99,14 +99,12 @@ files:
99
99
  - bake/console.rb
100
100
  - lib/console.rb
101
101
  - lib/console/adapter.rb
102
- - lib/console/buffer.rb
103
102
  - lib/console/capture.rb
104
103
  - lib/console/clock.rb
105
104
  - lib/console/compatible/logger.rb
106
105
  - lib/console/event.rb
107
106
  - lib/console/event/failure.rb
108
107
  - lib/console/event/generic.rb
109
- - lib/console/event/progress.rb
110
108
  - lib/console/event/spawn.rb
111
109
  - lib/console/filter.rb
112
110
  - lib/console/format.rb
@@ -114,27 +112,28 @@ files:
114
112
  - lib/console/logger.rb
115
113
  - lib/console/output.rb
116
114
  - lib/console/output/default.rb
117
- - lib/console/output/json.rb
118
115
  - lib/console/output/null.rb
119
116
  - lib/console/output/sensitive.rb
117
+ - lib/console/output/serialized.rb
120
118
  - lib/console/output/split.rb
121
- - lib/console/output/text.rb
122
- - lib/console/output/xterm.rb
119
+ - lib/console/output/terminal.rb
120
+ - lib/console/output/wrapper.rb
123
121
  - lib/console/progress.rb
124
122
  - lib/console/resolver.rb
125
- - lib/console/serialized/logger.rb
126
- - lib/console/split.rb
127
123
  - lib/console/terminal.rb
128
- - lib/console/terminal/logger.rb
124
+ - lib/console/terminal/formatter/failure.rb
125
+ - lib/console/terminal/formatter/progress.rb
126
+ - lib/console/terminal/formatter/spawn.rb
129
127
  - lib/console/terminal/text.rb
130
128
  - lib/console/terminal/xterm.rb
131
129
  - lib/console/version.rb
132
130
  - license.md
133
131
  - readme.md
134
- homepage: https://socketry.github.io/console/
132
+ homepage: https://socketry.github.io/console
135
133
  licenses:
136
134
  - MIT
137
- metadata: {}
135
+ metadata:
136
+ documentation_uri: https://socketry.github.io/console/
138
137
  post_install_message:
139
138
  rdoc_options: []
140
139
  require_paths:
@@ -143,7 +142,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
143
142
  requirements:
144
143
  - - ">="
145
144
  - !ruby/object:Gem::Version
146
- version: '3.0'
145
+ version: '3.1'
147
146
  required_rubygems_version: !ruby/object:Gem::Requirement
148
147
  requirements:
149
148
  - - ">="
metadata.gz.sig CHANGED
Binary file
@@ -1,25 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Released under the MIT License.
4
- # Copyright, 2019-2022, by Samuel Williams.
5
-
6
- require 'stringio'
7
-
8
- module Console
9
- class Buffer < StringIO
10
- def initialize(prefix = nil)
11
- @prefix = prefix
12
-
13
- super()
14
- end
15
-
16
- def puts(*args, prefix: @prefix)
17
- args.each do |arg|
18
- self.write(prefix) if prefix
19
- super(arg)
20
- end
21
- end
22
-
23
- alias << puts
24
- end
25
- end