shellopts 0.9.3 → 1.0.0
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/.gitignore +1 -0
- data/README.md +49 -17
- data/TODO +17 -1
- data/bin/mkdoc +11 -6
- data/lib/shellopts.rb +33 -10
- data/lib/shellopts/compiler.rb +1 -1
- data/lib/shellopts/version.rb +1 -1
- data/shellopts.gemspec +1 -1
- metadata +8 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4cc3c3ef5b7b13876949b290b9d247c41778eaa38ea01432d5abac47b663478a
|
4
|
+
data.tar.gz: bc2c44f163f81d8b51545679e8f8280ac889fbb1f96a7898e5a65fa63d337d4a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d3c501335a899e4b14280cbb14b7f9e8a0d9ef28715760394d8f13ea837d0a5d3d9b91b9d07dcb42a747753e97a10c157ae5c5b20b95804b1746e0ebad7d88c9
|
7
|
+
data.tar.gz: 387c888158bbbebe127c6efde55c7bbb14436b7dcb8227cac38e2c9dac85e9a3d956c062e04b86101e28fa0bde4977fefadad4d1a643dd48d9218e5c04558ad2
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -1,20 +1,19 @@
|
|
1
1
|
# Shellopts
|
2
2
|
|
3
|
-
`ShellOpts` is a simple command line parsing libray that covers most modern use
|
3
|
+
`ShellOpts` is a simple Linux command line parsing libray that covers most modern use
|
4
4
|
cases incl. sub-commands. Options and commands are specified using a
|
5
5
|
getopt(1)-like string that is interpreted by the library to process the command
|
6
6
|
line
|
7
7
|
|
8
8
|
## Usage
|
9
9
|
|
10
|
-
The following program accepts
|
11
|
-
|
12
|
-
|
13
|
-
|
10
|
+
The following program accepts the options -a or --all, --count, --file, and -v
|
11
|
+
or --verbose. It expects `--count` to have an optional integer argument,
|
12
|
+
`--file` to have a mandatory argument, and allows `-v` and `--verbose` to be
|
13
|
+
repeated:
|
14
14
|
|
15
15
|
```ruby
|
16
|
-
|
17
|
-
|
16
|
+
|
18
17
|
# Define options
|
19
18
|
USAGE = "a,all count=#? file= +v,verbose -- FILE..."
|
20
19
|
|
@@ -36,7 +35,7 @@ args = ShellOpts.process(USAGE, ARGV) do |opt, arg|
|
|
36
35
|
end
|
37
36
|
end
|
38
37
|
|
39
|
-
# Process remaining arguments
|
38
|
+
# Process remaining command line arguments
|
40
39
|
args.each { |arg| ... }
|
41
40
|
```
|
42
41
|
|
@@ -50,10 +49,10 @@ error
|
|
50
49
|
|
51
50
|
## Processing
|
52
51
|
|
53
|
-
`ShellOpts.process` compiles a usage definition string into a grammar and use
|
54
|
-
parse the command line. If given a block, the block is called with a
|
55
|
-
pair for each option or command and return a list of the remaining
|
56
|
-
arguments
|
52
|
+
`ShellOpts.process` compiles a usage definition string into a grammar and use
|
53
|
+
that to parse the command line. If given a block, the block is called with a
|
54
|
+
name/value pair for each option or command and return a list of the remaining
|
55
|
+
non-option arguments
|
57
56
|
|
58
57
|
```ruby
|
59
58
|
args = ShellOpts.process(USAGE, ARGV) do |opt, arg|
|
@@ -74,7 +73,7 @@ line at a time and to inspect the grammar and AST
|
|
74
73
|
|
75
74
|
```ruby
|
76
75
|
shellopts = ShellOpts.process(USAGE, ARGV) # Returns a ShellOpts::ShellOpts object
|
77
|
-
shellopts.each { |opt,
|
76
|
+
shellopts.each { |opt, arg| ... } # Access options
|
78
77
|
args = shellopts.args # Access remaining arguments
|
79
78
|
shellopts.error "Something went wrong" # Emit an error message and exit
|
80
79
|
```
|
@@ -101,6 +100,18 @@ An option is defined by a list of comma-separated names optionally prefixed by a
|
|
101
100
|
[ "+" ] name-list [ "=" [ "#" | "$" ] [ label ] [ "?" ] ]
|
102
101
|
```
|
103
102
|
|
103
|
+
#### Flags
|
104
|
+
|
105
|
+
There are the following flags:
|
106
|
+
|
107
|
+
|Flag|Effect|
|
108
|
+
|---|---|
|
109
|
+
|+|Repeated option (prefix)|
|
110
|
+
|=|Argument. Mandatory unless `?` is also used|
|
111
|
+
|#|Integer argument|
|
112
|
+
|$|Floating point argument|
|
113
|
+
|?|Optional argument|
|
114
|
+
|
104
115
|
#### Repeated options
|
105
116
|
|
106
117
|
Options are unique by default and the user will get an error if an option is
|
@@ -184,11 +195,11 @@ sub-commands) to the command:
|
|
184
195
|
```ruby
|
185
196
|
USAGE = "a cmd! b c"
|
186
197
|
|
187
|
-
args = ShellOpts.process(USAGE, ARGV) { |opt,
|
198
|
+
args = ShellOpts.process(USAGE, ARGV) { |opt, arg|
|
188
199
|
case opt
|
189
200
|
when '-a'; # Handle -a
|
190
201
|
when 'cmd'
|
191
|
-
|
202
|
+
arg.each { |opt, arg|
|
192
203
|
case opt
|
193
204
|
when '-b'; # Handle -b
|
194
205
|
when '-c'; # Handle -c
|
@@ -260,6 +271,27 @@ The methods are defined as instance methods on `ShellOpts::ShellOpts` and as
|
|
260
271
|
class methods on `ShellOpts`. They can also be included in the global scope by
|
261
272
|
`include ShellOpts::Utils`
|
262
273
|
|
274
|
+
#### Usage string
|
275
|
+
|
276
|
+
The error handling methods prints a prettified version of the usage string
|
277
|
+
given to `ShellOpts.parse`. The usage string can be overridden by assigning to
|
278
|
+
`ShellOpts.usage`. A typical use case is when you want to split the usage
|
279
|
+
description over multiple lines:
|
280
|
+
|
281
|
+
```ruby
|
282
|
+
|
283
|
+
USAGE="long-and-complex-usage-string"
|
284
|
+
ShellOpts.usage = <<~EOD
|
285
|
+
usage explanation
|
286
|
+
split over
|
287
|
+
multiple lines
|
288
|
+
EOD
|
289
|
+
```
|
290
|
+
|
291
|
+
Note that this only affects the module-level `ShellOpts.error` method and not
|
292
|
+
object-level `ShellOpts::ShellOpts#error` method. This is considered a bug and
|
293
|
+
will fixed at some point
|
294
|
+
|
263
295
|
## Example
|
264
296
|
|
265
297
|
The rm(1) command could be implemented like this
|
@@ -287,12 +319,12 @@ preserve_root = true
|
|
287
319
|
verbose = false
|
288
320
|
|
289
321
|
# Process command line
|
290
|
-
args = ShellOpts.process(USAGE, ARGV) { |opt,
|
322
|
+
args = ShellOpts.process(USAGE, ARGV) { |opt, arg|
|
291
323
|
case opt
|
292
324
|
when '-f', '--force'; force = true
|
293
325
|
when '-i'; prompt = true
|
294
326
|
when '-I'; prompt_once = true
|
295
|
-
when '--interactive'; interactive = true; interactive_when =
|
327
|
+
when '--interactive'; interactive = true; interactive_when = arg
|
296
328
|
when '-r', '-R', '--recursive'; recursive = true
|
297
329
|
when '-d', '--dir'; remove_empty_dirs = true
|
298
330
|
when '--one-file-system'; one_file_system = true
|
data/TODO
CHANGED
@@ -1,12 +1,27 @@
|
|
1
1
|
|
2
2
|
TODO
|
3
|
+
o Also allow assignment to usage string for ShellOpts::ShellOpts objects
|
4
|
+
o Create a ShellOpts.args method? It would be useful when processing commands:
|
5
|
+
case opt
|
6
|
+
when "command"
|
7
|
+
call_command_method(ShellOpts.args[1], ShellOpts.args[2])
|
8
|
+
end
|
9
|
+
ShellOpts.args would be a shorthand for ShellOpts.shellopts.args
|
10
|
+
Another option would be to create an argument-processing method:
|
11
|
+
shellopts.argv(2) -> call error if not exactly two arguments else return elements
|
12
|
+
|
13
|
+
o Check on return value from #process block to see if all options was handled:
|
14
|
+
case opt
|
15
|
+
when '-v'; verbose = true # Return value 'true' is ok
|
16
|
+
# Unhandled option means return value is nil
|
17
|
+
end
|
3
18
|
o Consolidate some of the 3 variations of #error and #fail
|
4
19
|
o Add a option flag for solitary options (--help)
|
5
20
|
o Make a #to_yaml
|
6
21
|
o Make an official dump method for debug
|
7
22
|
o Make a note that all options are processed at once and not as-you-go
|
8
23
|
o Test that arguments with spaces work
|
9
|
-
o Long version usage strings
|
24
|
+
o Long version usage strings (major release)
|
10
25
|
o Doc: Example of processing of sub-commands and sub-sub-commands
|
11
26
|
|
12
27
|
+ More tests
|
@@ -55,6 +70,7 @@ LATER
|
|
55
70
|
o Escape of separator in lists
|
56
71
|
o Handle output of subcommand usage like "cmd1 cmd1.cmd2 cmd2"
|
57
72
|
o Command-specific arguments: clone! o,opt ++ ARG1 ARG2...
|
73
|
+
o Hostname and email as basic types
|
58
74
|
|
59
75
|
ON TO_H BRANCH
|
60
76
|
ShellOpts.process(usage, argv) { |opt,val| ... } => args
|
data/bin/mkdoc
CHANGED
@@ -1,10 +1,15 @@
|
|
1
1
|
#!/usr/bin/bash
|
2
2
|
|
3
|
-
|
3
|
+
set -e
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
5
|
+
# Generate github-like page
|
6
|
+
(
|
7
|
+
cd doc
|
8
|
+
{
|
9
|
+
echo '<link rel="stylesheet" type="text/css" href="stylesheet.css">'
|
10
|
+
pandoc ../README.md
|
11
|
+
} >index.html
|
12
|
+
)
|
9
13
|
|
10
|
-
rdoc
|
14
|
+
# Generate rdoc
|
15
|
+
rdoc --output=rdoc --force-output lib
|
data/lib/shellopts.rb
CHANGED
@@ -12,6 +12,18 @@ require 'shellopts/utils.rb'
|
|
12
12
|
# name of the program
|
13
13
|
#
|
14
14
|
module ShellOpts
|
15
|
+
# Return the hidden +ShellOpts::ShellOpts+ object (see .process)
|
16
|
+
def self.shellopts()
|
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
|
+
|
15
27
|
# Process command line options and arguments. #process takes a usage string
|
16
28
|
# defining the options and the array of command line arguments to be parsed
|
17
29
|
# as arguments
|
@@ -72,14 +84,16 @@ module ShellOpts
|
|
72
84
|
# #process saves a hidden {ShellOpts::ShellOpts} class variable used by the
|
73
85
|
# class methods #error and #fail. Call #reset to clear the global object if
|
74
86
|
# you really need to parse more than one command line. Alternatively you can
|
75
|
-
# create +ShellOpts::ShellOpts+ objects yourself and use the object methods
|
76
|
-
# #error and #fail
|
87
|
+
# create +ShellOpts::ShellOpts+ objects yourself and also use the object methods
|
88
|
+
# #error and #fail:
|
77
89
|
#
|
78
90
|
# shellopts = ShellOpts::ShellOpts.new(USAGE, ARGS)
|
79
91
|
# shellopts.each { |name, value| ... }
|
80
92
|
# shellopts.args.each { |arg| ... }
|
81
93
|
# shellopts.error("Something went wrong")
|
82
94
|
#
|
95
|
+
# Use #shellopts to get the hidden +ShellOpts::ShellOpts+ object
|
96
|
+
#
|
83
97
|
def self.process(usage, argv, program_name: PROGRAM, &block)
|
84
98
|
if !block_given?
|
85
99
|
ShellOpts.new(usage, argv, program_name: program_name)
|
@@ -95,15 +109,20 @@ module ShellOpts
|
|
95
109
|
# another command line
|
96
110
|
def self.reset()
|
97
111
|
@shellopts = nil
|
112
|
+
@usage = nil
|
98
113
|
end
|
99
114
|
|
100
115
|
# Print error message and usage string and exit with status 1. It use the
|
101
116
|
# current ShellOpts object if defined. This method should be called in
|
102
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
|
103
122
|
def self.error(*msgs)
|
104
123
|
program = @shellopts&.program_name || PROGRAM
|
105
|
-
|
106
|
-
emit_and_exit(program, usage, *msgs)
|
124
|
+
usage_string = usage || (defined?(USAGE) && USAGE ? Grammar.compile(PROGRAM, USAGE).usage : nil)
|
125
|
+
emit_and_exit(program, @usage.nil?, usage_string, *msgs)
|
107
126
|
end
|
108
127
|
|
109
128
|
# Print error message and exit with status 1. It use the current ShellOpts
|
@@ -111,7 +130,7 @@ module ShellOpts
|
|
111
130
|
# user-errors but system errors (like disk full)
|
112
131
|
def self.fail(*msgs)
|
113
132
|
program = @shellopts&.program_name || PROGRAM
|
114
|
-
emit_and_exit(program, nil, *msgs)
|
133
|
+
emit_and_exit(program, false, nil, *msgs)
|
115
134
|
end
|
116
135
|
|
117
136
|
# The compilation object
|
@@ -119,7 +138,7 @@ module ShellOpts
|
|
119
138
|
# Name of program
|
120
139
|
attr_reader :program_name
|
121
140
|
|
122
|
-
#
|
141
|
+
# Prettified usage string used by #error and #fail. Shorthand for +grammar.usage+
|
123
142
|
def usage() @grammar.usage end
|
124
143
|
|
125
144
|
# The grammar compiled from the usage string. If #ast is defined, it's
|
@@ -172,13 +191,13 @@ module ShellOpts
|
|
172
191
|
# should be called in response to user-errors (eg. specifying an illegal
|
173
192
|
# option)
|
174
193
|
def error(*msgs)
|
175
|
-
::ShellOpts.emit_and_exit(program_name, usage, msgs)
|
194
|
+
::ShellOpts.emit_and_exit(program_name, true, usage, msgs)
|
176
195
|
end
|
177
196
|
|
178
197
|
# Print error message and exit with status 1. This method should not be
|
179
198
|
# called in response to user-errors but system errors (like disk full)
|
180
199
|
def fail(*msgs)
|
181
|
-
::ShellOpts.emit_and_exit(program_name, nil, msgs)
|
200
|
+
::ShellOpts.emit_and_exit(program_name, false, nil, msgs)
|
182
201
|
end
|
183
202
|
end
|
184
203
|
|
@@ -199,9 +218,13 @@ module ShellOpts
|
|
199
218
|
private
|
200
219
|
@shellopts = nil
|
201
220
|
|
202
|
-
def self.emit_and_exit(program, usage, *msgs)
|
221
|
+
def self.emit_and_exit(program, use_usage, usage, *msgs)
|
203
222
|
$stderr.puts "#{program}: #{msgs.join}"
|
204
|
-
|
223
|
+
if use_usage
|
224
|
+
$stderr.puts "Usage: #{program} #{usage}" if usage
|
225
|
+
else
|
226
|
+
$stderr.puts usage if usage
|
227
|
+
end
|
205
228
|
exit 1
|
206
229
|
end
|
207
230
|
end
|
data/lib/shellopts/compiler.rb
CHANGED
@@ -27,7 +27,7 @@ module ShellOpts
|
|
27
27
|
|
28
28
|
# Initialize a Compiler object. source is the option definition string
|
29
29
|
def initialize(program_name, source)
|
30
|
-
@program_name, @tokens = program_name, source.split(/\s+/)
|
30
|
+
@program_name, @tokens = program_name, source.split(/\s+/).reject(&:empty?)
|
31
31
|
|
32
32
|
# @commands_by_path is an hash from command-path to Command or Program
|
33
33
|
# object. The top level Program object has nil as its path.
|
data/lib/shellopts/version.rb
CHANGED
data/shellopts.gemspec
CHANGED
@@ -33,7 +33,7 @@ Gem::Specification.new do |spec|
|
|
33
33
|
spec.require_paths = ["lib"]
|
34
34
|
|
35
35
|
spec.add_development_dependency "bundler", "~> 1.16"
|
36
|
-
spec.add_development_dependency "rake", "
|
36
|
+
spec.add_development_dependency "rake", ">= 12.3.3"
|
37
37
|
spec.add_development_dependency "rspec", "~> 3.0"
|
38
38
|
spec.add_development_dependency "indented_io"
|
39
39
|
spec.add_development_dependency "simplecov"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: shellopts
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Claus Rasmussen
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-07-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -28,16 +28,16 @@ dependencies:
|
|
28
28
|
name: rake
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - "
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
33
|
+
version: 12.3.3
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - "
|
38
|
+
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
40
|
+
version: 12.3.3
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rspec
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -103,6 +103,7 @@ files:
|
|
103
103
|
- bin/console
|
104
104
|
- bin/mkdoc
|
105
105
|
- bin/setup
|
106
|
+
- doc/stylesheet.css
|
106
107
|
- lib/ext/array.rb
|
107
108
|
- lib/shellopts.rb
|
108
109
|
- lib/shellopts/ast/command.rb
|
@@ -137,8 +138,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
137
138
|
- !ruby/object:Gem::Version
|
138
139
|
version: '0'
|
139
140
|
requirements: []
|
140
|
-
|
141
|
-
rubygems_version: 2.7.6
|
141
|
+
rubygems_version: 3.0.8
|
142
142
|
signing_key:
|
143
143
|
specification_version: 4
|
144
144
|
summary: Parse command line options and arguments
|