class CTioga2::Commands::Command
One of the commands that can be used.
todo Write a Shortcut command that would simply be a shortcut for other things. Possibly taking arguments ? It could take a description, though that wouldn't be necessary.
todo Use this Shortcut to write DeprecatedShortcut for old ctioga options.
Attributes
The compulsory arguments it can take, in the form of an array of CommandArgument
The code that will be called. It must be a Proc object, or any objects that answers a call method.
The corresponding block will be called with the following arguments:
-
first, the
PlotMaker
instance where the command will be running -
second, as many arguments as there are
arguments
. -
third, if
optional_arguments
is non-empty, a hash containing the values of the optional arguments. It will be an empty hash if no optional arguments are given in the command). It will be empty if the command is called as an option in the command-line.
Few rules for writing the code:
-
code should avoid as much as possible to rely on closures.
-
the
CommandArgument
framework will make sure the arguments are given with the appropriate type or raise an exception. Don't bother.
The context of definition [file, line]
The context of the documentation
The CommandGroup
to which the command belongs
A longer description. Typically input using a here-document.
Its long command-line option, or nil if it should not be called from the command-line (but you really don't want that).
The name of the command, ie how to call it in a commands file
Optional arguments to a command, in the form of a Hash
'option name' => CommandArgument
A short one-line description of the command
Its short command-line option, or nil if none
Public Class Methods
Sets the long documentation of the given command
# File lib/ctioga2/commands/commands.rb, line 151 def self.document_command(cmd, desc) tg = Commands::Interpreter.command(cmd) if tg tg.documentation_context = Command.get_calling_context(1) tg.long_description = desc end end
# File lib/ctioga2/commands/commands.rb, line 94 def self.get_calling_context(id=2) caller[id].gsub(/.*\/ctioga2\//, 'lib/ctioga2/') =~ /(.*):(\d+)/ return [$1, $2.to_i] end
Creates a Command
, with all attributes set up. The code can be set using set_code
.
Single and double dashes are stripped from the beginning of the short and long options respectively.
# File lib/ctioga2/commands/commands.rb, line 104 def initialize(n, short, long, args = [], opts = {}, d_short = nil, d_long = nil, group = nil, register = true, &code) @name = n @short_option = short && short.gsub(/^-/,'') @long_option = long && long.gsub(/^--/,'') @arguments = args @optional_arguments = opts if(@short_option and ! @long_option) raise "A long option must always be present if a short one is" end @code = code self.describe(d_short, d_long, group) @context = Command.get_calling_context # Registers automatically the command if register Commands::Interpreter.register_command(self) end end
Public Instance Methods
Returns the number of compulsory arguments
# File lib/ctioga2/commands/commands.rb, line 133 def argument_number return @arguments.size end
Converts the Array of String
given into an Array of the type suitable for the code
of the Command
. This deals only with compulsory arguments. Returns the array.
Any object which is not a String
is left as is (useful for instance for the OptionParser with boolean options)
As a special case, if the command takes no arguments and the arguments is [true], no exception is raised, and the correct number of arguments is returned.
# File lib/ctioga2/commands/commands.rb, line 202 def convert_arguments(args) if args.size != @arguments.size if(@arguments.size == 0 && args.size == 1 && args[0] == true) return [] else ar = args.map { |x| "'#{x}'"} raise ArgumentNumberMismatch, "Command #{@name} was called with #{args.size} arguments: #{ar.join(", ")}, but it takes #{@arguments.size}" end end retval = [] @arguments.each_index do |i| if ! args[i].is_a? String retval << args[i] else retval << @arguments[i].type.string_to_type(args[i]) end end return retval end
Converts the Hash
of String
given into a Hash
of the type suitable for the code
of the Command
. Only optional arguments are taken into account.
Any object which is not a String
is left as is (useful for instance for the OptionParser with boolean options)
# File lib/ctioga2/commands/commands.rb, line 228 def convert_options(options) target_options = {} conv = target_option_names() for k,v in options kn = normalize_option_name(k) if ! conv.key? kn raise CommandOptionUnkown, "Unkown option #{k} for command #{@name}" end opt = @optional_arguments[conv[kn]] if v.is_a? String v = opt.type.string_to_type(v) end target = opt.option_target || conv[kn] if opt.option_deprecated expl = "" if opt.option_target expl = " -- please use #{opt.option_target} instead" elsif opt.option_deprecated != true # Ie more than plain # true/false expl = " -- #{opt.option_deprecated}" end Log::warn { "Deprecated option #{k}#{expl}" } end target_options[target] = v end return target_options end
Sets the descriptions of the command. If the long description is ommitted, the short is reused.
# File lib/ctioga2/commands/commands.rb, line 139 def describe(short, long = nil, group = nil) @documentation_context = Command.get_calling_context(1) @short_description = short @long_description = long || short if(group) group = Interpreter::group(group) if group.is_a? String @group = group group.commands << self end end
Whether the Command
accepts the named option.
# File lib/ctioga2/commands/commands.rb, line 275 def has_option?(option) return target_option_names.key?(normalize_option_name(option)) end
Whether the Command
accepts any option at all ?
# File lib/ctioga2/commands/commands.rb, line 280 def has_options? return !(@optional_arguments.empty?) end
Returns a lowercase
# File lib/ctioga2/commands/commands.rb, line 270 def normalize_option_name(opt) return opt.gsub(/_/,"-").downcase end
Returns a list of three strings:
-
the short option
-
the long option with arguments
-
the description string
Returns nil if the long option is not defined.
# File lib/ctioga2/commands/commands.rb, line 165 def option_strings if ! @long_option return nil end retval = [] # Short option retval << ( @short_option ? "-#{@short_option}" : nil) # Long option + arguments if @arguments.size > 0 retval << @arguments.first.type. option_parser_long_option(@long_option, @arguments.first.displayed_name) + if @arguments.size > 1 " " + @arguments[1..-1].map do |t| t.displayed_name.upcase end.join(" ") else "" end else retval << "--#{@long_option}" end retval << @short_description return retval end
Runs the command with the given plotmaker_target, the compulsory arguments and the optional ones. Any mismatch in the number of things will result in an Exception.
The arguments will not be processed further.
# File lib/ctioga2/commands/commands.rb, line 290 def run_command(plotmaker_target, compulsory_args, optional_args = nil) args = [plotmaker_target] if compulsory_args.size != @arguments.size raise ArgumentNumberMismatch, "Command #{@name} was called with #{args.size} arguments, but it takes #{@arguments.size}" end plotmaker_target.interpreter. add_instruction(Instruction.new(self, compulsory_args, optional_args)) args += compulsory_args if has_options? if optional_args args << optional_args else args << {} end end @code.call(*args) end
Sets the code to the block given.
# File lib/ctioga2/commands/commands.rb, line 128 def set_code(&code) @code = code end
Returns a hash “normalized option names” => 'real option name'
# File lib/ctioga2/commands/commands.rb, line 258 def target_option_names return @tg_op_names if @tg_op_names @tg_op_names = {} for k in @optional_arguments.keys @tg_op_names[normalize_option_name(k)] = k end return @tg_op_names end