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.4.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

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


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

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

#clear_nextsObject

:nodoc:



373
374
375
376
377
378
379
380
# File 'lib/gli.rb', line 373

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:



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

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:



390
391
392
# File 'lib/gli.rb', line 390

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('~').



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

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)


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

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



329
330
331
332
# File 'lib/gli.rb', line 329

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_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



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

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



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

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

#error_device=(e) ⇒ Object

Override the device of stderr; exposed only for testing



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

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



280
281
282
283
284
285
286
287
288
289
290
291
# File 'lib/gli.rb', line 280

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:



296
297
298
# File 'lib/gli.rb', line 296

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

#find_command(name) ⇒ Object

:nodoc:



514
515
516
517
518
519
520
521
# File 'lib/gli.rb', line 514

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.



358
359
360
361
362
363
364
# File 'lib/gli.rb', line 358

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


123
124
125
126
127
128
129
# File 'lib/gli.rb', line 123

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



366
367
368
369
370
371
# File 'lib/gli.rb', line 366

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

#flagsObject

:nodoc:



384
385
386
# File 'lib/gli.rb', line 384

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.



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

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



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

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

#parse_configObject

:nodoc:



334
335
336
337
338
339
340
341
342
# File 'lib/gli.rb', line 334

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)


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

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



412
413
414
415
416
417
418
419
420
421
422
423
424
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
# File 'lib/gli.rb', line 412

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



183
184
185
# File 'lib/gli.rb', line 183

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



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

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)


258
259
260
261
262
263
264
265
266
# File 'lib/gli.rb', line 258

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



63
64
65
66
67
68
# File 'lib/gli.rb', line 63

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



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

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)


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

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



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

def reset # :nodoc:
  switches.clear
  flags.clear
  commands.clear
  @@version = nil
  @@config_file = nil
  @@use_openstruct = false
  @@prog_desc = nil
  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



223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
# File 'lib/gli.rb', line 223

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[:help] 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



80
81
82
# File 'lib/gli.rb', line 80

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



73
74
75
# File 'lib/gli.rb', line 73

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.



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

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:



387
388
389
# File 'lib/gli.rb', line 387

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



214
215
216
# File 'lib/gli.rb', line 214

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



524
525
526
527
528
529
# File 'lib/gli.rb', line 524

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.



204
205
206
# File 'lib/gli.rb', line 204

def version(version)
  @@version = version
end