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, msgopts: false, float: true, exception: false) ⇒ ShellOpts

Returns a new instance of ShellOpts.



125
126
127
128
129
130
131
# File 'lib/shellopts.rb', line 125

def initialize(name: nil, help: true, version: true, msgopts: false, float: true, exception: false)
  @name = name || File.basename($PROGRAM_NAME)
  @help = help
  @use_version = version ? true : false
  @version = @use_version && @version != true ? @version : nil
  @msgopts, @float, @exception = msgopts, float, exception
end

Instance Attribute Details

#argsObject (readonly)

Array of remaining arguments. Initialized by #interpret



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

def args
  @args
end

#argvObject (readonly)

Array of arguments. Initialized by #interpret



84
85
86
# File 'lib/shellopts.rb', line 84

def argv
  @argv
end

#exceptionObject

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



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

def exception
  @exception
end

#fileObject (readonly)

File of source



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

def file
  @file
end

#floatObject

Floating options



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

def float
  @float
end

#grammarObject (readonly) Also known as: ast

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



87
88
89
# File 'lib/shellopts.rb', line 87

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



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

def help
  @help
end

#msgoptsObject

Add message options (TODO)



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

def msgopts
  @msgopts
end

#nameObject (readonly)

Name of program. Defaults to the name of the executable



78
79
80
# File 'lib/shellopts.rb', line 78

def name
  @name
end

#specObject (readonly)

Specification (String). Initialized by #compile



81
82
83
# File 'lib/shellopts.rb', line 81

def spec
  @spec
end

#tokensObject (readonly)

Debug: Internal variables made public



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

def tokens
  @tokens
end

Class Method Details

.briefObject



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

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



267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
# File 'lib/shellopts.rb', line 267

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



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

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



185
186
187
188
189
# File 'lib/shellopts.rb', line 185

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

.usageObject



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

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

Instance Method Details

#briefObject

Print brief help



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

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

#compile(spec) ⇒ Object

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



135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/shellopts.rb', line 135

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)
    ast.add_version_option if @use_version
    ast.add_help_options if @help
    @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



196
197
198
199
200
201
202
203
204
205
206
# File 'lib/shellopts.rb', line 196

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



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

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

#find_spec_in_fileObject



300
301
302
# File 'lib/shellopts.rb', line 300

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



313
314
315
316
317
318
319
320
321
322
# File 'lib/shellopts.rb', line 313

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



152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/shellopts.rb', line 152

def interpret(argv)
  handle_exceptions { 
    @argv = argv.dup
    @program, @args = Interpreter.interpret(grammar, argv, float: float, exception: exception)
    if @program.version?
      puts version 
      exit
    elsif @program.help?
      if @program[:help].name == "-h"
        ShellOpts.brief
      else
        ShellOpts.help
      end
      exit
    end
  }
  self
end

#lookup(name) ⇒ Object



304
305
306
307
308
309
310
311
# File 'lib/shellopts.rb', line 304

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



174
175
176
177
178
# File 'lib/shellopts.rb', line 174

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

#programObject

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



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

def program() @program end

#usageObject

Print usage



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

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

#versionObject

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



100
101
102
103
104
105
106
# File 'lib/shellopts.rb', line 100

def version
  return @version if @version
  exe = caller.find { |line| line =~ /`<top \(required\)>'$/ }&.sub(/:.*/, "")
  file = Dir.glob(File.dirname(exe) + "/../lib/*/version.rb").first
  @version = IO.read(file).sub(/^.*VERSION\s*=\s*"(.*?)".*$/m, '\1') or
      raise ArgumentError, "ShellOpts needs an explicit version"
end