#!/usr/local/bin/ruby

# Seafaring Drydock Examples
#
# This is a functioning script so you can copy it, run it,
# and just generally be a longshoreman about things. This is
# a drydock after all.
#
# If you're reading this via the Rdocs you won't see the code. See:
#
# http://github.com/delano/drydock/blob/master/bin/example
#
# For an example of a complex command-line application using
# Drydock, see:
#
# http://github.com/solutious/rudy/blob/master/bin/rudy
#

require_relative '../lib/drydock'

module Example
  extend Drydock     # Tell Drydock you want its methods!

  default :welcome   # The welcome command will be run if no command is given
  capture :stderr    # Drydock will capture STDERR and keep it in the hold.
  # You can use this to suppress errors.

  about 'A friendly welcome to the Drydock'
  command :welcome do
    puts 'Welcome to Drydock.', $/
    puts "For available commands: #{$0} show-commands"
  end

  usage "USAGE: #{$0} laugh [-f]"
  about 'The captain commands his crew to laugh'
  option :f, :faster, 'A boolean value. Go even faster!'
  command :laugh do |obj|
    # +obj+ is an instance of Drydock::Command. The options you define are available
    # via obj.option.name

    answer = !obj.option.faster ? 'Sort of' : "Yes! I'm literally laughing as fast as possible."

    puts 'Captain Stubing: Are you laughing?'
    puts 'Dr. Bricker: ' << answer
  end

  global_usage "USAGE: #{File.basename($0)} [global options] command [command options]"
  global :s, :seconds, 'Display values in seconds'
  global :v, :verbose, 'Verbosity level (i.e. -vvv is greater than -v)' do |_v|
    # Use instance variables to maintain values between option blocks.
    # This will increment for every -v found (i.e. -vvv)
    @val ||= 0
    @val += 1
  end

  before do |obj|
    # You can execute a block before the requests command is executed. Instance
    # variables defined here will be available to all commands.
    # +obj+ is a reference to the command object, just like in command blocks.
  end

  after do |obj|
    # And this will be called after the command.
  end

  usage "#{$PROGRAM_NAME} [-s] [-vv] date"
  about 'Display the current date'
  command :date do |obj|
    require 'time'
    now = Time.now
    puts '(Not verbose enough. Try adding a -v.)' if (obj.global.verbose || 0) == 1
    puts 'More verbosely, the date is now: ' if (obj.global.verbose || 0) >= 2
    puts obj.global.seconds ? now.to_i : now.to_s
  end

  ignore :options
  about 'This command ignores options'
  command :rogue do |obj|
    # You can use ignore :options to tell Drydock to not process the
    # command-specific options.
    # Unnamed arguments are available from obj.argv
    if obj.argv.empty?
      puts 'Had you supplied some arguments, I would have ignored them.'
    else
      puts 'Hi! You supplied some arguments but I ignored them.'
      puts "They're all still here in this array: %s" % obj.argv.join(', ')
    end
  end

  # You can write your own command classes by inheriting from Drydock::Command
  # and referencing it in the command definition.
  class JohnWestSmokedOysters < Drydock::Command
    def ahoy!
      p('matey')
    end
  end

  about "Do something with John West's Smoked Oysters"
  command oysters: JohnWestSmokedOysters do |obj|
    p obj # => #<JohnWestSmokedOysters:0x42179c ... >
  end

  about 'My way of saying hello!'
  command %i[ahoy! hello!] => JohnWestSmokedOysters
  # If you don't provide a block, Drydock will call JohnWestSmokedOysters#ahoy!

  require 'yaml'

  usage 'ruby bin/example uri -c -d " " -t 15 http://solutious.com/'
  usage 'echo "http://solutious.com/" | ruby bin/example uri -c -d " " -t 15'
  about 'Check for broken URIs'
  option :c, :check, 'Check response codes for each URI'
  option :d, :delim, String, 'Output delimiter'
  option :t, :timeout, Float, 'Timeout value for HTTP request' do |v|
    # You can provide an block to process the option value.
    # This block must return the final value.
    v = 10 if v > 10
    v
  end
  argv :uris

  command :uri do |obj|
    # This command processes the output of the stdin block (below this definition).
    # The output of that block is available as obj.stdin. If there is no stdin block
    # obj.stdin will be STDIN's IO object.

    require 'net/http'
    require 'uri'
    require 'timeout'

    uris = []
    uris += obj.stdin if obj.stdin
    uris += obj.argv.uris if obj.argv.uris

    delim = obj.option.delim || ','
    timeout = obj.option.timeout || 5
    code = :notchecked # The default code when :check is false

    if uris.empty?
      puts "Frylock: You didn't provide any URIs. "
      puts "Master Shake: Ya, see #{$0} #{obj.alias} -h"
      exit 0
    end

    uris.each_with_index do |uri, index|
      code = response_code(uri, timeout) if obj.option.check
      puts [index + 1, uri, code].join(delim)
    end
  end

  about 'Prints the alias used to access the command'
  # We can define command aliases by providing a list of command
  # names. The first name is still consider to be the main name.
  command :printalias, :reveal do |obj|
    puts 'This is printalias!'
    if obj.alias == obj.cmd
      puts 'You did not use an alias'
    else
      puts 'You used the alias ' << obj.alias
    end
  end

  stdin do |stdin, output|
    # Pre-process STDIN for all commands. This example returns an array of lines.
    # The command processuris uses this array.

    # We only want piped data. If this is not included
    # execution will wait for input from the user.
    unless stdin.tty?

      until stdin.eof?
        line = stdin.readline
        line.chomp!
        (output ||= []) << line
      end

    end
    output
  end

  # And one final feature for the intrepid swabbies like myself.
  # Drydock can handle unknown commands by catching them with a
  # trawler. It's like the captain of all aliases. Just specify
  # the command name to direct all unknown commands to. Simple!
  trawler :printalias

  # Return the HTTP response code for the given URI. Used by
  # uri command.
  #
  # +uri+ A valid HTTP URI
  # +duration+ The timeout threshold (in seconds) for the request.
  def response_code(uri_str, duration = 5) # :nodoc:
    response = :unavailable
    begin
      uri = uri_str.is_a?(URI::HTTP) ? uri_str : URI.parse(uri_str)
      timeout(duration) do
        response = Net::HTTP.get_response(uri).code
      end
    rescue StandardError => e
      puts "Error: #{e.message}"
    end
    response
  end
end

Drydock.run!
