Class: Backticks::Runner

Inherits:
Object
  • Object
show all
Defined in:
lib/backticks/runner.rb

Overview

An easy-to-use interface for invoking commands and capturing their output. Instances of Runner can be interactive, which prints the command’s output to the terminal and also allows the user to interact with the command. By default commands are unbuffered, using a pseudoterminal to capture the output with no delay.

Constant Summary collapse

BUFFERED =

Default streams to buffer if someone calls bufferered= with Boolean.

[:stdin, :stdout, :stderr].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ Runner

Create an instance of Runner.

Examples:

buffer stdout

Runner.new(buffered:[:stdout])

Parameters:

  • [#include?,Boolean] (Hash)

    a customizable set of options

  • [#parameters] (Hash)

    a customizable set of options

  • [Boolean] (Hash)

    a customizable set of options



50
51
52
53
54
55
56
57
58
59
60
# File 'lib/backticks/runner.rb', line 50

def initialize(options={})
  options = {
    :buffered => false,
    :cli => Backticks::CLI::Getopt,
    :interactive => false,
  }.merge(options)

  @cli = options[:cli]
  self.buffered = options[:buffered]
  self.interactive = options[:interactive]
end

Instance Attribute Details

#bufferedArray

List of I/O streams that should be captured using a pipe instead of a pseudoterminal.

When read, this attribute is always an Array of stream names from the set ‘[:stdin, :stdout, :stderr]`.

Note: if you set ‘interactive` to true, then stdin and stdout are unbuffered regardless of how you have set `buffered`!

Returns:

  • (Array)

    list of symbolic stream names



34
35
36
# File 'lib/backticks/runner.rb', line 34

def buffered
  @buffered
end

#chdirString?

Returns PWD for new child processes, default is Dir.pwd.

Returns:

  • (String, nil)

    PWD for new child processes, default is Dir.pwd



37
38
39
# File 'lib/backticks/runner.rb', line 37

def chdir
  @chdir
end

#cli#parameters (readonly)

Returns the CLI-translation object used by this runner.

Returns:

  • (#parameters)

    the CLI-translation object used by this runner



40
41
42
# File 'lib/backticks/runner.rb', line 40

def cli
  @cli
end

#interactiveBoolean

If true, commands will have their stdio streams tied to the parent process so the user can view their output and send input to them. Commands’ output is still captured normally when they are interactive.

Note: if you set ‘interactive` to true, then stdin and stdout will be unbuffered regardless of how you have set `buffered`!

Returns:

  • (Boolean)


22
23
24
# File 'lib/backticks/runner.rb', line 22

def interactive
  @interactive
end

Instance Method Details

#run(*sugar) ⇒ Command

Run a command whose parameters are expressed using some Rubyish sugar. This method accepts an arbitrary number of positional parameters; each parameter can be a Hash, an array, or a simple Object. Arrays and simple objects are appended to argv as words of the command; Hashes are translated to command-line options and then appended to argv.

Hashes are processed by @cli, defaulting to Backticks::CLI::Getopt and easily overridden by passing the ‘cli` option to #initialize.

Examples:

Run docker-compose with complex parameters

run('docker-compose', {file: 'joe.yml'}, 'up', {d:true}, 'mysvc')

Parameters:

  • sugar (Array)

    list of command words and options

Returns:

  • (Command)

    the running command

See Also:



96
97
98
# File 'lib/backticks/runner.rb', line 96

def run(*sugar)
  run_without_sugar(@cli.parameters(*sugar))
end

#run_without_sugar(argv) ⇒ Command

Run a command whose argv is specified in the same manner as Kernel#exec, with no Rubyish sugar.

Parameters:

  • argv (Array)

    command to run; argv is program name and the remaining elements are parameters and flags

Returns:

  • (Command)

    the running command



106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/backticks/runner.rb', line 106

def run_without_sugar(argv)
  stdin_r, stdin = if buffered.include?(:stdin) && !interactive
    IO.pipe
  else
    PTY.open
  end
  stdout, stdout_w = if buffered.include?(:stdout) && !interactive
    IO.pipe
  else
    PTY.open
  end
  stderr, stderr_w = if buffered.include?(:stderr)
    IO.pipe
  else
    PTY.open
  end

  dir = @chdir || Dir.pwd
  pid = spawn(*argv, in: stdin_r, out: stdout_w, err: stderr_w, chdir: dir)
  stdin_r.close
  stdout_w.close
  stderr_w.close
  unless interactive
    stdin.close
    stdin = nil
  end

  Command.new(pid, stdin, stdout, stderr, interactive:interactive)
end