diff --git a/lib/dry/cli.rb b/lib/dry/cli.rb index a61621f0..9405838c 100644 --- a/lib/dry/cli.rb +++ b/lib/dry/cli.rb @@ -83,12 +83,13 @@ def initialize(command_or_registry = nil, &block) # Invoke the CLI # # @param arguments [Array] 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) @@ -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 # @@ -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 @@ -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) @@ -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 diff --git a/lib/dry/cli/command.rb b/lib/dry/cli/command.rb index 84e50d70..3a2de5a9 100644 --- a/lib/dry/cli/command.rb +++ b/lib/dry/cli/command.rb @@ -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[ @@ -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 diff --git a/lib/dry/cli/inline.rb b/lib/dry/cli/inline.rb index 87c1f79f..e058bba2 100644 --- a/lib/dry/cli/inline.rb +++ b/lib/dry/cli/inline.rb @@ -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 diff --git a/spec/unit/dry/cli/cli_spec.rb b/spec/unit/dry/cli/cli_spec.rb index b48a73ce..d51d3fcc 100644 --- a/spec/unit/dry/cli/cli_spec.rb +++ b/spec/unit/dry/cli/cli_spec.rb @@ -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