Class: ShellOpts::ShellOpts

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

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name: nil, help: true, version: true, quiet: nil, verbose: nil, debug: nil, version_number: nil, float: true, exception: false) ⇒ ShellOpts

Returns a new instance of ShellOpts.



139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/shellopts.rb', line 139

def initialize(name: nil, 
    # Options
    help: true, 
    version: true, 
    quiet: nil,
    verbose: nil,
    debug: nil,

    # Version number (usually detected)
    version_number: nil,

    # Floating options
    float: true,

    # Let exceptions through
    exception: false
  )
    
  @name = name || File.basename($PROGRAM_NAME)
  @help = help
  @version = version || (version.nil? && !version_number.nil?)
  @quiet = quiet
  @verbose = verbose
  @debug = debug
  @version_number = version_number || find_version_number
  @float = float
  @exception = exception
end

Instance Attribute Details

#argsObject (readonly)

Array of remaining arguments. Initialized by #interpret



105
106
107
# File 'lib/shellopts.rb', line 105

def args
  @args
end

#argvObject (readonly)

Array of arguments. Initialized by #interpret



95
96
97
# File 'lib/shellopts.rb', line 95

def argv
  @argv
end

#debugObject (readonly)

Automatically add a –debug option if true



120
121
122
# File 'lib/shellopts.rb', line 120

def debug
  @debug
end

#exceptionObject

True if ShellOpts lets exceptions through instead of writing an error message and exit



130
131
132
# File 'lib/shellopts.rb', line 130

def exception
  @exception
end

#fileObject (readonly)

File of source



133
134
135
# File 'lib/shellopts.rb', line 133

def file
  @file
end

#floatObject

Floating options



126
127
128
# File 'lib/shellopts.rb', line 126

def float
  @float
end

#grammarObject (readonly) Also known as: ast

Grammar. Grammar::Program object. Initialized by #compile



98
99
100
# File 'lib/shellopts.rb', line 98

def grammar
  @grammar
end

#help(subject = nil, clear: true) ⇒ Object (readonly)

Print help for the given subject or the full documentation if subject is nil. Clears the screen beforehand if :clear is true



108
109
110
# File 'lib/shellopts.rb', line 108

def help
  @help
end

#nameObject (readonly)

Name of program. Defaults to the name of the executable



89
90
91
# File 'lib/shellopts.rb', line 89

def name
  @name
end

#quietObject (readonly)

Automatically add a -q and a –quiet option if true



114
115
116
# File 'lib/shellopts.rb', line 114

def quiet
  @quiet
end

#specObject (readonly)

Specification (String). Initialized by #compile



92
93
94
# File 'lib/shellopts.rb', line 92

def spec
  @spec
end

#tokensObject (readonly)

Debug: Internal variables made public



136
137
138
# File 'lib/shellopts.rb', line 136

def tokens
  @tokens
end

#verboseObject (readonly)

Automatically add a -v and a –verbose option if true



117
118
119
# File 'lib/shellopts.rb', line 117

def verbose
  @verbose
end

#versionObject (readonly)

Version of client program. If not nil, a –version option is added to the program



111
112
113
# File 'lib/shellopts.rb', line 111

def version
  @version
end

#version_numberObject (readonly)

Version number (this is usually detected dynamically)



123
124
125
# File 'lib/shellopts.rb', line 123

def version_number
  @version_number
end

Class Method Details

.briefObject



298
# File 'lib/shellopts.rb', line 298

def self.brief() ::ShellOpts.instance.brief end

.find_spec_in_text(text, spec, oneline) ⇒ Object

Find line and char index of spec in text. Returns [nil, nil] if not found



338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
# File 'lib/shellopts.rb', line 338

def self.find_spec_in_text(text, spec, oneline)
  text_lines = text.split("\n")
  spec_lines = spec.split("\n")
  spec_lines.pop_while { |line| line =~ /^\s*$/ }

  if oneline
    line_i = nil
    char_i = nil
    char_z = 0

    (0 ... text_lines.size).each { |text_i|
      curr_char_i, curr_char_z = 
          LCS.find_longest_common_substring_index(text_lines[text_i], spec_lines.first.strip)
      if curr_char_z > char_z
        line_i = text_i
        char_i = curr_char_i
        char_z = curr_char_z
      end
    }
    line_i ? [line_i, char_i] : [nil, nil]
  else
    spec_string = spec_lines.first.strip
    line_i = (0 ... text_lines.size - spec_lines.size + 1).find { |text_i|
      (0 ... spec_lines.size).all? { |spec_i|
        compare_lines(text_lines[text_i + spec_i], spec_lines[spec_i])
      }
    } or return [nil, nil]
    char_i, char_z = 
        LCS.find_longest_common_substring_index(text_lines[line_i], spec_lines.first.strip)
    [line_i, char_i || 0]
  end
end

.help(subject = nil) ⇒ Object



299
# File 'lib/shellopts.rb', line 299

def self.help(subject = nil) ::ShellOpts.instance.help(subject) end

.process(spec, argv, **opts) ⇒ Object

Create a ShellOpts object and sets the global instance, then process the spec and arguments. Returns a tuple of a ShellOpts::Program with the options and subcommands and a ShellOpts::Args object with the remaining arguments



249
250
251
252
253
# File 'lib/shellopts.rb', line 249

def self.process(spec, argv, **opts)
  ::ShellOpts.instance = shellopts = ShellOpts.new(**opts)
  shellopts.process(spec, argv)
  [shellopts.program, shellopts.args]
end

.usageObject



297
# File 'lib/shellopts.rb', line 297

def self.usage() ::ShellOpts.instance.usage end

Instance Method Details

#briefObject

Print brief help



286
# File 'lib/shellopts.rb', line 286

def brief() Formatter.brief(@grammar) end

#compile(spec) ⇒ Object

Compile source and return grammar object. Also sets #spec and #grammar. Returns the grammar



170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
# File 'lib/shellopts.rb', line 170

def compile(spec)
  handle_exceptions {
    @oneline = spec.index("\n").nil?
    @spec = spec.sub(/^\s*\n/, "")
    @file = find_caller_file
    @tokens = Lexer.lex(name, @spec, @oneline)
    ast = Parser.parse(tokens)

    help_spec = (@help == true ? "-h,help" : @help)
    version_spec = (@version == true ? "--version" : @version)
    quiet_spec = (@quiet == true ? "-q,quiet" : @quiet)
    verbose_spec = (@verbose == true ? "+v,verbose" : @verbose)
    debug_spec = (@debug == true ? "--debug" : @debug)

    @quiet_option = 
        ast.inject_option(quiet_spec, "Quiet", "Do not write anything to standard output") if @quiet
    @verbose_option = 
        ast.inject_option(verbose_spec, "Increase verbosity", "Write verbose output") if @verbose
    @debug_option = 
        ast.inject_option(debug_spec, "Write debug information") if @debug
    @help_option = 
        ast.inject_option(help_spec, "Write short or long help") { |option|
          short_option = option.short_names.first 
          long_option = option.long_names.first
          [
            short_option && "#{short_option} prints a brief help text",
            long_option && "#{long_option} prints a longer man-style description of the command"
          ].compact.join(", ")
        } if @help
    @version_option = 
        ast.inject_option(version_spec, "Write version number and exit") if @version

    @grammar = Analyzer.analyze(ast)
  }
  self
end

#error(subject = nil, message) ⇒ Object

Write short usage and error message to standard error and terminate program with status 1

#error is supposed to be used when the user made an error and the usage is written to help correcting the error



260
261
262
263
264
265
266
267
268
269
270
# File 'lib/shellopts.rb', line 260

def error(subject = nil, message)
  $stderr.puts "#{name}: #{message}"
  saved = $stdout
  begin
    $stdout = $stderr
    Formatter.usage(grammar)
    exit 1
  ensure
    $stdout = saved
  end
end

#failure(message) ⇒ Object

Write error message to standard error and terminate program with status 1

#failure doesn’t print the program usage because is supposed to be used when the user specified the correct arguments but something else went wrong during processing



277
278
279
280
# File 'lib/shellopts.rb', line 277

def failure(message)
  $stderr.puts "#{name}: #{message}"
  exit 1
end

#find_spec_in_fileObject



371
372
373
# File 'lib/shellopts.rb', line 371

def find_spec_in_file
  self.class.find_spec_in_text(IO.read(@file), @spec, @oneline).map { |i| (i || 0) + 1 }
end

#find_subject(obj) ⇒ Object



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

def find_subject(obj)
  case obj
    when String; lookup(obj)
    when Ast::Command; Command.grammar(obj) # FIXME
    when Grammar::Command; obj
    when NilClass; grammar
  else
    raise Internal, "Illegal object: #{obj.class}"
  end
end

#interpret(argv) ⇒ Object

Use grammar to interpret arguments. Return a ShellOpts::Program and ShellOpts::Args tuple



210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
# File 'lib/shellopts.rb', line 210

def interpret(argv)
  handle_exceptions { 
    @argv = argv.dup
    @program, @args = Interpreter.interpret(grammar, argv, float: float, exception: exception)

    # Process standard options (that may have been renamed)
    if @program.__send__(:"#{@help_option.ident}?")
      if @program[:help].name =~ /^--/
        ShellOpts.help
      else
        ShellOpts.brief
      end
      exit
    elsif @program.__send__(:"#{@version_option.ident}?")
      puts version_number
      exit
    else
      @program.__quiet__ = @program.__send__(:"#{@quiet_option.ident}?") if @quiet
      @program.__verbose__ = @program.__send__(:"#{@verbose_option.ident}") if @verbose
      @program.__debug__ = @program.__send__(:"#{@debug_option.ident}?") if @debug
    end
  }
  self
end

#lookup(name) ⇒ Object



375
376
377
378
379
380
381
382
# File 'lib/shellopts.rb', line 375

def lookup(name)
  a = name.split(".")
  cmd = grammar
  while element = a.shift
    cmd = cmd.commands[element]
  end
  cmd
end

#process(spec, argv) ⇒ Object

Compile spec and interpret argv. Returns a tuple of a ShellOpts::Program and ShellOpts::Args object



238
239
240
241
242
# File 'lib/shellopts.rb', line 238

def process(spec, argv)
  compile(spec)
  interpret(argv)
  self
end

#programObject

Resulting ShellOpts::Program object containing options and optional subcommand. Initialized by #interpret



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

def program() @program end

#usageObject

Print usage



283
# File 'lib/shellopts.rb', line 283

def usage() Formatter.usage(@grammar) end