Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions lib/dry/cli.rb
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ def call(arguments: ARGV, out: $stdout, err: $stderr)
# @api private
def perform_command(arguments)
command, args = parse(kommand, arguments, [])

command.instance_variable_set(:@stderr, err)
Copy link
Contributor Author

@aaronmallen aaronmallen Jul 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea here is to not have to change the signature of Command#initialize causing a breaking change

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about initializing a pair of StringIO in the command, and then

def perform_command(arguments)
  command, args = parse(kommand, arguments, [])
  command.call(**args)
  IO.copy_stream(command.out, out)
  IO.copy_stream(command.err, err)
end

This would make testing the stdout and stderr of commands much simpler.

Copy link
Contributor Author

@aaronmallen aaronmallen Jul 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The intention is to utilize the out/err passed to the CLI in the first place. dry-cil uses them to print help and errors and "inline" commands. We should also allow the commands to use the user specified out/err. Testing is already trivial as the values are DI

command.instance_variable_set(:@stdout, out)

command.call(**args)
end

Expand All @@ -113,6 +117,9 @@ def perform_registry(arguments)

command, args = parse(result.command, result.arguments, result.names)

command.instance_variable_set(:@stderr, err)
command.instance_variable_set(:@stdout, out)

result.before_callbacks.run(command, args)
command.call(**args)
result.after_callbacks.run(command, args)
Expand Down
33 changes: 33 additions & 0 deletions lib/dry/cli/command.rb
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,39 @@ def self.superclass_options
optional_arguments
subcommands
] => "self.class"

protected

# The error output used to print error messaging
#
# @example
# class MyCommand
# def call
# stdout.puts "Hello World!"
# exit(0)
# rescue StandardError => e
# stderr.puts "Uh oh: #{e.message}"
# exit(1)
# end
# end
#
# @since unreleased
# @return [IO]
attr_reader :stderr

# The standard output object used to print messaging
#
# @example
# class MyCommand
# def call
# stdout.puts "Hello World!"
# exit(0)
# end
# end
#
# @since unreleased
# @return [IO]
attr_reader :stdout
end
end
end