brut 0.18.2 → 0.19.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.
@@ -19,15 +19,27 @@ class Brut::CLI::Commands::BaseCommand
19
19
  self.run
20
20
  end
21
21
 
22
+ def delegate_to_command(command,execution_context=:use_ivar)
23
+ execution_context = if execution_context == :use_ivar
24
+ @execution_context
25
+ else
26
+ execution_context
27
+ end
28
+ if execution_context.nil?
29
+ raise ArgumentError, "No execution context provided and none set on this command"
30
+ end
31
+ command.execute(execution_context)
32
+ end
33
+
22
34
  # True if the command requires Brut to fully bootstrap and start itself up. Bootstrapping isn't running a web server but it will
23
35
  # do everything else, including connecting too all databases. Your command should return true for this if it needs to access a database
24
36
  # or make API calls outside `Brut::CLI`. If this returns false, Brut's configuration options will still be available.
25
37
  #
26
- # By default, this returns the value for `bootstrap?` of the `default_command`. If there is no `default_command`, this returns false.
38
+ # By default, this returns false
27
39
  #
28
40
  # @return [true|false] True if Brut should be fully bootstrap and connect to all database servers (e.g.). False if Brut should only
29
41
  # set up its configuration options.
30
- def bootstrap? = default_command&.bootstrap? || false
42
+ def bootstrap? = false
31
43
 
32
44
  # The default `RACK_ENV` to use for this command. This value is used when no `RACK_ENV` is present in the UNIX environment
33
45
  # and when `--env` has not been used on the command line. Do note that setting this in an app or parent command does
@@ -35,11 +47,15 @@ class Brut::CLI::Commands::BaseCommand
35
47
  #
36
48
  # @return [String|nil] If nil, Brut configuration will not be loaded and the command will run more or less as if it were a plain
37
49
  # Ruby script. If a `String`, this value will be set as the `RACK_ENV` if it's not been otherwise specified.
38
- def default_rack_env = default_command&.default_rack_env
50
+ def default_rack_env = nil
39
51
 
40
52
  # @return [String] description of this command for use in help output
41
53
  def description = ""
42
54
 
55
+ # Returns a more detaile description of the command. This can includes paragraphs which will be maintained, however
56
+ # any additional formatting is not rendered or used.
57
+ def detailed_description = nil
58
+
43
59
  # @return [String] description of the arguments this command accepts. Used for documentation only.
44
60
  def args_description = nil
45
61
 
@@ -85,13 +101,6 @@ class Brut::CLI::Commands::BaseCommand
85
101
  # where index 0 is the class and index 1 is a proc to convert the command line argument to the class's type.
86
102
  def accepts = []
87
103
 
88
- # Command to run if none provided on the command line.
89
- # @return [Brut::CLI::Commands::BaseCommand|nil] if `nil`, it is an error to run this command without a subcommand. If not-`nil`,
90
- # the returned command will be executed.
91
- def default_command = self.commands.detect { it.class == default_command_class }
92
-
93
- def default_command_class = nil
94
-
95
104
  # Returns a list of commands that represent the subcommands available to this command. By default, this
96
105
  # will return all commands that are inner classes of this command.
97
106
  #
@@ -104,6 +113,11 @@ class Brut::CLI::Commands::BaseCommand
104
113
  }.map(&:new)
105
114
  end
106
115
 
116
+ def theme
117
+ @theme = Brut::CLI::TerminalTheme.new(terminal:)
118
+ end
119
+
120
+
107
121
  private
108
122
 
109
123
  # Provides access the `Brut::CLI::Commands::ExecutionContext` used the last time `#execute` was called.
@@ -121,25 +135,48 @@ private
121
135
  # Convienience methods to defer to `Brut::CLI::Commands::ExecutionContext`'s `Brut::CLI::Executor#system!`.
122
136
  # @!visibility public
123
137
  def system!(*args,&block)
124
- self.execution_context.executor.system!(*args,&block)
138
+ output = ""
139
+ block ||= ->(output_chunk) {
140
+ output << output_chunk
141
+ }
142
+ begin
143
+ self.execution_context.executor.system!(*args,&block)
144
+ ensure
145
+ if output.length > 0
146
+ progname = args.detect { it.kind_of?(String) }.split(" ")
147
+ output.lines.each do |line|
148
+ self.execution_context.logger.add(Logger::INFO, line.chomp, progname)
149
+ end
150
+ end
151
+ end
125
152
  end
126
153
 
127
154
  # Convienience methods to defer to `Brut::CLI::Commands::ExecutionContext#stdout`'s `puts`.
128
155
  # @!visibility public
129
156
  def puts(*args)
130
- self.execution_context.stdout.puts(*args)
157
+ if !options.quiet?
158
+ self.execution_context.stdout.puts(*args)
159
+ end
160
+ end
161
+ def print(*args)
162
+ if !options.quiet?
163
+ self.execution_context.stdout.print(*args)
164
+ end
165
+ end
166
+
167
+ def debug(message) = self.execution_context.logger.debug(message)
168
+ def info(message) = self.execution_context.logger.info(message)
169
+ def warn(message) = self.execution_context.logger.warn(message)
170
+ def error(message) = self.execution_context.logger.error(message)
171
+ def fatal(message) = self.execution_context.logger.fatal(message)
172
+
173
+ def terminal
174
+ @terminal ||= Brut::CLI::Terminal.new
131
175
  end
132
176
 
133
- # Convienience methods to defer to `Brut::CLI::Commands::ExecutionContext#stderr`. You should use this over `STDERR` or `$stderr`.
134
- # @!visibility public
135
- def stderr = self.execution_context.stderr
136
177
  # Convienience methods to defer to `Brut::CLI::Commands::ExecutionContext#stdin`. You should use this over `STDIN` of `$stdin`.
137
178
  # @!visibility public
138
179
  def stdin = self.execution_context.stdin
139
- # Convienience methods to defer to `Brut::CLI::Commands::ExecutionContext#stdout`. You should use this over `STDOUT` of `$stdout`. Note
140
- # that if you just want to output a string, use `puts`.
141
- # @!visibility public
142
- def stdout = self.execution_context.stdout
143
180
  # Convienience methods to defer to `Brut::CLI::Commands::ExecutionContext#options`.
144
181
  # @!visibility public
145
182
  def options = self.execution_context.options
@@ -163,12 +200,11 @@ private
163
200
  # @!visibility public
164
201
  def run
165
202
  if argv[0]
166
- stderr.puts "No such command '#{argv[0]}'"
203
+ puts theme.error.render("No such command '#{argv[0]}'")
167
204
  return 1
168
205
  end
169
206
 
170
-
171
- stderr.puts "Command is required"
207
+ puts theme.error.render("Command is required")
172
208
  1
173
209
  end
174
210
  end
@@ -16,12 +16,10 @@ class Brut::CLI::Commands::CompoundCommand < Brut::CLI::Commands::BaseCommand
16
16
  def execute(execution_context)
17
17
  @commands.each do |command|
18
18
  execute_result = Brut::CLI::ExecuteResult.new do
19
- command.execute(execution_context)
19
+ delegate_to_command(command,execution_context)
20
20
  end
21
21
  if execute_result.failed?
22
- return execute_result.exit_status do |error_message|
23
- @stderr.puts error_message
24
- end
22
+ return execute_result.actual_result
25
23
  end
26
24
  end
27
25
  0
@@ -1,6 +1,6 @@
1
1
  # The context in which a command's execution is run. This holds essentially all values that are input to
2
2
  # the command, including parsed command line options, unparsed arguments, the UNIX environment, and
3
- # the three s/O streams (stderr, stdout, stdin).
3
+ # the three I/O streams (stderr, stdout, stdin).
4
4
  class Brut::CLI::Commands::ExecutionContext
5
5
 
6
6
  def initialize(
@@ -10,15 +10,21 @@ class Brut::CLI::Commands::ExecutionContext
10
10
  stdout: :default,
11
11
  stderr: :default,
12
12
  stdin: :default,
13
- executor: :default
13
+ executor: :default,
14
+ logger: :default
14
15
  )
15
- @argv = argv == :default ? [] : argv
16
- @options = options == :default ? Brut::CLI::Options.new({}) : options
17
- @env = env == :default ? {} : env
18
- @stdout = Brut::CLI::Output.from_io(stdout == :default ? $stdout : stdout)
19
- @stderr = Brut::CLI::Output.from_io(stderr == :default ? $stderr : stderr)
20
- @stdin = stdin == :default ? $stdin : stdin
21
- @executor = executor == :default ? Brut::CLI::Executor.new(out: self.stdout, err: self.stderr) : executor
16
+ @argv = argv == :default ? [] : argv
17
+ @options = options == :default ? Brut::CLI::Options.new({}) : options
18
+ @env = env == :default ? {} : env
19
+ @stdout = stdout == :default ? $stdout : stdout
20
+ @stderr = stderr == :default ? $stderr : stderr
21
+ @stdin = stdin == :default ? $stdin : stdin
22
+ @logger = logger == :default ? Brut::CLI::Logger.new(app_name: $0, stdout: @stdout, stderr: @stderr) : logger
23
+ @executor = executor == :default ? Brut::CLI::Executor.new(logger: @logger, out: self.stdout, err: self.stderr) : executor
24
+
25
+ @logger.level = @options.log_level
26
+ @logger.log_file = @options.log_file
27
+ @logger.log_to_stdout = @options.log_stdout?
22
28
  end
23
29
 
24
30
  attr_reader :argv,
@@ -27,6 +33,7 @@ class Brut::CLI::Commands::ExecutionContext
27
33
  :stdout,
28
34
  :stderr,
29
35
  :stdin,
30
- :executor
36
+ :executor,
37
+ :logger
31
38
 
32
39
  end
@@ -9,18 +9,124 @@ class Brut::CLI::Commands::Help < Brut::CLI::Commands::BaseCommand
9
9
 
10
10
  def commands = []
11
11
 
12
+ class DescriptionList
13
+ def initialize(term_align: :right, padding_bottom: 1)
14
+ @term_align = term_align
15
+ @padding_bottom = padding_bottom
16
+ @rows = []
17
+ end
18
+ def <<(row)
19
+ @rows << row
20
+ end
21
+
22
+ def lipgloss_table(theme:, terminal:)
23
+ indent = " "
24
+ term_width = @rows.map { |r| r[0].length }.max
25
+ description_width = terminal.cols - indent.length - term_width - 2
26
+ formatted_rows = @rows.map { |row|
27
+ [
28
+ row[0],
29
+ theme.wrap(row[1], indent: 0, max_width: description_width).strip,
30
+ ]
31
+ }
32
+ Lipgloss::Table.new.
33
+ border(:hidden).
34
+ rows(formatted_rows).
35
+ style_func(rows: formatted_rows.length, columns: 2) { |row,column|
36
+ if column == 0
37
+ Lipgloss::Style.new.inherit(theme.subheader).padding_bottom(@padding_bottom).align(@term_align)
38
+ else
39
+ Lipgloss::Style.new.padding_bottom(@padding_bottom)
40
+ end
41
+ }
42
+ end
43
+ end
44
+
45
+
12
46
  def run
13
- stdout.puts_no_prefix @option_parser.to_s
47
+ cli = [@command.name ]
48
+ cmd = @command
49
+ while cmd.parent_command
50
+ cmd = cmd.parent_command
51
+ cli.unshift cmd.name
52
+ end
53
+ invocation = cli.join(" ")
54
+ puts theme.code.render(invocation) + " - " + theme.title.render(theme.wrap(@command.description, indent: invocation.length + 3, max_width: 65).strip)
55
+ puts
56
+ usage = theme.code.render(invocation)
57
+ options = @option_parser.top.list
58
+ if options.size > 0
59
+ usage << theme.weak.render(" [options]")
60
+ end
14
61
  if @command.commands.any?
15
- stdout.puts_no_prefix
16
- stdout.puts_no_prefix "COMMANDS"
17
- stdout.puts_no_prefix
18
- @command.commands.each do |command|
19
- stdout.puts_no_prefix " #{command.name} - #{command.description}"
62
+ usage << theme.code.render(" command")
63
+ end
64
+ if @command.args_description
65
+ usage << " #{@command.args_description}"
66
+ end
67
+ puts theme.header.render("USAGE")
68
+ puts
69
+ puts " " + usage
70
+ puts
71
+ if @command.detailed_description
72
+ puts theme.header.render("DESCRIPTION")
73
+ puts
74
+ puts theme.wrap(@command.detailed_description.strip, indent: 2, max_width: 65)
75
+ puts
76
+ end
77
+ if options.size > 0
78
+ puts theme.header.render("OPTIONS")
79
+ list = DescriptionList.new
80
+ options.each do |option|
81
+ switches = option.long.map { |switch|
82
+ if option.arg
83
+ if option.arg[0] == "="
84
+ "#{switch.strip}#{theme.weak.render(option.arg.strip)}"
85
+ else
86
+ "#{switch.strip}=#{theme.weak.render(option.arg.strip)}"
87
+ end
88
+ else
89
+ switch
90
+ end
91
+ } + option.short.map { |switch|
92
+ if option.arg
93
+ "#{switch} #{theme.weak.render(option.arg)}"
94
+ else
95
+ switch
96
+ end
97
+ }
98
+ list << [
99
+ switches.join("\n"),
100
+ option.desc.join(" "),
101
+ ]
102
+ end
103
+ puts list.lipgloss_table(theme:, terminal:).render
104
+ end
105
+ if @command.env_vars.any?
106
+ puts theme.header.render("ENVIRONMENT VARIABLES")
107
+ list = DescriptionList.new
108
+ @command.env_vars.sort_by(&:first).each do |env_var|
109
+ list << [
110
+ env_var[0],
111
+ env_var[1],
112
+ ]
20
113
  end
114
+ puts list.lipgloss_table(theme:, terminal:).render
115
+ end
116
+ if @command.commands.any?
117
+ puts theme.header.render("COMMANDS")
118
+ list = DescriptionList.new(term_align: :left, padding_bottom: 0)
119
+ @command.commands.sort_by(&:name).each do |command|
120
+ list << [
121
+ command.name,
122
+ command.description,
123
+ ]
124
+ end
125
+ puts list.lipgloss_table(theme:, terminal:).render
21
126
  end
22
127
  0
23
128
  end
129
+
24
130
  def bootstrap? = false
25
131
  def default_rack_env = nil
26
132
  end
@@ -4,7 +4,7 @@ class Brut::CLI::Commands::OutputError < Brut::CLI::Commands::BaseCommand
4
4
  @exception = exception
5
5
  end
6
6
  def run
7
- stderr.puts @exception.message
7
+ error(@exception.message)
8
8
  65
9
9
  end
10
10
 
@@ -1,11 +1,15 @@
1
1
  # Wraps the result of calling `Brut::CLI::Commands::BaseCommand#execute` and
2
2
  # interpreting it as an exit code
3
3
  class Brut::CLI::ExecuteResult
4
+ attr_reader :actual_result
4
5
  def initialize(&block)
5
6
  @actual_result = begin
6
7
  block.()
7
8
  rescue Brut::CLI::Error => ex
8
9
  ex
10
+ rescue => ex2
11
+ raise
12
+
9
13
  end
10
14
  end
11
15
 
@@ -9,11 +9,10 @@ require "open3"
9
9
  class Brut::CLI::Executor
10
10
  # Create the executor
11
11
  #
12
- # @param [Brut::CLI::Output] out an IO used to send messages to the standard output
13
- # @param [Brut::CLI::Output] err an IO used to send messages to the standard error
14
- def initialize(out:,err:)
15
- @out = out
16
- @err = err
12
+ def initialize(out:,err:,logger:)
13
+ @out = out
14
+ @err = err
15
+ @logger = logger
17
16
  end
18
17
 
19
18
  # Execute a command, logging it to the standard output and outputing the
@@ -57,7 +56,7 @@ class Brut::CLI::Executor
57
56
  @out.flush
58
57
  }
59
58
 
60
- @out.puts "Executing #{args}"
59
+ @logger.info "Executing #{args}"
61
60
  wait_thread = Open3.popen3(*args) do |_stdin,stdout,stderr,wait_thread|
62
61
  o = stdout.read_nonblock(10, exception: false)
63
62
  e = stderr.read_nonblock(10, exception: false)
@@ -79,8 +78,9 @@ class Brut::CLI::Executor
79
78
  wait_thread
80
79
  end
81
80
  if wait_thread.value.success?
82
- @out.puts "#{args.length == 1 ? args[0] : args} succeeded"
81
+ @logger.info "#{args.length == 1 ? args[0] : args} succeeded"
83
82
  else
83
+ @logger.info "'#{args.length == 1 ? args[0] : args}' failed with exit status #{wait_thread.value.exitstatus}"
84
84
  raise Brut::CLI::SystemExecError.new(args,wait_thread.value.exitstatus)
85
85
  end
86
86
  0
@@ -0,0 +1,122 @@
1
+ require "logger"
2
+ require "fileutils"
3
+ require "delegate"
4
+
5
+ class Brut::CLI::Logger < SimpleDelegator
6
+ def initialize(app_name:, stdout:, stderr:, theme: nil)
7
+ @app_name = app_name
8
+ @stdout = stdout
9
+ @logger = ::Logger.new("/dev/null")
10
+
11
+ super(@logger)
12
+
13
+ @simple_formatter = ->(severity, _time, progname, msg) {
14
+ formatted_severity = if theme
15
+ case severity
16
+ when "DEBUG"
17
+ theme.weak.render("DEBUG")
18
+ when "INFO"
19
+ theme.header.render("INFO")
20
+ when "WARN"
21
+ theme.warning.render("WARN")
22
+ when "ERROR"
23
+ theme.error.render("ERROR")
24
+ when "FATAL"
25
+ theme.error.render("FATAL")
26
+ else
27
+ severity
28
+ end
29
+ else
30
+ severity
31
+ end
32
+ [
33
+ theme ? theme.weak.render("[ #{progname} ]") : "[ #{progname} ]",
34
+ "{#{formatted_severity}}",
35
+ msg,
36
+ ].join(" ") + "\n"
37
+ }
38
+ @file_formatter = ->(severity, time, progname, msg) {
39
+ "#{time} - [ #{progname} ]{#{severity}} #{msg}\n"
40
+ }
41
+
42
+ @stdout_logger = ::Logger.new("/dev/null")
43
+ @stderr_logger = if stderr.nil?
44
+ ::Logger.new("/dev/null")
45
+ else
46
+ ::Logger.new(stderr, progname: @app_name, formatter: @simple_formatter)
47
+ end
48
+
49
+ @log_file = nil
50
+ end
51
+
52
+ def level=(level)
53
+ if level
54
+ @logger.level = level
55
+ @stdout_logger.level = level
56
+ @stderr_logger.level = level
57
+ end
58
+ end
59
+
60
+ def log_file=(log_file)
61
+ @log_file = log_file
62
+ if log_file
63
+ log_dir = log_file.dirname
64
+ if !log_dir.exist?
65
+ FileUtils.mkdir_p(log_dir)
66
+ end
67
+ @logger = ::Logger.new(@log_file, formatter: @file_formatter, progname: @app_name, level: @logger.level)
68
+ __setobj__(@logger)
69
+ if @logger.level == ::Logger::DEBUG
70
+ @stdout.puts "Logging to file #{@log_file}"
71
+ end
72
+ end
73
+ end
74
+
75
+ def log_to_stdout=(log_to_stdout)
76
+ @log_to_stdout = log_to_stdout
77
+ if @log_to_stdout
78
+ @stdout_logger = ::Logger.new(@stdout, formatter: @simple_formatter, progname: @app_name, level: @logger.level)
79
+ else
80
+ @stdout_logger = ::Logger.new("/dev/null")
81
+ end
82
+ end
83
+ def debug(message=nil,&block)
84
+ @logger.debug(message,&block)
85
+ @stdout_logger.debug(message,&block)
86
+ end
87
+ def info(message=nil,&block)
88
+ @logger.info(message,&block)
89
+ @stdout_logger.info(message,&block)
90
+ end
91
+ def warn(message=nil,&block)
92
+ @logger.warn(message,&block)
93
+ @stdout_logger.warn(message,&block)
94
+ @stderr_logger.warn(message,&block)
95
+ end
96
+ def error(message=nil,&block)
97
+ @logger.error(message,&block)
98
+ @stdout_logger.error(message,&block)
99
+ @stderr_logger.error(message,&block)
100
+ end
101
+ def fatal(message=nil,&block)
102
+ @logger.fatal(message,&block)
103
+ @stdout_logger.fatal(message,&block)
104
+ @stderr_logger.fatal(message,&block)
105
+ end
106
+
107
+ def add(severity, message=nil, progname=nil, &block)
108
+ @logger.add(severity, message, progname, &block)
109
+ @stdout_logger.add(severity, message, progname, &block)
110
+ if severity >= ::Logger::WARN
111
+ @stderr_logger.add(severity, message, progname, &block)
112
+ end
113
+ end
114
+
115
+ def without_stderr
116
+ logger = self.class.new(app_name: @app_name, stdout: @stdout, stderr: nil)
117
+ logger.level = @logger.level
118
+ logger.log_file = @log_file
119
+ logger.log_to_stdout = @log_to_stdout
120
+ logger
121
+ end
122
+ end
@@ -1,18 +1,8 @@
1
- # An `IO`-like class that provides user-helpful output, to be used in place of `puts`. This is not strictly an `IO` and is intended to provide only a few options for sending output to a human.
1
+ # An `IO`-like class that provides user-helpful output, to be used in place of `puts` and a logger. This is not replaceable for an IO.
2
2
  #
3
- # Command line apps that a human runs are often executed in the context of other apps or themselves spawn child processes. Thus, it
4
- # can be hard to know what output is coming from where. This class addresses that by prefixing every line with the name of the
5
- # command line app:
6
- #
7
- # ```
8
- # > bin/my_app doit
9
- # [ bin/my_app ] About to do sometohing
10
- # Cannot connect to house
11
- # [ bin/my_app ] Problem connecting
12
- # ```
13
- #
14
- # The prefix allows the human to know what output was generated by the app they ran and what not. This can be extremely helpful for
15
- # debugging or just understanding what is going on.
3
+ # The problem this solves is allowing your CLI app to be clutter-free in the user's terminal, but to not hide information
4
+ # unnecessarily or make it unclear where the output is coming from. Your CLI should use this class (or the methods that proxy
5
+ # to it, see `Brut::CLI::Commands::BaseCommand`) for all output and logging.
16
6
  class Brut::CLI::Output
17
7
 
18
8
  def self.from_io(io_or_output)
@@ -20,7 +10,7 @@ class Brut::CLI::Output
20
10
  in Brut::CLI::Output
21
11
  io_or_output
22
12
  else
23
- self.new(io: io_or_output, prefix: "")
13
+ self.new(io: io_or_output, app_name: $0)
24
14
  end
25
15
  end
26
16
 
@@ -30,55 +20,29 @@ class Brut::CLI::Output
30
20
  # Create a wrapper for the given IO that will use the given prefix
31
21
  #
32
22
  # @param [IO] io an IO where output should be sent, for example `$stdout`.
33
- # @param [String] prefix a prefix to be placed in front of all messages (unless a `_no_prefix` version is called)
34
- def initialize(io:, prefix:)
23
+ # @param [String] app_name the name of the app that would be using this to generate output.
24
+ def initialize(io:, app_name:)
35
25
  @io = io
36
- @prefix = prefix
26
+ @app_name = app_name
37
27
  @sync_status = @io.sync
38
28
  end
39
29
 
40
- # Calls `puts` without the prefix. This is useful if the output is going to be obvious to the human user (e.g. CLI help), or if
41
- # it's intended to be piped into another app.
42
- #
43
- # @see https://ruby-doc.org/3.3.6/Kernel.html#method-i-puts
44
- def puts_no_prefix(*objects)
45
- @io.puts(*objects)
46
- end
47
-
48
- # Calls `puts`, adding a prefix to each of the objects in `*objects`. This is useful for sending messages that a human may want to
49
- # read.
50
- #
51
30
  # @see https://ruby-doc.org/3.3.6/Kernel.html#method-i-puts
52
31
  def puts(*objects)
53
32
  if objects.empty?
54
33
  objects << ""
55
34
  end
56
35
  objects.each do |object|
57
- @io.puts(@prefix + object.to_s)
36
+ @io.puts(object)
58
37
  end
59
38
  nil
60
39
  end
61
40
 
62
- # Calls `print` without any prefix. In theory, this is the escape hatch to sending arbitrary data to the underlying `IO` without
63
- # expose said `IO`
64
- #
65
- # @see https://ruby-doc.org/3.3.6/Kernel.html#method-i-print
66
- def print(*objects)
67
- @io.print(*objects)
68
- end
69
-
70
41
  # Prints a string via `printf`, using the prefix. This is useful for communciating to a human, but you need more power for
71
42
  # formatting than is afforded by {#puts}.
72
43
  #
73
44
  # @see https://ruby-doc.org/3.3.6/Kernel.html#method-i-printf
74
45
  def printf(format_string,*objects)
75
- @io.printf(@prefix + format_string,*objects)
76
- end
77
-
78
- # Prints a string via `printf`, without the prefix.
79
- #
80
- # @see https://ruby-doc.org/3.3.6/Kernel.html#method-i-printf
81
- def printf_no_prefix(format_string,*objects)
82
46
  @io.printf(format_string,*objects)
83
47
  end
84
48