module Twine::CLI

Constants

ALL_FORMATS
COMMANDS
DEPRECATED_COMMAND_MAPPINGS
OPTIONS

Public Class Methods

parse(args) click to toggle source
# File lib/twine/cli.rb, line 241
def self.parse(args)
  command = args.select { |a| a[0] != '-' }[0]
  args = args.reject { |a| a == command }

  if args.any? { |a| a == '--version' }
    Twine::stdout.puts "Twine version #{Twine::VERSION}"
    return false
  end

  mapped_command = DEPRECATED_COMMAND_MAPPINGS[command]
  if mapped_command
    Twine::stdout.puts "WARNING: Twine commands names have changed. `#{command}` is now `#{mapped_command}`. The old command is deprecated and will soon stop working. For more information please check the documentation at https://github.com/mobiata/twine"
    command = mapped_command
  end

  if command.nil?
    print_help(args)
    return false
  elsif not COMMANDS.keys.include? command
    raise Twine::Error.new "Invalid command: #{command}"
  end

  parse_command_options(command, args)
end

Private Class Methods

indent(string, first_line, following_lines) click to toggle source
# File lib/twine/cli.rb, line 306
def self.indent(string, first_line, following_lines)
  lines = string.split("\n")
  indentation = ' ' * following_lines
  lines.map! { |line| indentation + line }
  result = lines.join("\n").strip
  ' ' * first_line + result
end
parse_command_options(command_name, args) click to toggle source
# File lib/twine/cli.rb, line 355
def self.parse_command_options(command_name, args)
  command = COMMANDS[command_name]

  result = {
    command: command_name
  }

  parser = OptionParser.new
  parser.banner = "Usage: twine #{command_name} #{command[:arguments].map { |c| "[#{c}]" }.join(' ')} [options]"

  [:required_options, :optional_options].each do |option_type|
    options = command[option_type]
    if options and options.size > 0
      parser.separator ''
      parser.separator option_type.to_s.gsub('_', ' ').capitalize + ":"
      
      options.each do |option_name|
        option = OPTIONS[option_name]

        result[option_name] = option[:default] if option[:default]

        prepare_description!(option, parser.summary_width)

        parser.define(*option[:switch]) do |value|
          if option[:repeated]
            result[option_name] = (result[option_name] || []) << value
          else
            result[option_name] = value
          end
        end
      end
    end
  end

  parser.define('-h', '--help', 'Show this message.') do
    Twine::stdout.puts parser.help
    return false
  end

  parser.separator ''
  parser.separator 'Examples:'
  parser.separator ''
  parser.separator "> #{command[:example]}"

  begin
    parser.parse! args
  rescue OptionParser::ParseError => e
    raise Twine::Error.new e.message
  end

  arguments = args.reject { |a| a[0] == '-' }
  number_of_missing_arguments = command[:arguments].size - arguments.size
  if number_of_missing_arguments > 0
    missing_arguments = command[:arguments][-number_of_missing_arguments, number_of_missing_arguments]
    raise Twine::Error.new "#{number_of_missing_arguments} missing argument#{number_of_missing_arguments > 1 ? "s" : ""}: #{missing_arguments.join(', ')}. Check `twine #{command_name} -h`"
  end

  if args.length > command[:arguments].size
    raise Twine::Error.new "Unknown argument: #{args[command[:arguments].size]}"
  end

  if command[:required_options]
    command[:required_options].each do |option_name|
      if result[option_name] == nil
        raise Twine::Error.new "missing option: #{OPTIONS[option_name][:switch][0]}"
      end
    end
  end

  command[:option_validation].call(result) if command[:option_validation]

  command[:arguments].each do |argument_name|
    result[argument_name] = args.shift
  end

  result
end
prepare_description!(options, summary_width) click to toggle source

ensure the description forms a neat block on the right

# File lib/twine/cli.rb, line 315
def self.prepare_description!(options, summary_width)
  lines = options[:description].split "\n"

  # remove leadinge HEREDOC spaces
  space_match = lines[0].match(/^\s+/)
  if space_match
    leading_spaces = space_match[0].length
    lines.map! { |l| l[leading_spaces..-1] }  
  end

  merged_lines = []
  lines.each do |line|
    # if the line is a continuation of the previous one
    if not merged_lines.empty? and (line[0] != ' ' or line[0, 4] == '    ')
      merged_lines[-1] += ' ' + line.strip
    else
      merged_lines << line.rstrip
    end
  end

  if IO.console
    console_width = IO.console.winsize[1]
  else
    console_width = 100
  end
  summary_width += 7  # account for description padding
  max_description_width = console_width - summary_width
  merged_lines.map! do |line|
    if line[0] == ' '
      line = word_wrap(line.strip, max_description_width - 2)
      line = indent(line, 2, 4)
    else
      line = word_wrap(line, max_description_width)
    end
    line
  end

  options[:switch] << indent(merged_lines.join("\n"), 0, summary_width)
end
print_help(args) click to toggle source
word_wrap(s, width) click to toggle source

source: www.safaribooksonline.com/library/view/ruby-cookbook/0596523696/ch01s15.html

# File lib/twine/cli.rb, line 302
def self.word_wrap(s, width)
  s.gsub(/(.{1,#{width}})(\s+|\Z)/, "\\1\n").rstrip
end