shellopts 2.0.2 → 2.0.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 013afe67b6f86e17d37bfbd1b18f3b4b7e7e5026d3249900326b23d1ed75ed95
4
- data.tar.gz: 7ccfcca5ef44d9958e3f03ea23ea211893357ff32c7c5e5fdc4953ec61548306
3
+ metadata.gz: a3a8b18b0a610c14ac4a85f80cd70ff5f85ca7dc868b25615a67a3ad71f737b5
4
+ data.tar.gz: 2b0442868d8ddf102a5f8ee1b3d3307ecbea17a95512f5164d4a64a9d3fa9ec7
5
5
  SHA512:
6
- metadata.gz: 5a50d291b69952d04bc054ea09cf292f6df414a4d6e1df69ac47cb3d2489626301c7eba4f66edd268f1c486463d6c37a1fd0b3cd431060c7100da980272c2b9e
7
- data.tar.gz: 0bd422a52a4b11f69ade0d744df2a0bf8db6a05d67e282bfadb6a1a72af6f1ebe21ec0dd24b8ce663379d9e69db643036606e2fc1e502995e24f2f7b5c8bdea3
6
+ metadata.gz: c90ab9e0049ba64e21c2b0fd408b438df84c0265fced52b84d221bdb2b96f92e75624c00ab59526c9caa80f67e2e7d55c5bcf0cb8138b803722b424e08ea9202
7
+ data.tar.gz: 372e29956c8f3e27f8f5617215cf610403d40a307257c0c63605e94bd39fffaa8bf9cb165e82fe61497fcc8f0519c4298c1bf4480fa26735f5bf5c01e12fe8e2
@@ -38,16 +38,17 @@ module ShellOpts
38
38
  using Ext::Array::Wrap
39
39
 
40
40
  def puts_usage(bol: false)
41
+ width = [Formatter.rest, Formatter::USAGE_MAX_WIDTH].min
41
42
  if descrs.size == 0
42
43
  print (lead = Formatter.command_prefix || "")
43
44
  indent(lead.size, ' ', bol: bol && lead == "") {
44
- puts render(:multi, Formatter::USAGE_MAX_WIDTH)
45
+ puts render(:multi, width)
45
46
  }
46
47
  else
47
48
  lead = Formatter.command_prefix || ""
48
49
  descrs.each { |descr|
49
50
  print lead
50
- puts render(:single, Formatter::USAGE_MAX_WIDTH, args: [descr.text])
51
+ puts render(:single, width, args: [descr.text])
51
52
  }
52
53
  end
53
54
  end
@@ -4,30 +4,33 @@
4
4
  module ShellOpts
5
5
  # Command represents a program or a subcommand. It is derived from
6
6
  # BasicObject to have only a minimum of inherited member methods.
7
- # Additional methods defined in Command use the '__<identifier>__' naming
8
- # convention that doesn't collide with option or subcommand names but
9
- # they're rarely used in application code
10
7
  #
11
8
  # The names of the inherited methods can't be used as options or
12
9
  # command namess. They are: instance_eval, instance_exec method_missing,
13
10
  # singleton_method_added, singleton_method_removed, and
14
- # singleton_method_undefined
11
+ # singleton_method_undefined.
12
+ #
13
+ # Additional methods defined in Command use the '__<identifier>__' naming
14
+ # convention that doesn't collide with option or subcommand names but
15
+ # they're rarely used in application code
15
16
  #
16
17
  # Command also defines #subcommand and #subcommand! but they can be
17
18
  # overshadowed by an option or command declaration. Their values can
18
19
  # still be accessed using the dashed name, though
19
20
  #
20
- # Options and subcommands can be accessed using #[]
21
+ # Option and Command objects can be accessed using #[]. #key? is also defined
21
22
  #
22
23
  # The following methods are created dynamically for each declared option
23
24
  # with an attribute name
24
25
  #
25
- # def <identifier>(default = nil) self["<identifier>"] || default end
26
- # def <identifier>=(value) self["<identifier>"] = value end
27
- # def <identifier>?() self.key?("<identifier>") end
26
+ # <identifier>(default = nil)
27
+ # <identifier>=(value)
28
+ # <identifier>?()
29
+ #
30
+ # The default value is used if the option or its value is missing
28
31
  #
29
32
  # Options without an an attribute can still be accessed using #[] or trough
30
- # #__options__ or #__options_list__):
33
+ # #__option_values__, #__option_hash, or #__options_list__
31
34
  #
32
35
  # Each subcommand has a single method:
33
36
  #
@@ -42,9 +45,9 @@ module ShellOpts
42
45
 
43
46
  # These names can't be used as option or command names
44
47
  RESERVED_OPTION_NAMES = %w(
45
- is_a
46
- instance_eval instance_exec method_missing singleton_method_added
47
- singleton_method_removed singleton_method_undefined)
48
+ is_a instance_eval instance_exec method_missing singleton_method_added
49
+ singleton_method_removed singleton_method_undefined
50
+ )
48
51
 
49
52
  # These methods can be overridden by an option (the value is not used -
50
53
  # this is just for informational purposes)
@@ -59,43 +62,25 @@ module ShellOpts
59
62
  object
60
63
  end
61
64
 
62
- # Return command object or option argument value if present, otherwise nil
65
+ # Return command or option object if present, otherwise nil. Returns a
66
+ # possibly empty array of option objects if the option is repeatable
63
67
  #
64
68
  # The key is the name or identifier of the object or any any option
65
69
  # alias. Eg. :f, '-f', :file, or '--file' are all usable as option keys
66
70
  # and :cmd! or 'cmd' as command keys
67
71
  #
68
- # For options, the returned value is the argument given by the user
69
- # optionally converted to Integer or Float or nil if the option doesn't
70
- # take arguments. If the option takes an argument and it is repeatable
71
- # the value is an array of the arguments. Repeatable options without
72
- # arguments have the number of occurences as the value
73
- #
74
72
  def [](key)
75
73
  case object = __grammar__[key]
76
74
  when ::ShellOpts::Grammar::Command
77
75
  object.ident == __subcommand__!.__ident__ ? __subcommand__! : nil
78
76
  when ::ShellOpts::Grammar::Option
79
- __options__[object.ident]
80
- else
81
- nil
82
- end
83
- end
84
-
85
- # Assign a value to an existing option. This can be used to implement
86
- # default values. #[]= doesn't currently check the type of the given
87
- # value so take care. Note that the corresponding option(s) in
88
- # #__option_list__ is not updated
89
- def []=(key, value)
90
- case object = __grammar__[key]
91
- when ::ShellOpts::Grammar::Command
92
- ::Kernel.raise ArgumentError, "#{key.inspect} is not an option"
93
- when ::ShellOpts::Grammar::Option
94
- object.argument? || object.repeatable? or
95
- ::Kernel.raise ArgumentError, "#{key.inspect} is not assignable"
96
- __options__[object.ident] = value
77
+ if object.repeatable?
78
+ __option_hash__[object.ident] || []
79
+ else
80
+ __option_hash__[object.ident]
81
+ end
97
82
  else
98
- ::Kernel.raise ArgumentError, "Unknown option or command: #{key.inspect}"
83
+ ::Kernel.raise ::ArgumentError, "Unknown command or option: '#{key}'"
99
84
  end
100
85
  end
101
86
 
@@ -103,11 +88,11 @@ module ShellOpts
103
88
  def key?(key)
104
89
  case object = __grammar__[key]
105
90
  when ::ShellOpts::Grammar::Command
106
- object.ident == __subcommand__!.ident ? __subcommand__! : nil
91
+ object.ident == __subcommand__
107
92
  when ::ShellOpts::Grammar::Option
108
- __options__.key?(object.ident)
93
+ __option_hash__.key?(object.ident)
109
94
  else
110
- nil
95
+ ::Kernel.raise ::ArgumentError, "Unknown command or option: '#{key}'"
111
96
  end
112
97
  end
113
98
 
@@ -153,13 +138,17 @@ module ShellOpts
153
138
  # depending on the option's type. Repeated options options without
154
139
  # arguments have the number of occurences as the value, with arguments
155
140
  # the value is an array of the given values
156
- attr_reader :__options__
141
+ attr_reader :__option_values__
157
142
 
158
143
  # List of Option objects for the subcommand in the same order as
159
144
  # given by the user but note that options are reordered to come after
160
145
  # their associated subcommand if float is true. Repeated options are not
161
146
  # collapsed
162
147
  attr_reader :__option_list__
148
+
149
+ # Map from identifier to option object or to a list of option objects if
150
+ # the option is repeatable
151
+ attr_reader :__option_hash__
163
152
 
164
153
  # The subcommand identifier (a Symbol incl. the exclamation mark) or nil
165
154
  # if not present. Use #subcommand!, or the dynamically generated
@@ -172,9 +161,10 @@ module ShellOpts
172
161
  private
173
162
  def __initialize__(grammar)
174
163
  @__grammar__ = grammar
175
- @__options__ = {}
164
+ @__option_values__ = {}
176
165
  @__option_list__ = []
177
- @__options__ = {}
166
+ @__option_hash__ = {}
167
+ @__option_values__ = {}
178
168
  @__subcommand__ = nil
179
169
 
180
170
  __define_option_methods__
@@ -182,36 +172,34 @@ module ShellOpts
182
172
 
183
173
  def __define_option_methods__
184
174
  @__grammar__.options.each { |opt|
185
- next if opt.attr.nil?
186
175
  if opt.argument? || opt.repeatable?
187
176
  if opt.optional?
188
177
  self.instance_eval %(
189
178
  def #{opt.attr}(default = nil)
190
- if @__options__.key?(:#{opt.attr})
191
- @__options__[:#{opt.attr}] || default
179
+ if @__option_values__.key?(:#{opt.attr})
180
+ @__option_values__[:#{opt.attr}]
192
181
  else
193
- nil
182
+ default
194
183
  end
195
184
  end
196
185
  )
197
- elsif !opt.argument?
186
+ elsif !opt.argument? # Repeatable w/o argument
198
187
  self.instance_eval %(
199
- def #{opt.attr}(default = nil)
200
- if @__options__.key?(:#{opt.attr})
201
- value = @__options__[:#{opt.attr}]
202
- value == 0 ? default : value
188
+ def #{opt.attr}(default = [])
189
+ if @__option_values__.key?(:#{opt.attr})
190
+ @__option_values__[:#{opt.attr}]
203
191
  else
204
- nil
192
+ default
205
193
  end
206
194
  end
207
195
  )
208
196
  else
209
- self.instance_eval("def #{opt.attr}() @__options__[:#{opt.attr}] end")
197
+ self.instance_eval("def #{opt.attr}() @__option_values__[:#{opt.attr}] end")
210
198
  end
211
- self.instance_eval("def #{opt.attr}=(value) @__options__[:#{opt.attr}] = value end")
212
- @__options__[opt.attr] = 0 if !opt.argument?
199
+ self.instance_eval("def #{opt.attr}=(value) @__option_values__[:#{opt.attr}] = value end")
200
+ @__option_values__[opt.attr] = 0 if !opt.argument?
213
201
  end
214
- self.instance_eval("def #{opt.attr}?() @__options__.key?(:#{opt.attr}) end")
202
+ self.instance_eval("def #{opt.attr}?() @__option_values__.key?(:#{opt.attr}) end")
215
203
  }
216
204
 
217
205
  @__grammar__.commands.each { |cmd|
@@ -228,14 +216,15 @@ module ShellOpts
228
216
  ident = option.grammar.ident
229
217
  @__option_list__ << option
230
218
  if option.repeatable?
219
+ (@__option_hash__[ident] ||= []) << option
231
220
  if option.argument?
232
- (@__options__[ident] ||= []) << option.argument
221
+ (@__option_values__[ident] ||= []) << option.argument
233
222
  else
234
- @__options__[ident] ||= 0
235
- @__options__[ident] += 1
223
+ @__option_values__[ident] = (@__option_values__[ident] || 0) + 1
236
224
  end
237
225
  else
238
- @__options__[ident] = option.argument
226
+ @__option_hash__[ident] = option
227
+ @__option_values__[ident] = option.argument
239
228
  end
240
229
  end
241
230
 
@@ -254,7 +243,7 @@ module ShellOpts
254
243
 
255
244
  # Option models an option as given by the user on the subcommand line.
256
245
  # Compiled options (and possibly aggregated) options are stored in the
257
- # Command#__options__ array
246
+ # Command#__option_values__ array
258
247
  class Option
259
248
  # Associated Grammar::Option object
260
249
  attr_reader :grammar
@@ -1,3 +1,3 @@
1
1
  module ShellOpts
2
- VERSION = "2.0.2"
2
+ VERSION = "2.0.5"
3
3
  end
data/lib/shellopts.rb CHANGED
@@ -102,6 +102,9 @@ module ShellOpts
102
102
  attr_accessor :stdopts
103
103
  attr_accessor :msgopts
104
104
 
105
+ # Version of client program. This is only used if +stdopts+ is true
106
+ attr_reader :version
107
+
105
108
  # Interpreter flags
106
109
  attr_accessor :float
107
110
 
@@ -116,14 +119,17 @@ module ShellOpts
116
119
  attr_reader :tokens
117
120
  alias_method :ast, :grammar # Oops - defined earlier FIXME
118
121
 
119
- def initialize(name: nil, stdopts: true, msgopts: false, float: true, exception: false)
122
+ def initialize(name: nil, stdopts: true, version: nil, msgopts: false, float: true, exception: false)
120
123
  @name = name || File.basename($PROGRAM_NAME)
121
- @stdopts, @msgopts, @float, @exception = stdopts, msgopts, float, exception
124
+ @stdopts, @version, @msgopts, @float, @exception = stdopts, version, msgopts, float, exception
122
125
  end
123
126
 
124
127
  # Compile source and return grammar object. Also sets #spec and #grammar.
125
128
  # Returns the grammar
126
129
  def compile(spec)
130
+ if stdopts
131
+ spec += "\n--version\n Write version number and exit\n-h,help @ Write help text and exit\n Write help text. -h prints a brief text, --help prints a longer man-style description of the command"
132
+ end
127
133
  handle_exceptions {
128
134
  @oneline = spec.index("\n").nil?
129
135
  @spec = spec.sub(/^\s*\n/, "")
@@ -143,6 +149,20 @@ module ShellOpts
143
149
  handle_exceptions {
144
150
  @argv = argv.dup
145
151
  @program, @args = Interpreter.interpret(grammar, argv, float: float, exception: exception)
152
+ if stdopts
153
+ if @program.version?
154
+ version or raise ArgumentError, "Version not specified"
155
+ puts version
156
+ exit
157
+ elsif @program.help?
158
+ if @program[:help].name == "-h"
159
+ ShellOpts.brief
160
+ else
161
+ ShellOpts.help
162
+ end
163
+ exit
164
+ end
165
+ end
146
166
  }
147
167
  self
148
168
  end
@@ -201,11 +221,12 @@ module ShellOpts
201
221
  def brief() Formatter.brief(@grammar) end
202
222
 
203
223
  # Print help for the given subject or the full documentation if +subject+
204
- # is nil
224
+ # is nil. Clears the screen beforehand if :clear is true
205
225
  #
206
- def help(subject = nil)
226
+ def help(subject = nil, clear: true)
207
227
  node = (subject ? @grammar[subject] : @grammar) or
208
228
  raise ArgumentError, "No such command: '#{subject&.sub(".", " ")}'"
229
+ print '' if clear
209
230
  Formatter.help(node)
210
231
  end
211
232
 
@@ -310,6 +331,7 @@ module ShellOpts
310
331
  def self.instance?() !@instance.nil? end
311
332
  def self.instance() @instance or raise Error, "ShellOpts is not initialized" end
312
333
  def self.instance=(instance) @instance = instance end
334
+ def self.shellopts() instance end
313
335
 
314
336
  forward_self_to :instance, :error, :failure
315
337
 
data/main CHANGED
@@ -7,18 +7,31 @@ require 'shellopts'
7
7
 
8
8
  include ShellOpts
9
9
 
10
+ VERSION = "1.2.3"
10
11
 
11
- SPEC = %(
12
- -a,alpha @ Brief comment for -a and --alpha options
13
- Longer and more elaborate description of the --alpha option
12
+ SPEC = "-a"
13
+ opts, args = ShellOpts::process(SPEC, ARGV, version: VERSION)
14
+ #ShellOpts::ShellOpts.help
14
15
 
15
- -b,beta=ARG
16
- @ Alternative style of brief comment
17
16
 
18
- Longer and more elaborate description of the --beta option
19
- )
20
17
 
21
- opts, args = ShellOpts.process(SPEC, ARGV)
18
+
19
+
20
+
21
+ __END__
22
+
23
+
24
+ #SPEC = %(
25
+ # -a,alpha @ Brief comment for -a and --alpha options
26
+ # Longer and more elaborate description of the --alpha option
27
+ #
28
+ # -b,beta=ARG
29
+ # @ Alternative style of brief comment
30
+ #
31
+ # Longer and more elaborate description of the --beta option
32
+ #)
33
+ #
34
+ #opts, args = ShellOpts.process(SPEC, ARGV)
22
35
  #puts "opts.alpha?: #{opts.alpha?.inspect}"
23
36
  #puts "opts.alpha: #{opts.alpha.inspect}"
24
37
  #puts "opts.beta?: #{opts.beta?.inspect}"
@@ -266,8 +279,9 @@ SPEC4 = %(
266
279
 
267
280
  SPEC5 = "cmd! cmd!"
268
281
 
269
- shellopts = ShellOpts::ShellOpts.new(exception: false)
282
+ shellopts = ShellOpts::ShellOpts.new(exception: true)
270
283
  #shellopts.compile("cmd! cmd!")
284
+
271
285
  shellopts.compile(SPEC)
272
286
  #shellopts.compile(SPEC2)
273
287
  #shellopts.compile(SPEC3)
@@ -277,9 +291,9 @@ shellopts.compile(SPEC)
277
291
  #shellopts.tokens.each(&:dump)
278
292
  #exit
279
293
 
280
- shellopts.usage
294
+ #shellopts.usage
281
295
  #shellopts.brief
282
- #shellopts.help
296
+ shellopts.help
283
297
  #shellopts.help("cmd")
284
298
  #shellopts.help(ARGV.first)
285
299
 
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.2
4
+ version: 2.0.5
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-02-28 00:00:00.000000000 Z
11
+ date: 2022-03-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: forward_to
@@ -187,7 +187,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
187
187
  - !ruby/object:Gem::Version
188
188
  version: '0'
189
189
  requirements: []
190
- rubygems_version: 3.2.26
190
+ rubygems_version: 3.1.4
191
191
  signing_key:
192
192
  specification_version: 4
193
193
  summary: Parse command line options and arguments