console 1.24.0 → 1.25.2

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: 9a62f7c19673292c5cd3c0d8b9d93350ca4b36ac0a5c0a0466deef6543b388ba
4
- data.tar.gz: f382353a7b052c056936fdd2c95ad0bd20cfb8619db2f6d2af1ce7e9e0cd7967
3
+ metadata.gz: 30f75a3ad0397c231c81b3b65e6fe073975fb39d482d81f0150b469b2aa1b24b
4
+ data.tar.gz: 7fe4d7597690387a1a9081e2f6826a1bb7d292fc05e8f4ae83151c0130b76eb9
5
5
  SHA512:
6
- metadata.gz: 6638ede52de5f0954a38adf71e910997eb643d2e39ef59645f320ce16ebefd4f804cb062e47e03472ac742fb46591c310217da72eb63eb87fe91d93090f8ee5f
7
- data.tar.gz: 15f4929956b45e0c445e95774dfa3ac1c4bb485401e1b0003835c9ae5435a25ebda70b69ad475cf0e6b48131b0cc41258e3c5bb8c70121daaa9fe482c12b5404
6
+ metadata.gz: 803ae3060f14047f2b05732158fbdd9c74cfa7a8db347c2bb9e6a4cec80ddaea2361b4e20a107ff817a502b0bfaf3b1a5c7068e33a931119fd2c0038e181dde4
7
+ data.tar.gz: 5fb29d841c4e7344573e43e9a974e91a6824905c4f8205e7ccd368830fd0edacc662b637664719b8fbf6775b32409bc25301ae4993287feee9869147cc3b8147
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,43 @@ 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 emit(*arguments, **options)
63
+ options[:severity] ||= :info
64
+ super
65
+ end
66
+
67
+ def status=(status)
68
+ @end_time = Time.now
69
+ @status = status
59
70
  end
60
71
  end
61
72
  end
62
-
63
- # Deprecated.
64
- Shell = Event::Spawn
65
73
  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,15 +1,13 @@
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
- UNKNOWN = 'unknown'
10
+ UNKNOWN = :unknown
13
11
 
14
12
  class Filter
15
13
  if Object.const_defined?(:Ractor) and RUBY_VERSION >= '3.1'
@@ -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
+ @output.call(subject, *arguments, severity: name, **@options, **options, &block)
41
39
  end
42
40
  end
43
41
 
@@ -145,8 +143,13 @@ module Console
145
143
  @subjects.delete(subject)
146
144
  end
147
145
 
148
- def call(*arguments, **options, &block)
149
- @output.call(*arguments, **options, &block)
146
+ def call(subject, *arguments, **options, &block)
147
+ severity = options[:severity] || UNKNOWN
148
+ level = self.class::LEVELS[severity]
149
+
150
+ if self.enabled?(subject, level)
151
+ @output.call(subject, *arguments, **options, &block)
152
+ end
150
153
  end
151
154
  end
152
155
  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
  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