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
39 changes: 20 additions & 19 deletions lib/dry/cli.rb
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,13 @@ def initialize(command_or_registry = nil, &block)
# Invoke the CLI
#
# @param arguments [Array<string>] the command line arguments (defaults to `ARGV`)
# @param out [IO] the standard output (defaults to `$stdout`)
# @param err [IO] the error output (defaults to `$stderr`)
# @param stderr [IO] the error output (defaults to `$stderr`)
# @param stdin [IO] the standard input (defaults to `$stdin`)
# @param stdout [IO] the standard output (defaults to `$stdout`)
#
# @since 0.1.0
def call(arguments: ARGV, out: $stdout, err: $stderr)
@out, @err = out, err
def call(arguments: ARGV, stderr: $stderr, stdin: $stdin, stdout: $stdout)
@stderr, @stdin, @stdout = stderr, stdin, stdout
kommand ? perform_command(arguments) : perform_registry(arguments)
rescue SignalException => exception
signal_exception(exception)
Expand All @@ -108,11 +109,15 @@ def call(arguments: ARGV, out: $stdout, err: $stderr)

# @since 0.6.0
# @api private
attr_reader :out
attr_reader :stderr

# @since unreleased
# @api private
attr_reader :stdin

# @since 0.6.0
# @api private
attr_reader :err
attr_reader :stdout

# Invoke the CLI if singular command passed
#
Expand All @@ -123,10 +128,6 @@ def call(arguments: ARGV, out: $stdout, err: $stderr)
# @api private
def perform_command(arguments)
command, args = parse(kommand, arguments, [])

command.instance_variable_set(:@err, err) unless command.instance_variable_defined?(:@err)
command.instance_variable_set(:@out, out) unless command.instance_variable_defined?(:@out)

command.call(**args)
end

Expand All @@ -142,10 +143,7 @@ def perform_registry(arguments)
return spell_checker(result, arguments) unless result.found?

command, args = parse(result.command, result.arguments, result.names)
return err.puts(Usage.call(result)) unless command.respond_to?(:call)

command.instance_variable_set(:@err, err) unless command.instance_variable_defined?(:@err)
command.instance_variable_set(:@out, out) unless command.instance_variable_defined?(:@out)
return stderr.puts(Usage.call(result)) unless command.respond_to?(:call)

result.before_callbacks.run(command, **args)
command.call(**args)
Expand Down Expand Up @@ -179,28 +177,31 @@ def parse(command, arguments, names)
# @since 0.6.0
# @api private
def build_command(command)
command.is_a?(Class) ? command.new : command
return command unless command.is_a?(Class)
return command.new(stderr: stderr, stdin: stdin, stdout: stdout) if CLI.command?(command)

command.new
end

# @since 0.6.0
# @api private
def help(command, prog_name)
out.puts Banner.call(command, prog_name)
stdout.puts Banner.call(command, prog_name)
exit(0) # Successful exit
end

# @since 0.6.0
# @api private
def error(result)
err.puts(result.error)
stderr.puts(result.error)
exit(1)
end

# @since 1.1.1
def spell_checker(result, arguments)
spell_checker = SpellChecker.call(result, arguments)
err.puts "#{spell_checker}\n\n" if spell_checker
err.puts Usage.call(result)
stderr.puts "#{spell_checker}\n\n" if spell_checker
stderr.puts Usage.call(result)
exit(1)
end

Expand Down
35 changes: 28 additions & 7 deletions lib/dry/cli/command.rb
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,14 @@ def self.superclass_options
superclass_variable_dup(:@options)
end

# @since unreleased
# @api private
def initialize(stderr: $stderr, stdin: $stdin, stdout: $stdout)
@stderr = stderr
@stdin = stdin
@stdout = stdout
end

extend Forwardable

delegate %i[
Expand All @@ -423,31 +431,44 @@ def self.superclass_options
# @example
# class MyCommand
# def call
# out.puts "Hello World!"
# stdout.puts "Hello World!"
# exit(0)
# rescue StandardError => e
# err.puts "Uh oh: #{e.message}"
# stderr.puts "Uh oh: #{e.message}"
# exit(1)
# end
# end
#
# @since unreleased
# @return [IO]
attr_reader :err
attr_reader :stderr

# The standard output object used to print messaging
# The standard input stream used for reading input
#
# @example
# class MyCommand
# def call
# out.puts "Hello World!"
# exit(0)
# name = stdin.gets.chomp
# stdout.puts "Hello #{name}!"
# end
# end
#
# @since unreleased
# @return [IO]
attr_reader :stdin

# The standard output stream used for normal output
#
# @example
# class MyCommand
# def call
# stdout.puts "Hello World!"
# end
# end
#
# @since unreleased
# @return [IO]
attr_reader :out
attr_reader :stdout
end
end
end
4 changes: 2 additions & 2 deletions lib/dry/cli/inline.rb
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,13 @@ module Inline
# end
#
# @since 0.6.0
def run(arguments: ARGV, out: $stdout)
def run(arguments: ARGV, stdout: $stdout)
command = AnonymousCommand
command.define_method(:call) do |**args|
yield(**args)
end

Dry.CLI(command).call(arguments: arguments, out: out)
Dry.CLI(command).call(arguments: arguments, stdout: stdout)
end
end
end
Expand Down
40 changes: 0 additions & 40 deletions spec/unit/dry/cli/cli_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -183,45 +183,5 @@
)
end
end

it "exposes @out and @err to command without overriding pre-existing ivars" do
# @out and @err are exposed by default
command_class = Class.new(Dry::CLI::Command) do
def call(**)
@out.puts "out"
@err.puts "err"
end
end
cli = Dry.CLI(command_class.new)

default_out = StringIO.new
default_err = StringIO.new
cli.call(arguments: [], out: default_out, err: default_err)

expect(default_out.string).to eq("out\n")
expect(default_err.string).to eq("err\n")

# @out and @err do not override pre-existing ivars
custom_command_class = Class.new(command_class) do
define_method(:initialize) do |out, err|
super()
@out = out
@err = err
end
end

custom_out = StringIO.new
custom_err = StringIO.new
cli = Dry.CLI(custom_command_class.new(custom_out, custom_err))

default_out = StringIO.new
default_err = StringIO.new
cli.call(arguments: [], out: default_out, err: default_err)

expect(custom_out.string).to eq("out\n")
expect(custom_err.string).to eq("err\n")
expect(default_out.string).to eq("")
expect(default_err.string).to eq("")
end
end
end