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, file: $PROGRAM_NAME, help: true, version: true, silent: nil, quiet: nil, verbose: nil, debug: nil, version_number: nil, float: true, exception: false) ⇒ ShellOpts

Returns a new instance of ShellOpts.



143
144
145
146
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
173
# File 'lib/shellopts.rb', line 143

def initialize(name: nil, file: $PROGRAM_NAME,
    # Options
    help: true,
    version: true,
    silent: nil,
    quiet: nil,
    verbose: nil,
    debug: nil,

    # Version number (auto-detected for gem packages)
    version_number: nil,

    # Floating options
    float: true,

    # Let exceptions through
    exception: false
  )

  @name = name || File.basename(file)
  @file = file
  @help = help
  @version_number = version_number || (version && find_version_number)
  @version = !@version_number.nil? && version
  @silent = silent
  @quiet = quiet
  @verbose = verbose
  @debug = debug
  @float = float
  @exception = exception
end

Instance Attribute Details

#argsObject (readonly)

Array of remaining arguments. Initialized by #interpret



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

def args
  @args
end

#argvObject (readonly)

Array of arguments. Initialized by #interpret



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

def argv
  @argv
end

#debugObject (readonly)

Automatically add a –debug option if true



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

def debug
  @debug
end

#exceptionObject

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



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

def exception
  @exception
end

#fileObject (readonly)

Executable. Default $PROGRAM_NAME



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

def file
  @file
end

#floatObject

Floating options



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

def float
  @float
end

#grammarObject (readonly) Also known as: ast

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



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

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



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

def help
  @help
end

#nameObject (readonly)

Name of program. Defaults to the name of the executable



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

def name
  @name
end

#quietObject (readonly)

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



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

def quiet
  @quiet
end

#silentObject (readonly)

Automatically add a -s and a –silent option if true



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

def silent
  @silent
end

#specObject (readonly)

Specification (String). Initialized by #compile



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

def spec
  @spec
end

#tokensObject (readonly)

Debug: Internal variables made public



140
141
142
# File 'lib/shellopts.rb', line 140

def tokens
  @tokens
end

#verboseObject (readonly)

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



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

def verbose
  @verbose
end

#versionObject (readonly)

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



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

def version
  @version
end

#version_numberObject (readonly)

Version number (this is usually detected dynamically)



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

def version_number
  @version_number
end

Class Method Details

.briefObject



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

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



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
370
371
372
373
374
# File 'lib/shellopts.rb', line 343

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



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

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



260
261
262
263
264
# File 'lib/shellopts.rb', line 260

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

.usageObject



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

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

Instance Method Details

#briefObject

Print brief help



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

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

#compile(spec) ⇒ Object

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



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
206
207
208
209
210
211
212
213
214
# File 'lib/shellopts.rb', line 177

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

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

    @silent_option =
        ast.inject_option(silent_spec, "Silent", "Do not write anything to standard output/error") if @silent
    @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



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

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



288
289
290
291
# File 'lib/shellopts.rb', line 288

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

#find_spec_in_fileObject



376
377
378
# File 'lib/shellopts.rb', line 376

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



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

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



219
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
# File 'lib/shellopts.rb', line 219

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 or be deactivated
    if @help && @program.__send__(:"#{@help_option.ident}?")
      if @program[:help].name =~ /^--/
        ShellOpts.help
      else
        ShellOpts.brief
      end
      exit
    elsif @version && @program.__send__(:"#{@version_option.ident}?")
      puts version_number
      exit
    else
      # Assign standard options. The targets doesn't change if the option is renamed
      @program.__silent__ = @program.__send__(:"#{@silent_option.ident}?") if @silent
      @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



380
381
382
383
384
385
386
387
# File 'lib/shellopts.rb', line 380

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



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

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

#programObject

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



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

def program() @program end

#usageObject

Print usage



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

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