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.



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

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



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

def args
  @args
end

#argvObject (readonly)

Array of arguments. Initialized by #interpret



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

def argv
  @argv
end

#exceptionObject

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



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

def exception
  @exception
end

#fileObject (readonly)

File of source



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

def file
  @file
end

#floatObject

Floating options



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

def float
  @float
end

#grammarObject (readonly) Also known as: ast

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



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

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



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

def help
  @help
end

#msgoptsObject

Add message options (TODO)



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

def msgopts
  @msgopts
end

#nameObject (readonly)

Name of program. Defaults to the name of the executable



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

def name
  @name
end

#specObject (readonly)

Specification (String). Initialized by #compile



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

def spec
  @spec
end

#tokensObject (readonly)

Debug: Internal variables made public



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

def tokens
  @tokens
end

Class Method Details

.briefObject



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

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



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

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



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

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



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

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

.usageObject



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

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

Instance Method Details

#briefObject

Print brief help



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

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

#compile(spec) ⇒ Object

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



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

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



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

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



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

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

#find_spec_in_fileObject



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

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



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

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



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

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



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

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



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

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

#programObject

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



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

def program() @program end

#usageObject

Print usage



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

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

#versionObject

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



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

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