command-runner 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,57 @@
1
+ # Command Runner [![Build Status](https://travis-ci.org/redjazz96/command-runner.png?branch=master)](https://travis-ci.org/redjazz96/command-runner)
2
+ Runs commands.
3
+
4
+ ```Ruby
5
+ require 'command/runner'
6
+
7
+ line = Command::Runner.new("echo", "hello")
8
+ message = line.pass # => #<Command::Runner::Message:...>
9
+ message.exit_code # => 0
10
+ message.stdout # => "hello\n"
11
+ message.time # => 0.00091773
12
+ message.process_id # => 9622
13
+ message.line # => "echo hello"
14
+ ```
15
+
16
+ with interpolations...
17
+
18
+ ```Ruby
19
+ line = Command::Runner.new("echo", "{interpolation}")
20
+ message = line.pass(:interpolation => "watermelons")
21
+ message.stdout # => "watermelons\n"
22
+ message.line # => "echo watermelons"
23
+ ```
24
+
25
+ that escapes bad stuff...
26
+
27
+ ```Ruby
28
+ message = line.pass(:interpolation => "`uname -a`")
29
+ message.stdout # => "`uname -a`\n"
30
+ message.line # => "echo \\`uname\\ -a\\`"
31
+ ```
32
+
33
+ unless you don't want it to.
34
+
35
+ ```Ruby
36
+ line = Command::Runner.new("echo", "{{interpolation}}")
37
+ message = line.pass(:interpolation => "`uname -a`")
38
+ message.stdout # => "Linux Hyperion 3.8.0-25-generic #37-Ubuntu SMP Thu Jun 6 20:47:07 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux\n"
39
+ message.line # => "echo `uname -a`"
40
+ ```
41
+
42
+ It can also use different methods to run commands...
43
+
44
+ ```Ruby
45
+ line = Command::Runner.new("echo", "something")
46
+ line.backends = Command::Runner::Backends::Spawn.new
47
+ line.pass
48
+ ```
49
+
50
+ but defaults to the best one.
51
+
52
+ ## Compatibility
53
+ It works on
54
+
55
+ - MRI 2.0.0 and 1.9.3
56
+
57
+ unless the travis build fails.
@@ -0,0 +1,84 @@
1
+ module Command
2
+ class Runner
3
+ module Backends
4
+
5
+ # A backend that uses ticks to do its bidding.
6
+ class Backticks
7
+
8
+ # Returns whether or not this backend is avialable on this
9
+ # platform.
10
+ def self.available?
11
+ true
12
+ end
13
+
14
+ # Initialize the fake backend.
15
+ def initialize
16
+ super
17
+ end
18
+
19
+ # Run the given command and arguments, in the given environment.
20
+ #
21
+ # @param command [String] the command to run.
22
+ # @param arguments [String] the arguments to pass to the
23
+ # command.
24
+ # @param env [Hash] the enviornment to run the command
25
+ # under.
26
+ # @param options [Hash] the options to run the command under.
27
+ # @return [Message] information about the process that ran.
28
+ def call(command, arguments, env = {}, options = {})
29
+ super
30
+ output = ""
31
+ start_time = nil
32
+ end_time = nil
33
+
34
+ with_modified_env(env) do
35
+ start_time = Time.now
36
+ output << `#{command} #{arguments}`
37
+ end_time = Time.now
38
+ end
39
+
40
+ Message.new :process_id => $?.pid,
41
+ :exit_code => $?.exitstatus,
42
+ :finished => true,
43
+ :time => (end_time - start_time).abs,
44
+ :env => env,
45
+ :options => {},
46
+ :stdout => output,
47
+ :line => line,
48
+ :executed => true,
49
+ :status => $?
50
+ end
51
+
52
+ private
53
+
54
+ # If ClimateControl is installed on this system, it runs the
55
+ # given block with the given environment. If it's not, it
56
+ # just yields.
57
+ #
58
+ # @yield
59
+ # @return [Object]
60
+ def with_modified_env(env)
61
+ if defined?(ClimateControl) || climate_control?
62
+ ClimateControl.modify(env, &Proc.new)
63
+ else
64
+ yield
65
+ end
66
+ end
67
+
68
+ # Checks to see if ClimateControl is on this system.
69
+ #
70
+ # @return [Boolean]
71
+ def climate_control?
72
+ begin
73
+ require 'climate_control'
74
+ true
75
+ rescue LoadError
76
+ false
77
+ end
78
+ end
79
+
80
+ end
81
+ end
82
+ end
83
+
84
+ end
@@ -0,0 +1,58 @@
1
+ module Command
2
+ class Runner
3
+ module Backends
4
+
5
+ # A fake backend. Used to a) define what backends should respond
6
+ # to, and b) provide default behavior for the backends.
7
+ #
8
+ # @abstract
9
+ class Fake
10
+
11
+ # Returns whether or not this backend is avialable on this
12
+ # platform.
13
+ #
14
+ # @abstract
15
+ def self.available?
16
+ true
17
+ end
18
+
19
+ # Initialize the fake backend.
20
+ def initialize
21
+ @ran = []
22
+
23
+ raise NotAvailableBackendError unless self.class.available?
24
+ end
25
+
26
+ # Run the given command and arguments, in the given environment.
27
+ #
28
+ # @abstract
29
+ # @note Does nothing.
30
+ # @param command [String] the command to run.
31
+ # @param arguments [String] the arguments to pass to the
32
+ # command.
33
+ # @param env [Hash] the enviornment to run the command
34
+ # under.
35
+ # @param options [Hash] the options to run the command under.
36
+ # @return [Message] information about the process that ran.
37
+ def call(command, arguments, env = {}, options = {})
38
+ @ran << [command, arguments]
39
+
40
+ Message.new :env => env, :options => options
41
+ end
42
+
43
+ # Determines whether or not the given command and arguments were
44
+ # ran with this backend.
45
+ #
46
+ # @see #call
47
+ # @param command [String]
48
+ # @param arguments [String]
49
+ # @return [Boolean]
50
+ def ran?(command, arguments)
51
+ @ran.include?([command, arguments])
52
+ end
53
+
54
+ end
55
+ end
56
+ end
57
+
58
+ end
@@ -0,0 +1,36 @@
1
+ module Command
2
+ class Runner
3
+ module Backends
4
+
5
+ # Spawns a process using POSIX::Spawn. This is the preferred
6
+ # method, as POSIX::Spawn is much faster than Process.spawn.
7
+ class PosixSpawn < Spawn
8
+
9
+ # Determines whether or not the PosixSpawn class is available as
10
+ # a backend. Does this by checking to see if posix-spawn has been
11
+ # installed on the local computer; if it hasn't, it returns
12
+ # false.
13
+ #
14
+ # @see Fake.available?
15
+ # @return [Boolean]
16
+ def self.available?
17
+ @_available ||= begin
18
+ require 'posix/spawn'
19
+ true
20
+ rescue LoadError => e
21
+ false
22
+ end
23
+ end
24
+
25
+ # Spawns a process with the given line, environment, and options.
26
+ #
27
+ # @see Spawn#spawn
28
+ # @return [Numeric]
29
+ def spawn(env, line, options)
30
+ POSIX::Spawn.spawn(env, line, options)
31
+ end
32
+
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,94 @@
1
+ module Command
2
+ class Runner
3
+ module Backends
4
+
5
+ # Spawns a process using ruby's Process.spawn.
6
+ class Spawn < Fake
7
+
8
+ # (see Fake.available?)
9
+ def self.available?
10
+ Process.respond_to?(:spawn)
11
+ end
12
+
13
+ # Initialize the backend.
14
+ def initialize
15
+ super
16
+ end
17
+
18
+ # Run the given command and arguments, in the given environment.
19
+ #
20
+ # @param (see Fake#call)
21
+ # @return (see Fake#call)
22
+ def call(command, arguments, env = {}, options = {})
23
+ super
24
+ stderr_r, stderr_w = IO.pipe
25
+ stdout_r, stdout_w = IO.pipe
26
+ stdin_r, stdin_w = IO.pipe
27
+
28
+ new_options = options.merge(:in => stdin_r,
29
+ :out => stdout_w, :err => stderr_w)
30
+
31
+ if new_options[:input]
32
+ stdin_w.write(new_options.delete(:input))
33
+ end
34
+ stdin_w.close
35
+
36
+ line = [command, arguments].join(' ')
37
+
38
+ start_time = Time.now
39
+ process_id = spawn(env, line, new_options)
40
+
41
+ future do
42
+ _, status = wait2(process_id)
43
+ end_time = Time.now
44
+
45
+ [stdout_w, stderr_w].each(&:close)
46
+
47
+ Message.new :process_id => process_id,
48
+ :exit_code => status.exitstatus,
49
+ :finished => true,
50
+ :time => (start_time - end_time).abs,
51
+ :env => env,
52
+ :options => options,
53
+ :stdout => stdout_r.read,
54
+ :stderr => stderr_r.read,
55
+ :line => line,
56
+ :executed => true,
57
+ :status => status
58
+ end
59
+
60
+ rescue Errno::ENOENT => e
61
+ Message.new :exit_code => 127,
62
+ :finished => true,
63
+ :time => -1,
64
+ :env => {},
65
+ :options => {},
66
+ :stdout => "",
67
+ :stderr => e.message,
68
+ :line => line,
69
+ :executed => false
70
+ end
71
+
72
+ # Spawn the given process, in the environment with the
73
+ # given options.
74
+ #
75
+ # @see Process.spawn
76
+ # @return [Numeric] the process id
77
+ def spawn(env, line, options)
78
+ Process.spawn(env, line, options)
79
+ end
80
+
81
+ # Waits for the given process, and returns the process id and the
82
+ # status.
83
+ #
84
+ # @see Process.wait2
85
+ # @return [Array<(Numeric, Process::Status)>]
86
+ def wait2(process_id = -1)
87
+ Process.wait2(process_id)
88
+ end
89
+
90
+ end
91
+
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,17 @@
1
+ module Command
2
+
3
+ class Runner
4
+
5
+ # The different backends that the runner can take. Each of them have a
6
+ # different method of executing the process.
7
+ module Backends
8
+
9
+ autoload :Fake, "command/runner/backends/fake"
10
+ autoload :Spawn, "command/runner/backends/spawn"
11
+ autoload :Backticks, "command/runner/backends/backticks"
12
+ autoload :PosixSpawn, "command/runner/backends/posix_spawn"
13
+
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,10 @@
1
+ module Command
2
+
3
+ class Runner
4
+
5
+ # Raised when a backend is instantized on a platform that doesn't
6
+ # support it.
7
+ class NotAvailableBackendError < StandardError; end
8
+
9
+ end
10
+ end
@@ -0,0 +1,101 @@
1
+ module Command
2
+ class Runner
3
+
4
+ # This contains information about the process that was run, such as
5
+ # its exit code, process id, and even time it took to run.
6
+ class Message
7
+
8
+ # Initialize the message with the given data about the process.
9
+ #
10
+ # @param data [Hash] the data about the process.
11
+ # @option data [Numeric] :exit_code (0) the code the process
12
+ # exited with.
13
+ # @option data [Numeric] :process_id (-1) the process id the
14
+ # process ran under.
15
+ # @option data [Numeric] :time (0) the amount of time the
16
+ # process took to run, in seconds.
17
+ # @option data [Boolean] :executed (false) whether or not the
18
+ # process was actually executed.
19
+ # @option data [Hash] :env ({}) the environment the process
20
+ # ran under.
21
+ # @option data [Hash] :options ({}) the options that the process
22
+ # was run under.
23
+ # @option data [String] :stdout ("\n") the output the command
24
+ # outputted on stdout. This does not include output on stderr.
25
+ # @option data [String] :stderr ("") the output the command
26
+ # outputted on stderr. This does not include output on stdout.
27
+ # @option data [String] :line ("") the line that was executed.
28
+ # @option data [Process::Status] :status (nil) the status of the
29
+ # process.
30
+ def initialize(data)
31
+ {
32
+ :executed => false,
33
+ :time => 0,
34
+ :process_id => -1,
35
+ :exit_code => 0,
36
+ :env => {},
37
+ :options => {},
38
+ :stdout => "\n",
39
+ :stderr => "",
40
+ :line => "",
41
+ :status => nil
42
+ }.merge(data).each do |k, v|
43
+ instance_variable_set(:"@#{k}", (v.dup rescue v))
44
+ end
45
+ end
46
+
47
+ # Whether or not the process was actually executed.
48
+ #
49
+ # @return [Boolean]
50
+ def executed?
51
+ @executed
52
+ end
53
+
54
+ # Whether or not the exit code was non-zero.
55
+ #
56
+ # @return [Boolean]
57
+ def nonzero_exit?
58
+ exit_code != 0
59
+ end
60
+
61
+ # @!attribute [r] exit_code
62
+ # The code the process exited with.
63
+ #
64
+ # @return [Numeric]
65
+ # @!attribute [r] process_id
66
+ # The process id the process ran under.
67
+ #
68
+ # @return [Numeric]
69
+ # @!attribute [r] time
70
+ # The amount of time the process took to run, in seconds.
71
+ #
72
+ # @return [Numeric]
73
+ # @!attribute [r] stdout
74
+ # The standard out of the process that was executed.
75
+ #
76
+ # @return [String]
77
+ # @!attribute [r] stderr
78
+ # The standard error of the process that was executed.
79
+ #
80
+ # @return [String]
81
+ # @!attribute [r] options
82
+ # The options that the user passed when executing the process.
83
+ #
84
+ # @return [Hash]
85
+ # @!attribute [r] line
86
+ # The exact line that was executed.
87
+ #
88
+ # @return [String]
89
+ # @!attribute [r] status
90
+ # The status of the process.
91
+ #
92
+ # @return [Process::Status]
93
+ [:exit_code, :process_id, :time, :stdout, :stderr, :options,
94
+ :line, :status].each do |key|
95
+ define_method(key) { instance_variable_get(:"@#{key}") }
96
+ end
97
+
98
+
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,9 @@
1
+ module Command
2
+
3
+ class Runner
4
+
5
+ # The current version of Runner.
6
+ VERSION = "0.2.0"
7
+
8
+ end
9
+ end
@@ -0,0 +1,133 @@
1
+ require 'shellwords'
2
+ require 'future'
3
+
4
+ require 'command/runner/version'
5
+ require 'command/runner/message'
6
+ require 'command/runner/exceptions'
7
+ require 'command/runner/backends'
8
+
9
+ module Command
10
+
11
+ # This handles the execution of commands.
12
+ class Runner
13
+
14
+ class << self
15
+ # Gets the default backend to use with the messenger.
16
+ # Defaults to the best available backend.
17
+ #
18
+ # @return [#call] a backend to use.
19
+ def backend
20
+ @backend ||= best_backend
21
+ end
22
+
23
+ # Returns the best backend for messenger to use.
24
+ #
25
+ # @return [#call] a backend to use.
26
+ def best_backend
27
+ if Backends::PosixSpawn.available?
28
+ Backends::PosixSpawn.new
29
+ elsif Backends::Spawn.available?
30
+ Backends::Spawn.new
31
+ elsif Backends::Backticks.available?
32
+ Backends::Backticks.new
33
+ else
34
+ Backends::Fake.new
35
+ end
36
+ end
37
+
38
+ # Sets the default backend to use with the messenger.
39
+ attr_writer :backend
40
+ end
41
+
42
+ # The command the messenger was initialized with.
43
+ #
44
+ # @return [String]
45
+ attr_reader :command
46
+
47
+ # The arguments the messenger was initialized with.
48
+ #
49
+ # @return [String]
50
+ attr_reader :arguments
51
+
52
+ # The options the messenger was initialized with.
53
+ #
54
+ # @return [Hash]
55
+ def options
56
+ @options.dup.freeze
57
+ end
58
+
59
+ # Gets the backend to be used by the messenger. If it is not defined
60
+ # on the instance, it'll get the class default.
61
+ #
62
+ # @see Messenger.backend
63
+ # @return [#call] a backend to use.
64
+ def backend
65
+ @backend || self.class.backend
66
+ end
67
+
68
+ # Sets the backend to be used by the messenger. This is local to the
69
+ # instance.
70
+ attr_writer :backend
71
+
72
+ # Initialize the messenger.
73
+ #
74
+ # @param command [String] the name of the command file to run.
75
+ # @param arguments [String] the arguments to pass to the command.
76
+ # may contain interpolated values, like +{key}+ or +{{key}}+.
77
+ # @param options [Hash] the options for the messenger.
78
+ def initialize(command, arguments, options = {})
79
+ @command = command
80
+ @arguments = arguments
81
+ @options = options
82
+ end
83
+
84
+ # Runs the command and arguments with the given interpolations;
85
+ # defaults to no interpolations.
86
+ def pass(interops = {}, options = {})
87
+ backend.call(*[contents(interops), options.delete(:env) || {}, options].flatten)
88
+ end
89
+
90
+ # The command line being run by the runner. Interpolates the
91
+ # arguments with the given interpolations.
92
+ #
93
+ # @see #interpolate
94
+ # @param interops [Hash] the interpolations to make.
95
+ # @return [Array<(String, String)>] the command line that will be run.
96
+ def contents(interops = {})
97
+ [command, interpolate(arguments, interops)]
98
+ end
99
+
100
+ # Interpolates the given string with the given interpolations.
101
+ # The keys of the interpolations should be alphanumeric,
102
+ # including underscores and dashes. It will search the given
103
+ # string for +{key}+ and +{{key}}+; if it finds the former, it
104
+ # replaces it with the escaped value. If it finds the latter, it
105
+ # replaces it with the value directly.
106
+ #
107
+ # @param string [String] the string to interpolate.
108
+ # @param interops [Hash] the interpolations to make.
109
+ # @return [String] the interpolated string.
110
+ def interpolate(string, interops = {})
111
+ interops = interops.to_a.map { |(k, v)| { k.to_s => v } }.inject(&:merge) || {}
112
+
113
+ string.gsub(/(\{{1,2})([0-9a-zA-Z_\-]+)(\}{1,2})/) do |m|
114
+ if interops.key?($2) && $1.length == $3.length
115
+ if $1.length < 2 then escape(interops[$2].to_s) else interops[$2] end
116
+ else
117
+ m
118
+ end
119
+ end
120
+ end
121
+
122
+ private
123
+
124
+ # Escape the given string for a shell.
125
+ #
126
+ # @param string [String] the string to escape.
127
+ # @return [String] the escaped string.
128
+ def escape(string)
129
+ Shellwords.escape(string)
130
+ end
131
+
132
+ end
133
+ end
@@ -0,0 +1,60 @@
1
+ describe Command::Runner do
2
+
3
+ before :each do
4
+ Command::Runner.backend = Command::Runner::Backends::Fake.new
5
+ end
6
+
7
+ subject do
8
+ Command::Runner.new(command, arguments)
9
+ end
10
+
11
+ context "interpolating strings" do
12
+ let(:command) { "echo" }
13
+ let(:arguments) { "some {interpolation}" }
14
+
15
+ it "interpolates correctly" do
16
+ subject.contents(:interpolation => "test").should == ["echo", "some test"]
17
+ end
18
+
19
+ it "escapes bad values" do
20
+ subject.contents(:interpolation => "`bad value`").should == ["echo", "some \\`bad\\ value\\`"]
21
+ end
22
+
23
+ it "doesn't interpolate interpolation values" do
24
+ subject.contents(:interpolation => "{other}", :other => "hi").should == ["echo", "some \\{other\\}"]
25
+ end
26
+ end
27
+
28
+ context "double interpolated strings" do
29
+ let(:command) { "echo" }
30
+ let(:arguments) { "some {{interpolation}}" }
31
+
32
+ it "interpolates correctly" do
33
+ subject.contents(:interpolation => "test").should == ["echo", "some test"]
34
+ end
35
+
36
+ it "doesn't escape bad values" do
37
+ subject.contents(:interpolation => "`bad value`").should == ["echo", "some `bad value`"]
38
+ end
39
+ end
40
+
41
+ context "misinterpolated strings" do
42
+ let(:command) { "echo" }
43
+ let(:arguments) { "some {{interpolation}" }
44
+
45
+ it "doesn't interpolate" do
46
+ subject.contents(:interpolation => "test").should == ["echo", "some {{interpolation}"]
47
+ end
48
+ end
49
+
50
+ context "selects backends" do
51
+ it "selects the best backend" do
52
+ Command::Runner::Backends::PosixSpawn.stub(:available?).and_return(false)
53
+ Command::Runner::Backends::Spawn.stub(:available?).and_return(true)
54
+ Command::Runner.best_backend.should be_instance_of Command::Runner::Backends::Spawn
55
+
56
+ Command::Runner::Backends::PosixSpawn.stub(:available?).and_return(true)
57
+ Command::Runner.best_backend.should be_instance_of Command::Runner::Backends::PosixSpawn
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,31 @@
1
+ describe Command::Runner::Backends::Spawn do
2
+
3
+ next unless Process.respond_to? :spawn
4
+
5
+ it "is available" do
6
+ Command::Runner::Backends::Spawn.should be_available
7
+ end
8
+
9
+ it "returns a message" do
10
+ value = subject.call("echo", "hello")
11
+ value.should be_instance_of Command::Runner::Message
12
+ value.should be_executed
13
+ end
14
+
15
+ it "doesn't block" do
16
+ start_time = Time.now
17
+ value = subject.call("sleep", "0.5")
18
+ end_time = Time.now
19
+
20
+ (end_time - start_time).should be_within((1.0/100)).of(0)
21
+ value.time.should be_within((1.0/100)).of(0.5)
22
+ end
23
+
24
+ it "can not be available" do
25
+ Command::Runner::Backends::Spawn.stub(:available?).and_return(false)
26
+
27
+ expect {
28
+ Command::Runner::Backends::Spawn.new
29
+ }.to raise_error(Command::Runner::NotAvailableBackendError)
30
+ end
31
+ end
metadata ADDED
@@ -0,0 +1,106 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: command-runner
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Jeremy Rodi
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-06-20 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: promise
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '0.3'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '0.3'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rspec
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: yard
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ description: ! " Runs a command or two in the shell with arguments that can be\n
63
+ \ interpolated with the interpolation syntax.\n"
64
+ email: redjazz96@gmail.com
65
+ executables: []
66
+ extensions: []
67
+ extra_rdoc_files: []
68
+ files:
69
+ - README.md
70
+ - lib/command/runner/version.rb
71
+ - lib/command/runner/exceptions.rb
72
+ - lib/command/runner/backends.rb
73
+ - lib/command/runner/message.rb
74
+ - lib/command/runner/backends/fake.rb
75
+ - lib/command/runner/backends/backticks.rb
76
+ - lib/command/runner/backends/posix_spawn.rb
77
+ - lib/command/runner/backends/spawn.rb
78
+ - lib/command/runner.rb
79
+ - spec/messenger_spec.rb
80
+ - spec/spawn_spec.rb
81
+ homepage: http://github.com/redjazz96/command-runner
82
+ licenses: []
83
+ post_install_message:
84
+ rdoc_options: []
85
+ require_paths:
86
+ - lib
87
+ required_ruby_version: !ruby/object:Gem::Requirement
88
+ none: false
89
+ requirements:
90
+ - - ! '>='
91
+ - !ruby/object:Gem::Version
92
+ version: '0'
93
+ required_rubygems_version: !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ! '>='
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ requirements: []
100
+ rubyforge_project:
101
+ rubygems_version: 1.8.25
102
+ signing_key:
103
+ specification_version: 3
104
+ summary: Runs commands.
105
+ test_files: []
106
+ has_rdoc: false