class CommandParser::CmdParser

Constants

REG_RANGE

Attributes

args[R]
options[R]

Public Class Methods

new(args=[], &block) click to toggle source
# File lib/command_parser.rb, line 56
def initialize(args=[], &block)
    @available_options = Array.new
    @commands = Hash.new
    @command_list = Array.new
    @formats = Hash.new

    @main = nil

    @exit_code = nil

    @args = args
    @options = Hash.new

    @before_proc=nil
    @comm_name=nil

    define_default_formats

    instance_eval(&block)

    addons = Dir["#{OpenNebulaHelper::CLI_ADDONS_LOCATION}/#{File.basename($0)}/*"]
    if defined?(addons) and !addons.nil?
            addons.each do |addon_path|
                addon_code = File.read(addon_path)
                instance_eval(addon_code)
            end
    end

    self.run
end

Public Instance Methods

before_proc(&block) click to toggle source

Defines a proc to be called before any command @param [Proc] block

# File lib/command_parser.rb, line 114
def before_proc(&block)
    @before_proc = block
end
command(name, desc, *args_format, &block) click to toggle source

Defines a new action for the command, several actions can be defined for a command. For example: create, delete, list. The options and args variables can be used inside the block, and they contain the parsedarguments and options.

@param [Symbol] name Name of the action (i.e: :create, :list) @param [String] desc Description of the action @param [Array<Symbol, Array<Symbol, nil>>, Hash] args_format arguments

or specific options for this actiion
Note that the first argument of the command is the
action and should not be defined using this parameter. The rest of
the argument must be defined using this parameter.
This parameter can use formats previously defined with the format
method
Options are specified using a hash :options => ... containing
the hashes representing the options. The option method doc contains
the hash that has to be used to specify an option

@yieldreturn [Integer, Array[Integer, String]] the block must

return the exit_code and if a String is returned it will be printed

@example

Definining two arguments:
    $ onetest test a1 a2

CommandParser::CmdParser.new(ARGV) do
    description "Test"
    usage "onetest <command> <args> [options]"
    version "1.0"

    options VERBOSE, HELP

    command :test, "Test", :test1, :test2, :options => XML do
        puts options[:xml]
        puts options[:verbose]
        puts args[0]
        puts args[1]
        [0, "It works"]
    end
end

Defining optional arguments: test1 is mandatory, test2 optional
    $ onetest test a1 | $ onetest test a1 a2

CommandParser::CmdParser.new(ARGV) do
    description "Test"
    usage "onetest <command> <args> [options]"
    version "1.0"

    options VERBOSE, HELP

    command :test, "Test", :test1, [:test2, nil], :options => XML do
        puts options[:xml]
        puts options[:verbose]
        puts args[0]
        puts "It works"
        0
    end
end

Defining an argument with different formats:
    $ onetest test a1 a2 | $ onetest test a1 123

CommandParser::CmdParser.new(ARGV) do
    description "Test"
    usage "onetest <command> <args> [options]"
    version "1.0"

    options VERBOSE, HELP

    format :format1, "String to Integer" do
        [0, arg.to_i]
    end

    command :test, "Test", :test1, [:format1, format2], :options => XML do
        puts options[:xml]
        puts options[:verbose]
        puts args[0]
        0
    end
end
# File lib/command_parser.rb, line 283
def command(name, desc, *args_format, &block)
    if name.is_a? (Array)
        name = name.join(" ").to_sym
    end

    cmd = Hash.new
    cmd[:desc] = desc
    cmd[:arity] = 0
    cmd[:options] = []
    cmd[:args_format] = Array.new
    args_format.each {|args|
        if args.instance_of?(Array)
            cmd[:arity]+=1 unless args.include?(nil)
            cmd[:args_format] << args
        elsif args.instance_of?(Hash) && args[:options]
            if args[:options].is_a? Array
                args[:options].flatten!
                args[:options] = args[:options].sort_by {|o| o[:name] }
            end

            cmd[:options] << args[:options]
        else
            cmd[:arity]+=1
            cmd[:args_format] << [args]
        end
    }
    cmd[:proc] = block
    @command_list << name.to_sym
    @commands[name.to_sym] = cmd
end
deprecated_command(name, new_command) click to toggle source
# File lib/command_parser.rb, line 314
def deprecated_command(name, new_command)
    cmd = Hash.new
    cmd[:desc] = "Deprecated, use #{new_command} instead"
    cmd[:arity] = 0
    cmd[:options] = []
    cmd[:args_format] = [[:string, nil]] * 20
    cmd[:deprecated] = new_command
    cmd[:proc] = lambda do
        print_deprecated(new_command)
    end

    @commands[name.to_sym] = cmd
end
description(str) click to toggle source

Defines the additional information of the command @param [String] str

# File lib/command_parser.rb, line 102
def description(str)
    @description = str
end
exit_code(code) click to toggle source

Defines the exit code to be returned by the command @param [Integer] code

# File lib/command_parser.rb, line 191
def exit_code(code)
    @exit_code = code
end
exit_with_code(code, output=nil) click to toggle source
# File lib/command_parser.rb, line 195
def exit_with_code(code, output=nil)
    puts output if output
    exit code
end
format(format, description, &block) click to toggle source

Defines a block that will be used to parse the arguments of the command. Formats defined using this method con be used in the arguments section of the command method, when defining a new action

@param [Symbol] format name of the format @param [String] description

@yieldreturn [Array[Integer, String]] the block must return an Array

containing the result (0:success, 1:failure) and the
new value for the argument.
# File lib/command_parser.rb, line 129
def format(format, description, &block)
    @formats[format] = {
        :desc => description,
        :proc => block
    }
end
main(*args_format, &block) click to toggle source

Defines a new action for the command, several actions can be defined for a command. For example: create, delete, list. The options and args variables can be used inside the block, and they contain the parsedarguments and options.

@param [Array<Symbol, Array<Symbol, nil>>] args_format arguments

or specific options for this actiion
Note that the first argument of the command is the
action and should not be defined using this parameter. The rest of
the argument must be defined using this parameter.
This parameter can use formats previously defined with the format
method

@yieldreturn [Integer, Array[Integer, String]] the block must

return the exit_code and if a String is returned it will be printed

@example

Definining two arguments:
    $ onetest a1 a2

CommandParser::CmdParser.new(ARGV) do
    description "Test"
    usage "onetest <args> [options]"
    version "1.0"

    options XML, VERBOSE, HELP

    main :test1, :test2 do
        puts options[:xml]
        puts options[:verbose]
        puts args[0]
        puts args[1]
        [0, "It works"]
    end
end

Defining optional arguments: test1 is mandatory, test2 optional
    $ onetest a1 | $ onetest a1 a2

CommandParser::CmdParser.new(ARGV) do
    description "Test"
    usage "onetest <args> [<options>]"
    version "1.0"

    options XML, VERBOSE, HELP

    main :test1, [:test2, nil] do
        puts options[:xml]
        puts options[:verbose]
        puts args[0]
        puts "It works"
        0
    end
end

Defining an argument with different formats:
    $ onetest a1 a2 | $ onetest a1 123

CommandParser::CmdParser.new(ARGV) do
    description "Test"
    usage "onetest <args> [<options>]"
    version "1.0"

    options XML, VERBOSE, HELP

    format :format1, "String to Integer" do
        [0, arg.to_i]
    end

    main :test1, [:format1, :format2] do
        puts options[:xml]
        puts options[:verbose]
        puts args[0]
        puts args[1]
        0
    end
end
# File lib/command_parser.rb, line 407
def main(*args_format, &block)
    @main=Hash.new
    @main[:arity] = 0
    @main[:args_format] = Array.new
    args_format.collect {|args|
        if args.instance_of?(Array)
            @main[:arity]+=1 unless args.include?(nil)
            @main[:args_format] << args
        elsif args.instance_of?(Hash) && args[:options]
            @available_options << args[:options]
        else
            @main[:arity]+=1
            @main[:args_format] << [args]
        end
    }

    @main[:proc] = block
end
name(str) click to toggle source

Defines the name of the command @param [String] str

# File lib/command_parser.rb, line 108
def name(str)
    @name = str
end
option(options) click to toggle source

Defines a global option for the command that will be used for all the actions @param [Hash, Array<Hash>] options the option to be included. An

array of options can be also provided

@option options [String] :name @option options [String] :short @option options [String] :large @option options [Boolean] :multiple @option options [String] :description @option options [Class] :format @option options [Block] :proc The block receives the value of the

option and the hash of options. The block must return an Array
containing the result (0:success, 1:failure) and the
new value for the argument or nil. More than one option can be
specified in the block using the options hash. This hash will be
available inside the command block.

@example

This example will define the following options:
    options[:type] = type

TYPE={
    :name => "type",
    :short => "-t type",
    :large => "--type type",
    :format => String,
    :description => "Type of the new Image"
}

This example will define the following options:
    options[:check] = true
    options[:datastore] = id

DATASTORE = {
    :name   => "datastore",
    :short  => "-d id|name",
    :large  => "--datastore id|name" ,
    :description => "Selects the datastore",
    :format => String,
    :proc   => lambda { |o, options|
        options[:check] = true
        [0, OpenNebulaHelper.dname_to_id(o)]
    }
}
# File lib/command_parser.rb, line 181
def option(options)
    if options.instance_of?(Array)
        options.each { |o| @available_options << o }
    elsif options.instance_of?(Hash)
        @available_options << options
    end
end
run() click to toggle source
# File lib/command_parser.rb, line 437
def run
    comm_name=""

    if @main
        comm_name = @name
        comm      = @main
    elsif
        if @args[0] && !@args[0].match(/^-/)
            while comm.nil? and @args.size > 0 do
                current = @args.shift

                if comm_name.empty?
                    @comm_name = comm_name = "#{current}".to_sym
                else
                    @comm_name = comm_name = "#{comm_name} #{current}".to_sym
                end

                comm      = @commands[comm_name]
            end
        end
    end

    if comm.nil?
        print_help
        exit 0
    end

    if comm[:deprecated]
        print_deprecated(comm[:deprecated])
    end

    extra_options = comm[:options] if comm
    parse(extra_options)

    if comm
        begin
            @before_proc.call if @before_proc
        rescue StandardError => e
            STDERR.puts e.message
            exit(-1)
        end

        check_args!(comm_name, comm[:arity], comm[:args_format])

        begin
            rc = comm[:proc].call

            if rc.instance_of?(Array) && rc[0] != 0
                STDERR.puts rc[1]
                exit(rc[0])
            elsif rc.instance_of?(Array)
                puts rc[1]
                exit(rc[0])
            else
                exit(@exit_code || rc)
            end
        rescue StandardError => e
            STDERR.puts e.message
            exit(-1)
        end
    end
end
set(e, *args, &block) click to toggle source

DEPRECATED, use format and options instead

# File lib/command_parser.rb, line 427
def set(e, *args, &block)
    case e
    when :option
        option(args[0])
    when :format
        format(args[0], args[1], &block)
    end
end
usage(str) click to toggle source

Defines the usage information of the command @param [String] str

# File lib/command_parser.rb, line 89
def usage(str)
    @usage = str
    @name ||= @usage.split(' ').first
end
version(str) click to toggle source

Defines the version the command @param [String] str

# File lib/command_parser.rb, line 96
def version(str)
    @version = str
end

Private Instance Methods

check_args!(name, arity, args_format) click to toggle source
# File lib/command_parser.rb, line 561
def check_args!(name, arity, args_format)
    if @args.length < arity
        STDERR.print "Command #{name} requires "
        if arity>1
            STDERR.puts "#{args_format.length} parameters to run."
        else
            STDERR.puts "one parameter to run"
        end

        print_command_help(name)

        exit -1
    else
        id=0
        @args.collect!{|arg|
            unless format=args_format[id]
                args_str=args_format.collect{ |a|
                    if a.include?(nil)
                        "[#{a.compact.join("|")}]"
                    else
                        "<#{a.join("|")}>"
                    end
                }.join(' ')

                STDERR.puts "Wrong number of arguments"
                if args_str.empty?
                    STDERR.puts "No argument is required"
                else
                    STDERR.puts "The arguments should be: #{args_str}"
                end
                exit -1
            end

            format = args_format[id]
            argument = nil
            error_msg = nil
            format.each { |f|
                if @formats[f]
                    format_hash = @formats[f]
                elsif f.nil?
                    argument = nil
                    break
                else
                    format_hash = @formats[:text]
                end

                rc = format_hash[:proc].call(arg)
                if rc[0]==0
                    argument=rc[1]
                    break
                else
                    error_msg=rc[1]
                    next
                end
            }

            unless argument
                if error_msg
                    STDERR.puts error_msg
                else
                    STDERR.puts "command #{name}: argument #{id} must be one of #{format.join(', ')}"
                end
                exit -1
            end

            id+=1
            argument
        }
    end
end
define_default_formats() click to toggle source
# File lib/command_parser.rb, line 865
def define_default_formats
    format :file, "Path to a file" do |arg|
        format_file(arg)
    end

    format :range, "List of id's in the form 1,8..15" do |arg|
        format_range(arg)
    end

    format :text, "String" do |arg|
        format_text(arg)
    end
end
format_file(arg) click to toggle source
# File lib/command_parser.rb, line 841
def format_file(arg)
    File.file?(arg) ? [0,arg] : [-1]
end
format_int(arg) click to toggle source
# File lib/command_parser.rb, line 837
def format_int(arg)
    arg.match(/^\d+$/) ? [0,arg] : [-1]
end
format_range(arg) click to toggle source
# File lib/command_parser.rb, line 847
def format_range(arg)
    arg_s = arg.gsub(" ","").to_s
    return [-1] unless arg_s.match(REG_RANGE)

    ids = Array.new
    arg_s.split(',').each { |e|
        if e.match(/^\d+$/)
            ids << e.to_i
        elsif m = e.match(/^(\d+)\.\.(\d+)$/)
            ids += (m[1].to_i..m[2].to_i).to_a
        else
            return [-1]
        end
    }

    return 0,ids.uniq
end
format_text(arg) click to toggle source

Default Formatters for arguments

# File lib/command_parser.rb, line 833
def format_text(arg)
    arg.instance_of?(String) ? [0,arg] : [-1]
end
parse(extra_options) click to toggle source
# File lib/command_parser.rb, line 502
def parse(extra_options)
    with_proc=Array.new

    @cmdparse=OptionParser.new do |opts|
        merge = @available_options
        merge = @available_options + extra_options if extra_options

        merge.flatten.each do |e|
            args = []
            args << e[:short] if e[:short]
            args << e[:large]
            args << e[:multiple] if e[:multiple]
            args << e[:format]
            args << e[:description]

            opts.on(*args) do |o|
                if e[:proc] && !e[:multiple]
                    @options[e[:name].to_sym]=o
                    with_proc<<e
                elsif e[:name]=="help"
                    print_help
                    exit 0
                elsif e[:name]=="version"
                    puts @version
                    exit 0
                elsif !e[:multiple]
                    @options[e[:name].to_sym]=o
                else
                    if @options[e[:name].to_sym].nil?
                        @options[e[:name].to_sym] = []
                    end

                    @options[e[:name].to_sym] << o
                end
            end
        end
    end

    begin
        @cmdparse.parse!(@args)
    rescue => e
        STDERR.puts e.message
        exit -1
    end

    with_proc.each do |e|
        rc = e[:proc].call(@options[e[:name].to_sym], @options)
        if rc.instance_of?(Array)
            if rc[0] == 0
                @options[e[:name].to_sym] = rc[1]
            else
                STDERR.puts rc[1]
                STDERR.puts "option #{e[:name]}: Parsing error"
                exit -1
            end
        end
    end
end
print_all_commands_help() click to toggle source
print_command(command) click to toggle source
print_command_help(name) click to toggle source
print_commands() click to toggle source
print_deprecated(new_command) click to toggle source
print_formatters() click to toggle source
print_help() click to toggle source

Printers

print_option(o) click to toggle source
print_options() click to toggle source
word_wrap(size, text, first_size=nil) click to toggle source
# File lib/command_parser.rb, line 806
def word_wrap(size, text, first_size=nil)
    output=[]
    line=""
    if first_size
        line_size=first_size
    else
        line_size=size
    end

    text.scan(/[^\s]+/) do |word|
        if line.length+word.length+1<=line_size
            line+=" #{word}"
        else
            output<<line
            line=word
            line_size=size
        end
    end

    output<<line
    output[0].strip!
    output
end