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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9a62f7c19673292c5cd3c0d8b9d93350ca4b36ac0a5c0a0466deef6543b388ba
4
- data.tar.gz: f382353a7b052c056936fdd2c95ad0bd20cfb8619db2f6d2af1ce7e9e0cd7967
3
+ metadata.gz: f91958a87f7e55b773b421ba83cedd33c65d8668fc9be859b8a49af0ed93dcd4
4
+ data.tar.gz: fe5c31ba8acb886bbdde2c28b8b958f64dd0bc376d76fa89c79685a5239ab267
5
5
  SHA512:
6
- metadata.gz: 6638ede52de5f0954a38adf71e910997eb643d2e39ef59645f320ce16ebefd4f804cb062e47e03472ac742fb46591c310217da72eb63eb87fe91d93090f8ee5f
7
- data.tar.gz: 15f4929956b45e0c445e95774dfa3ac1c4bb485401e1b0003835c9ae5435a25ebda70b69ad475cf0e6b48131b0cc41258e3c5bb8c70121daaa9fe482c12b5404
6
+ metadata.gz: bddf649708410d79d0d979342bdd98c1c4bb1687ddbe79c8b0f1da8de36aab881872bb3b086256083044e366627c111fd642146c6781d4bc669f9d26511cc4a4
7
+ data.tar.gz: 49a2cc984ba416722d712eaf8133e1ce1e8be95fd36f056e57a157539b6e1033f6a1a9074b9b7283510c2fa8214db3ba8bfa70de4971bd9fe0227e15a6dab32d
checksums.yaml.gz.sig CHANGED
Binary file
@@ -1,9 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2023, by Samuel Williams.
4
+ # Copyright, 2023-2024, by Samuel Williams.
5
5
 
6
6
  module Console
7
+ # This namespace is reserved for logging adapters provided by other gems.
7
8
  module Adapter
8
9
  end
9
10
  end
@@ -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_relative 'filter'
7
7
 
@@ -40,7 +40,7 @@ module Console
40
40
  @verbose
41
41
  end
42
42
 
43
- def call(subject = nil, *arguments, severity: UNKNOWN, **options, &block)
43
+ def call(subject = nil, *arguments, severity: UNKNOWN, event: nil, **options, &block)
44
44
  message = {
45
45
  time: ::Time.now.iso8601,
46
46
  severity: severity,
@@ -51,6 +51,10 @@ module Console
51
51
  message[:subject] = subject
52
52
  end
53
53
 
54
+ if event
55
+ message[:event] = event.to_hash
56
+ end
57
+
54
58
  if arguments.any?
55
59
  message[:arguments] = arguments
56
60
  end
@@ -1,12 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2022-2023, by Samuel Williams.
4
+ # Copyright, 2022-2024, by Samuel Williams.
5
5
 
6
6
  require 'logger'
7
7
 
8
8
  module Console
9
9
  module Compatible
10
+ # A compatible interface for {::Logger} which can be used with {Console}.
10
11
  class Logger < ::Logger
11
12
  class LogDevice
12
13
  def initialize(subject, output)
@@ -1,83 +1,74 @@
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
  # Copyright, 2021, by Robert Schulze.
6
6
 
7
7
  require_relative 'generic'
8
8
 
9
9
  module Console
10
10
  module Event
11
+ # Represents a failure event.
12
+ #
13
+ # ```ruby
14
+ # Console::Event::Failure.for(exception).emit(self)
15
+ # ```
11
16
  class Failure < Generic
12
- def self.current_working_directory
17
+ def self.default_root
13
18
  Dir.getwd
14
19
  rescue # e.g. Errno::EMFILE
15
20
  nil
16
21
  end
17
22
 
18
23
  def self.for(exception)
19
- self.new(exception, self.current_working_directory)
24
+ self.new(exception, self.default_root)
20
25
  end
21
26
 
22
- def initialize(exception, root = nil)
27
+ def self.log(subject, exception, **options)
28
+ Console.error(subject, **self.for(exception).to_hash, **options)
29
+ end
30
+
31
+ def initialize(exception, root = Dir.getwd)
23
32
  @exception = exception
24
33
  @root = root
25
34
  end
26
35
 
27
- attr :exception
28
- attr :root
29
-
30
- def self.register(terminal)
31
- terminal[:exception_title] ||= terminal.style(:red, nil, :bold)
32
- terminal[:exception_detail] ||= terminal.style(:yellow)
33
- terminal[:exception_backtrace] ||= terminal.style(:red)
34
- terminal[:exception_backtrace_other] ||= terminal.style(:red, nil, :faint)
35
- terminal[:exception_message] ||= terminal.style(:default)
36
+ def to_hash
37
+ Hash.new.tap do |hash|
38
+ hash[:type] = :failure
39
+ hash[:root] = @root if @root
40
+ extract(@exception, hash)
41
+ end
36
42
  end
37
43
 
38
- def to_h
39
- {exception: @exception, root: @root}
44
+ def emit(*arguments, **options)
45
+ options[:severity] ||= :error
46
+
47
+ super
40
48
  end
41
49
 
42
- def format(output, terminal, verbose)
43
- format_exception(@exception, nil, output, terminal, verbose)
44
- end
50
+ private
45
51
 
46
- if Exception.method_defined?(:detailed_message)
47
- def detailed_message(exception)
48
- exception.detailed_message
49
- end
50
- else
51
- def detailed_message(exception)
52
- exception.message
53
- end
54
- end
55
-
56
- def format_exception(exception, prefix, output, terminal, verbose)
57
- lines = detailed_message(exception).lines.map(&:chomp)
58
-
59
- output.puts " #{prefix}#{terminal[:exception_title]}#{exception.class}#{terminal.reset}: #{lines.shift}"
60
-
61
- lines.each do |line|
62
- output.puts " #{terminal[:exception_detail]}#{line}#{terminal.reset}"
63
- end
52
+ def extract(exception, hash)
53
+ hash[:class] = exception.class.name
64
54
 
65
- root_pattern = /^#{@root}\// if @root
66
-
67
- exception.backtrace&.each_with_index do |line, index|
68
- path, offset, message = line.split(":", 3)
69
- style = :exception_backtrace
55
+ if exception.respond_to?(:detailed_message)
56
+ message = exception.detailed_message
70
57
 
71
- # Make the path a bit more readable
72
- if root_pattern and path.sub!(root_pattern, "").nil?
73
- style = :exception_backtrace_other
74
- end
58
+ # We want to remove the trailling exception class as we format it differently:
59
+ message.sub!(/\s*\(.*?\)$/, '')
75
60
 
76
- output.puts " #{index == 0 ? "→" : " "} #{terminal[style]}#{path}:#{offset}#{terminal[:exception_message]} #{message}#{terminal.reset}"
61
+ hash[:message] = message
62
+ else
63
+ hash[:message] = exception.message
77
64
  end
78
65
 
79
- if exception.cause
80
- format_exception(exception.cause, "Caused by ", output, terminal, verbose)
66
+ hash[:backtrace] = exception.backtrace
67
+
68
+ if cause = exception.cause
69
+ hash[:cause] = Hash.new.tap do |cause_hash|
70
+ extract(cause, cause_hash)
71
+ end
81
72
  end
82
73
  end
83
74
  end
@@ -1,22 +1,25 @@
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
  module Console
7
7
  module Event
8
8
  class Generic
9
- def self.register(terminal)
9
+ def as_json(...)
10
+ to_hash
10
11
  end
11
12
 
12
- def to_h
13
+ def to_json(...)
14
+ JSON.generate(as_json, ...)
13
15
  end
14
16
 
15
- def to_json(*arguments)
16
- JSON.generate([self.class, to_h], *arguments)
17
+ def to_s
18
+ to_json
17
19
  end
18
20
 
19
- def format(buffer, terminal)
21
+ def emit(*arguments, **options)
22
+ Console.call(*arguments, event: self, **options)
20
23
  end
21
24
  end
22
25
  end
@@ -1,12 +1,21 @@
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_relative 'generic'
7
+ require_relative '../clock'
7
8
 
8
9
  module Console
9
10
  module Event
11
+ # Represents a spawn event.
12
+ #
13
+ # ```ruby
14
+ # Console.info(self, **Console::Event::Spawn.for("ls", "-l"))
15
+ #
16
+ # event = Console::Event::Spawn.for("ls", "-l")
17
+ # event.status = Process.wait
18
+ # ```
10
19
  class Spawn < Generic
11
20
  def self.for(*arguments, **options)
12
21
  # Extract out the command environment:
@@ -22,44 +31,38 @@ module Console
22
31
  @environment = environment
23
32
  @arguments = arguments
24
33
  @options = options
34
+
35
+ @start_time = Clock.now
36
+
37
+ @end_time = nil
38
+ @status = nil
25
39
  end
26
40
 
27
- attr :environment
28
- attr :arguments
29
- attr :options
30
-
31
- def chdir_string(options)
32
- if options and chdir = options[:chdir]
33
- " in #{chdir}"
41
+ def duration
42
+ if @end_time
43
+ @end_time - @start_time
34
44
  end
35
45
  end
36
46
 
37
- def self.register(terminal)
38
- terminal[:shell_command] ||= terminal.style(:blue, nil, :bold)
39
- end
40
-
41
- def to_h
47
+ def to_hash
42
48
  Hash.new.tap do |hash|
49
+ hash[:type] = :spawn
43
50
  hash[:environment] = @environment if @environment&.any?
44
51
  hash[:arguments] = @arguments if @arguments&.any?
45
52
  hash[:options] = @options if @options&.any?
53
+
54
+ hash[:status] = @status.to_i if @status
55
+
56
+ if duration = self.duration
57
+ hash[:duration] = duration
58
+ end
46
59
  end
47
60
  end
48
61
 
49
- def format(output, terminal, verbose)
50
- arguments = @arguments.flatten.collect(&:to_s)
51
-
52
- output.puts " #{terminal[:shell_command]}#{arguments.join(' ')}#{terminal.reset}#{chdir_string(options)}"
53
-
54
- if verbose and @environment
55
- @environment.each do |key, value|
56
- output.puts " export #{key}=#{value}"
57
- end
58
- end
62
+ def status=(status)
63
+ @end_time = Time.now
64
+ @status = status
59
65
  end
60
66
  end
61
67
  end
62
-
63
- # Deprecated.
64
- Shell = Event::Spawn
65
68
  end
data/lib/console/event.rb CHANGED
@@ -1,8 +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_relative 'event/spawn'
7
7
  require_relative 'event/failure'
8
- require_relative 'event/progress'
@@ -1,13 +1,11 @@
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 Robert Schulze.
8
8
 
9
- require_relative 'buffer'
10
-
11
9
  module Console
12
10
  UNKNOWN = 'unknown'
13
11
 
@@ -37,7 +35,7 @@ module Console
37
35
 
38
36
  define_immutable_method(name) do |subject = nil, *arguments, **options, &block|
39
37
  if self.enabled?(subject, level)
40
- self.call(subject, *arguments, severity: name, **options, **@options, &block)
38
+ self.call(subject, *arguments, severity: name, **@options, **options, &block)
41
39
  end
42
40
  end
43
41
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2023, by Samuel Williams.
4
+ # Copyright, 2023-2024, by Samuel Williams.
5
5
 
6
6
  require_relative 'format/safe'
7
7
 
@@ -10,9 +10,5 @@ module Console
10
10
  def self.default
11
11
  Safe.new(format: ::JSON)
12
12
  end
13
-
14
- def self.default_json
15
- self.default
16
- end
17
13
  end
18
14
  end
@@ -1,17 +1,15 @@
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
  # Copyright, 2021, by Bryan Powell.
6
6
  # Copyright, 2021, by Robert Schulze.
7
7
 
8
8
  require_relative 'output'
9
9
  require_relative 'filter'
10
- require_relative 'progress'
11
-
10
+ require_relative 'event'
12
11
  require_relative 'resolver'
13
- require_relative 'terminal/logger'
14
- require_relative 'serialized/logger'
12
+ require_relative 'progress'
15
13
 
16
14
  require 'fiber/local'
17
15
 
@@ -67,12 +65,24 @@ module Console
67
65
  end
68
66
 
69
67
  def progress(subject, total, **options)
70
- Progress.new(self, subject, total, **options)
68
+ options[:severity] ||= :info
69
+
70
+ Progress.new(subject, total, **options)
71
+ end
72
+
73
+ def error(subject, *arguments, **options, &block)
74
+ # This is a special case where we want to create a failure event from an exception.
75
+ # It's common to see `Console.error(self, exception)` in code.
76
+ if arguments.first.is_a?(Exception)
77
+ exception = arguments.shift
78
+ options[:event] = Event::Failure.for(exception)
79
+ end
80
+
81
+ super
71
82
  end
72
83
 
73
- # @deprecated Use `fatal` instead.
74
- def failure(subject, exception, *arguments, &block)
75
- self.fatal(subject, exception, *arguments, &block)
84
+ def failure(subject, exception, **options)
85
+ error(subject, event: Event::Failure.for(exception), **options)
76
86
  end
77
87
  end
78
88
  end
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2021-2022, by Samuel Williams.
4
+ # Copyright, 2021-2024, by Samuel Williams.
5
5
 
6
- require_relative 'xterm'
7
- require_relative 'json'
6
+ require_relative 'terminal'
7
+ require_relative 'serialized'
8
8
 
9
9
  module Console
10
10
  module Output
@@ -13,9 +13,9 @@ module Console
13
13
  output ||= $stderr
14
14
 
15
15
  if output.tty?
16
- XTerm.new(output, **options)
16
+ Terminal.new(output, **options)
17
17
  else
18
- JSON.new(output, **options)
18
+ Serialized.new(output, **options)
19
19
  end
20
20
  end
21
21
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2023, by Samuel Williams.
4
+ # Copyright, 2023-2024, by Samuel Williams.
5
5
 
6
6
  module Console
7
7
  module Output
@@ -1,17 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2021-2022, by Samuel Williams.
4
+ # Copyright, 2021-2024, by Samuel Williams.
5
5
 
6
- require_relative '../serialized/logger'
6
+ require_relative 'wrapper'
7
7
 
8
8
  module Console
9
9
  module Output
10
- class Sensitive
11
- def initialize(output, **options)
12
- @output = output
13
- end
14
-
10
+ class Sensitive < Wrapper
15
11
  REDACT = /
16
12
  phone
17
13
  | email
@@ -38,8 +34,14 @@ module Console
38
34
  | password
39
35
  /xi
40
36
 
37
+ def initialize(output, redact: REDACT, **options)
38
+ super(output, **options)
39
+
40
+ @redact = redact
41
+ end
42
+
41
43
  def redact?(text)
42
- text.match?(REDACT)
44
+ text.match?(@redact)
43
45
  end
44
46
 
45
47
  def redact_hash(arguments, filter)
@@ -96,7 +98,7 @@ module Console
96
98
  arguments = redact_array(arguments, filter)
97
99
  end
98
100
 
99
- @output.call(subject, *arguments, **options)
101
+ super(subject, *arguments, **options)
100
102
  end
101
103
  end
102
104
  end
@@ -1,34 +1,23 @@
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
- require_relative '../buffer'
7
- require_relative '../filter'
8
6
  require_relative '../format'
9
-
10
7
  require 'time'
11
-
12
8
  require 'fiber/annotation'
13
9
 
14
10
  module Console
15
- module Serialized
16
- class Logger
17
- def initialize(io = $stderr, format: Format.default, verbose: false, **options)
18
- @io = io
19
- @start = Time.now
11
+ module Output
12
+ class Serialized
13
+ def initialize(output, format: Format.default, **options)
14
+ @io = output
20
15
  @format = format
21
- @verbose = verbose
22
16
  end
23
17
 
24
18
  attr :io
25
- attr :start
26
19
  attr :format
27
20
 
28
- def verbose!(value = true)
29
- @verbose = true
30
- end
31
-
32
21
  def dump(record)
33
22
  @format.dump(record)
34
23
  end
@@ -72,41 +61,12 @@ module Console
72
61
  record[:message] = message
73
62
  end
74
63
 
75
- if exception = find_exception(message)
76
- record[:error] = {
77
- kind: exception.class,
78
- message: exception.message,
79
- stack: format_stack(exception)
80
- }
81
- end
82
-
83
64
  record.update(options)
84
65
 
85
66
  @io.puts(self.dump(record))
86
67
  end
87
-
88
- private
89
-
90
- def find_exception(message)
91
- message.find{|part| part.is_a?(Exception)}
92
- end
93
-
94
- def format_stack(exception)
95
- buffer = StringIO.new
96
- format_backtrace(exception, buffer)
97
- return buffer.string
98
- end
99
-
100
- def format_backtrace(exception, buffer)
101
- buffer.puts exception.backtrace
102
-
103
- if exception = exception.cause
104
- buffer.puts
105
- buffer.puts "Caused by: #{exception.class} #{exception.message}"
106
-
107
- format_backtrace(exception, buffer)
108
- end
109
- end
110
68
  end
69
+
70
+ JSON = Serialized
111
71
  end
112
72
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2022-2023, by Samuel Williams.
4
+ # Copyright, 2022-2024, by Samuel Williams.
5
5
 
6
6
  module Console
7
7
  module Output
@@ -18,9 +18,9 @@ module Console
18
18
  @outputs.each{|output| output.verbose!(value)}
19
19
  end
20
20
 
21
- def call(level, subject = nil, *arguments, **options, &block)
21
+ def call(...)
22
22
  @outputs.each do |output|
23
- output.call(level, subject, *arguments, **options, &block)
23
+ output.call(...)
24
24
  end
25
25
  end
26
26
  end