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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 35b358de39fe1966b1f9ad95dd2e92b296e86f122439a0e69d9ca3367005e279
4
- data.tar.gz: 6080ae9b374490c0a39f70a440b65e80a711756646997e2589d059c627c9c12a
3
+ metadata.gz: 4cc3c3ef5b7b13876949b290b9d247c41778eaa38ea01432d5abac47b663478a
4
+ data.tar.gz: bc2c44f163f81d8b51545679e8f8280ac889fbb1f96a7898e5a65fa63d337d4a
5
5
  SHA512:
6
- metadata.gz: 7ac618dcd5a918827b7a57dd8b462352f8f464e844428eac60aabd5c3cca220a89898f3bd1f4abf7e5d8f8dbac9bfb46dfd22c8a1d4baf300683fc2ccbe24630
7
- data.tar.gz: a278e013c2a8e2ec673c4dc8ebb0198a8060e71164a5b3d3d68f48ec5f56009b1d2589f5f069855d640e7c76f50fe7493ea21e2118c331803e1e0ef003e83178
6
+ metadata.gz: d3c501335a899e4b14280cbb14b7f9e8a0d9ef28715760394d8f13ea837d0a5d3d9b91b9d07dcb42a747753e97a10c157ae5c5b20b95804b1746e0ebad7d88c9
7
+ data.tar.gz: 387c888158bbbebe127c6efde55c7bbb14436b7dcb8227cac38e2c9dac85e9a3d956c062e04b86101e28fa0bde4977fefadad4d1a643dd48d9218e5c04558ad2
data/.gitignore CHANGED
@@ -2,6 +2,7 @@
2
2
  /.yardoc
3
3
  /_yardoc/
4
4
  /doc/
5
+ /rdoc/
5
6
  /pkg/
6
7
  /spec/reports/
7
8
  /tmp/
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 `-a` and `--all` that are aliases
11
- for the same option, `--count` that may be given an integer argument but
12
- defaults to 42, `--file` that has a mandatory argument, and `-v` and
13
- `--verbose` that can be repeated to increase the verbosity level
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
- require 'shellopts'
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 that to
54
- parse the command line. If given a block, the block is called with a name/value
55
- pair for each option or command and return a list of the remaining non-option
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, val| ... } # Access options
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,val|
198
+ args = ShellOpts.process(USAGE, ARGV) { |opt, arg|
188
199
  case opt
189
200
  when '-a'; # Handle -a
190
201
  when 'cmd'
191
- opt.each { |opt, val|
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, val|
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 = val
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
- LINK='<link rel="stylesheet" type="text/css" href="stylesheet.css">'
3
+ set -e
4
4
 
5
- {
6
- echo $LINK
7
- pandoc README.md
8
- } >index.html
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 lib
14
+ # Generate rdoc
15
+ rdoc --output=rdoc --force-output lib
@@ -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 instead:
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
- usage = @shellopts&.usage || (defined?(USAGE) && USAGE ? Grammar.compile(PROGRAM, USAGE).usage : nil)
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
- # Usage string. Shorthand for +grammar.usage+
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
- $stderr.puts "Usage: #{program} #{usage}" if usage
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
@@ -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.
@@ -1,3 +1,3 @@
1
1
  module Shellopts
2
- VERSION = "0.9.3"
2
+ VERSION = "1.0.0"
3
3
  end
@@ -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", "~> 10.0"
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.9.3
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: 2019-10-30 00:00:00.000000000 Z
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: '10.0'
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: '10.0'
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
- rubyforge_project:
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