console 1.24.0 → 1.25.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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