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.3.5'
@@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


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

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

#clear_nextsObject

:nodoc:



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

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:



160
161
162
163
164
165
# File 'lib/gli.rb', line 160

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:



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

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



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

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)


319
320
321
# File 'lib/gli.rb', line 319

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



326
327
328
329
# File 'lib/gli.rb', line 326

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



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

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



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

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



277
278
279
280
281
282
283
284
285
286
287
288
# File 'lib/gli.rb', line 277

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:



293
294
295
# File 'lib/gli.rb', line 293

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

#find_command(name) ⇒ Object

:nodoc:



511
512
513
514
515
516
517
518
# File 'lib/gli.rb', line 511

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.



355
356
357
358
359
360
361
# File 'lib/gli.rb', line 355

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


120
121
122
123
124
125
126
# File 'lib/gli.rb', line 120

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



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

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

#flagsObject

:nodoc:



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

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.



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

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



194
195
196
# File 'lib/gli.rb', line 194

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

#parse_configObject

:nodoc:



331
332
333
334
335
336
337
338
339
# File 'lib/gli.rb', line 331

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)


346
347
348
349
350
351
# File 'lib/gli.rb', line 346

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



409
410
411
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
# File 'lib/gli.rb', line 409

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



180
181
182
# File 'lib/gli.rb', line 180

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



173
174
175
# File 'lib/gli.rb', line 173

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)


255
256
257
258
259
260
261
262
263
# File 'lib/gli.rb', line 255

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



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

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



304
305
306
307
308
309
# File 'lib/gli.rb', line 304

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)


267
268
269
270
271
272
273
# File 'lib/gli.rb', line 267

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



220
221
222
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
# File 'lib/gli.rb', line 220

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



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

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



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

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.



132
133
134
135
136
137
138
# File 'lib/gli.rb', line 132

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:



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

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



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

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



521
522
523
524
525
526
# File 'lib/gli.rb', line 521

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.



201
202
203
# File 'lib/gli.rb', line 201

def version(version)
  @@version = version
end