class Obstinately::Command

Constants

DEFAULT_BACKOFF
DEFAULT_RETRIES

Public Class Methods

new(argv) click to toggle source
Calls superclass method
# File lib/obstinately/command.rb, line 34
def initialize(argv)
  @retries = argv.option('retries', DEFAULT_RETRIES)
  @backoff = argv.option('backoff', DEFAULT_BACKOFF)
  @command = argv.arguments!
  super
end
options() click to toggle source
Calls superclass method
# File lib/obstinately/command.rb, line 27
def self.options
  super.concat([
                 ['--retries=RETRIES', "The number of retries. Defaults to #{DEFAULT_RETRIES}"],
                 ['--backoff=BACKOFF', "The number of seconds to wait after the first failure. Doubles after each subsequent failure. Defaults to #{DEFAULT_BACKOFF}"]
               ]).reject { |name, _| name == '--no-ansi' }
end

Public Instance Methods

message() { || ... } click to toggle source
# File lib/obstinately/command.rb, line 80
def message
  return unless verbose?

  warn yield
end
run() click to toggle source
# File lib/obstinately/command.rb, line 41
def run
  backoff = @backoff
  @retries.succ.times do |i|
    message { "Running `#{@command.shelljoin}`" }
    exe, *args = *@command

    begin
      subprocess = spawn([exe, exe], *args)
    rescue SystemCallError => e
      raise MissingExecutableError, "Unable to find executable `#{exe}`#{" (#{e.class})" if verbose?}"
    else
      Process.wait(subprocess)
      status = $CHILD_STATUS
    end

    break if status.success?

    if i == @retries
      message { "Running `#{@command.shelljoin}` failed #{i.succ} times" }
      exit status.exitstatus
    end

    message { "Running `#{@command.shelljoin}` failed#{", sleeping #{backoff} seconds, then" if backoff > 0} retrying (#{i.succ}/#{@retries.succ})" }

    sleep backoff
    backoff *= 2
  end
end
validate!() click to toggle source
Calls superclass method
# File lib/obstinately/command.rb, line 70
def validate!
  super

  help! "Must specify a positive number of retries, not #{@retries}" unless @retries =~ /\A\d+\z/ && (@retries = @retries.to_i) && (@retries >= 0)

  help! "Must specify a positive number of seconds for backoff, not #{@backoff}" unless @backoff =~ /\A(\d+)?\.?\d+\z/ && (@backoff = @backoff.to_f) && (@backoff >= 0)

  help! 'Must specify a command to run' if @command.empty?
end