shellopts 2.0.21 → 2.0.24

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: a2e9d4e1a3ad5eca9fd21a3bad085a252ff7b6921ec889e4da404c989304bf9c
4
- data.tar.gz: fa9f67483fcfb303e76f8b6b2282948f117f2497b734b6fdd6ca6f91a29b3d96
3
+ metadata.gz: fff67178855b9164ee7e432d2074040c4c7d8249231e83f27ce1b6a345dbad4c
4
+ data.tar.gz: 9c9816fda6e7361e310080fd9d150d0c9e7419de929e42a552cfa8dd4c9d7740
5
5
  SHA512:
6
- metadata.gz: 29c8d07d8fdf63c8b4e04d031ec1c7b779ed3410265103514ade6f8e5fd2b45d3f5b804de934722e0f49d11119e14e463507b7d63c0c6d2059018b75fec281a6
7
- data.tar.gz: 2f7cad783bd109dc264574cb6ed76ec1f947d1591e15267bf071ebd243b5b646039bdec9fc2dac7983db38463fb6eddb328d920beaa35052647e647f9020ed49
6
+ metadata.gz: 4c6f20dd0afbf624cc671ffc2468977861a3533c5f447acadf3082cdf577dbb9547a96e5ece6cc78fc9f93045dea99581d37f6f7e521e2bcc43261bec07cf653
7
+ data.tar.gz: 773fde53cd201e58c8d860d19861c5d33c2ce0214d5c5986cc9ce5d91d5b920e33f54398a2b6e9185c7300504ce82169c94e1d92854d68ce76c8cf9050b035f8
@@ -18,7 +18,8 @@ module ShellOpts
18
18
  # if type is an IntegerArgument
19
19
  def value?(value) true end
20
20
 
21
- # Convert value to Ruby type
21
+ # Convert value to Ruby type. This method can also be used to translate
22
+ # special keywords
22
23
  def convert(value) value end
23
24
 
24
25
  # String representation. Equal to #name
@@ -60,69 +61,120 @@ module ShellOpts
60
61
  attr_reader :kind
61
62
 
62
63
  def initialize(kind)
63
- constrain kind, :file, :dir, :path, :efile, :edir, :epath, :nfile, :ndir, :npath
64
+ constrain kind, :file, :dir, :path, :efile, :edir, :epath, :nfile, :ndir, :npath, :ifile, :ofile
64
65
  @kind = kind
65
66
  end
66
67
 
67
68
  def match?(name, literal)
68
- case kind
69
- when :file; match_path(name, literal, kind, :file?, :default)
70
- when :dir; match_path(name, literal, kind, :directory?, :default)
71
- when :path; match_path(name, literal, kind, :exist?, :default)
72
-
73
- when :efile; match_path(name, literal, kind, :file?, :exist)
74
- when :edir; match_path(name, literal, kind, :directory?, :exist)
75
- when :epath; match_path(name, literal, kind, :exist?, :exist)
76
-
77
- when :nfile; match_path(name, literal, kind, :file?, :new)
78
- when :ndir; match_path(name, literal, kind, :directory?, :new)
79
- when :npath; match_path(name, literal, kind, :exist?, :new)
69
+ # Special-case '-' keyword
70
+ if literal == '-' && [:ifile, :ofile].include?(kind)
71
+ true
72
+
73
+ # Special-case standard I/O files
74
+ elsif %w(/dev/stdin /dev/stdout /dev/stderr /dev/null).include?(literal)
75
+ case kind
76
+ when :file, :path, :efile, :epath, :nfile, :npath
77
+ true
78
+ when :ifile
79
+ %w(/dev/stdin /dev/null).include? literal or
80
+ set_message "Can't read #{literal}"
81
+ when :ofile
82
+ %w(/dev/stdout /dev/stderr /dev/null).include?(literal) or
83
+ set_message "Can't write to #{literal}"
84
+ when :dir, :edir, :ndir
85
+ set_message "Expected file, got directory #{literal}"
86
+ else
87
+ raise ArgumentError, "Unhandled kind: #{kind.inspect}"
88
+ end
89
+
90
+ # All other files or directories
80
91
  else
81
- raise InternalError, "Illegal kind: #{kind.inspect}"
92
+ case kind # TODO: node, enode, npath - special files: character, block, socket, etc.
93
+ when :file; match_path(name, literal, kind, :file?, :default)
94
+ when :dir; match_path(name, literal, kind, :directory?, :default)
95
+ when :path; match_path(name, literal, kind, :exist?, :default)
96
+
97
+ when :efile; match_path(name, literal, kind, :file?, :exist)
98
+ when :edir; match_path(name, literal, kind, :directory?, :exist)
99
+ when :epath; match_path(name, literal, kind, :exist?, :exist)
100
+
101
+ when :nfile; match_path(name, literal, kind, :file?, :new)
102
+ when :ndir; match_path(name, literal, kind, :directory?, :new)
103
+ when :npath; match_path(name, literal, kind, :exist?, :new)
104
+
105
+ when :ifile; match_path(name, literal, kind, :readable?, :default)
106
+ when :ofile; match_path(name, literal, kind, :writable?, :default)
107
+ else
108
+ raise InternalError, "Illegal kind: #{kind.inspect}"
109
+ end
82
110
  end
83
111
  end
84
112
 
85
113
  # Note: No checks done, not sure if it is a feature or a bug
86
114
  def value?(value) value.is_a?(String) end
87
115
 
116
+ def convert(value)
117
+ if value == "-"
118
+ case kind
119
+ when :ifile; "/dev/stdin"
120
+ when :ofile; "/dev/stdout"
121
+ else
122
+ value
123
+ end
124
+ else
125
+ value
126
+ end
127
+ end
128
+
88
129
  protected
89
130
  def match_path(name, literal, kind, method, mode)
90
131
  subject =
91
132
  case kind
92
- when :file, :efile, :nfile; "regular file"
133
+ when :file, :efile, :nfile, :ifile, :ofile; "file"
93
134
  when :dir, :edir, :ndir; "directory"
94
135
  when :path, :epath, :npath; "path"
95
136
  else
96
137
  raise ArgumentError
97
138
  end
98
139
 
99
- if File.send(method, literal) # exists?
140
+ # file exists and is the rigth type?
141
+ if File.send(method, literal)
100
142
  if mode == :new
101
143
  set_message "#{subject.capitalize} already exists in #{name}: #{literal}"
102
144
  elsif kind == :path || kind == :epath
103
145
  if File.file?(literal) || File.directory?(literal)
104
146
  true
105
147
  else
106
- set_message "Expected regular file or directory as #{name} argument: #{literal}"
148
+ set_message "Expected file or directory as #{name} argument: #{literal}"
107
149
  end
150
+ elsif (kind == :ifile || kind == :ofile) && File.directory?(literal)
151
+ set_message "File expected, got directory: #{literal}"
108
152
  else
109
153
  true
110
154
  end
111
- elsif File.exist?(literal) # exists but not the right type
112
- if mode == :new
113
- set_message "#{subject.capitalize} already exists"
155
+
156
+ # file exists but not the right type?
157
+ elsif File.exist?(literal)
158
+ if kind == :ifile
159
+ set_message "Can't read #{literal}"
160
+ elsif kind == :ofile
161
+ set_message "Can't write to #{literal}"
162
+ elsif mode == :new
163
+ set_message "#{subject.capitalize} already exists - #{literal}"
114
164
  else
115
165
  set_message "Expected #{subject} as #{name} argument: #{literal}"
116
166
  end
117
- else # does not exist
167
+
168
+ # file does not exist
169
+ else
118
170
  if [:default, :new].include? mode
119
- if File.exist?(File.dirname(literal))
171
+ if File.exist?(File.dirname(literal)) || kind == :ofile
120
172
  true
121
173
  else
122
174
  set_message "Illegal path in #{name}: #{literal}"
123
175
  end
124
176
  else
125
- set_message "Error in #{name} argument: Can't find #{literal}"
177
+ set_message "Can't find #{literal}"
126
178
  end
127
179
  end
128
180
  end
@@ -30,8 +30,8 @@ module ShellOpts
30
30
  class Command
31
31
  using Ext::Array::Wrap
32
32
 
33
- def puts_usage(bol: false)
34
- width = [Formatter.rest, Formatter::USAGE_MAX_WIDTH].min
33
+ def puts_usage(bol: false, max_width: Formatter::USAGE_MAX_WIDTH)
34
+ width = [Formatter.rest, max_width].min
35
35
  if descrs.size == 0
36
36
  print (lead = Formatter.command_prefix || "")
37
37
  indent(lead.size, ' ', bol: bol && lead == "") {
@@ -41,7 +41,7 @@ module ShellOpts
41
41
  lead = Formatter.command_prefix || ""
42
42
  descrs.each { |descr|
43
43
  print lead
44
- puts render(:single, width, args: [descr.text])
44
+ puts render(:multi, width, args: descr.text.split(' '))
45
45
  }
46
46
  end
47
47
  end
@@ -58,7 +58,7 @@ module ShellOpts
58
58
  end
59
59
 
60
60
  puts "Usage"
61
- indent { puts_usage(bol: true) }
61
+ indent { puts_usage(bol: true, max_width: Formatter::HELP_MAX_WIDTH) }
62
62
 
63
63
  if options.any?
64
64
  puts
@@ -112,14 +112,13 @@ module ShellOpts
112
112
  puts
113
113
 
114
114
  puts Ansi.bold "USAGE"
115
- indent { puts_usage(bol: true) }
115
+ indent { puts_usage(bol: true, max_width: Formatter::HELP_MAX_WIDTH) }
116
116
 
117
117
  section = {
118
118
  Paragraph => "DESCRIPTION",
119
119
  OptionGroup => "OPTION",
120
120
  Command => "COMMAND"
121
121
  }
122
-
123
122
  seen_sections = {}
124
123
  newline = false # True if a newline should be printed before child
125
124
  indent {
@@ -173,7 +172,10 @@ module ShellOpts
173
172
  end
174
173
 
175
174
  module WrappedNode
176
- def puts_descr(width = Formatter.rest) puts lines(width) end
175
+ def puts_descr
176
+ width = [Formatter.rest, Formatter::HELP_MAX_WIDTH].min
177
+ puts lines(width)
178
+ end
177
179
  end
178
180
 
179
181
  class Code
@@ -217,6 +219,9 @@ module ShellOpts
217
219
  # Indent to use in help output
218
220
  HELP_INDENT = 4
219
221
 
222
+ # Max. width of help text (not including indent)
223
+ HELP_MAX_WIDTH = 85
224
+
220
225
  # Command prefix when subject is a sub-command
221
226
  def self.command_prefix() @command_prefix end
222
227
 
@@ -83,8 +83,8 @@ module ShellOpts
83
83
  when "$"
84
84
  @argument_name ||= "NUM"
85
85
  @argument_type = FloatArgument.new
86
- when "FILE", "DIR", "PATH", "EFILE", "EDIR", "EPATH", "NFILE", "NDIR", "NPATH"
87
- @argument_name ||= arg.sub(/^(?:E|N)/, "")
86
+ when "FILE", "DIR", "PATH", "EFILE", "EDIR", "EPATH", "NFILE", "NDIR", "NPATH", "IFILE", "OFILE"
87
+ @argument_name ||= arg.sub(/^(?:E|N|I|O)/, "")
88
88
  @argument_type = FileArgument.new(arg.downcase.to_sym)
89
89
  when /,/
90
90
  @argument_name ||= arg
@@ -15,9 +15,9 @@ require 'terminfo'
15
15
  #
16
16
  # Command rendering
17
17
  # cmd --all --beta [cmd1|cmd2] ARG1 ARG2 # Single-line formats (:single)
18
- # cmd --all --beta [cmd1|cmd2] ARGS...
18
+ # cmd --all --beta [cmd1|cmd2] ARGS... # Not used
19
19
  # cmd -a -b [cmd1|cmd2] ARG1 ARG2
20
- # cmd -a -b [cmd1|cmd2] ARGS...
20
+ # cmd -a -b [cmd1|cmd2] ARGS... # Not used
21
21
  #
22
22
  # cmd -a -b [cmd1|cmd2] ARG1 ARG2 # One line for each argument description (:enum)
23
23
  # cmd -a -b [cmd1|cmd2] ARG3 ARG4 # (used in the USAGE section)
@@ -108,7 +108,7 @@ module ShellOpts
108
108
  def get_args(args: nil)
109
109
  case descrs.size
110
110
  when 0; []
111
- when 1; [descrs.first.text]
111
+ when 1; descrs.first.text.split(' ')
112
112
  else [DESCRS_ABBR]
113
113
  end
114
114
  end
@@ -120,6 +120,7 @@ module ShellOpts
120
120
  end
121
121
 
122
122
  # Force one line. Compact options, commands, arguments if needed
123
+ #
123
124
  def render_single(width, args: nil)
124
125
  long_options = options.map { |option| option.render(:long) }
125
126
  short_options = options.map { |option| option.render(:short) }
@@ -164,32 +165,30 @@ module ShellOpts
164
165
  def render_multi(width, args: nil)
165
166
  long_options = options.map { |option| option.render(:long) }
166
167
  short_options = options.map { |option| option.render(:short) }
167
- short_commands = commands.empty? ? [] : ["[#{commands.map(&:name).join("|")}]"]
168
- compact_commands = [COMMANDS_ABBR]
168
+ compact_options = options.empty? ? [] : [OPTIONS_ABBR]
169
+
170
+ # Only compact commands if they can't fit on one line
171
+ if commands.empty?
172
+ use_commands = []
173
+ else
174
+ short_command = "[#{commands.map(&:name).join("|")}]"
175
+ use_commands = [short_command.size > width ? COMMANDS_ABBR : short_command]
176
+ end
169
177
 
170
178
  args ||= get_args
171
179
 
172
180
  # On one line
173
- words = [name] + long_options + short_commands + args
181
+ words = [name] + long_options + use_commands + args
174
182
  return [words.join(" ")] if pass?(words, width)
175
- words = [name] + short_options + short_commands + args
183
+ words = [name] + short_options + use_commands + args
184
+ return [words.join(" ")] if pass?(words, width)
185
+ words = [name] + compact_options + use_commands + args
176
186
  return [words.join(" ")] if pass?(words, width)
177
187
 
178
188
  # On multiple lines
179
189
  lead = name + " "
180
- options = long_options.wrap(width - lead.size)
181
- options = [lead + options[0]] + indent_lines(lead.size, options[1..-1])
182
-
183
- begin
184
- words = short_commands + args
185
- break if pass?(words, width)
186
- words = compact_commands + args
187
- break if pass?(words, width)
188
- words = compact_commands + [DESCRS_ABBR]
189
- end while false
190
-
191
- cmdargs = words.empty? ? [] : [words.join(" ")]
192
- options + indent_lines(lead.size, cmdargs)
190
+ words = (long_options + use_commands + args).wrap(width - lead.size)
191
+ lines = [lead + words[0]] + indent_lines(lead.size, words[1..-1])
193
192
  end
194
193
 
195
194
  protected
@@ -1,3 +1,3 @@
1
1
  module ShellOpts
2
- VERSION = "2.0.21"
2
+ VERSION = "2.0.24"
3
3
  end
data/lib/shellopts.rb CHANGED
@@ -140,7 +140,7 @@ module ShellOpts
140
140
  float: true,
141
141
 
142
142
  # Let exceptions through
143
- exceptions: false
143
+ exception: false
144
144
  )
145
145
 
146
146
  @name = name || File.basename($PROGRAM_NAME)
data/shellopts.gemspec CHANGED
@@ -28,8 +28,7 @@ Gem::Specification.new do |spec|
28
28
  spec.add_dependency "ruby-terminfo"
29
29
  spec.add_dependency "indented_io"
30
30
 
31
- spec.add_development_dependency "bundler", "~> 2.2.10"
32
- spec.add_development_dependency "rake", ">= 12.3.3"
33
- spec.add_development_dependency "rspec", "~> 3.0"
31
+ spec.add_development_dependency "rake"
32
+ spec.add_development_dependency "rspec"
34
33
  spec.add_development_dependency "simplecov"
35
34
  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.0.21
4
+ version: 2.0.24
5
5
  platform: ruby
6
6
  authors:
7
7
  - Claus Rasmussen
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-04-21 00:00:00.000000000 Z
11
+ date: 2022-05-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: forward_to
@@ -66,48 +66,34 @@ dependencies:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
- - !ruby/object:Gem::Dependency
70
- name: bundler
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - "~>"
74
- - !ruby/object:Gem::Version
75
- version: 2.2.10
76
- type: :development
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - "~>"
81
- - !ruby/object:Gem::Version
82
- version: 2.2.10
83
69
  - !ruby/object:Gem::Dependency
84
70
  name: rake
85
71
  requirement: !ruby/object:Gem::Requirement
86
72
  requirements:
87
73
  - - ">="
88
74
  - !ruby/object:Gem::Version
89
- version: 12.3.3
75
+ version: '0'
90
76
  type: :development
91
77
  prerelease: false
92
78
  version_requirements: !ruby/object:Gem::Requirement
93
79
  requirements:
94
80
  - - ">="
95
81
  - !ruby/object:Gem::Version
96
- version: 12.3.3
82
+ version: '0'
97
83
  - !ruby/object:Gem::Dependency
98
84
  name: rspec
99
85
  requirement: !ruby/object:Gem::Requirement
100
86
  requirements:
101
- - - "~>"
87
+ - - ">="
102
88
  - !ruby/object:Gem::Version
103
- version: '3.0'
89
+ version: '0'
104
90
  type: :development
105
91
  prerelease: false
106
92
  version_requirements: !ruby/object:Gem::Requirement
107
93
  requirements:
108
- - - "~>"
94
+ - - ">="
109
95
  - !ruby/object:Gem::Version
110
- version: '3.0'
96
+ version: '0'
111
97
  - !ruby/object:Gem::Dependency
112
98
  name: simplecov
113
99
  requirement: !ruby/object:Gem::Requirement