https://github.com/redding/scmd
Build and run system commands.
https://github.com/redding/scmd
ruby system-commands
Last synced: 3 months ago
JSON representation
Build and run system commands.
- Host: GitHub
- URL: https://github.com/redding/scmd
- Owner: redding
- License: mit
- Created: 2012-08-17T15:44:01.000Z (almost 13 years ago)
- Default Branch: main
- Last Pushed: 2021-10-29T19:05:43.000Z (over 3 years ago)
- Last Synced: 2024-03-15T03:55:11.017Z (over 1 year ago)
- Topics: ruby, system-commands
- Language: Ruby
- Homepage:
- Size: 74.2 KB
- Stars: 2
- Watchers: 3
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Scmd
Build and run system commands. Scmd uses `posix-spawn` to fork child processes to run the commands.
## Usage
Create a command object:
```ruby
cmd = Scmd.new("echo hi")cmd.to_s #=> "echo hi"
cmd.inspect #=> #cmd.pid #=> nil
cmd.exitstatus #=> nil
cmd.stdout #=> ''
cmd.stderr #=> ''
```Run it:
```ruby
cmd.run
```**OR**, async run it:
```ruby
cmd.start
cmd.running? # => true
cmd.pid #=> 12345# do other stuff...
cmd.wait # indefinitely until cmd exits
```**OR**, async run it with a timeout:
```ruby
cmd.startbegin
cmd.wait(10)
rescue Scmd::TimeoutError => err
cmd.stop # attempt to stop the cmd nicely, kill if doesn't stop in time
cmd.kill # just kill the cmd now
end
```Results:
```ruby
# written to the cmd instance
cmd.pid #=> 12345
cmd.exitstatus #=> 0
cmd.stdout #=> 'hi'
cmd.stderr #=> ''# the cmd instance is returned by `run` for chaining as well
cmd.run.stdout #=> 'hi'
```### Run with input on stdin
A single input line
```ruby
input = "echo hi"
cmd = Scmd.new("sh").run(input)
cmd.stdout #=> 'hi'
```Multiple input lines:
```ruby
input = ["echo hi", "echo err 1>&2"]
cmd = Scmd.new("sh").run(input)
cmd.stdout #=> 'hi'
cmd.stderr #=> 'err'
```### Some helpers
Ask if cmd was successful:
```ruby
puts cmd.stderr if !cmd.success?
```Raise an exception if not successful with `run!`:
```ruby
Scmd.new("cd /path/that/does/not/exist").run! #=> Scmd::Command::Failure
```### Environment variables
Pass environment variables:
```ruby
cmd = Scmd.new("echo $TEST_VAR", {
:env => {
'TEST_VAR' => 'hi'
}
})
```### Process spawn options
Pass options:
```ruby
reader, writer = IO.pipe
# this is an example that uses file descriptor redirection options
cmd = Scmd.new("echo test 1>{writer.fileno}", {
:options => { writer => writer }
})
reader.gets # => "test\n"
```For all the possible options see [posix-spawn](https://github.com/rtomayko/posix-spawn#status).
## Testing
Scmd comes with some testing utilities built in. Specifically this includes a command spy and a "test mode" API on the main `Scmd` namespace.
### Command Spy
```ruby
require 'scmd/command_spy'
spy = Scmd::CommandSpy.new(cmd_str)
spy.exitstatus = 1
spy.stdout = 'some test output'
Assert.stub(Scmd, :new).with(cmd_str){ spy }cmd = Scmd.new(cmd_str) # => spy
cmd.run('some input')cmd.run_called? # => true
cmd.run_calls.size # => 1
cmd.run_calls.first.input # => 'some input'
```The spy is useful for stubbing out system commands that you don't want to call or aren't safe to call in the test suite. It responds to the same API that commands do but doesn't run any system commands.
### "Test Mode" API
```ruby
Scmd.add_command(cmd_str){ |cmd| cmd.stdout = 'some output' } # => raises NoMethodErrorENV['SCMD_TEST_MODE'] = '1'
Scmd.add_command(cmd_str){ |cmd| cmd.stdout = 'some output' }
Scmd.add_command(cmd_str).with({:env => { :SOME_ENV_VAR => '1' }}) do |cmd|
cmd.stdout = 'some other output'
end
Scmd.commands.empty? # => falsecmd = Scmd.new(cmd_str)
cmd.class # => Scmd::CommandSpy
cmd.stdout # => 'some output'
cmd.run('some input')
Scmd.calls.size # => 1
Scmd.calls.last.class # => Scmd::Call
Scmd.calls.last.cmd_str # => cmd_str
Scmd.calls.last.input # => 'some input'
Scmd.calls.last.cmd.class # => Scmd::CommandSpycmd = Scmd.new(cmd_str, {:env => { 'SOME_ENV_VAR' => '1' }})
cmd.class # => Scmd::CommandSpy
cmd.stdout # => 'some other output'
cmd.run('some input')
Scmd.calls.size # => 2
Scmd.calls.last.class # => Scmd::Call
Scmd.calls.last.cmd_str # => cmd_str
Scmd.calls.last.input # => 'some input'
Scmd.calls.last.cmd.class # => Scmd::CommandSpy
Scmd.calls.last.cmd.env # => { 'SOME_ENV_VAR' => '1' }Scmd.reset
Scmd.commands.empty? # => true
Scmd.calls.empty? # => true
```Use these singleton methods on the `Scmd` namespace to add specific command spies in specific contexts and to track command calls (runs, starts). Use `reset` to reset the state of things.
**Note:** these methods are only available when test mode is enabled (when the `SCMD_TEST_MODE` env var has a non-falsey value). Otherwise these methods will raise `NoMethodError`.
## Installation
Add this line to your application's Gemfile:
gem 'scmd'
And then execute:
$ bundle
Or install it yourself as:
$ gem install scmd
## Contributing
1. Fork it
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Added some feature'`)
4. Push to the branch (`git push origin my-new-feature`)
5. Create new Pull Request