class Benry::CLI::Application

Attributes

desc[R]
script_name[R]
version[R]

Public Class Methods

new(desc=nil, version: '0.0', script_name: nil, action_classes: nil) click to toggle source
# File lib/benry/cli.rb, line 406
def initialize(desc=nil, version: '0.0', script_name: nil, action_classes: nil)
  @desc        = desc
  @version     = version
  @script_name = script_name || File.basename($0)
  @action_dict = accept(action_classes || Action::SUBCLASSES)
end

Public Instance Methods

help_message(command, action_name=nil) click to toggle source
# File lib/benry/cli.rb, line 542
def help_message(command, action_name=nil)
  if action_name
    action_info = @action_dict[action_name]  or
      raise err("#{action_name}: no such action.")
    return action_info.help_message(command)
  end
  #
  msg = ""
  #; [!1zpv4] adds command desc if it is specified at initializer.
  if @desc
    #msg << "#{command}  -- #{@desc}\n"
    #msg << "\n"
    msg << @desc << "\n\n"
  end
  msg << "Usage:\n"
  msg << "  #{command} [<options>] <action> [<args>...]\n"
  msg << "\n"
  msg << "Options:\n"
  self.class.instance_variable_get('@_global_option_schemas').each do |schema|
    msg << "  %-20s : %s\n" % [schema.option_string, schema.desc]
  end
  msg << "\n"
  msg << "Actions:\n"
  #msg << "  %-20s : %s\n" % ["help", "show this help"]
  @action_dict.keys.sort.each do |action_full_name|
    action_info = @action_dict[action_full_name]
    #; [!m3mry] skips action name when description is not provided.
    msg << "  %-20s : %s\n" % [action_full_name, action_info.desc] if action_info.desc
  end
  msg << "\n"
  msg << "(Run `#{command} help <action>' to show help message of each action.)\n"
  return msg
end
main(argv=ARGV) click to toggle source
# File lib/benry/cli.rb, line 469
def main(argv=ARGV)
  begin
    ret = run(*argv)
  rescue OptionError => ex
    $stderr.puts "ERROR: #{ex}"
    exit 1
  else
    case ret
    when String
      output = ret
      puts output
      exit 0
    when Integer
      status = ret
      exit status
    else
      exit 0
    end
  end
end
run(*args) click to toggle source
# File lib/benry/cli.rb, line 438
def run(*args)
  ## global options
  gopt_values = parse_global_options(args)
  output = handle_global_options(args, gopt_values)
  return output if output
  ## global help
  #; [!p5pr6] returns global help message when action is 'help'.
  #; [!3hyvi] returns help message of action when action is 'help' with action name.
  action_full_name = args.shift || "help"
  if action_full_name == "help"
    return help_message(self.script_name, args[0])
  end
  ## action and options
  #; [!mb92l] raises error when action name is unknown.
  action_info = @action_dict[action_full_name]  or
    raise err("#{action_full_name}: unknown action.")
  option_values = parse_options(args, action_info.option_schemas)
  ## show help
  #; [!13m3q] returns help message if '-h' or '--help' specified to action.
  if option_values['help']
    return action_info.help_message(self.script_name)
  end
  ## validation
  obj = action_info.action_class.new()
  method_name = action_info.action_method
  validate_args(obj, method_name, args, action_full_name)
  ## do action
  ret = kick_action(obj, method_name, args, option_values)
  return ret
end

Protected Instance Methods

handle_global_options(args, global_option_values) click to toggle source
# File lib/benry/cli.rb, line 498
def handle_global_options(args, global_option_values)
  g_opts = global_option_values
  #; [!b8isy] returns help message when global option '-h' or '--help' is specified.
  if g_opts['help']
    return help_message(self.script_name, nil)
  end
  #; [!4irzw] returns version string when global option '--version' is specified.
  if g_opts['version']
    return @version || '0.0'
  end
end
kick_action(action_obj, method_name, args, option_values) click to toggle source
# File lib/benry/cli.rb, line 527
def kick_action(action_obj, method_name, args, option_values)
  #; [!rph9y] converts 'foo-bar' option name into :foo_bar keyword.
  kwargs = Hash[option_values.map {|k, v| [k.gsub(/-/, '_').intern, v] }]
  #; [!qwd9x] passes command arguments and options as method arguments and options.
  method_obj = action_obj.method(method_name)
  has_kwargs = method_obj.parameters.any? {|x| x[0] == :key }
  if has_kwargs
    return method_obj.call(*args, kwargs)
  else
    return method_obj.call(*args)
  end
end
parse_global_options(args) click to toggle source
# File lib/benry/cli.rb, line 492
def parse_global_options(args)
  gopt_schemas = self.class.instance_variable_get('@_global_option_schemas')
  gopt_values = parse_options(args, gopt_schemas)
  return gopt_values
end
validate_args(action_obj, method_name, args, action_full_name) click to toggle source
# File lib/benry/cli.rb, line 510
def validate_args(action_obj, method_name, args, action_full_name)
  #; [!yhry7] raises error when required argument is missing.
  meth = action_obj.method(method_name)
  n_min = meth.parameters.count {|x| x[0] == :req }
  args.length >= n_min  or
    raise err("too few arguments (at least #{n_min} args expected).\n" +\
              "(run `#{@script_name} help #{action_full_name}' for details.)")
  #; [!h5522] raises error when too much arguments specified.
  #; [!hq8b0] not raise error when many argument specified but method has *args.
  unless meth.parameters.find {|x| x[0] == :rest }
    n_max = meth.parameters.count {|x| x[0] == :req || x[0] == :opt }
    args.length <= n_max  or
      raise err("too many arguments (at most #{n_max} args expected).\n" +\
                "(run `#{@script_name} help #{action_full_name}' for details.)")
  end
end

Private Instance Methods

accept(action_classes) click to toggle source
# File lib/benry/cli.rb, line 417
def accept(action_classes)
  #; [!ue26k] builds action dictionary.
  action_dict = {}
  action_classes.each do |klass|
    prefix = klass.instance_variable_get('@prefix')
    (klass.instance_variable_get('@__mappings') || []).each do |tuple|
      action_name, desc, option_schemas, method_name = tuple
      action_name ||= method_name
      full_name = prefix ? "#{prefix}:#{action_name}" : action_name.to_s
      #; [!x6rh1] registers action name replacing '_' with '-'.
      full_name = full_name.gsub('_', '-')
      #
      action_dict[full_name] = ActionInfo.new(full_name, action_name, desc,
                                        option_schemas, klass, method_name)
    end
  end
  return action_dict
end
err(msg) click to toggle source
# File lib/benry/cli.rb, line 582
def err(msg)
  return OptionError.new(msg)
end
parse_options(args, option_schemas) click to toggle source
# File lib/benry/cli.rb, line 578
def parse_options(args, option_schemas)
  return OptionParser.new(option_schemas).parse(args)
end