shellopts 2.4.0 → 2.4.2

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: 68428ade0dd8ec48fa85a8d43362743d6dc5595d9f4af3347d0fa402eaf0e7bb
4
- data.tar.gz: 51404fbc53ddbacb0b7a4552777e3de7c8abce1d508782f6c901da4b2cd332ba
3
+ metadata.gz: 93dc09edd7bd318c85134a93f7c45352f4b2e550663c1a80a029d4fcb1d8ab16
4
+ data.tar.gz: c9e0b523c15f372d5dd988d2fd804c01539e90f0ee02ef68d284b4a1340e7c9c
5
5
  SHA512:
6
- metadata.gz: ca5499680c53cc74388102b130eb9a7dfc7dc53ba7556615991d93d882ddc4993e1854891d78bbd97ccb4d37e8d493fb1c4e199cc4361f96ddb2e55c536f4cc7
7
- data.tar.gz: d75bef2e9b154a0332ed55a272341072654db8c615c5e4c9a27a94823efd6bae354fe32ca14679c92553af3e0e4f69f50ca3aeb675c5149d8394195f1f5a9f4a
6
+ metadata.gz: 520132f149801fb6d2bb8e325e38cdb6bf81a01c9d881f88165b02550dfae4a0f95706bcbd558e34b2232015b327c87f779cbf2cdef6d0355640db6e87acaaea
7
+ data.tar.gz: 7a26fe6f1a8501f9a52103224ea4cdfa2d95441e6253af6ecfcb71f09f8f57c84bae7734079fa5ab1c47756f859ac0eaddbac3c350ce1d8a6b845be011ecedc3
data/README.md CHANGED
@@ -193,7 +193,6 @@ indicate that the option can be repeated
193
193
  --gamma=ARG? @ --gamma, takes an optional argument
194
194
  ```
195
195
 
196
-
197
196
  An option argument has a name and a type. The type can be specified as '#'
198
197
  (integer), '$' (float), or as a comma-separated list of allowed values. It can
199
198
  also be defined by a keyword that expects a file or directory argument:
@@ -202,15 +201,17 @@ also be defined by a keyword that expects a file or directory argument:
202
201
  | --------- | ---- |
203
202
  | FILE | A file if present or in an existing directory if not |
204
203
  | DIR | A directory if present or in an existing directory if not |
205
- | PATH | Either a FILE or DIR |
204
+ | PATH | FILE or DIR |
206
205
  | EFILE | An existing file |
207
206
  | EDIR | An existing directory |
208
- | EPATH | Either an EFILE or EDIR |
207
+ | EPATH | EFILE or EDIR |
209
208
  | NFILE | A new file |
210
209
  | NDIR | A new directory |
211
- | NPATH | A new file or directory |
210
+ | NPATH | NFILE or NDIR |
211
+
212
212
 
213
- By default the option name is inferred from the type but it can be specified explicitly by separating it from the type with a ':'. Examples:
213
+ By default the option name is inferred from the type but it can be specified
214
+ explicitly by separating it from the type with a ':'. Examples:
214
215
 
215
216
  ```
216
217
  -a=# @ -a takes an integer argument
@@ -244,7 +245,7 @@ In single-line format, subcommands are specified by prefixing the supercommand's
244
245
 
245
246
  ## Example
246
247
 
247
- The rm(1) command could be specified like this:
248
+ The standard rm(1) command could be specified like this:
248
249
 
249
250
  ```ruby
250
251
 
data/TODO CHANGED
@@ -1,4 +1,14 @@
1
1
 
2
+ o Remove IFILE and OFILE. Alternatively document them
3
+ o Document special-case file argument '-'
4
+ o Accept both strings and symbols for commands (maybe... ruins the global namespace in #[])
5
+ o ShellOpts#command -> Full path. Eg. ':command.subcommand!'
6
+ ShellOpts#commands -> Array of commands. Eg. [:command!, :subcommand!]
7
+ o Exclude -h, -v, and -q from short description and only include them in the
8
+ detailed option description
9
+ o Output handling: mesg, verb, ... should record eols. error and failure should
10
+ use that to end the current line with a newline if necessary
11
+ o Strings from ShellOpts are frozen. Is that what we want?
2
12
  o Assigner methods for options
3
13
  o Make opts[] point at the main object
4
14
  opts[].subcommand is the direct subcommand of main
@@ -26,7 +26,7 @@ module ShellOpts
26
26
  def to_s() name end
27
27
 
28
28
  protected
29
- # it is important that #set_message return false
29
+ # Note that it is important that #set_message return false
30
30
  def set_message(msg)
31
31
  @message = msg
32
32
  false
@@ -60,6 +60,18 @@ module ShellOpts
60
60
  class FileArgument < ArgumentType
61
61
  attr_reader :kind
62
62
 
63
+ def subject # Used in error messages
64
+ @subject ||=
65
+ case kind
66
+ when :file, :efile; "regular file"
67
+ when :nfile, :ifile, :ofile; "file"
68
+ when :dir, :edir, :ndir; "directory"
69
+ when :path, :epath, :npath; "path"
70
+ else
71
+ raise ArgumentError
72
+ end
73
+ end
74
+
63
75
  def initialize(kind)
64
76
  constrain kind, :file, :dir, :path, :efile, :edir, :epath, :nfile, :ndir, :npath, :ifile, :ofile
65
77
  @kind = kind
@@ -70,19 +82,18 @@ module ShellOpts
70
82
  if literal == '-' && [:ifile, :ofile].include?(kind)
71
83
  true
72
84
 
73
- # Special-case standard I/O files
85
+ # Special-case standard I/O files. These files have read-write (rw)
86
+ # filesystem permissions so they need to be handled individually
74
87
  elsif %w(/dev/stdin /dev/stdout /dev/stderr /dev/null).include?(literal)
75
88
  case kind
76
89
  when :file, :path, :efile, :epath, :nfile, :npath
77
90
  true
78
91
  when :ifile
79
- %w(/dev/stdin /dev/null).include? literal or
80
- set_message "Can't read #{literal}"
92
+ %w(/dev/stdin /dev/null).include? literal or set_message "Can't read #{literal}"
81
93
  when :ofile
82
- %w(/dev/stdout /dev/stderr /dev/null).include?(literal) or
83
- set_message "Can't write to #{literal}"
94
+ %w(/dev/stdout /dev/stderr /dev/null).include?(literal) or set_message "Can't write to #{literal}"
84
95
  when :dir, :edir, :ndir
85
- set_message "Expected file, got directory #{literal}"
96
+ set_message "#{literal} is not a directory"
86
97
  else
87
98
  raise ArgumentError, "Unhandled kind: #{kind.inspect}"
88
99
  end
@@ -128,50 +139,44 @@ module ShellOpts
128
139
 
129
140
  protected
130
141
  def match_path(name, literal, kind, method, mode)
131
- subject =
132
- case kind
133
- when :file, :efile, :nfile, :ifile, :ofile; "file"
134
- when :dir, :edir, :ndir; "directory"
135
- when :path, :epath, :npath; "path"
136
- else
137
- raise ArgumentError
138
- end
139
-
140
- # file exists and is the rigth type?
142
+ # file exists and the method returned true
141
143
  if File.send(method, literal)
142
144
  if mode == :new
143
- set_message "#{subject.capitalize} already exists in #{name}: #{literal}"
145
+ set_message "Won't overwrite #{literal}"
144
146
  elsif kind == :path || kind == :epath
145
147
  if File.file?(literal) || File.directory?(literal)
146
148
  true
147
149
  else
148
- set_message "Expected file or directory as #{name} argument: #{literal}"
150
+ set_message "#{literal} is not a file or a directory"
149
151
  end
150
152
  elsif (kind == :ifile || kind == :ofile) && File.directory?(literal)
151
- set_message "File expected, got directory: #{literal}"
153
+ set_message "#{literal} is not a device file"
152
154
  else
153
155
  true
154
156
  end
155
157
 
156
- # file exists but not the right type?
158
+ # file exists but the method returned false
157
159
  elsif File.exist?(literal)
158
160
  if kind == :ifile
159
161
  set_message "Can't read #{literal}"
160
162
  elsif kind == :ofile
161
163
  set_message "Can't write to #{literal}"
162
164
  elsif mode == :new
163
- set_message "#{subject.capitalize} already exists - #{literal}"
165
+ set_message "Won't overwrite #{literal}"
164
166
  else
165
- set_message "Expected #{subject} as #{name} argument: #{literal}"
167
+ set_message "#{literal} is not a #{subject}"
166
168
  end
167
169
 
168
170
  # file does not exist
169
171
  else
170
172
  if [:default, :new].include? mode
171
- if File.exist?(File.dirname(literal)) || kind == :ofile
172
- true
173
+ dir = File.dirname(literal)
174
+ if !File.directory?(dir)
175
+ set_message "Illegal path - #{literal}"
176
+ elsif !File.writable?(dir)
177
+ set_message "Can't create #{subject} #{literal}"
173
178
  else
174
- set_message "Illegal path in #{name}: #{literal}"
179
+ true
175
180
  end
176
181
  else
177
182
  set_message "Can't find #{literal}"
@@ -183,7 +188,7 @@ module ShellOpts
183
188
  class EnumArgument < ArgumentType
184
189
  attr_reader :values
185
190
  def initialize(values) @values = values.dup end
186
- def match?(name, literal) value?(literal) or set_message "Illegal value in #{name}: '#{literal}'" end
191
+ def match?(name, literal) value?(literal) or set_message "Illegal value - #{literal}" end
187
192
  def value?(value) @values.include?(value) end
188
193
  end
189
194
  end
@@ -1,3 +1,3 @@
1
1
  module ShellOpts
2
- VERSION = "2.4.0"
2
+ VERSION = "2.4.2"
3
3
  end
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: 2.4.0
4
+ version: 2.4.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Claus Rasmussen
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-01-31 00:00:00.000000000 Z
11
+ date: 2024-03-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: forward_to