Class: CommandLine::Application

Inherits:
Object
  • Object
show all
Defined in:
lib/commandline/application.rb

Defined Under Namespace

Classes: ApplicationError, ArgumentError, InvalidArgumentArityError, MissingMainError, OptionError, UnknownOptionError

Constant Summary collapse

DEFAULT_CONSOLE_WIDTH =

TODO: Consolidate these with OptionParser - put in command line

70
MIN_CONSOLE_WIDTH =
10
DEFAULT_BODY_INDENT =
4

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeApplication

Returns a new instance of Application.



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/commandline/application.rb', line 41

def initialize
  @synopsis    = ""
  @arg_arity   = [0,0]
  @options     = []
  @arg_names   = []
  @args        = []
  @replay      = false
  @replay_file = ".replay"

  __initialize_text_formatting

  # Call the child usurped initialize
  __child_initialize if 
    self.class.private_instance_methods(false).include?("__child_initialize")

  @option_parser ||= CommandLine::OptionParser.new(@options)
end

Instance Attribute Details

#argsObject (readonly)

Returns the value of attribute args.



30
31
32
# File 'lib/commandline/application.rb', line 30

def args
  @args
end

#argvObject (readonly)

Returns the value of attribute argv.



30
31
32
# File 'lib/commandline/application.rb', line 30

def argv
  @argv
end

Class Method Details

.__set_auto_runObject



282
283
284
# File 'lib/commandline/application.rb', line 282

def self.__set_auto_run
  at_exit { @@child_class.run }
end

.inherited(child_class) ⇒ Object



274
275
276
277
278
279
280
# File 'lib/commandline/application.rb', line 274

def self.inherited(child_class)
  @@appname = caller[0][/.*:/][0..-2]
  @@child_class = child_class
  if @@appname == $0
    __set_auto_run
  end
end

.run(argv = ARGV) ⇒ Object



251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
# File 'lib/commandline/application.rb', line 251

def self.run(argv=ARGV)
  # Usurp an existing initialize so ours can be called first.
  # We rename it __child_initialize and call it from initialize.
  if self.private_instance_methods(false).include?("initialize")
    $VERBOSE, verbose = nil, $VERBOSE
    self.class_eval {
      alias :__child_initialize :initialize
      remove_method :initialize
    }
    $VERBOSE = verbose
  end
  obj = self.new
  obj.__parse_command_line(argv)
  obj.main

  #alias :user_init :initialize
  #@@child_class.new.main if ($0 == @@appname)
  obj
  rescue => err
    puts "ERROR: #{err}"
    exit(-1)
end

Instance Method Details

#__debugObject



418
419
420
421
422
423
424
425
426
# File 'lib/commandline/application.rb', line 418

def __debug
   {
     :names           => %w(--debug -d),
     :arity           => [0,0],
     :opt_description => "Sets debug to true.",
     :arg_description => "",
     :opt_found       => lambda { $DEBUG = true }
   }
end

#__helpObject



377
378
379
380
381
382
383
384
385
386
# File 'lib/commandline/application.rb', line 377

def __help
   {
     :names           => %w(--help -h),
     :arity           => [0,0],
     :opt_description => "Displays help page.",
     :arg_description => "",
     :opt_found       => lambda { puts man; exit },
     :opt_not_found   => false
   }
end

#__initialize_text_formattingObject



347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
# File 'lib/commandline/application.rb', line 347

def __initialize_text_formatting
  #
  # Formatting defaults
  #
  console_width = ENV["COLUMNS"]
  @columns = 
    if console_width.nil?
      DEFAULT_CONSOLE_WIDTH
    elsif console_width < MIN_CONSOLE_WIDTH
      console_width
    else
      console_width - DEFAULT_BODY_INDENT
    end
  @body_indent   = DEFAULT_BODY_INDENT
  @tag_paragraph = false
  @order         = :index  # | :alpha
end

#__parse_command_line(argv) ⇒ Object



310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
# File 'lib/commandline/application.rb', line 310

def __parse_command_line(argv)
  @argv = argv
  if @replay && File.exist?(@replay_file) && !@argv.grep("-r").empty?
    __restore_argv
  elsif @argv.empty? && @arg_arity[0] != 0
    puts usage
    exit(0)
  end

  begin
    @option_data = @option_parser.parse(@argv)
    @args = @option_data.args
  rescue => err
    puts err
    puts
    puts usage
    exit(-1)
  end

  __validate_args(@option_data.args)
  @arg_names.each_with_index { |name, idx|
    instance_variable_set("@#{name}", @option_data.args[idx])
  }
  
  __save_argv
end

#__restore_argvObject



305
306
307
308
# File 'lib/commandline/application.rb', line 305

def __restore_argv
  @argv = File.read(@replay_file).gsub(/\n/, "").split
  raise "Bad @argv" unless @argv.kind_of?(Array)
end

#__save_argvObject



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

def __save_argv
  return unless @replay

  line = 0
  File.open(@replay_file, "w") { |f|
    @argv.each { |arg|
      f.puts "\n" if arg[0] == ?- && line != 0
      f.print " #{arg}"
      line += 1
    }
  }
end

#__validate_arg_arity(arity) ⇒ Object



337
338
339
340
341
342
343
344
345
# File 'lib/commandline/application.rb', line 337

def __validate_arg_arity(arity)
  min, max = *arity
  raise(InvalidArgumentArityError, "Minimum argument arity '#{min}' must be "+
    "greater than or equal to 0.") unless min >= 0
  raise(InvalidArgumentArityError, "Maximum argument arity '#{max}' must be "+
    "greater than or equal to -1.") if max < -1
  raise(InvalidArgumentArityError, "Maximum argument arity '#{max}' must be "+
    "greater than minimum arg_arity '#{min}'.") if max < min && max != -1
end

#__validate_args(od_args) ⇒ Object

Raises:



365
366
367
368
369
370
371
372
373
374
375
# File 'lib/commandline/application.rb', line 365

def __validate_args(od_args)
  size = od_args.size
  min, max = @arg_arity
  max = 1.0/0.0 if -1 == max
  raise(ArgumentError,
    "Missing expected arguments. Found #{size} but expected #{min}. "+
    "#{od_args.inspect}\n"+
    "#{usage}") if size < min
  raise(ArgumentError, "Too many arguments. Found #{size} but "+
    "expected #{max}.\n#{usage}") if size > max
end

#__verboseObject



388
389
390
391
392
393
394
395
396
397
398
# File 'lib/commandline/application.rb', line 388

def __verbose
   {
     :names           => %w(--verbose -v),
     :arity           => [0,0],
     :opt_description => "Sets verbosity level. Subsequent "+
                         "flags increase verbosity level",
     :arg_description => "",
     :opt_found       => lambda { @verbose ||= -1; @verbose += 1 },
     :opt_not_found   => nil
   }
end

#__versionObject



400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
# File 'lib/commandline/application.rb', line 400

def __version
   {
     :names           => %w(--version -V),
     :arity           => [0,0],
     :opt_description => "Displays application version.",
     :arg_description => "",
     :opt_found       => lambda { 
                           begin 
                             puts "#{name} - Version: #{version}"
                           rescue 
                             puts "No version specified" 
                           end; 
                           exit 
                         },
     :opt_not_found   => nil
   }
end

#append_argObject



243
244
245
# File 'lib/commandline/application.rb', line 243

def append_arg
  CommandLine::OptionParser::GET_ARG_ARRAY
end

#expected_args(*exp_args) ⇒ Object

expected_args :cmd

Now, what to do if command line has more args than expected
app --app-option cmd --cmd-option arg-for-cmd


147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/commandline/application.rb', line 147

def expected_args(*exp_args)
  @arg_names = []
  case exp_args.size
  when 0 then @arg_arity = [0,0]
  when 1
    case exp_args[0]
    when Fixnum 
      v = exp_args[0]
      @arg_arity = [v,v]
    when Symbol
      @arg_names = exp_args
      @arg_arity = [1,1]
    when Array
      v = exp_args[0]
      __validate_arg_arity(v)
      @arg_arity = v
    else 
      raise(InvalidArgumentArityError, 
        "Args must be a Fixnum or Array: #{exp_args[0].inspect}.")
    end
  else
    @arg_names = exp_args
    size = exp_args.size
    @arg_arity = [size, size]
  end
end

#get_argObject Also known as: get_args



238
239
240
# File 'lib/commandline/application.rb', line 238

def get_arg
  CommandLine::OptionParser::GET_ARGS
end

#mainObject



286
287
288
289
290
# File 'lib/commandline/application.rb', line 286

def main
  #raise(MissingMainError, "Method #main must be defined in class #{@@child_class}.")
  @@child_class.class_eval %{ def main; end }
  #self.class_eval %{ def main; end }
end

#manObject Also known as: help



183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
# File 'lib/commandline/application.rb', line 183

def man
  require 'text/format'
  f = Text::Format.new
  f = Text::Format.new
  f.columns = @columns
  f.first_indent  = 4
  f.body_indent   = @body_indent
  f.tag_paragraph = false

  s = []
  s << ["NAME\n"]

  nm = "#{short_description}".empty? ? name : "#{name} - #{short_description}"
  s << f.format(nm)

  sn = "#{synopsis}".empty? ? "" : "#{name} #{synopsis}"
  unless sn.empty?
    s << "SYNOPSIS\n"
    s << f.format(sn)
  end

  dc = "#{long_description}"
  unless dc.empty?
    s << "DESCRIPTION\n"
    s << f.format(dc)
  end

  op = option_parser.to_s
  unless op.empty?
    s << option_parser.to_s
  end

  ar = "#{author}"
  unless ar.empty?
    s << "AUTHOR:  #{ar}"
  end


  ct = "COPYRIGHT (c) #{copyright}"
  unless "#{copyright}".empty?
    s << ct
  end

  s.join("\n")
end

#nameObject



230
231
232
# File 'lib/commandline/application.rb', line 230

def name
  File.basename(pathname)
end

#opt(name) ⇒ Object

Alternative for @option_data, but with symbols



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/commandline/application.rb', line 81

def opt(name)
  if name.kind_of?(Symbol)
    o1 = "--#{name}"
    o2 = "-#{name}"
    if @option_data.has_option?(o1)
      @option_data[o1]
    elsif @option_data.has_option?(o2)
      @option_data[o2]
    else
      raise UnknownOptionError, "Unknown options '#{o1}' and '#{o2}'."
    end
  else
    raise UnknownOptionError, "Unknown option '#{name}'." unless @option_data.has_option?(name)
    @option_data[name]
  end
end

#option(*args) ⇒ Object



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/commandline/application.rb', line 63

def option(*args)
  @options ||= []
  new_list = []
  args.each { |arg|
    new_list << 
    case arg
      when :help    then __help
      when :debug   then __debug
      when :verbose then __verbose
      when :version then __version
      else arg
    end
  }
  #p new_list
  @options << CommandLine::Option.new(*new_list)
end

#options(*opts) ⇒ Object



59
60
61
# File 'lib/commandline/application.rb', line 59

def options(*opts)
  opts.each { |opt| option(*[opt].flatten) }
end

#pathnameObject



234
235
236
# File 'lib/commandline/application.rb', line 234

def pathname
  @@appname
end

#requiredObject



247
248
249
# File 'lib/commandline/application.rb', line 247

def required
  CommandLine::OptionParser::OPT_NOT_FOUND_BUT_REQUIRED
end

#usageObject



179
180
181
# File 'lib/commandline/application.rb', line 179

def usage
  " Usage: #{name} #{synopsis}"
end

#use_replay(attribs = {}) ⇒ Object



174
175
176
177
# File 'lib/commandline/application.rb', line 174

def use_replay(attribs = {})
  @replay = true
  @replay_file = attribs[:replay_file] || @replay_file
end