module GLI
# The primary DSL for GLI. This represents the methods shared between your top-level app and
# the commands. See GLI::Command for additional methods that apply only to command objects.
module DSL
# Describe the next switch, flag, or command. This should be a
# short, one-line description
#
# +description+:: A String of the short descripiton of the switch, flag, or command following
def desc(description); @next_desc = description; end
alias :d :desc
# Provide a longer, more detailed description. This
# will be reformatted and wrapped to fit in the terminal's columns
#
# +long_desc+:: A String that is s longer description of the switch, flag, or command following.
def long_desc(long_desc); @next_long_desc = long_desc; end
# Describe the argument name of the next flag. It's important to keep
# this VERY short and, ideally, without any spaces (see Example).
#
# +name+:: A String that *briefly* describes the argument given to the following command or flag.
# +options+:: Symbol or array of symbols to annotate this argument. This doesn't affect parsing, just
# the help output. Values recognized are:
# +:optional+:: indicates this argument is optional; will format it with square brackets
# +:multiple+:: indicates multiple values are accepted; will format appropriately
#
# Example:
# desc 'Set the filename'
# arg_name 'file_name'
# flag [:f,:filename]
#
# Produces:
# -f, --filename=file_name Set the filename
def arg_name(name,options=[])
@next_arg_name = name
@next_arg_options = options
end
# set the default value of the next flag or switch
#
# +val+:: The default value to be used for the following flag if the user doesn't specify one
# and, when using a config file, the config also doesn't specify one. For a switch, this is
# the value to be used if the switch isn't specified on the command-line. Note that if you
# set a switch to have a default of true, using the switch on the command-line has no effect.
# To disable a switch where the default is true, use the --no- form.
def default_value(val); @next_default_value = val; end
# Create a flag, which is a switch that takes an argument
#
# +names+:: a String or Symbol, or an Array of String or Symbol that represent all the different names
# and aliases for this flag. The last element can be a hash of options:
# +:desc+:: the description, instead of using #desc
# +:long_desc+:: the long_description, instead of using #long_desc
# +:default_value+:: the default value, instead of using #default_value
# +:arg_name+:: the arg name, instead of using #arg_name
# +:must_match+:: A regexp that the flag's value must match
# +:type+:: A Class (or object you passed to GLI::App#accept) to trigger type coversion
#
# Example:
#
# desc 'Set the filename'
# flag [:f,:filename,'file-name']
#
# flag :ipaddress, :desc => "IP Address", :must_match => /\d+\.\d+\.\d+\.\d+/
#
# flag :names, :desc => "list of names", :type => Array
#
# Produces:
#
# -f, --filename, --file-name=arg Set the filename
def flag(*names)
options = extract_options(names)
names = [names].flatten
verify_unused(names)
flag = Flag.new(names,options)
flags[flag.name] = flag
clear_nexts
flags_declaration_order << flag
flag
end
alias :f :flag
# Create a switch, which is a command line flag that takes no arguments (thus, it _switches_ something on)
#
# +names+:: a String or Symbol, or an Array of String or Symbol that represent all the different names
# and aliases for this switch. The last element can be a hash of options:
# +:desc+:: the description, instead of using #desc
# +:long_desc+:: the long_description, instead of using #long_desc
# +:default_value+:: if the switch is omitted, use this as the default value. By default, switches default to off, or +false+
# +:negatable+:: if true, this switch will get a negatable form (e.g. --[no-]switch, false it will not. Default is true
def switch(*names)
options = extract_options(names)
names = [names].flatten
verify_unused(names)
switch = Switch.new(names,options)
switches[switch.name] = switch
clear_nexts
switches_declaration_order << switch
switch
end
alias :s :switch
def clear_nexts # :nodoc:
@next_desc = nil
@next_arg_name = nil
@next_arg_options = nil
@next_default_value = nil
@next_long_desc = nil
end
# Define a new command. This can be done in a few ways, but the most common method is
# to pass a symbol (or Array of symbols) representing the command name (or names) and a block.
# The block will be given an instance of the Command that was created.
# You then may call methods on this object to define aspects of that Command.
#
# Alternatively, you can call this with a one element Hash, where the key is the symbol representing the name
# of the command, and the value being an Array of symbols representing the commands to call in order, as a
# chained or compound command. Note that these commands must exist already, and that only those command-specific
# options defined in *this* command will be parsed and passed to the chained commands. This might not be what you expect
#
# +names+:: a String or Symbol, or an Array of String or Symbol that represent all the different names and aliases
# for this command *or* a Hash, as described above.
#
# ==Examples
#
# # Make a command named list
# command :list do |c|
# c.action do |global_options,options,args|
# # your command code
# end
# end
#
# # Make a command named list, callable by ls as well
# command [:list,:ls] do |c|
# c.action do |global_options,options,args|
# # your command code
# end
# end
#
# # Make a command named all, that calls list and list_contexts
# command :all => [ :list, :list_contexts ]
#
# # Make a command named all, aliased as :a:, that calls list and list_contexts
# command [:all,:a] => [ :list, :list_contexts ]
#
def command(*names)
command_options = {
:description => @next_desc,
:arguments_name => @next_arg_name,
:arguments_options => @next_arg_options,
:long_desc => @next_long_desc,
:skips_pre => @skips_pre,
:skips_post => @skips_post,
:skips_around => @skips_around,
}
if names.first.kind_of? Hash
command = GLI::Commands::CompoundCommand.new(self,
names.first,
command_options)
command.parent = self
commands[command.name] = command
else
command = Command.new(command_options.merge(:names => [names].flatten))
command.parent = self
commands[command.name] = command
yield command
end
@commands_declaration_order ||= []
@commands_declaration_order << command
clear_nexts
end
alias :c :command
def flags_declaration_order # :nodoc:
@flags_declaration_order ||= []
end
def switches_declaration_order # :nodoc:
@switches_declaration_order ||= []
end
private
# Checks that the names passed in have not been used in another flag or option
def verify_unused(names) # :nodoc:
names.each do |name|
verify_unused_in_option(name,flags,"flag")
verify_unused_in_option(name,switches,"switch")
end
end
def verify_unused_in_option(name,option_like,type) # :nodoc:
return if name.to_s == 'help'
raise ArgumentError.new("#{name} has already been specified as a #{type} #{context_description}") if option_like[name]
option_like.each do |one_option_name,one_option|
if one_option.aliases
if one_option.aliases.include? name
raise ArgumentError.new("#{name} has already been specified as an alias of #{type} #{one_option_name} #{context_description}")
end
end
end
end
# Extract the options hash out of the argument to flag/switch and
# set the values if using classic style
def extract_options(names)
options = {}
options = names.pop if names.last.kind_of? Hash
options = { :desc => @next_desc,
:long_desc => @next_long_desc,
:default_value => @next_default_value,
:arg_name => @next_arg_name}.merge(options)
end
end
end