diff --git a/README.md b/README.md index d6439d8..9a72fff 100644 --- a/README.md +++ b/README.md @@ -36,15 +36,15 @@ Command.run("date") And that's all! -*But* if you need more information about what happened, the return value is a `Command` object with all the goodies that you would expect. +*But* if you need more information about what happened, the return value is a `Command::Result` object with all the goodies that you would expect. ```ruby -command = Command.run("date") -command.stdout # => "Tue Nov 26 14:45:03 EST 2013\n" -command.stderr # => "" -command.status # => 0 -command.success? # => true -command.pid # => 32157 +result = Command.run("date") +result.stdout # => "Tue Nov 26 14:45:03 EST 2013\n" +result.stderr # => "" +result.status # => 0 +result.success? # => true +result.pid # => 32157 ``` Now, drawing boundaries in your tests is easy. @@ -56,7 +56,7 @@ describe DateGetter do date = "Tue Nov 26 14:45:03 EST 2013" expect(Command).to receive(:run).with("date") do - double(:command, success?: true, stdout: "#{date}\n") + double(:result, success?: true, stdout: "#{date}\n") end expect(DateGetter.get).to eq(date) diff --git a/lib/command.rb b/lib/command.rb index 11cd054..7bf3ed3 100644 --- a/lib/command.rb +++ b/lib/command.rb @@ -1,32 +1,17 @@ require "open3" -class Command - attr_reader :stdin, :stdout, :stderr - - def self.run(stdin) - new(stdin).run - end - - def initialize(stdin) - @stdin = stdin +require "command/definition" +require "command/runner" +require "command/result" + +module Command + + # @param [String] cmd + # @return [Command::Result] + def self.run(cmd) + definition = Command::Definition.new(cmd) + runner = Command::Runner.new + runner.run(definition) end - def run - @stdout, @stderr, @status = Open3.capture3(stdin) - self - end - - def exitstatus - @status && @status.exitstatus - end - - alias_method :status, :exitstatus - - def success? - @status && @status.success? - end - - def pid - @status && @status.pid - end end diff --git a/lib/command/definition.rb b/lib/command/definition.rb new file mode 100644 index 0000000..fe66e31 --- /dev/null +++ b/lib/command/definition.rb @@ -0,0 +1,12 @@ +module Command + class Definition + + attr_reader :cmd + + # @param [String] cmd + def initialize(cmd) + @cmd = cmd + end + + end +end diff --git a/lib/command/result.rb b/lib/command/result.rb new file mode 100644 index 0000000..829ca7d --- /dev/null +++ b/lib/command/result.rb @@ -0,0 +1,36 @@ +module Command + class Result + + # @param [Hash] output + # @option output_streams [String] :stdout + # @option output_streams [String] :stderr + # @param [Process::Status] status + def initialize(output, status) + @output = output + @status = status + end + + def stdout + @output[:stdout] + end + + def stderr + @output[:stderr] + end + + def exitstatus + @status.exitstatus + end + + alias_method :status, :exitstatus + + def success? + @status.success? + end + + def pid + @status.pid + end + + end +end diff --git a/lib/command/runner.rb b/lib/command/runner.rb new file mode 100644 index 0000000..5100508 --- /dev/null +++ b/lib/command/runner.rb @@ -0,0 +1,13 @@ +module Command + class Runner + + # @param [Command::Definition] definition + # @return [Command::Result] + def run(definition) + stdout, stderr, status = Open3.capture3(definition.cmd) + output = {:stdout => stdout, :stderr => stderr} + Command::Result.new(output, status) + end + + end +end diff --git a/spec/lib/command/definition_spec.rb b/spec/lib/command/definition_spec.rb new file mode 100644 index 0000000..07a91ca --- /dev/null +++ b/spec/lib/command/definition_spec.rb @@ -0,0 +1,14 @@ +require "spec_helper" + +describe Command::Definition do + + describe "#initialize" do + let(:cmd) { "my command" } + let(:definition) { Command::Definition.new(cmd) } + + it "sets the cmd" do + expect(definition.cmd).to eq(cmd) + end + end + +end diff --git a/spec/lib/command/result_spec.rb b/spec/lib/command/result_spec.rb new file mode 100644 index 0000000..9957eab --- /dev/null +++ b/spec/lib/command/result_spec.rb @@ -0,0 +1,48 @@ +require "spec_helper" + +describe Command::Result do + + describe "#initialize" do + let(:output) { {:stdout => "my stdout", :stderr => "my stderr"} } + let(:status) { double(Process::Status, exitstatus: 12, success?: false, pid: 123) } + let(:result) { Command::Result.new(output, status) } + + it "sets the output" do + expect(result.stdout).to eq("my stdout") + expect(result.stderr).to eq("my stderr") + end + + it "sets the status" do + expect(result.exitstatus).to be(12) + end + end + + describe "#exitstatus" do + let(:status) { double(Process::Status, exitstatus: 12) } + let(:result) { Command::Result.new({}, status) } + + it "returns the exit status" do + expect(result.exitstatus).to eq(12) + expect(result.status).to eq(12) + end + end + + describe "#success" do + let(:status) { double(Process::Status, success?: false) } + let(:result) { Command::Result.new({}, status) } + + it "returns the success" do + expect(result.success?).to eq(false) + end + end + + describe "#pid" do + let(:status) { double(Process::Status, pid: 123) } + let(:result) { Command::Result.new({}, status) } + + it "returns the success" do + expect(result.pid).to eq(123) + end + end + +end diff --git a/spec/lib/command/runner_spec.rb b/spec/lib/command/runner_spec.rb new file mode 100644 index 0000000..09e2b8b --- /dev/null +++ b/spec/lib/command/runner_spec.rb @@ -0,0 +1,66 @@ +require "spec_helper" + +describe Command::Runner do + + describe "#run" do + + context "when command exits successfully" do + let(:cmd) { "ruby -e 'STDOUT.print \"hello\"; STDERR.print \"world\";'" } + let(:definition) { Command::Definition.new(cmd) } + let(:runner) { Command::Runner.new } + let(:result) { runner.run(definition) } + + it "returns a result" do + expect(result).to be_a(Command::Result) + end + + it "sets the standard output" do + expect(result.stdout).to eq("hello") + end + + it "sets the standard error" do + expect(result.stderr).to eq("world") + end + + it "sets the exit status" do + expect(result.exitstatus).to eq(0) + end + + it "sets the success" do + expect(result.success?).to eq(true) + end + + it "sets the PID" do + expect(result.pid).to be_a(Fixnum) + end + end + + context "when command fails" do + let(:cmd) { "ruby -e 'STDOUT.print \"hello\"; STDERR.print \"world\"; exit(1);'" } + let(:definition) { Command::Definition.new(cmd) } + let(:runner) { Command::Runner.new } + let(:result) { runner.run(definition) } + + it "returns a result" do + expect(result).to be_a(Command::Result) + end + + it "sets the standard output" do + expect(result.stdout).to eq("hello") + end + + it "sets the standard error" do + expect(result.stderr).to eq("world") + end + + it "sets the exit status" do + expect(result.exitstatus).to eq(1) + end + + it "sets the success" do + expect(result.success?).to eq(false) + end + end + + end +end diff --git a/spec/lib/command_spec.rb b/spec/lib/command_spec.rb index 6ca2568..770208b 100644 --- a/spec/lib/command_spec.rb +++ b/spec/lib/command_spec.rb @@ -1,88 +1,20 @@ require "spec_helper" describe Command do + describe ".run" do - let!(:stdin) { double(:stdin) } - let(:command) { double(:command) } + let(:cmd) { double(:cmd) } + let(:runner) { double(Command::Runner) } + let(:definition) { double(Command::Definition) } + let(:result) { double(Command::Result) } it "initializes, runs and returns a new command" do - expect(Command).to receive(:new).once.with(stdin) { command } - expect(command).to receive(:run).once.with(no_args) { command } - - expect(Command.run(stdin)).to eq(command) - end - end - - describe "#initialize" do - let!(:stdin) { "man touch" } - - it "sets the standard input" do - command = Command.new(stdin) + expect(Command::Runner).to receive(:new).once.with(no_args) { runner } + expect(Command::Definition).to receive(:new).once.with(cmd) { definition } + expect(runner).to receive(:run).once.with(definition) { result } - expect(command.stdin).to eq(stdin) + expect(Command.run(cmd)).to eq(result) end end - describe "#run" do - let!(:stdin) { "man touch" } - let!(:command) { Command.new(stdin) } - let(:stdout) { double(:stdout) } - let(:stderr) { double(:stderr) } - let(:status) { double(:status, exitstatus: 1, success?: false, pid: 123) } - let(:result) { [stdout, stderr, status] } - - before do - Open3.stub(:capture3) { result } - end - - it "runs the given input" do - expect(Open3).to receive(:capture3).once.with(stdin) { result } - - command.run - end - - it "sets the standard output" do - expect { - command.run - }.to change { - command.stdout - }.from(nil).to(stdout) - end - - it "sets the standard error" do - expect { - command.run - }.to change { - command.stdout - }.from(nil).to(stdout) - end - - it "sets the exit status" do - expect { - command.run - }.to change { - command.exitstatus - }.from(nil).to(status.exitstatus) - end - - it "sets the success" do - expect { - command.run - }.to change { - command.success? - }.from(nil).to(status.success?) - end - - it "sets the PID" do - expect { - command.run - }.to change { - command.pid - }.from(nil).to(status.pid) - end - - it "returns the command" do - expect(command.run).to eq(command) - end - end end