class Shellany::Sheller


calls and allow easier stubbing.
The Guard sheller abstract the actual subshell

def self._shellize_if_needed(args)

Only needed on JRUBY, because MRI properly detects ';' and metachars
def self._shellize_if_needed(args)
  return args unless RUBY_PLATFORM == "java"
  return args unless args.size == 1
  return args unless /[;<>]/ =~ args.first
  # NOTE: Sheller was originally meant for Guard (which basically only uses
  # UNIX commands anyway) and JRuby doesn't support options to
  # Kernel.system (and doesn't automatically shell when there's a
  # metacharacter in the command).
  #
  # So ... I'm assuming /bin/sh exists - if not, PRs are welcome,
  # because I have no clue what to do if /bin/sh doesn't exist.
  # (use ENV["RUBYSHELL"] ? Detect cmd.exe ?)
  ["/bin/sh", "-c", args.first]
end

def self._system_with_capture(*args)

def self._system_with_capture(*args)
  # We use popen3, because it started working on recent versions
  # of JRuby, while JRuby doesn't handle options to Kernel.system
  args = _shellize_if_needed(args)
  stdout, stderr, status = nil
  Open3.popen3(*args) do |_stdin, _stdout, _stderr, _thr|
    stdout = _stdout.read
    stderr = _stderr.read
    status = _thr.value
  end
  [status, stdout, stderr]
rescue Errno::ENOENT, IOError => e
  [nil, nil, "Guard::Sheller failed (#{e.inspect})"]
end

def self._system_with_no_capture(*args)

def self._system_with_no_capture(*args)
  Kernel.system(*args)
  result = $?
  errors = (result == 0) || "Guard failed to run: #{args.inspect}"
  [result, nil, errors]
end

def self.run(*args)


Shortcut for new(command).run
def self.run(*args)
  new(*args).run
end

def self.stderr(*args)


Shortcut for new(command).run.stderr
def self.stderr(*args)
  new(*args).stderr
end

def self.stdout(*args)


Shortcut for new(command).run.stdout
def self.stdout(*args)
  new(*args).stdout
end

def self.system(*args)

able to call Kernel.system directly.
it does for on systems with ansi terminals, so we need to be
NOTE: `$stdout.puts system('cls')` on Windows won't work like

No output capturing
def self.system(*args)
  _system_with_no_capture(*args)
end

def initialize(*args)

Parameters:
  • args (*String) -- a list of command parts to run in a subshell
  • args (Array) -- an array of command parts to run in a subshell
  • args (String) -- a command to run in a subshell
def initialize(*args)
  fail ArgumentError, "no command given" if args.empty?
  @command = args
  @ran = false
end

def ok?

Returns:
  • (Boolean) - whether or not the command succeeded
def ok?
  run unless ran?
  @status && @status.success?
end

def ran?

Returns:
  • (Boolean) - whether or not the command has already been run
def ran?
  @ran
end

def run

Returns:
  • (Boolean) - whether or not the command succeeded.
def run
  unless ran?
    status, output, errors = self.class._system_with_capture(*@command)
    @ran = true
    @stdout = output
    @stderr = errors
    @status = status
  end
  ok?
end

def stderr

Returns:
  • (String) - the command output
def stderr
  run unless ran?
  @stderr
end

def stdout

Returns:
  • (String) - the command output
def stdout
  run unless ran?
  @stdout
end