console 1.29.3 → 1.30.1

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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/lib/console/capture.rb +27 -2
  4. data/lib/console/clock.rb +7 -2
  5. data/lib/console/compatible/logger.rb +31 -2
  6. data/lib/console/config.rb +93 -0
  7. data/lib/console/event/failure.rb +33 -4
  8. data/lib/console/event/generic.rb +15 -1
  9. data/lib/console/event/spawn.rb +39 -7
  10. data/lib/console/event.rb +7 -1
  11. data/lib/console/filter.rb +63 -3
  12. data/lib/console/format/safe.rb +36 -8
  13. data/lib/console/format.rb +4 -1
  14. data/lib/console/interface.rb +17 -9
  15. data/lib/console/logger.rb +23 -38
  16. data/lib/console/output/default.rb +12 -6
  17. data/lib/console/output/failure.rb +8 -1
  18. data/lib/console/output/null.rb +5 -2
  19. data/lib/console/output/sensitive.rb +42 -1
  20. data/lib/console/output/serialized.rb +25 -4
  21. data/lib/console/output/split.rb +12 -1
  22. data/lib/console/output/terminal.rb +67 -15
  23. data/lib/console/output/wrapper.rb +12 -1
  24. data/lib/console/output.rb +12 -1
  25. data/lib/console/progress.rb +56 -9
  26. data/lib/console/resolver.rb +19 -2
  27. data/lib/console/terminal/formatter/failure.rb +18 -6
  28. data/lib/console/terminal/formatter/progress.rb +16 -3
  29. data/lib/console/terminal/formatter/spawn.rb +16 -5
  30. data/lib/console/terminal/formatter.rb +16 -0
  31. data/lib/console/terminal/text.rb +61 -20
  32. data/lib/console/terminal/xterm.rb +17 -2
  33. data/lib/console/terminal.rb +7 -9
  34. data/lib/console/version.rb +2 -2
  35. data/lib/console/warn.rb +2 -2
  36. data/lib/console.rb +2 -1
  37. data/license.md +1 -0
  38. data/readme.md +7 -1
  39. data/releases.md +24 -1
  40. data.tar.gz.sig +4 -3
  41. metadata +5 -2
  42. metadata.gz.sig +0 -0
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2019-2024, by Samuel Williams.
4
+ # Copyright, 2019-2025, by Samuel Williams.
5
5
  # Copyright, 2019, by Bryan Powell.
6
6
  # Copyright, 2020, by Michael Adams.
7
7
  # Copyright, 2021, by Robert Schulze.
@@ -9,18 +9,28 @@
9
9
  module Console
10
10
  UNKNOWN = :unknown
11
11
 
12
+ # A log filter which can be used to filter log messages based on severity, subject, and other criteria.
12
13
  class Filter
13
14
  if Object.const_defined?(:Ractor) and RUBY_VERSION >= "3.1"
15
+ # Define a method which can be shared between ractors.
14
16
  def self.define_immutable_method(name, &block)
15
17
  block = Ractor.make_shareable(block)
16
18
  self.define_method(name, &block)
17
19
  end
18
20
  else
21
+ # Define a method.
19
22
  def self.define_immutable_method(name, &block)
20
23
  define_method(name, &block)
21
24
  end
22
25
  end
23
26
 
27
+ # Create a new log filter with specific log levels.
28
+ #
29
+ # ```ruby
30
+ # class MyLogger < Console::Filter[debug: 0, okay: 1, bad: 2, terrible: 3]
31
+ # ```
32
+ #
33
+ # @parameter levels [Hash(Symbol, Integer)] A hash of log levels.
24
34
  def self.[] **levels
25
35
  klass = Class.new(self)
26
36
  minimum_level, maximum_level = levels.values.minmax
@@ -54,6 +64,12 @@ module Console
54
64
  return klass
55
65
  end
56
66
 
67
+ # Create a new log filter.
68
+ #
69
+ # @parameter output [Console::Output] The output destination.
70
+ # @parameter verbose [Boolean] Enable verbose output.
71
+ # @parameter level [Integer] The log level.
72
+ # @parameter options [Hash] Additional options.
57
73
  def initialize(output, verbose: true, level: self.class::DEFAULT_LEVEL, **options)
58
74
  @output = output
59
75
  @verbose = verbose
@@ -64,6 +80,12 @@ module Console
64
80
  @options = options
65
81
  end
66
82
 
83
+ # Create a new log filter with the given options, from an existing log filter.
84
+ #
85
+ # @parameter level [Integer] The log level.
86
+ # @parameter verbose [Boolean] Enable verbose output.
87
+ # @parameter options [Hash] Additional options.
88
+ # @returns [Console::Filter] The new log filter.
67
89
  def with(level: @level, verbose: @verbose, **options)
68
90
  dup.tap do |logger|
69
91
  logger.level = level
@@ -72,14 +94,24 @@ module Console
72
94
  end
73
95
  end
74
96
 
97
+ # @attribute [Console::Output] The output destination.
75
98
  attr_accessor :output
99
+
100
+ # @attribute [Boolean] Whether to enable verbose output.
76
101
  attr :verbose
102
+
103
+ # @attribute [Integer] The current log level.
77
104
  attr :level
78
105
 
106
+ # @attribute [Hash(Module, Integer)] The log levels for specific subject (classes).
79
107
  attr :subjects
80
108
 
109
+ # @attribute [Hash] Additional options.
81
110
  attr_accessor :options
82
111
 
112
+ # Set the log level.
113
+ #
114
+ # @parameter level [Integer | Symbol] The log level.
83
115
  def level= level
84
116
  if level.is_a? Symbol
85
117
  @level = self.class::LEVELS[level]
@@ -88,19 +120,30 @@ module Console
88
120
  end
89
121
  end
90
122
 
123
+ # Set verbose output (enable by default with no arguments).
124
+ #
125
+ # @parameter value [Boolean] Enable or disable verbose output.
91
126
  def verbose!(value = true)
92
127
  @verbose = value
93
128
  @output.verbose!(value)
94
129
  end
95
130
 
131
+ # Disable all logging.
96
132
  def off!
97
133
  @level = self.class::MAXIMUM_LEVEL + 1
98
134
  end
99
135
 
136
+ # Enable all logging.
100
137
  def all!
101
138
  @level = self.class::MINIMUM_LEVEL - 1
102
139
  end
103
140
 
141
+ # Filter log messages based on the subject and log level.
142
+ #
143
+ # You must provide the subject's class, not an instance of the class.
144
+ #
145
+ # @parameter subject [Module] The subject to filter.
146
+ # @parameter level [Integer] The log level.
104
147
  def filter(subject, level)
105
148
  unless subject.is_a?(Module)
106
149
  raise ArgumentError, "Expected a class, got #{subject.inspect}"
@@ -109,8 +152,13 @@ module Console
109
152
  @subjects[subject] = level
110
153
  end
111
154
 
155
+ # Whether logging is enabled for the given subject and log level.
156
+ #
112
157
  # You can enable and disable logging for classes. This function checks if logging for a given subject is enabled.
113
- # @param subject [Object] the subject to check.
158
+ #
159
+ # @parameter subject [Module] The subject to check.
160
+ # @parameter level [Integer] The log level.
161
+ # @returns [Boolean] Whether logging is enabled.
114
162
  def enabled?(subject, level = self.class::MINIMUM_LEVEL)
115
163
  subject = subject.class unless subject.is_a?(Module)
116
164
 
@@ -124,19 +172,24 @@ module Console
124
172
  end
125
173
 
126
174
  # Enable specific log level for the given class.
175
+ #
127
176
  # @parameter name [Module] The class to enable.
128
177
  def enable(subject, level = self.class::MINIMUM_LEVEL)
129
178
  # Set the filter level of logging for a given subject which passes all log messages:
130
179
  filter(subject, level)
131
180
  end
132
181
 
182
+ # Disable logging for the given class.
183
+ #
184
+ # @parameter name [Module] The class to disable.
133
185
  def disable(subject)
134
186
  # Set the filter level of the logging for a given subject which filters all log messages:
135
187
  filter(subject, self.class::MAXIMUM_LEVEL + 1)
136
188
  end
137
189
 
138
190
  # Clear any specific filters for the given class.
139
- # @parameter name [Module] The class to disable.
191
+ #
192
+ # @parameter subject [Module] The class to disable.
140
193
  def clear(subject)
141
194
  unless subject.is_a?(Module)
142
195
  raise ArgumentError, "Expected a class, got #{subject.inspect}"
@@ -145,6 +198,13 @@ module Console
145
198
  @subjects.delete(subject)
146
199
  end
147
200
 
201
+ # Log a message with the given severity.
202
+ #
203
+ # @parameter subject [Object] The subject of the log message.
204
+ # @parameter arguments [Array] The arguments to log.
205
+ # @parameter options [Hash] Additional options to pass to the output.
206
+ # @parameter block [Proc] A block passed to the output.
207
+ # @returns [Nil] Always returns nil.
148
208
  def call(subject, *arguments, **options, &block)
149
209
  severity = options[:severity] || UNKNOWN
150
210
  level = self.class::LEVELS[severity]
@@ -1,21 +1,32 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2023-2024, by Samuel Williams.
4
+ # Copyright, 2023-2025, by Samuel Williams.
5
5
 
6
6
  require "json"
7
7
 
8
8
  module Console
9
+ # @namespace
9
10
  module Format
10
- # This class is used to safely dump objects.
11
- # It will attempt to dump the object using the given format, but if it fails, it will generate a safe version of the object.
11
+ # A safe format for converting objects to strings.
12
+ #
13
+ # Handles issues like circular references and encoding errors.
12
14
  class Safe
15
+ # Create a new safe format.
16
+ #
17
+ # @parameter format [JSON] The format to use for serialization.
18
+ # @parameter limit [Integer] The maximum depth to recurse into objects.
19
+ # @parameter encoding [Encoding] The encoding to use for strings.
13
20
  def initialize(format: ::JSON, limit: 8, encoding: ::Encoding::UTF_8)
14
21
  @format = format
15
22
  @limit = limit
16
23
  @encoding = encoding
17
24
  end
18
25
 
26
+ # Dump the given object to a string.
27
+ #
28
+ # @parameter object [Object] The object to dump.
29
+ # @returns [String] The dumped object.
19
30
  def dump(object)
20
31
  @format.dump(object, @limit)
21
32
  rescue SystemStackError, StandardError => error
@@ -24,6 +35,10 @@ module Console
24
35
 
25
36
  private
26
37
 
38
+ # Filter the backtrace to remove duplicate frames and reduce verbosity.
39
+ #
40
+ # @parameter error [Exception] The exception to filter.
41
+ # @returns [Array(String)] The filtered backtrace.
27
42
  def filter_backtrace(error)
28
43
  frames = error.backtrace
29
44
  filtered = {}
@@ -61,6 +76,13 @@ module Console
61
76
  return frames
62
77
  end
63
78
 
79
+ # Dump the given object to a string, replacing it with a safe representation if there is an error.
80
+ #
81
+ # This is a slow path so we try to avoid it.
82
+ #
83
+ # @parameter object [Object] The object to dump.
84
+ # @parameter error [Exception] The error that occurred while dumping the object.
85
+ # @returns [Hash] The dumped (truncated) object including error details.
64
86
  def safe_dump(object, error)
65
87
  object = safe_dump_recurse(object)
66
88
 
@@ -74,6 +96,10 @@ module Console
74
96
  return object
75
97
  end
76
98
 
99
+ # Replace the given object with a safe truncated representation.
100
+ #
101
+ # @parameter object [Object] The object to replace.
102
+ # @returns [String] The replacement string.
77
103
  def replacement_for(object)
78
104
  case object
79
105
  when Array
@@ -85,15 +111,17 @@ module Console
85
111
  end
86
112
  end
87
113
 
114
+ # Create a new hash with identity comparison.
88
115
  def default_objects
89
116
  Hash.new.compare_by_identity
90
117
  end
91
118
 
92
- # This will recursively generate a safe version of the object.
93
- # Nested hashes and arrays will be transformed recursively.
94
- # Strings will be encoded with the given encoding.
95
- # Primitive values will be returned as-is.
96
- # Other values will be converted using `as_json` if available, otherwise `to_s`.
119
+ # This will recursively generate a safe version of the object. Nested hashes and arrays will be transformed recursively. Strings will be encoded with the given encoding. Primitive values will be returned as-is. Other values will be converted using `as_json` if available, otherwise `to_s`.
120
+ #
121
+ # @parameter object [Object] The object to dump.
122
+ # @parameter limit [Integer] The maximum depth to recurse into objects.
123
+ # @parameter objects [Hash] The objects that have already been visited.
124
+ # @returns [Object] The dumped object as a primitive representation.
97
125
  def safe_dump_recurse(object, limit = @limit, objects = default_objects)
98
126
  if limit <= 0 || objects[object]
99
127
  return replacement_for(object)
@@ -1,12 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2023-2024, by Samuel Williams.
4
+ # Copyright, 2023-2025, by Samuel Williams.
5
5
 
6
6
  require_relative "format/safe"
7
7
 
8
8
  module Console
9
9
  module Format
10
+ # A safe format for converting objects to strings.
11
+ #
12
+ # @returns [Console::Format::Safe]
10
13
  def self.default
11
14
  Safe.new(format: ::JSON)
12
15
  end
@@ -3,51 +3,59 @@
3
3
  # Released under the MIT License.
4
4
  # Copyright, 2024-2025, by Samuel Williams.
5
5
 
6
- require_relative "logger"
6
+ require "fiber/local"
7
+ require_relative "config"
7
8
 
8
9
  module Console
9
10
  # The public logger interface.
10
11
  module Interface
12
+ extend Fiber::Local
13
+
14
+ # Create a new (thread local) logger instance.
15
+ def self.local
16
+ Config::DEFAULT.make_logger
17
+ end
18
+
11
19
  # Get the current logger instance.
12
20
  def logger
13
- Logger.instance
21
+ Interface.instance
14
22
  end
15
23
 
16
24
  # Set the current logger instance.
17
25
  #
18
26
  # The current logger instance is assigned per-fiber.
19
27
  def logger= instance
20
- Logger.instance= instance
28
+ Interface.instance= instance
21
29
  end
22
30
 
23
31
  # Emit a debug log message.
24
32
  def debug(...)
25
- Logger.instance.debug(...)
33
+ Interface.instance.debug(...)
26
34
  end
27
35
 
28
36
  # Emit an informational log message.
29
37
  def info(...)
30
- Logger.instance.info(...)
38
+ Interface.instance.info(...)
31
39
  end
32
40
 
33
41
  # Emit a warning log message.
34
42
  def warn(...)
35
- Logger.instance.warn(...)
43
+ Interface.instance.warn(...)
36
44
  end
37
45
 
38
46
  # Emit an error log message.
39
47
  def error(...)
40
- Logger.instance.error(...)
48
+ Interface.instance.error(...)
41
49
  end
42
50
 
43
51
  # Emit a fatal log message.
44
52
  def fatal(...)
45
- Logger.instance.fatal(...)
53
+ Interface.instance.fatal(...)
46
54
  end
47
55
 
48
56
  # Emit a log message with arbitrary arguments and options.
49
57
  def call(...)
50
- Logger.instance.call(...)
58
+ Interface.instance.call(...)
51
59
  end
52
60
  end
53
61
  end
@@ -1,68 +1,47 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2019-2024, by Samuel Williams.
4
+ # Copyright, 2019-2025, by Samuel Williams.
5
5
  # Copyright, 2021, by Bryan Powell.
6
6
  # Copyright, 2021, by Robert Schulze.
7
+ # Copyright, 2025, by Shigeru Nakajima.
7
8
 
8
9
  require_relative "output"
9
10
  require_relative "output/failure"
10
11
 
11
- require_relative "filter"
12
- require_relative "event"
13
- require_relative "resolver"
14
12
  require_relative "progress"
15
13
 
16
- require "fiber/local"
17
-
18
14
  module Console
15
+ # The standard logger interface with support for log levels and verbosity.
16
+ #
17
+ # The log levels are: `debug`, `info`, `warn`, `error`, and `fatal`.
19
18
  class Logger < Filter[debug: 0, info: 1, warn: 2, error: 3, fatal: 4]
20
- extend Fiber::Local
21
-
22
19
  # Set the default log level based on `$DEBUG` and `$VERBOSE`.
23
20
  # You can also specify CONSOLE_LEVEL=debug or CONSOLE_LEVEL=info in environment.
24
21
  # https://mislav.net/2011/06/ruby-verbose-mode/ has more details about how it all fits together.
25
- def self.default_log_level(env = ENV)
22
+ #
23
+ # @parameter env [Hash] The environment to read the log level from.
24
+ # @parameter verbose [Boolean] The verbose flag.
25
+ # @parameter debug [Boolean] The debug flag.
26
+ # @returns [Integer] The default log level.
27
+ def self.default_log_level(env = ENV, verbose: $VERBOSE, debug: $DEBUG)
26
28
  if level = env["CONSOLE_LEVEL"]
27
29
  LEVELS[level.to_sym] || level.to_i
28
- elsif $DEBUG
30
+ elsif debug
29
31
  DEBUG
30
- elsif $VERBOSE.nil?
32
+ elsif verbose.nil?
31
33
  WARN
32
34
  else
33
35
  INFO
34
36
  end
35
37
  end
36
38
 
37
- # Controls verbose output using `$VERBOSE`.
38
- def self.verbose?(env = ENV)
39
- !$VERBOSE.nil? || env["CONSOLE_VERBOSE"]
40
- end
41
-
42
- def self.default_logger(output = $stderr, env = ENV, **options)
43
- if options[:verbose].nil?
44
- options[:verbose] = self.verbose?(env)
45
- end
46
-
47
- if options[:level].nil?
48
- options[:level] = self.default_log_level(env)
49
- end
50
-
51
- output = Output.new(output, env, **options)
52
-
53
- logger = self.new(output, **options)
54
-
55
- Resolver.default_resolver(logger)
56
-
57
- return logger
58
- end
59
-
60
- def self.local
61
- self.default_logger
62
- end
63
-
64
39
  DEFAULT_LEVEL = 1
65
40
 
41
+ # Create a new logger.
42
+ #
43
+ # @parameter output [Console::Output] The output destination.
44
+ # @parameter options [Hash] Additional options.
66
45
  def initialize(output, **options)
67
46
  # This is the expected default behaviour, but it may be nice to have a way to override it.
68
47
  output = Output::Failure.new(output, **options)
@@ -70,6 +49,12 @@ module Console
70
49
  super(output, **options)
71
50
  end
72
51
 
52
+ # Create a progress indicator for the given subject.
53
+ #
54
+ # @parameter subject [String] The subject of the progress indicator.
55
+ # @parameter total [Integer] The total number of items to process.
56
+ # @parameter options [Hash] Additional options passed to {Progress}.
57
+ # @returns [Progress] The progress indicator.
73
58
  def progress(subject, total, **options)
74
59
  options[:severity] ||= :info
75
60
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2021-2024, by Samuel Williams.
4
+ # Copyright, 2021-2025, by Samuel Williams.
5
5
 
6
6
  require_relative "terminal"
7
7
  require_relative "serialized"
@@ -9,14 +9,20 @@ require_relative "failure"
9
9
 
10
10
  module Console
11
11
  module Output
12
+ # Default output format selection.
12
13
  module Default
13
- def self.new(output, **options)
14
- output ||= $stderr
14
+ # Create a new output format based on the given stream.
15
+ #
16
+ # @parameter io [IO] The output stream.
17
+ # @parameter options [Hash] Additional options to customize the output.
18
+ # @returns [Console::Output::Terminal | Console::Output::Serialized] The output instance, depending on whether the `io` is a terminal or not.
19
+ def self.new(stream, **options)
20
+ stream ||= $stderr
15
21
 
16
- if output.tty?
17
- output = Terminal.new(output, **options)
22
+ if stream.tty?
23
+ output = Terminal.new(stream, **options)
18
24
  else
19
- output = Serialized.new(output, **options)
25
+ output = Serialized.new(stream, **options)
20
26
  end
21
27
 
22
28
  return output
@@ -10,11 +10,18 @@ module Console
10
10
  module Output
11
11
  # A wrapper for outputting failure messages, which can include exceptions.
12
12
  class Failure < Wrapper
13
+ # Create a new failure output wrapper.
13
14
  def initialize(output, **options)
14
15
  super(output, **options)
15
16
  end
16
17
 
17
18
  # The exception must be either the last argument or passed as an option.
19
+ #
20
+ # @parameter subject [String] The subject of the message.
21
+ # @parameter arguments [Array] The arguments to output.
22
+ # @parameter exception [Exception] The exception to output.
23
+ # @parameter options [Hash] Additional options to pass to the output.
24
+ # @parameter block [Proc] An optional block to pass to the output.
18
25
  def call(subject = nil, *arguments, exception: nil, **options, &block)
19
26
  if exception.nil?
20
27
  last = arguments.last
@@ -28,7 +35,7 @@ module Console
28
35
  options[:exception] = exception
29
36
  end
30
37
 
31
- super(subject, *arguments, **options)
38
+ super(subject, *arguments, **options, &block)
32
39
  end
33
40
  end
34
41
  end
@@ -1,20 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2023-2024, by Samuel Williams.
4
+ # Copyright, 2023-2025, by Samuel Williams.
5
5
 
6
6
  module Console
7
7
  module Output
8
+ # A null output that does nothing.
8
9
  class Null
10
+ # Create a new null output.
9
11
  def initialize(...)
10
12
  end
11
13
 
14
+ # The last output is always self.
12
15
  def last_output
13
16
  self
14
17
  end
15
18
 
19
+ # Do nothing.
16
20
  def call(...)
17
- # Do nothing.
18
21
  end
19
22
  end
20
23
  end
@@ -1,13 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2021-2024, by Samuel Williams.
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)