shellopts 1.0.0 → 2.0.0.pre.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.ruby-version +1 -1
- data/README.md +10 -0
- data/TODO +23 -1
- data/lib/shellopts.rb +209 -199
- data/lib/shellopts/args.rb +48 -0
- data/lib/shellopts/ast/command.rb +6 -6
- data/lib/shellopts/ast/node.rb +1 -1
- data/lib/shellopts/compiler.rb +25 -27
- data/lib/shellopts/generator.rb +15 -0
- data/lib/shellopts/grammar/command.rb +35 -18
- data/lib/shellopts/grammar/node.rb +11 -3
- data/lib/shellopts/grammar/option.rb +12 -1
- data/lib/shellopts/grammar/program.rb +4 -4
- data/lib/shellopts/idr.rb +236 -0
- data/lib/shellopts/option_struct.rb +148 -0
- data/lib/shellopts/parser.rb +11 -11
- data/lib/shellopts/shellopts.rb +116 -0
- data/lib/shellopts/version.rb +1 -1
- metadata +9 -5
- data/lib/shellopts/utils.rb +0 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c7ac01e4c6feb1897f74056a36c74db0cf78802d13437908aa18631ead7c97c5
|
4
|
+
data.tar.gz: 5443ff421ceb38fcf07aaf87ae7b3740fd804679ccdec377fb195c688c50b57b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0d8180a2acc6dac8e234567db9409cd9d01cebe8d4551f6cfa24488ce5d1fb848ec8b42b21d40e7d8f6990bf61e4dc8d482370e9cb4ceb409e3f18fdb70a877c
|
7
|
+
data.tar.gz: f07d22ab7976d5efa317356fc796f8bbe20b8c6a8ee609eb24106f60e8e56d9874181dcab4e873268b1aea5a1aa27ffd1253bb10499052edd995894c6eec9d88
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
ruby-2.
|
1
|
+
ruby-2.6.6
|
data/README.md
CHANGED
@@ -378,6 +378,16 @@ release a new version, update the version number in `version.rb`, and then run
|
|
378
378
|
git commits and tags, and push the `.gem` file to
|
379
379
|
[rubygems.org](https://rubygems.org).
|
380
380
|
|
381
|
+
## Implementation
|
382
|
+
|
383
|
+
FIXME
|
384
|
+
# ShellOpts is a library for parsing command line options and commands. It
|
385
|
+
# consists of the interface module {ShellOpts}, the implementation class
|
386
|
+
# {ShellOpts::ShellOpts} and the representation classes
|
387
|
+
# {ShellOpts::OptionsHash} and {ShellOpts::OptionsStruct}.
|
388
|
+
# {ShellOpts::Messenger} is used for error messages
|
389
|
+
|
390
|
+
|
381
391
|
## Contributing
|
382
392
|
|
383
393
|
Bug reports and pull requests are welcome on GitHub at
|
data/TODO
CHANGED
@@ -1,5 +1,23 @@
|
|
1
1
|
|
2
2
|
TODO
|
3
|
+
o Rethink #error and #fail <- The use-case is one-file ruby scripts. Idea: Only use in main exe file?
|
4
|
+
o Create exceptions: UserError SystemFail and allow them to be used instead of #error and #fail
|
5
|
+
? Remove ! from OptionStruct#subcommand return value. We know we're
|
6
|
+
processing commands so there is no need to have a distinct name and it
|
7
|
+
feels a lot more intuitive without it
|
8
|
+
o Add validation block to ShellOpts class methods
|
9
|
+
o Get rid of key_name. Define #name on Grammar::Node instead
|
10
|
+
o Define #name to the string name of the option/command without prefixed '--'
|
11
|
+
for options. This can cause collisions but they can be avoided using aliases
|
12
|
+
o Clean-up
|
13
|
+
o Grammar::options -> Grammar::option_multihash
|
14
|
+
o Clean-up identifiers etc.
|
15
|
+
o Un-multi-izing Grammar::option_multihash and turn it into a regular hash from key to option
|
16
|
+
o subcommand vs. command consistency
|
17
|
+
o Implement ObjectStruct#key! and ObjectStruct#value! (?)
|
18
|
+
o Allow command_alias == nil to suppress the method
|
19
|
+
o Raise on non-existing names/keys. Only return nil for declared names/keys that are not present
|
20
|
+
o Use hash_tree
|
3
21
|
o Also allow assignment to usage string for ShellOpts::ShellOpts objects
|
4
22
|
o Create a ShellOpts.args method? It would be useful when processing commands:
|
5
23
|
case opt
|
@@ -9,7 +27,10 @@ TODO
|
|
9
27
|
ShellOpts.args would be a shorthand for ShellOpts.shellopts.args
|
10
28
|
Another option would be to create an argument-processing method:
|
11
29
|
shellopts.argv(2) -> call error if not exactly two arguments else return elements
|
12
|
-
|
30
|
+
o Add a ShellOpts.option method:
|
31
|
+
file = ShellOpts.option("--file")
|
32
|
+
This will only work for options on the outermost level... maybe:
|
33
|
+
file = ShellOpts.option("load! --file")
|
13
34
|
o Check on return value from #process block to see if all options was handled:
|
14
35
|
case opt
|
15
36
|
when '-v'; verbose = true # Return value 'true' is ok
|
@@ -24,6 +45,7 @@ TODO
|
|
24
45
|
o Long version usage strings (major release)
|
25
46
|
o Doc: Example of processing of sub-commands and sub-sub-commands
|
26
47
|
|
48
|
+
+ Add a 'mandatory' argument to #subcommand
|
27
49
|
+ More tests
|
28
50
|
+ More doc
|
29
51
|
+ Implement value-name-before-flags rule
|
data/lib/shellopts.rb
CHANGED
@@ -2,231 +2,241 @@ require "shellopts/version"
|
|
2
2
|
|
3
3
|
require 'shellopts/compiler.rb'
|
4
4
|
require 'shellopts/parser.rb'
|
5
|
-
require 'shellopts/
|
5
|
+
require 'shellopts/generator.rb'
|
6
|
+
require 'shellopts/option_struct.rb'
|
7
|
+
require 'shellopts/main.rb'
|
6
8
|
|
7
|
-
#
|
8
|
-
#
|
9
|
-
|
9
|
+
# Name of program. Defined as the basename of the program file
|
10
|
+
#PROGRAM = File.basename($PROGRAM_NAME)
|
11
|
+
|
12
|
+
# ShellOpts main Module
|
13
|
+
#
|
14
|
+
# This module contains methods to process command line options and arguments.
|
15
|
+
# ShellOpts keeps a reference in ShellOpts.shellopts to the result of the last
|
16
|
+
# command that was processed through its interface and use it as the implicit
|
17
|
+
# object of many of its methods. This matches the typical use case where only
|
18
|
+
# one command line is ever processed and makes it possible to create class
|
19
|
+
# methods that knows about the command like #error and #fail
|
20
|
+
#
|
21
|
+
# For example; the following process and convert a command line into a struct
|
22
|
+
# representation and also sets ShellOpts.shellopts object so that the #error
|
23
|
+
# method can print a relevant spec string:
|
24
|
+
#
|
25
|
+
# USAGE = "a,all f,file=FILE -- ARG1 ARG2"
|
26
|
+
# opts, args = ShellOpts.as_struct(USAGE, ARGV)
|
27
|
+
# File.exist?(opts.file) or error "Can't find #{opts.file}"
|
28
|
+
#
|
29
|
+
# The command line is processed through one of the methods #process, #as_array,
|
30
|
+
# #as_hash, or #as_struct that returns a [data, args] tuple. The data type
|
31
|
+
# depends on the method: #process yields a Idr object that internally serves as
|
32
|
+
# the base for the #as_array and #as_hash and #as_struct that converts it into
|
33
|
+
# an Array, Hash, or ShellOpts::OptionStruct object. For example:
|
34
|
+
#
|
35
|
+
# USAGE = "..."
|
36
|
+
# ShellOpts.process(USAGE, ARGV)
|
37
|
+
# program, args = ShellOpts.as_program(USAGE, ARGV)
|
38
|
+
# array, args = ShellOpts.as_array(USAGE, ARGV)
|
39
|
+
# hash, args = ShellOpts.as_hash(USAGE, ARGV)
|
40
|
+
# struct, args = ShellOpts.as_struct(USAGE, ARGV)
|
41
|
+
#
|
42
|
+
# +args+ is a ShellOpts::Argv object containing the the remaning command line
|
43
|
+
# arguments. Argv is derived from Array
|
44
|
+
#
|
45
|
+
# ShellOpts can raise the exception CompilerError is there is an error in the
|
46
|
+
# USAGE string. If there is an error in the user supplied command line, #error
|
47
|
+
# is called instead and the program terminates with exit code 1. ShellOpts
|
48
|
+
# raises ConversionError is there is a name collision when converting to the
|
49
|
+
# hash or struct representations. Note that CompilerError and ConversionError
|
50
|
+
# are caused by misuse of the library and the problem should be corrected by
|
51
|
+
# the developer
|
10
52
|
#
|
11
|
-
# ShellOpts
|
53
|
+
# ShellOpts injects the constant PROGRAM into the global scope. It contains the
|
12
54
|
# name of the program
|
13
55
|
#
|
56
|
+
# INCLUDING SHELLOPTS
|
57
|
+
#
|
58
|
+
# ShellOpts can optionally be included in your shell application main file but
|
59
|
+
# it is not supposed to be included anywhere else
|
60
|
+
#
|
61
|
+
# Some behind the scenes magic happen if you include the ShellOpts module in your
|
62
|
+
# main exe file
|
63
|
+
#
|
14
64
|
module ShellOpts
|
15
|
-
|
16
|
-
|
17
|
-
@shellopts
|
18
|
-
end
|
19
|
-
|
20
|
-
# Prettified usage string used by #error and #fail. Default is +usage+ of
|
21
|
-
# the current +ShellOpts::ShellOpts+ object
|
22
|
-
def self.usage() @usage || @shellopts&.usage end
|
23
|
-
|
24
|
-
# Set the usage string
|
25
|
-
def self.usage=(usage) @usage = usage end
|
26
|
-
|
27
|
-
# Process command line options and arguments. #process takes a usage string
|
28
|
-
# defining the options and the array of command line arguments to be parsed
|
29
|
-
# as arguments
|
30
|
-
#
|
31
|
-
# If called with a block, the block is called with name and value of each
|
32
|
-
# option or command and #process returns a list of remaining command line
|
33
|
-
# arguments. If called without a block a ShellOpts::ShellOpts object is
|
34
|
-
# returned
|
35
|
-
#
|
36
|
-
# The value of an option is its argument, the value of a command is an array
|
37
|
-
# of name/value pairs of options and subcommands. Option values are converted
|
38
|
-
# to the target type (String, Integer, Float) if specified
|
39
|
-
#
|
40
|
-
# Example
|
41
|
-
#
|
42
|
-
# # Define options
|
43
|
-
# USAGE = 'a,all g,global +v,verbose h,help save! snapshot f,file=FILE h,help'
|
44
|
-
#
|
45
|
-
# # Define defaults
|
46
|
-
# all = false
|
47
|
-
# global = false
|
48
|
-
# verbose = 0
|
49
|
-
# save = false
|
50
|
-
# snapshot = false
|
51
|
-
# file = nil
|
52
|
-
#
|
53
|
-
# # Process options
|
54
|
-
# argv = ShellOpts.process(USAGE, ARGV) do |name, value|
|
55
|
-
# case name
|
56
|
-
# when '-a', '--all'; all = true
|
57
|
-
# when '-g', '--global'; global = value
|
58
|
-
# when '-v', '--verbose'; verbose += 1
|
59
|
-
# when '-h', '--help'; print_help(); exit(0)
|
60
|
-
# when 'save'
|
61
|
-
# save = true
|
62
|
-
# value.each do |name, value|
|
63
|
-
# case name
|
64
|
-
# when '--snapshot'; snapshot = true
|
65
|
-
# when '-f', '--file'; file = value
|
66
|
-
# when '-h', '--help'; print_save_help(); exit(0)
|
67
|
-
# end
|
68
|
-
# end
|
69
|
-
# else
|
70
|
-
# raise "Not a user error. The developer forgot or misspelled an option"
|
71
|
-
# end
|
72
|
-
# end
|
73
|
-
#
|
74
|
-
# # Process remaining arguments
|
75
|
-
# argv.each { |arg| ... }
|
76
|
-
#
|
77
|
-
# If an error is encountered while compiling the usage string, a
|
78
|
-
# +ShellOpts::Compiler+ exception is raised. If the error happens while
|
79
|
-
# parsing the command line arguments, the program prints an error message and
|
80
|
-
# exits with status 1. Failed assertions raise a +ShellOpts::InternalError+
|
81
|
-
# exception
|
82
|
-
#
|
83
|
-
# Note that you can't process more than one command line at a time because
|
84
|
-
# #process saves a hidden {ShellOpts::ShellOpts} class variable used by the
|
85
|
-
# class methods #error and #fail. Call #reset to clear the global object if
|
86
|
-
# you really need to parse more than one command line. Alternatively you can
|
87
|
-
# create +ShellOpts::ShellOpts+ objects yourself and also use the object methods
|
88
|
-
# #error and #fail:
|
89
|
-
#
|
90
|
-
# shellopts = ShellOpts::ShellOpts.new(USAGE, ARGS)
|
91
|
-
# shellopts.each { |name, value| ... }
|
92
|
-
# shellopts.args.each { |arg| ... }
|
93
|
-
# shellopts.error("Something went wrong")
|
94
|
-
#
|
95
|
-
# Use #shellopts to get the hidden +ShellOpts::ShellOpts+ object
|
96
|
-
#
|
97
|
-
def self.process(usage, argv, program_name: PROGRAM, &block)
|
98
|
-
if !block_given?
|
99
|
-
ShellOpts.new(usage, argv, program_name: program_name)
|
100
|
-
else
|
101
|
-
@shellopts.nil? or raise InternalError, "ShellOpts class variable already initialized"
|
102
|
-
@shellopts = ShellOpts.new(usage, argv, program_name: program_name)
|
103
|
-
@shellopts.each(&block)
|
104
|
-
@shellopts.args
|
105
|
-
end
|
65
|
+
def self.default_name()
|
66
|
+
@default_name || defined?(PROGRAM) ? PROGRAM : File.basename($0)
|
106
67
|
end
|
107
68
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
@shellopts = nil
|
112
|
-
@usage = nil
|
113
|
-
end
|
114
|
-
|
115
|
-
# Print error message and usage string and exit with status 1. It use the
|
116
|
-
# current ShellOpts object if defined. This method should be called in
|
117
|
-
# response to user-errors (eg. specifying an illegal option)
|
118
|
-
#
|
119
|
-
# If there is no current ShellOpts object +error+ will look for USAGE to make
|
120
|
-
# it possible to use +error+ before the command line is processed and also as
|
121
|
-
# a stand-alone error reporting method
|
122
|
-
def self.error(*msgs)
|
123
|
-
program = @shellopts&.program_name || PROGRAM
|
124
|
-
usage_string = usage || (defined?(USAGE) && USAGE ? Grammar.compile(PROGRAM, USAGE).usage : nil)
|
125
|
-
emit_and_exit(program, @usage.nil?, usage_string, *msgs)
|
126
|
-
end
|
127
|
-
|
128
|
-
# Print error message and exit with status 1. It use the current ShellOpts
|
129
|
-
# object if defined. This method should not be called in response to
|
130
|
-
# user-errors but system errors (like disk full)
|
131
|
-
def self.fail(*msgs)
|
132
|
-
program = @shellopts&.program_name || PROGRAM
|
133
|
-
emit_and_exit(program, false, nil, *msgs)
|
134
|
-
end
|
135
|
-
|
136
|
-
# The compilation object
|
137
|
-
class ShellOpts
|
138
|
-
# Name of program
|
139
|
-
attr_reader :program_name
|
140
|
-
|
141
|
-
# Prettified usage string used by #error and #fail. Shorthand for +grammar.usage+
|
142
|
-
def usage() @grammar.usage end
|
143
|
-
|
144
|
-
# The grammar compiled from the usage string. If #ast is defined, it's
|
145
|
-
# equal to ast.grammar
|
146
|
-
attr_reader :grammar
|
147
|
-
|
148
|
-
# The AST resulting from parsing the command line arguments
|
149
|
-
attr_reader :ast
|
150
|
-
|
151
|
-
# List of remaining non-option command line arguments. Shorthand for ast.arguments
|
152
|
-
def args() @ast.arguments end
|
153
|
-
|
154
|
-
# Compile a usage string into a grammar and use that to parse command line
|
155
|
-
# arguments
|
156
|
-
#
|
157
|
-
# +usage+ is the usage string, and +argv+ the command line (typically the
|
158
|
-
# global ARGV array). +program_name+ is the name of the program and is
|
159
|
-
# used in error messages. It defaults to the basename of the program
|
160
|
-
#
|
161
|
-
# Errors in the usage string raise a CompilerError exception. Errors in the
|
162
|
-
# argv arguments terminates the program with an error message
|
163
|
-
def initialize(usage, argv, program_name: File.basename($0))
|
164
|
-
@program_name = program_name
|
165
|
-
begin
|
166
|
-
@grammar = Grammar.compile(program_name, usage)
|
167
|
-
@ast = Ast.parse(@grammar, argv)
|
168
|
-
rescue Grammar::Compiler::Error => ex
|
169
|
-
raise CompilerError.new(5, ex.message)
|
170
|
-
rescue Ast::Parser::Error => ex
|
171
|
-
error(ex.message)
|
172
|
-
end
|
173
|
-
end
|
69
|
+
def self.default_name=(name)
|
70
|
+
@default_name = name
|
71
|
+
end
|
174
72
|
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
end
|
73
|
+
def self.default_usage()
|
74
|
+
@default_usage || defined?(USAGE) ? USAGE : nil
|
75
|
+
end
|
179
76
|
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
if block_given?
|
184
|
-
to_a.each { |*args| yield(*args) }
|
185
|
-
else
|
186
|
-
to_a # FIXME: Iterator
|
187
|
-
end
|
188
|
-
end
|
77
|
+
def self.default_usage=(usage)
|
78
|
+
@default_usage = usage
|
79
|
+
end
|
189
80
|
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
def error(*msgs)
|
194
|
-
::ShellOpts.emit_and_exit(program_name, true, usage, msgs)
|
195
|
-
end
|
81
|
+
def self.default_key_type()
|
82
|
+
@default_key_type || ::ShellOpts::DEFAULT_KEY_TYPE
|
83
|
+
end
|
196
84
|
|
197
|
-
|
198
|
-
|
199
|
-
def fail(*msgs)
|
200
|
-
::ShellOpts.emit_and_exit(program_name, false, nil, msgs)
|
201
|
-
end
|
85
|
+
def self.default_key_type=(type)
|
86
|
+
@default_key_type = type
|
202
87
|
end
|
203
88
|
|
204
89
|
# Base class for ShellOpts exceptions
|
205
90
|
class Error < RuntimeError; end
|
206
91
|
|
207
|
-
# Raised when
|
92
|
+
# Raised when a syntax error is detected in the spec string
|
208
93
|
class CompilerError < Error
|
209
|
-
def initialize(start,
|
210
|
-
super(
|
94
|
+
def initialize(start, usage)
|
95
|
+
super(usage)
|
211
96
|
set_backtrace(caller(start))
|
212
97
|
end
|
213
98
|
end
|
214
99
|
|
100
|
+
# Raised when an error is detected in the command line
|
101
|
+
class ParserError < Error; end
|
102
|
+
|
103
|
+
# Raised when the command line error is caused by the user. It is raised by
|
104
|
+
# the parser but can also be used by the application if the command line
|
105
|
+
# fails a semantic check
|
106
|
+
class UserError < ParserError; end
|
107
|
+
|
108
|
+
# Raised when the error is caused by a failed assumption about the system. It
|
109
|
+
# is not raised by the ShellOpts library as it only concerns itself with
|
110
|
+
# command line syntax but can be used by the application to report a failure
|
111
|
+
# through ShellOpts#fail method when the ShellOpts module is included
|
112
|
+
class SystemFail < Error; end
|
113
|
+
|
114
|
+
# Raised when an error is detected during conversion from the Idr to array,
|
115
|
+
# hash, or struct
|
116
|
+
class ConversionError < Error; end
|
117
|
+
|
215
118
|
# Raised when an internal error is detected
|
216
119
|
class InternalError < Error; end
|
217
120
|
|
218
|
-
|
219
|
-
@shellopts
|
121
|
+
# The current compilation object. It is set by #process
|
122
|
+
def self.shellopts() @shellopts end
|
220
123
|
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
124
|
+
# Name of program
|
125
|
+
def program_name() shellopts!.name end
|
126
|
+
def program_name=(name) shellopts!.name = name end
|
127
|
+
|
128
|
+
# Usage string
|
129
|
+
def usage() shellopts!.spec end
|
130
|
+
def usage=(spec) shellopts!.spec = spec end
|
131
|
+
|
132
|
+
# Process command line, set current shellopts object, and return it.
|
133
|
+
# Remaining arguments from the command line can be accessed through
|
134
|
+
# +shellopts.args+
|
135
|
+
def self.process(spec, argv, name: ::ShellOpts.default_name, usage: ::ShellOpts.default_usage)
|
136
|
+
@shellopts.nil? or reset
|
137
|
+
@shellopts = ShellOpts.new(spec, argv, name: name, usage: usage)
|
138
|
+
end
|
139
|
+
|
140
|
+
# Process command line, set current shellopts object, and return a
|
141
|
+
# [Idr::Program, argv] tuple. Automatically includes the ShellOpts module
|
142
|
+
# if called from the main Ruby object (ie. your executable)
|
143
|
+
def self.as_program(spec, argv, name: ::ShellOpts.default_name, usage: ::ShellOpts.default_usage)
|
144
|
+
Main.main.send(:include, ::ShellOpts) if caller.last =~ Main::CALLER_RE
|
145
|
+
process(spec, argv, name: name, usage: usage)
|
146
|
+
[shellopts.idr, shellopts.args]
|
147
|
+
end
|
148
|
+
|
149
|
+
# Process command line, set current shellopts object, and return a [array,
|
150
|
+
# argv] tuple. Automatically includes the ShellOpts module if called from the
|
151
|
+
# main Ruby object (ie. your executable)
|
152
|
+
def self.as_array(spec, argv, name: ::ShellOpts.default_name, usage: ::ShellOpts.default_usage)
|
153
|
+
Main.main.send(:include, ::ShellOpts) if caller.last =~ Main::CALLER_RE
|
154
|
+
process(spec, argv, name: name, usage: usage)
|
155
|
+
[shellopts.to_a, shellopts.args]
|
156
|
+
end
|
157
|
+
|
158
|
+
# Process command line, set current shellopts object, and return a [hash,
|
159
|
+
# argv] tuple. Automatically includes the ShellOpts module if called from the
|
160
|
+
# main Ruby object (ie. your executable)
|
161
|
+
def self.as_hash(
|
162
|
+
spec, argv,
|
163
|
+
name: ::ShellOpts.default_name, usage: ::ShellOpts.default_usage,
|
164
|
+
key_type: ::ShellOpts.default_key_type,
|
165
|
+
aliases: {})
|
166
|
+
Main.main.send(:include, ::ShellOpts) if caller.last =~ Main::CALLER_RE
|
167
|
+
process(spec, argv, name: name, usage: usage)
|
168
|
+
[shellopts.to_h(key_type: key_type, aliases: aliases), shellopts.args]
|
169
|
+
end
|
170
|
+
|
171
|
+
# Process command line, set current shellopts object, and return a [struct,
|
172
|
+
# argv] tuple. Automatically includes the ShellOpts module if called from the
|
173
|
+
# main Ruby object (ie. your executable)
|
174
|
+
def self.as_struct(
|
175
|
+
spec, argv,
|
176
|
+
name: ::ShellOpts.default_name, usage: ::ShellOpts.default_usage,
|
177
|
+
aliases: {})
|
178
|
+
Main.main.send(:include, ::ShellOpts) if caller.last =~ Main::CALLER_RE
|
179
|
+
process(spec, argv, name: name, usage: usage)
|
180
|
+
[shellopts.to_struct(aliases: aliases), shellopts.args]
|
181
|
+
end
|
182
|
+
|
183
|
+
# Process command line, set current shellopts object, and then iterate
|
184
|
+
# options and commands as an array. Returns an enumerator to the array
|
185
|
+
# representation of the current shellopts object if not given a block
|
186
|
+
# argument. Automatically includes the ShellOpts module if called from the
|
187
|
+
# main Ruby object (ie. your executable)
|
188
|
+
def self.each(spec = nil, argv = nil, name: ::ShellOpts.default_name, usage: ::ShellOpts.default_usage, &block)
|
189
|
+
Main.main.send(:include, ::ShellOpts) if caller.last =~ Main::CALLER_RE
|
190
|
+
process(spec, argv, name: name, usage: usage)
|
191
|
+
shellopts.each(&block)
|
192
|
+
end
|
193
|
+
|
194
|
+
# Print error usage and spec string and exit with status 1. This method
|
195
|
+
# should be called in response to user-errors (eg. specifying an illegal
|
196
|
+
# option)
|
197
|
+
def self.error(*msgs, exit: true)
|
198
|
+
shellopts!.error(msgs, exit: exit)
|
199
|
+
end
|
200
|
+
|
201
|
+
# Print error usage and exit with status 1. This method should not be
|
202
|
+
# called in response to system errors (eg. disk full)
|
203
|
+
def self.fail(*msgs, exit: true)
|
204
|
+
shellopts!.fail(*msgs, exit: exit)
|
205
|
+
end
|
206
|
+
|
207
|
+
def self.included(base)
|
208
|
+
# base.equal?(Object) is only true when included in main (we hope)
|
209
|
+
if !@is_included_in_main && base.equal?(Object)
|
210
|
+
@is_included_in_main = true
|
211
|
+
at_exit do
|
212
|
+
case $!
|
213
|
+
when ShellOpts::UserError
|
214
|
+
::ShellOpts.error($!.message, exit: false)
|
215
|
+
exit!(1)
|
216
|
+
when ShellOpts::SystemFail
|
217
|
+
::ShellOpts.fail($!.message)
|
218
|
+
exit!(1)
|
219
|
+
end
|
220
|
+
end
|
227
221
|
end
|
228
|
-
|
222
|
+
super
|
223
|
+
end
|
224
|
+
|
225
|
+
private
|
226
|
+
# Default default key type
|
227
|
+
DEFAULT_KEY_TYPE = :name
|
228
|
+
|
229
|
+
# Reset state variables
|
230
|
+
def self.reset()
|
231
|
+
@shellopts = nil
|
229
232
|
end
|
233
|
+
|
234
|
+
# (shorthand) Raise an InternalError if shellopts is nil. Return shellopts
|
235
|
+
def self.shellopts!
|
236
|
+
::ShellOpts.shellopts or raise UserError, "No ShellOpts.shellopts object"
|
237
|
+
end
|
238
|
+
|
239
|
+
@shellopts = nil
|
240
|
+
@is_included_in_main = false
|
230
241
|
end
|
231
242
|
|
232
|
-
PROGRAM = File.basename($PROGRAM_NAME)
|