Module: GLI

Extended by:
GLI
Includes:
CopyOptionsToAliases
Included in:
GLI
Defined in:
lib/gli.rb,
lib/gli/flag.rb,
lib/gli/switch.rb,
lib/gli/command.rb,
lib/gli/options.rb,
lib/gli_version.rb,
lib/gli/terminal.rb,
lib/support/help.rb,
lib/support/rdoc.rb,
lib/gli/exceptions.rb,
lib/support/scaffold.rb,
lib/support/initconfig.rb,
lib/gli/command_line_token.rb,
lib/gli/copy_options_to_aliases.rb

Overview

A means to define and parse a command line interface that works as Git’s does, in that you specify global options, a command name, command specific options, and then command arguments.

Defined Under Namespace

Modules: CopyOptionsToAliases Classes: BadCommandLine, Command, CommandLineToken, CustomExit, DefaultHelpCommand, Flag, InitConfig, Options, RDocCommand, Scaffold, Switch, Terminal, UnknownCommand, UnknownCommandArgument, UnknownGlobalArgument

Constant Summary collapse

VERSION =
'1.6.0'
@@program_name =
$0.split(/\//)[-1]
@@post_block =
nil
@@pre_block =
nil
@@error_block =
nil
@@config_file =
nil
@@use_openstruct =
false
@@version =
nil
@@stderr =
$stderr
@@program_desc =
nil
@@skips_pre =
false
@@skips_post =
false
@@default_command =
:help

Instance Method Summary collapse

Methods included from CopyOptionsToAliases

#copy_options_to_aliases

Instance Method Details

#arg_name(name) ⇒ Object

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.

Example:

desc 'Set the filename'
arg_name 'file_name'
flag [:f,:filename]

Produces:

-f, --filename=file_name      Set the filename


115
# File 'lib/gli.rb', line 115

def arg_name(name); @@next_arg_name = name; end

#clear_nextsObject

:nodoc:



386
387
388
389
390
391
392
393
# File 'lib/gli.rb', line 386

def clear_nexts # :nodoc:
  @@next_desc = nil
  @@next_arg_name = nil
  @@next_default_value = nil
  @@next_long_desc = nil
  @@skips_pre = false
  @@skips_post = false
end

#command(*names) {|command| ... } ⇒ Object Also known as: c

Define a new command. This takes a block that 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.

names

a String or Symbol, or an Array of String or Symbol that represent all the different names and aliases for this command.

Yields:



176
177
178
179
180
181
# File 'lib/gli.rb', line 176

def command(*names)
  command = Command.new([names].flatten,@@next_desc,@@next_arg_name,@@next_long_desc,@@skips_pre,@@skips_post)
  commands[command.name] = command
  yield command
  clear_nexts
end

#commandsObject

:nodoc:



403
404
405
# File 'lib/gli.rb', line 403

def commands # :nodoc:
  @@commands ||= {}
end

#config_file(filename) ⇒ Object

Sets that this app uses a config file as well as the name of the config file.

filename

A String representing the path to the file to use for the config file. If it’s an absolute path, this is treated as the path to the file. If it’s not, it’s treated as relative to the user’s home directory as produced by File.expand_path('~').



161
162
163
164
165
166
167
168
169
# File 'lib/gli.rb', line 161

def config_file(filename)
  if filename =~ /^\//
    @@config_file = filename
  else
    @@config_file = File.join(File.expand_path('~'),filename)
  end
  commands[:initconfig] = InitConfig.new(@@config_file)
  @@config_file
end

#convert_to_openstruct?(options) ⇒ Boolean

Possibly returns a copy of the passed-in Hash as an instance of GLI::Option. By default, it will not. However by putting use_openstruct true in your CLI definition, it will

Returns:

  • (Boolean)


335
336
337
# File 'lib/gli.rb', line 335

def convert_to_openstruct?(options) # :nodoc:
  @@use_openstruct ? Options.new(options) : options
end

#copy_options_to_aliased_versions(global_options, command, options) ⇒ Object

Copies all options in both global_options and options to keys for the aliases of those flags. For example, if a flag works with either -f or –flag, this will copy the value from [:f] to [:flag] to allow the user to access the options by any alias



342
343
344
345
# File 'lib/gli.rb', line 342

def copy_options_to_aliased_versions(global_options,command,options) # :nodoc:
  copy_options_to_aliases(global_options)
  command.copy_options_to_aliases(options)
end

#default_command(command) ⇒ Object

Sets a default command to run when none is specified on the command line. Note that if you use this, you won’t be able to pass arguments, flags, or switches to the command when run in default mode. All flags and switches are treated as global, and any argument will be interpretted as the command name and likely fail.

command

Command to run as default



62
63
64
# File 'lib/gli.rb', line 62

def default_command(command)
  @@default_command = command.to_sym
end

#default_value(val) ⇒ Object

set the default value of the next flag

val

A String reprensenting 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



121
# File 'lib/gli.rb', line 121

def default_value(val); @@next_default_value = val; end

#desc(description) ⇒ Object Also known as: d

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



70
# File 'lib/gli.rb', line 70

def desc(description); @@next_desc = description; end

#error_device=(e) ⇒ Object

Override the device of stderr; exposed only for testing



35
36
37
# File 'lib/gli.rb', line 35

def error_device=(e) #:nodoc:
  @@stderr = e
end

#error_message(ex) ⇒ Object

Returns a String of the error message to show the user

ex

The exception we caught that launched the error handling routines



293
294
295
296
297
298
299
300
301
302
303
304
# File 'lib/gli.rb', line 293

def error_message(ex) #:nodoc:
  msg = "error: #{ex.message}"
  case ex
  when UnknownCommand
    msg += ". Use '#{program_name} help' for a list of commands"
  when UnknownCommandArgument
    msg += ". Use '#{program_name} help #{ex.command.name}' for a list of command options"
  when UnknownGlobalArgument
    msg += ". Use '#{program_name} help' for a list of global options"
  end
  msg
end

#exit_now!(message, exit_code) ⇒ Object

Simpler means of exiting with a custom exit code. This will raise a CustomExit with the given message and exit code, which will ultimatley cause your application to exit with the given exit_code as its exit status

Raises:



309
310
311
# File 'lib/gli.rb', line 309

def exit_now!(message,exit_code)
  raise CustomExit.new(message,exit_code)
end

#find_command(name) ⇒ Object

:nodoc:



527
528
529
530
531
532
533
534
# File 'lib/gli.rb', line 527

def find_command(name) # :nodoc:
  sym = name.to_sym
  return commands[name.to_sym] if commands[sym]
  commands.each do |command_name,command|
    return command if (command.aliases && command.aliases.include?(sym))
  end
  nil
end

#find_non_flag_index(args) ⇒ Object

Finds the index of the first non-flag argument or -1 if there wasn’t one.



371
372
373
374
375
376
377
# File 'lib/gli.rb', line 371

def find_non_flag_index(args) # :nodoc:
  args.each_with_index do |item,index|
    return index if item =~ /^[^\-]/
    return index-1 if item =~ /^\-\-$/
  end
  -1
end

#flag(*names) ⇒ Object Also known as: f

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.

Example:

desc 'Set the filename'
flag [:f,:filename,'file-name']

Produces:

-f, --filename, --file-name=arg     Set the filename


136
137
138
139
140
141
142
# File 'lib/gli.rb', line 136

def flag(*names)
  names = [names].flatten
  verify_unused(names,flags,switches,"in global options")
  flag = Flag.new(names,@@next_desc,@@next_arg_name,@@next_default_value,@@next_long_desc)
  flags[flag.name] = flag
  clear_nexts
end

#flag_switch_index(args) ⇒ Object



379
380
381
382
383
384
# File 'lib/gli.rb', line 379

def flag_switch_index(args)
  args.each_with_index do |item,index|
    return index if item =~ /^[\-]/
  end
  -1
end

#flagsObject

:nodoc:



397
398
399
# File 'lib/gli.rb', line 397

def flags # :nodoc:
  @@flags ||= {}
end

#long_desc(long_desc) ⇒ Object

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.



101
# File 'lib/gli.rb', line 101

def long_desc(long_desc); @@next_long_desc = long_desc; end

#on_error(&a_proc) ⇒ Object

Define a block to run if an error occurs. The block will receive any Exception that was caught. It should evaluate to false to avoid the built-in error handling (which basically just prints out a message). GLI uses a variety of exceptions that you can use to find out what errors might’ve occurred during command-line parsing:

  • GLI::CustomExit

  • GLI::UnknownCommandArgument

  • GLI::UnknownGlobalArgument

  • GLI::UnknownCommand

  • GLI::BadCommandLine



210
211
212
# File 'lib/gli.rb', line 210

def on_error(&a_proc)
  @@error_block = a_proc
end

#parse_configObject

:nodoc:



347
348
349
350
351
352
353
354
355
# File 'lib/gli.rb', line 347

def parse_config # :nodoc:
  return nil if @@config_file.nil?
  require 'yaml'
  if File.exist?(@@config_file)
    File.open(@@config_file) { |file| YAML::load(file) }
  else
    {}
  end
end

#parse_options(args) ⇒ Object

Returns an array of four values:

* global options (as a Hash)
* Command
* command options (as a Hash)
* arguments (as an Array)


362
363
364
365
366
367
# File 'lib/gli.rb', line 362

def parse_options(args) # :nodoc:
  global_options,command,options,arguments = parse_options_helper(args.clone,Hash.new,nil,Hash.new,Array.new)
  flags.each { |name,flag| global_options[name] = flag.default_value if !global_options[name] }
  command.flags.each { |name,flag| options[name] = flag.default_value if !options[name] }
  return [global_options,command,options,arguments]
end

#parse_options_helper(args, global_options, command, command_options, arguments) ⇒ Object

Recursive helper for parsing command line options

args

the arguments that have yet to be processed

global_options

the global options hash

command

the Command that has been identified (or nil if not identified yet)

command_options

options for Command

arguments

the arguments for Command

This works by finding the first non-switch/flag argument, and taking that sublist and trying to pick out flags and switches. After this is done, one of the following is true:

* the sublist is empty - in this case, go again, as there might be more flags to parse
* the sublist has a flag left in it - unknown flag; we bail
* the sublist has a non-flag left in it - this is the command (or the start of the arguments list)

This sort of does the same thing in two phases; in the first phase, the command hasn’t been identified, so we are looking for global switches and flags, ending when we get the command.

Once the command has been found, we start looking for command-specific flags and switches. When those have been found, we know the rest of the argument list is arguments for the command



425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
# File 'lib/gli.rb', line 425

def parse_options_helper(args,global_options,command,command_options,arguments) # :nodoc:
  non_flag_i = find_non_flag_index(args)
  all_flags = false
  if non_flag_i == 0
    # no flags
    if !command
      command_name = args.shift
      command = find_command(command_name)
      raise UnknownCommand.new("Unknown command '#{command_name}'") if !command
      return parse_options_helper(args,
                                  global_options,
                                  command,
                                  Hash.new,
                                  arguments)
    elsif((index = flag_switch_index(args)) >= 0)
      try_me = args[0..index-1]
      rest = args[index..args.length]
      new_args = rest + try_me
      return parse_options_helper(new_args,
                                  global_options,
                                  command,
                                  Hash.new,
                                  arguments)
    else
      return global_options,command,command_options,arguments + args
    end
  elsif non_flag_i == -1
    all_flags = true
  end

  try_me = args[0..non_flag_i]
  rest = args[(non_flag_i+1)..args.length]
  if all_flags
    try_me = args
    rest = []
  end

  # Suck up whatever options we can
  switch_hash = switches
  flag_hash = flags
  options = global_options
  if command
    switch_hash = command.switches
    flag_hash = command.flags
    options = command_options
  end

  switch_hash.each do |name,switch|
    value = switch.get_value!(try_me)
    options[name] = value if !options[name]
  end

  flag_hash.each do |name,flag|
    value = flag.get_value!(try_me)
    # So, there's a case where the first time we request the value for a flag,
    # we get the default and not the user-provided value.  The next time we request
    # it, we want to override it with the real value.
    # HOWEVER, sometimes this happens in reverse, so we want to err on taking the
    # user-provided, non-default value where possible.
    if value
      if options[name]
        options[name] = value if options[name] == flag.default_value
      else
        options[name] = value
      end
    end
  end

  if try_me.empty?
    return [global_options,command,command_options,arguments] if rest.empty?
    # If we have no more options we've parsed them all
    # and rest may have more
    return parse_options_helper(rest,global_options,command,command_options,arguments)
  else
    if command
      check = rest
      check = rest + try_me if all_flags
      check.each() do |arg|
        if arg =~ /^\-\-$/
          try_me.delete arg
          break
        end
        raise UnknownCommandArgument.new("Unknown option #{arg}",command) if arg =~ /^\-/
      end
      return [global_options,command,command_options,try_me + rest]
    else
      # Now we have our command name
      command_name = try_me.shift
      raise UnknownGlobalArgument.new("Unknown option #{command_name}") if command_name =~ /^\-/

      command = find_command(command_name)
      raise UnknownCommand.new("Unknown command '#{command_name}'") if !command

      return parse_options_helper(rest,
                                  global_options,
                                  command,
                                  Hash.new,
                                  arguments)
    end
  end
end

#post(&a_proc) ⇒ Object

Define a block to run after the command was executed, only if there was not an error. The block will receive the global-options,command,options, and arguments



196
197
198
# File 'lib/gli.rb', line 196

def post(&a_proc)
  @@post_block = a_proc
end

#pre(&a_proc) ⇒ Object

Define a block to run after command line arguments are parsed but before any command is run. If this block raises an exception the command specified will not be executed. The block will receive the global-options,command,options, and arguments If this block evaluates to true, the program will proceed; otherwise the program will end immediately



189
190
191
# File 'lib/gli.rb', line 189

def pre(&a_proc)
  @@pre_block = a_proc
end

#proceed?(global_options, command, options, arguments) ⇒ Boolean

True if we should proceed with executing the command; this calls the pre block if it’s defined

Returns:

  • (Boolean)


271
272
273
274
275
276
277
278
279
# File 'lib/gli.rb', line 271

def proceed?(global_options,command,options,arguments) #:nodoc:
  if command && command.skips_pre
    true
  elsif @@pre_block
    @@pre_block.call(global_options,command,options,arguments)
  else
    true
  end
end

#program_desc(description = nil) ⇒ Object

Describe the overall application/programm. This should be a one-sentence summary of what your program does that will appear in the help output.

description

A String of the short description of your program’s purpose



76
77
78
79
80
81
# File 'lib/gli.rb', line 76

def program_desc(description=nil)
  if description
    @@program_desc = description
  end
  @@program_desc
end

#program_name(override = nil) ⇒ Object

Set or get the name of the program, if you don’t want the default (which is the name of the command line program). This is only used currently in the help and rdoc commands.

override

A String that represents the name of the program to use, other than the default.

Returns the current program name, as a String



320
321
322
323
324
325
# File 'lib/gli.rb', line 320

def program_name(override=nil)
  if override
    @@program_name = override
  end
  @@program_name
end

#regular_error_handling?(ex) ⇒ Boolean

Returns true if we should proceed with GLI’s basic error handling. This calls the error block if the user provided one

Returns:

  • (Boolean)


283
284
285
286
287
288
289
# File 'lib/gli.rb', line 283

def regular_error_handling?(ex) #:nodoc:
  if @@error_block
    @@error_block.call(ex)
  else
    true
  end
end

#resetObject

Reset the GLI module internal data structures; mostly useful for testing



40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/gli.rb', line 40

def reset # :nodoc:
  switches.clear
  flags.clear
  commands.clear
  @@version = nil
  @@config_file = nil
  @@use_openstruct = false
  @@prog_desc = nil
  @@default_command = :help
  clear_nexts

  desc 'Show this message'
  switch :help
end

#run(args) ⇒ Object

Runs whatever command is needed based on the arguments.

args

the command line ARGV array

Returns a number that would be a reasonable exit code



236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
# File 'lib/gli.rb', line 236

def run(args) #:nodoc:
  rdoc = RDocCommand.new
  commands[:rdoc] = rdoc if !commands[:rdoc]
  commands[:help] = DefaultHelpCommand.new(@@version,rdoc) if !commands[:help]
  exit_code = 0
  begin
    config = parse_config
    override_defaults_based_on_config(config)
    global_options,command,options,arguments = parse_options(args)
    copy_options_to_aliased_versions(global_options,command,options)
    global_options = convert_to_openstruct?(global_options)
    options = convert_to_openstruct?(options)
    if proceed?(global_options,command,options,arguments)
      command = commands[@@default_command] if !command
      command.execute(global_options,options,arguments)
      if !command.skips_post && @@post_block
        @@post_block.call(global_options,command,options,arguments)
      end
    end
  rescue Exception => ex

    @@stderr.puts error_message(ex) if regular_error_handling?(ex)

    exit_code = if ex.respond_to? :exit_code
      ex.exit_code
    else
      -2
    end
    raise ex if ENV['GLI_DEBUG'] == 'true'
  end
  exit_code
end

#skips_postObject

Use this if the following command should not have the post block executed. By default, the post block is executed after each command. Using this will avoid that behavior for the following command



93
94
95
# File 'lib/gli.rb', line 93

def skips_post
  @@skips_post = true
end

#skips_preObject

Use this if the following command should not have the pre block executed. By default, the pre block is executed before each command and can result in aborting the call. Using this will avoid that behavior for the following command



86
87
88
# File 'lib/gli.rb', line 86

def skips_pre
  @@skips_pre = true
end

#switch(*names) ⇒ Object Also known as: s

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.



148
149
150
151
152
153
154
# File 'lib/gli.rb', line 148

def switch(*names)
  names = [names].flatten
  verify_unused(names,flags,switches,"in global options")
  switch = Switch.new(names,@@next_desc,@@next_long_desc)
  switches[switch.name] = switch
  clear_nexts
end

#switchesObject

:nodoc:



400
401
402
# File 'lib/gli.rb', line 400

def switches # :nodoc:
  @@switches ||= {}
end

#use_openstruct(use_openstruct) ⇒ Object

Call this with true will cause the global_options and options passed to your code to be wrapped in Options, which is a subclass of OpenStruct that adds [] and []= methods.

use_openstruct

a Boolean indicating if we should use OpenStruct instead of Hashes



227
228
229
# File 'lib/gli.rb', line 227

def use_openstruct(use_openstruct)
  @@use_openstruct = use_openstruct
end

#verify_unused(names, flags, switches, context) ⇒ Object

Checks that the names passed in have not been used in another flag or option



537
538
539
540
541
542
# File 'lib/gli.rb', line 537

def verify_unused(names,flags,switches,context) # :nodoc:
  names.each do |name|
    verify_unused_in_option(name,flags,"flag",context)
    verify_unused_in_option(name,switches,"switch",context)
  end
end

#version(version) ⇒ Object

Indicate the version of your application

version

String containing the version of your application.



217
218
219
# File 'lib/gli.rb', line 217

def version(version)
  @@version = version
end