caliph 0.1.0
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.
- checksums.yaml +7 -0
- data/lib/caliph.rb +4 -0
- data/lib/caliph/command-chain.rb +58 -0
- data/lib/caliph/command-line-dsl.rb +11 -0
- data/lib/caliph/command-line.rb +185 -0
- data/lib/caliph/command-run-result.rb +129 -0
- data/lib/caliph/define-op.rb +29 -0
- data/lib/caliph/shell-escaped.rb +25 -0
- data/lib/caliph/testing/mock-command-line.rb +67 -0
- data/lib/caliph/testing/record-commands.rb +38 -0
- data/spec/command-chain.rb +9 -0
- data/spec/command-line-dsl.rb +48 -0
- data/spec/command-line.rb +135 -0
- data/spec_help/gem_test_suite.rb +17 -0
- data/spec_help/spec_helper.rb +11 -0
- metadata +67 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 2b0f24364e478c354208e04a309d201339ebaa57
|
4
|
+
data.tar.gz: 1de70f9e8bb89ee483b7d28ffbcbb76d10428687
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 94590b96bf71222807e9b9c96c3a5ea52cd89a826191169608d02603879edb3bc02948bea3f92e97fdf0366baedcf1ad1403c0a6248f857ee79051ffaa24de13
|
7
|
+
data.tar.gz: 255d5824af358a59fdfb92b8798bd62015f81cfe9f242d51e9f1ac9c0318c50bfa9ae20dd9bce372567eea5775927db1e48c947daabbd4f0cd84f60cf8c80e6f
|
data/lib/caliph.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'caliph/define-op'
|
2
|
+
|
3
|
+
module Caliph
|
4
|
+
class CommandChain < CommandLine
|
5
|
+
include DefineOp
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@commands = []
|
9
|
+
@command_environment = {}
|
10
|
+
super(nil)
|
11
|
+
end
|
12
|
+
|
13
|
+
attr_reader :commands
|
14
|
+
|
15
|
+
def add(cmd)
|
16
|
+
yield cmd if block_given?
|
17
|
+
@commands << cmd
|
18
|
+
self
|
19
|
+
end
|
20
|
+
|
21
|
+
#Honestly this is sub-optimal - biggest driver for considering the
|
22
|
+
#mini-shell approach here.
|
23
|
+
def command_environment
|
24
|
+
@command_environment = @commands.reverse.inject(@command_environment) do |env, command|
|
25
|
+
env.merge(command.command_environment)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def name
|
30
|
+
@name || @commands.last.name
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class WrappingChain < CommandChain
|
35
|
+
define_op('-')
|
36
|
+
|
37
|
+
def command
|
38
|
+
@commands.map{|cmd| cmd.command}.join(" -- ")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class PrereqChain < CommandChain
|
43
|
+
define_op('&')
|
44
|
+
|
45
|
+
def command
|
46
|
+
@commands.map{|cmd| cmd.command}.join(" && ")
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
class PipelineChain < CommandChain
|
51
|
+
define_op('|')
|
52
|
+
|
53
|
+
def command
|
54
|
+
@commands.map{|cmd| cmd.command}.join(" | ")
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
@@ -0,0 +1,185 @@
|
|
1
|
+
require 'caliph/define-op'
|
2
|
+
require 'caliph/command-run-result'
|
3
|
+
|
4
|
+
module Caliph
|
5
|
+
class CommandLine
|
6
|
+
include DefineOp
|
7
|
+
|
8
|
+
class << self
|
9
|
+
attr_accessor :output_stream
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(executable, *options)
|
13
|
+
@output_stream = self.class.output_stream || $stderr
|
14
|
+
@executable = executable.to_s
|
15
|
+
@options = options
|
16
|
+
@redirections = []
|
17
|
+
@env = {}
|
18
|
+
yield self if block_given?
|
19
|
+
end
|
20
|
+
|
21
|
+
attr_accessor :name, :executable, :options, :env, :output_stream
|
22
|
+
attr_reader :redirections
|
23
|
+
|
24
|
+
alias_method :command_environment, :env
|
25
|
+
|
26
|
+
def set_env(name, value)
|
27
|
+
command_environment[name] = value
|
28
|
+
return self
|
29
|
+
end
|
30
|
+
|
31
|
+
def verbose
|
32
|
+
#::Rake.verbose && ::Rake.verbose != ::Rake::FileUtilsExt::DEFAULT
|
33
|
+
end
|
34
|
+
|
35
|
+
def name
|
36
|
+
@name || executable
|
37
|
+
end
|
38
|
+
|
39
|
+
# The command as a string, including arguments and options
|
40
|
+
def command
|
41
|
+
([executable] + options_composition + @redirections).join(" ")
|
42
|
+
end
|
43
|
+
|
44
|
+
# The command as a string, including arguments and options, plus prefixed
|
45
|
+
# environment variables.
|
46
|
+
def string_format
|
47
|
+
(command_environment.map do |key, value|
|
48
|
+
[key, value].join("=")
|
49
|
+
end + [command]).join(" ")
|
50
|
+
end
|
51
|
+
|
52
|
+
def options_composition
|
53
|
+
options
|
54
|
+
end
|
55
|
+
|
56
|
+
def redirect_to(stream, path)
|
57
|
+
@redirections << "#{stream}>#{path}"
|
58
|
+
self
|
59
|
+
end
|
60
|
+
|
61
|
+
def redirect_from(path, stream)
|
62
|
+
@redirections << "#{stream}<#{path}"
|
63
|
+
end
|
64
|
+
|
65
|
+
def copy_stream_to(from, to)
|
66
|
+
@redirections << "#{from}>&#{to}"
|
67
|
+
end
|
68
|
+
|
69
|
+
def redirect_stdout(path)
|
70
|
+
redirect_to(1, path)
|
71
|
+
end
|
72
|
+
|
73
|
+
def redirect_stderr(path)
|
74
|
+
redirect_to(2, path)
|
75
|
+
end
|
76
|
+
|
77
|
+
def redirect_stdin(path)
|
78
|
+
redirect_from(path, 0)
|
79
|
+
end
|
80
|
+
|
81
|
+
def redirect_both(path)
|
82
|
+
redirect_stdout(path).redirect_stderr(path)
|
83
|
+
end
|
84
|
+
|
85
|
+
# Run the command, wait for termination, and collect the results.
|
86
|
+
# Returns an instance of CommandRunResult that contains the output
|
87
|
+
# and exit code of the command.
|
88
|
+
#
|
89
|
+
# This version adds some information to STDOUT to document that the
|
90
|
+
# command is running. For a terser version, call #execute directly
|
91
|
+
def run
|
92
|
+
report string_format + " ", false
|
93
|
+
result = execute
|
94
|
+
report "=> #{result.exit_code}"
|
95
|
+
report result.format_streams if verbose
|
96
|
+
return result
|
97
|
+
ensure
|
98
|
+
report "" if verbose
|
99
|
+
end
|
100
|
+
|
101
|
+
# Fork a new process for the command, then terminate our process.
|
102
|
+
def run_as_replacement
|
103
|
+
output_stream.puts "Ceding execution to: "
|
104
|
+
output_stream.puts string_format
|
105
|
+
Process.exec(command_environment, command)
|
106
|
+
end
|
107
|
+
alias replace_us run_as_replacement
|
108
|
+
|
109
|
+
# Run the command in the background. The command can survive the caller.
|
110
|
+
def run_detached
|
111
|
+
pid, out, err = spawn_process
|
112
|
+
Process.detach(pid)
|
113
|
+
return pid, out, err
|
114
|
+
end
|
115
|
+
alias spin_off run_detached
|
116
|
+
|
117
|
+
# Run the command, wait for termination, and collect the results.
|
118
|
+
# Returns an instance of CommandRunResult that contains the output
|
119
|
+
# and exit code of the command.
|
120
|
+
#
|
121
|
+
def execute
|
122
|
+
collect_result(*spawn_process)
|
123
|
+
end
|
124
|
+
|
125
|
+
# Run the command in parallel with the parent process - will kill it if it
|
126
|
+
# outlasts us
|
127
|
+
def run_in_background
|
128
|
+
pid, out, err = spawn_process
|
129
|
+
Process.detach(pid)
|
130
|
+
at_exit do
|
131
|
+
kill_process(pid)
|
132
|
+
end
|
133
|
+
return pid, out, err
|
134
|
+
end
|
135
|
+
alias background run_in_background
|
136
|
+
|
137
|
+
# Given a process ID for a running command and a pair of stdout/stdin
|
138
|
+
# streams, records the results of the command and returns them in a
|
139
|
+
# CommandRunResult instance.
|
140
|
+
def collect_result(pid, host_stdout, host_stderr)
|
141
|
+
result = CommandRunResult.new(pid, self)
|
142
|
+
result.streams = {1 => host_stdout, 2 => host_stderr}
|
143
|
+
result.wait
|
144
|
+
return result
|
145
|
+
end
|
146
|
+
|
147
|
+
|
148
|
+
def kill_process(pid)
|
149
|
+
Process.kill("INT", pid)
|
150
|
+
end
|
151
|
+
|
152
|
+
def complete(pid, out, err)
|
153
|
+
kill_process(pid)
|
154
|
+
collect_result(pid, out, err)
|
155
|
+
end
|
156
|
+
|
157
|
+
def report(message, newline=true)
|
158
|
+
output_stream.print(message + (newline ? "\n" : ""))
|
159
|
+
end
|
160
|
+
|
161
|
+
|
162
|
+
def succeeds?
|
163
|
+
run.succeeded?
|
164
|
+
end
|
165
|
+
|
166
|
+
def must_succeed!
|
167
|
+
run.must_succeed!
|
168
|
+
end
|
169
|
+
|
170
|
+
def spawn_process
|
171
|
+
host_stdout, cmd_stdout = IO.pipe
|
172
|
+
host_stderr, cmd_stderr = IO.pipe
|
173
|
+
|
174
|
+
pid = Process.spawn(command_environment, command, :out => cmd_stdout, :err => cmd_stderr)
|
175
|
+
cmd_stdout.close
|
176
|
+
cmd_stderr.close
|
177
|
+
|
178
|
+
return pid, host_stdout, host_stderr
|
179
|
+
end
|
180
|
+
|
181
|
+
|
182
|
+
end
|
183
|
+
|
184
|
+
|
185
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
module Caliph
|
2
|
+
class CommandRunResult
|
3
|
+
def initialize(pid, command)
|
4
|
+
@command = command
|
5
|
+
@pid = pid
|
6
|
+
|
7
|
+
#####
|
8
|
+
@process_status = nil
|
9
|
+
@streams = {}
|
10
|
+
@consume_timeout = nil
|
11
|
+
end
|
12
|
+
attr_reader :process_status, :pid
|
13
|
+
attr_accessor :consume_timeout, :streams
|
14
|
+
|
15
|
+
def stdout
|
16
|
+
@streams[1]
|
17
|
+
end
|
18
|
+
|
19
|
+
def stderr
|
20
|
+
@streams[2]
|
21
|
+
end
|
22
|
+
|
23
|
+
def exit_code
|
24
|
+
@process_status.exitstatus
|
25
|
+
end
|
26
|
+
alias exit_status exit_code
|
27
|
+
|
28
|
+
def succeeded?
|
29
|
+
must_succeed!
|
30
|
+
return true
|
31
|
+
rescue
|
32
|
+
return false
|
33
|
+
end
|
34
|
+
|
35
|
+
def format_streams
|
36
|
+
"stdout:#{stdout.nil? || stdout.empty? ? "[empty]\n" : "\n#{stdout}"}" +
|
37
|
+
"stderr:#{stderr.nil? || stderr.empty? ? "[empty]\n" : "\n#{stderr}"}---"
|
38
|
+
end
|
39
|
+
|
40
|
+
def must_succeed!
|
41
|
+
case exit_code
|
42
|
+
when 0
|
43
|
+
return exit_code
|
44
|
+
else
|
45
|
+
fail "Command #{@command.inspect} failed with exit status #{exit_code}: \n#{format_streams}"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def wait
|
50
|
+
@accumulators = {}
|
51
|
+
waits = {}
|
52
|
+
@buffered_echo = []
|
53
|
+
|
54
|
+
ioes = streams.values
|
55
|
+
ioes.each do |io|
|
56
|
+
@accumulators[io] = []
|
57
|
+
waits[io] = 3
|
58
|
+
end
|
59
|
+
begin_echoing = Time.now + (@consume_timeout || 3)
|
60
|
+
|
61
|
+
@live_ioes = ioes.dup
|
62
|
+
|
63
|
+
until @live_ioes.empty? do
|
64
|
+
newpid, @process_status = Process.waitpid2(pid, Process::WNOHANG)
|
65
|
+
|
66
|
+
unless @process_status.nil?
|
67
|
+
consume_buffers(@live_ioes)
|
68
|
+
break
|
69
|
+
end
|
70
|
+
|
71
|
+
timeout = 0
|
72
|
+
|
73
|
+
if !@buffered_echo.nil?
|
74
|
+
timeout = begin_echoing - Time.now
|
75
|
+
if timeout < 0
|
76
|
+
@command.report ""
|
77
|
+
@command.report "Long running command output:"
|
78
|
+
@command.report @buffered_echo.join
|
79
|
+
@buffered_echo = nil
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
if timeout > 0
|
84
|
+
result = IO::select(@live_ioes, [], @live_ioes, timeout)
|
85
|
+
else
|
86
|
+
result = IO::select(@live_ioes, [], @live_ioes, 1)
|
87
|
+
end
|
88
|
+
|
89
|
+
unless result.nil? #timeout
|
90
|
+
readable, _writeable, errored = *result
|
91
|
+
unless errored.empty?
|
92
|
+
raise "Error on IO: #{errored.inspect}"
|
93
|
+
end
|
94
|
+
|
95
|
+
consume_buffers(readable)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
if @process_status.nil?
|
100
|
+
newpid, @process_status = Process.waitpid2(pid)
|
101
|
+
end
|
102
|
+
|
103
|
+
ioes.each do |io|
|
104
|
+
io.close
|
105
|
+
end
|
106
|
+
@streams = Hash[ioes.each_with_index.map{|io, index| [index + 1, @accumulators[io].join]}]
|
107
|
+
end
|
108
|
+
|
109
|
+
def consume_buffers(readable)
|
110
|
+
if not(readable.nil? or readable.empty?)
|
111
|
+
readable.each do |io|
|
112
|
+
begin
|
113
|
+
while chunk = io.read_nonblock(4096)
|
114
|
+
if @buffered_echo.nil?
|
115
|
+
@command.report chunk, false
|
116
|
+
else
|
117
|
+
@buffered_echo << chunk
|
118
|
+
end
|
119
|
+
@accumulators[io] << chunk
|
120
|
+
end
|
121
|
+
rescue IO::WaitReadable => ex
|
122
|
+
rescue EOFError => ex
|
123
|
+
@live_ioes.delete(io)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Caliph
|
2
|
+
module DefineOp
|
3
|
+
def self.included(base)
|
4
|
+
base.extend(ClassMethods)
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def define_chain_op(opname, klass)
|
9
|
+
define_method(opname) do |other|
|
10
|
+
unless CommandLine === other
|
11
|
+
other = CommandLine.new(*[*other])
|
12
|
+
end
|
13
|
+
chain = nil
|
14
|
+
if klass === self
|
15
|
+
chain = self
|
16
|
+
else
|
17
|
+
chain = klass.new
|
18
|
+
chain.add(self)
|
19
|
+
end
|
20
|
+
chain.add(other)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def define_op(opname)
|
25
|
+
CommandLine.define_chain_op(opname, self)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'caliph/command-line'
|
2
|
+
|
3
|
+
module Caliph
|
4
|
+
class ShellEscaped < CommandLine
|
5
|
+
def initialize(cmd)
|
6
|
+
@escaped = cmd
|
7
|
+
end
|
8
|
+
|
9
|
+
def command
|
10
|
+
"'" + @escaped.string_format.gsub(/'/,"\'") + "'"
|
11
|
+
end
|
12
|
+
|
13
|
+
def command_environment
|
14
|
+
{}
|
15
|
+
end
|
16
|
+
|
17
|
+
def name
|
18
|
+
@name || @escaped.name
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_s
|
22
|
+
command
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'caliph/command-line'
|
2
|
+
|
3
|
+
module Caliph
|
4
|
+
class MockCommandResult < CommandRunResult
|
5
|
+
def self.create(*args)
|
6
|
+
if args.length == 1
|
7
|
+
args = [args[0], {1 => ""}]
|
8
|
+
end
|
9
|
+
|
10
|
+
if String == args[1]
|
11
|
+
args[1] = {1 => args[1]}
|
12
|
+
end
|
13
|
+
|
14
|
+
return self.new(*args)
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize(code, streams)
|
18
|
+
@streams = streams
|
19
|
+
@exit_code = code
|
20
|
+
end
|
21
|
+
|
22
|
+
attr_reader :exit_code, :streams
|
23
|
+
|
24
|
+
alias exit_status exit_code
|
25
|
+
end
|
26
|
+
|
27
|
+
class CommandLine
|
28
|
+
def self.execute(*args)
|
29
|
+
fail "Command line executed in specs without 'expect_command' or 'expect_some_commands'"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
module CommandLineExampleGroup
|
34
|
+
include CommandLineDSL
|
35
|
+
module MockingExecute
|
36
|
+
def execute
|
37
|
+
Caliph::CommandLine.execute(command)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
def self.included(group)
|
43
|
+
group.before :each do
|
44
|
+
@original_execute = Caliph::CommandLine.instance_method(:execute)
|
45
|
+
unless MockingExecute > Caliph::CommandLine
|
46
|
+
Caliph::CommandLine.send(:include, MockingExecute)
|
47
|
+
end
|
48
|
+
Caliph::CommandLine.send(:remove_method, :execute)
|
49
|
+
end
|
50
|
+
|
51
|
+
group.after :each do
|
52
|
+
Caliph::CommandLine.send(:define_method, :execute, @original_execute)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
#Registers indifference as to exactly what commands get called
|
57
|
+
def expect_some_commands
|
58
|
+
Caliph::CommandLine.should_receive(:execute).any_number_of_times.and_return(MockCommandResult.create(0))
|
59
|
+
end
|
60
|
+
|
61
|
+
#Registers an expectation about a command being run - expectations are
|
62
|
+
#ordered
|
63
|
+
def expect_command(cmd, *result)
|
64
|
+
Caliph::CommandLine.should_receive(:execute, :expected_from => caller(1)[0]).with(cmd).ordered.and_return(MockCommandResult.create(*result))
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'caliph/command-line'
|
2
|
+
|
3
|
+
module Caliph
|
4
|
+
class CommandLine
|
5
|
+
@@commands = []
|
6
|
+
alias original_execute execute
|
7
|
+
|
8
|
+
def execute
|
9
|
+
result = original_execute
|
10
|
+
@@commands << [command, result]
|
11
|
+
return result
|
12
|
+
end
|
13
|
+
|
14
|
+
class << self
|
15
|
+
attr_accessor :command_recording_path
|
16
|
+
|
17
|
+
def command_recording_path
|
18
|
+
@command_recording_path ||= ENV['CALIPH_CMDREC']
|
19
|
+
end
|
20
|
+
|
21
|
+
def emit_recording
|
22
|
+
io = $stderr
|
23
|
+
if command_recording_path
|
24
|
+
io = File.open(command_recording_path, "w")
|
25
|
+
else
|
26
|
+
io.puts "Set CALIPH_CMDREC to write to a path"
|
27
|
+
end
|
28
|
+
@@commands.each do |pair|
|
29
|
+
io.puts "[/#{pair[0]}/, #{[pair[1].exit_code, pair[1].streams].inspect}]"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
at_exit do
|
37
|
+
Caliph::CommandLine.emit_recording
|
38
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'caliph/command-line-dsl'
|
2
|
+
|
3
|
+
describe Caliph::CommandLineDSL do
|
4
|
+
include described_class
|
5
|
+
|
6
|
+
describe "using the - operator" do
|
7
|
+
let :command do
|
8
|
+
cmd("sudo") - ["gem", "install", "bundler"]
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should define commands" do
|
12
|
+
command.should be_an_instance_of(Caliph::WrappingChain)
|
13
|
+
command.should have(2).commands
|
14
|
+
command.commands[0].should be_an_instance_of(Caliph::CommandLine)
|
15
|
+
command.commands[1].should be_an_instance_of(Caliph::CommandLine)
|
16
|
+
command.command.should == "sudo -- gem install bundler"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "using the | operator" do
|
21
|
+
let :command do
|
22
|
+
cmd("cat", "/etc/passwd") | ["grep", "root"]
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should define commands" do
|
26
|
+
command.should be_an_instance_of(Caliph::PipelineChain)
|
27
|
+
command.should have(2).commands
|
28
|
+
command.commands[0].should be_an_instance_of(Caliph::CommandLine)
|
29
|
+
command.commands[1].should be_an_instance_of(Caliph::CommandLine)
|
30
|
+
command.command.should == "cat /etc/passwd | grep root"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "using the & operator" do
|
35
|
+
let :command do
|
36
|
+
p method(:cmd).source_location
|
37
|
+
cmd("cd", "/tmp/trash") & %w{rm -rf *}
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should define commands" do
|
41
|
+
command.should be_an_instance_of(Caliph::PrereqChain)
|
42
|
+
command.should have(2).commands
|
43
|
+
command.commands[0].should be_an_instance_of(Caliph::CommandLine)
|
44
|
+
command.commands[1].should be_an_instance_of(Caliph::CommandLine)
|
45
|
+
command.command.should == "cd /tmp/trash && rm -rf *"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
require 'caliph'
|
2
|
+
require 'caliph/testing/mock-command-line'
|
3
|
+
|
4
|
+
require 'caliph/testing/record-commands'
|
5
|
+
|
6
|
+
Caliph::CommandLine.command_recording_path = "/dev/null"
|
7
|
+
|
8
|
+
describe Caliph::CommandLine do
|
9
|
+
let :commandline do
|
10
|
+
Caliph::CommandLine.new('echo', "-n") do |cmd|
|
11
|
+
cmd.options << "Some text"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should have a name set" do
|
16
|
+
commandline.name.should == "echo"
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should produce a command string" do
|
20
|
+
commandline.command.should == "echo -n Some text"
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should succeed" do
|
24
|
+
commandline.succeeds?.should be_true
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should not complain about success" do
|
28
|
+
expect do
|
29
|
+
commandline.must_succeed!
|
30
|
+
end.to_not raise_error
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
describe Caliph::CommandLine, "setting environment variables" do
|
36
|
+
let :commandline do
|
37
|
+
Caliph::CommandLine.new("env") do |cmd|
|
38
|
+
cmd.env["TEST_ENV"] = "indubitably"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
let :result do
|
43
|
+
commandline.run
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should succeed" do
|
47
|
+
result.succeeded?.should be_true
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should alter the command's environment variables" do
|
51
|
+
result.stdout.should =~ /TEST_ENV.*indubitably/
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
describe Caliph::CommandLine, 'redirecting' do
|
57
|
+
let :commandline do
|
58
|
+
Caliph::CommandLine.new("env")
|
59
|
+
end
|
60
|
+
|
61
|
+
let :result do
|
62
|
+
commandline.string_format
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'should allow redirect stdout' do
|
66
|
+
commandline.redirect_stdout('some_file')
|
67
|
+
result.should =~ /1>some_file$/
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'should allow redirect stderr' do
|
71
|
+
commandline.redirect_stderr('some_file')
|
72
|
+
result.should =~ /2>some_file$/
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'should allow chain redirects' do
|
76
|
+
commandline.redirect_stdout('stdout_file').redirect_stderr('stderr_file')
|
77
|
+
result.should =~ /\b1>stdout_file\b/
|
78
|
+
result.should =~ /\b2>stderr_file\b/
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'should redirect both' do
|
82
|
+
commandline.redirect_both('output_file')
|
83
|
+
result.should =~ /\b1>output_file\b/
|
84
|
+
result.should =~ /\b2>output_file\b/
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
describe Caliph::PipelineChain do
|
89
|
+
let :commandline do
|
90
|
+
Caliph::PipelineChain.new do |chain|
|
91
|
+
chain.add Caliph::CommandLine.new("env")
|
92
|
+
chain.add Caliph::CommandLine.new("cat") do |cmd|
|
93
|
+
cmd.env["TEST_ENV"] = "indubitably"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
let :result do
|
99
|
+
commandline.run
|
100
|
+
end
|
101
|
+
|
102
|
+
it "should produce the right command" do
|
103
|
+
commandline.command.should == 'env | cat'
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should produce a runnable command with format_string" do
|
107
|
+
commandline.string_format.should == 'TEST_ENV=indubitably env | cat'
|
108
|
+
end
|
109
|
+
|
110
|
+
it "should succeed" do
|
111
|
+
result.succeeded?.should be_true
|
112
|
+
end
|
113
|
+
|
114
|
+
it "should alter the command's environment variables" do
|
115
|
+
result.stdout.should =~ /TEST_ENV.*indubitably/
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
|
120
|
+
|
121
|
+
describe Caliph::CommandLine, "that fails" do
|
122
|
+
let :commandline do
|
123
|
+
Caliph::CommandLine.new("false")
|
124
|
+
end
|
125
|
+
|
126
|
+
it "should not succeed" do
|
127
|
+
commandline.succeeds?.should == false
|
128
|
+
end
|
129
|
+
|
130
|
+
it "should raise error if succeed demanded" do
|
131
|
+
expect do
|
132
|
+
commandline.must_succeed
|
133
|
+
end.to raise_error
|
134
|
+
end
|
135
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
puts Dir::pwd
|
2
|
+
require 'test/unit'
|
3
|
+
begin
|
4
|
+
require 'spec'
|
5
|
+
rescue LoadError
|
6
|
+
false
|
7
|
+
end
|
8
|
+
|
9
|
+
class RSpecTest < Test::Unit::TestCase
|
10
|
+
def test_that_rspec_is_available
|
11
|
+
assert_nothing_raised("\n\n * RSpec isn't available - please run: gem install rspec *\n\n"){ ::Spec }
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_that_specs_pass
|
15
|
+
assert(system(*%w{spec -f e -p **/*.rb spec}),"\n\n * Specs failed *\n\n")
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'rspec'
|
2
|
+
require 'rspec/core/formatters/base_formatter'
|
3
|
+
|
4
|
+
require 'caliph'
|
5
|
+
require 'cadre/rspec'
|
6
|
+
|
7
|
+
RSpec.configure do |config|
|
8
|
+
config.run_all_when_everything_filtered = true
|
9
|
+
config.add_formatter(Cadre::RSpec::NotifyOnCompleteFormatter)
|
10
|
+
config.add_formatter(Cadre::RSpec::QuickfixFormatter)
|
11
|
+
end
|
metadata
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: caliph
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Evan Dorn
|
8
|
+
- Judson Lester
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2014-06-12 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: |2
|
15
|
+
TDD-suitable Ruby tool for generating command-line commands via an OOP interface.
|
16
|
+
email:
|
17
|
+
- evan@lrdesign.com
|
18
|
+
- judson@lrdesign.com
|
19
|
+
executables: []
|
20
|
+
extensions: []
|
21
|
+
extra_rdoc_files: []
|
22
|
+
files:
|
23
|
+
- lib/caliph.rb
|
24
|
+
- lib/caliph/command-chain.rb
|
25
|
+
- lib/caliph/command-line-dsl.rb
|
26
|
+
- lib/caliph/command-line.rb
|
27
|
+
- lib/caliph/command-run-result.rb
|
28
|
+
- lib/caliph/define-op.rb
|
29
|
+
- lib/caliph/shell-escaped.rb
|
30
|
+
- lib/caliph/testing/mock-command-line.rb
|
31
|
+
- lib/caliph/testing/record-commands.rb
|
32
|
+
- spec/command-chain.rb
|
33
|
+
- spec/command-line-dsl.rb
|
34
|
+
- spec/command-line.rb
|
35
|
+
- spec_help/gem_test_suite.rb
|
36
|
+
- spec_help/spec_helper.rb
|
37
|
+
homepage: ''
|
38
|
+
licenses:
|
39
|
+
- MIT
|
40
|
+
metadata: {}
|
41
|
+
post_install_message:
|
42
|
+
rdoc_options:
|
43
|
+
- --inline-source
|
44
|
+
- --main
|
45
|
+
- doc/README
|
46
|
+
- --title
|
47
|
+
- caliph-0.1.0 Documentation
|
48
|
+
require_paths:
|
49
|
+
- lib/
|
50
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - '>='
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '0'
|
60
|
+
requirements: []
|
61
|
+
rubyforge_project: caliph
|
62
|
+
rubygems_version: 2.2.2
|
63
|
+
signing_key:
|
64
|
+
specification_version: 4
|
65
|
+
summary: TDD-suitable Ruby tool for generating command-line commands via an OOP interface.
|
66
|
+
test_files:
|
67
|
+
- spec_help/gem_test_suite.rb
|