shellopts 2.0.6 → 2.0.9

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: 52971494b88f77b6b66a95e8409de7cbebcbf030588089c395424b7e86f5e372
4
- data.tar.gz: e00e482a0db690c2b7bd0861589a5179a67d7cec9b285ebcc1d2b5a7b6f73ff7
3
+ metadata.gz: a9d576d24bd0aec0dd43a2a3645ede04751d80ae22a4c88e8f66ed53bbe7e405
4
+ data.tar.gz: 9027ac55689a345099f6515e5280808528603dfafe385219e6cce77d1c166d96
5
5
  SHA512:
6
- metadata.gz: 2206c1e56c4239472bf5b697854fc4d7ba06bd9dedfe4eb2f86c2e115f4ddd52f66e9b4f47cbba41b1b4eafb08b8666b2115df7f70d769465dcb3e4a344d322a
7
- data.tar.gz: f9bc90bdba87159a2821afb3c719ba4c4fbc7077601266d9e61c5f83273924bc23e9fa2208bf727855fc99b6acac09a97ec3dc58be179f649c9ed6dbb4fcc259
6
+ metadata.gz: b44a4d95aa0585dd0ec31abccde4942c98ad712d9e3bdf9f0c0b6ead7333ff4c5b555ec5b45786a6f539388b2460c93d12f3755b2e80cbb81fd269c42a9b4955
7
+ data.tar.gz: 3c948a743d809df7d22bc142dcfa9aa4e943fed144004c4fbf5d1995c314f4aea3f91b032533222eed953e5344a45c55666339797e5ae2b1be262eea17071cd5
data/TODO CHANGED
@@ -1,4 +1,5 @@
1
1
 
2
+ o Add brackets to optional option arguments: '--all=FILE?' -> '--all[=FILE]'
2
3
  o Ignore all text after ' # ' (doesn't conflict with option flag)
3
4
  o Command aliases
4
5
  o Add user-defined setions
@@ -28,10 +28,13 @@ module ShellOpts
28
28
  @options = option_groups.map(&:options).flatten
29
29
  end
30
30
 
31
- # Move options before first command
31
+ # Move options before first command or before explicit COMMAND section
32
32
  def reorder_options
33
33
  if commands.any?
34
- if i = children.find_index { |child| child.is_a?(Command) }
34
+ i = children.find_index { |child|
35
+ child.is_a?(Command) || child.is_a?(Section) && child.name == "COMMAND"
36
+ }
37
+ if i
35
38
  options, rest = children[i+1..-1].partition { |child| child.is_a?(OptionGroup) }
36
39
  @children = children[0, i] + options + children[i..i] + rest
37
40
  end
@@ -2,21 +2,31 @@
2
2
  module ShellOpts
3
3
  # Specialization of Array for arguments lists. Args extends Array with a
4
4
  # #extract and an #expect method to extract elements from the array. The
5
- # methods raise a ShellOpts::UserError exception in case of errors
5
+ # methods raise a ShellOpts::Error exception in case of errors
6
6
  #
7
7
  class Args < Array
8
+ def initialize(*args, exception: false)
9
+ super(*args)
10
+ @exception = exception
11
+ end
12
+
13
+ # :call-seq:
14
+ # extract(count, message = nil)
15
+ # extract(range, message = nil)
16
+ #
8
17
  # Remove and return elements from beginning of the array
9
18
  #
10
19
  # If +count_or_range+ is a number, that number of elements will be
11
20
  # returned. If the count is one, a simple value is returned instead of an
12
21
  # array. If the count is negative, the elements will be removed from the
13
22
  # end of the array. If +count_or_range+ is a range, the number of elements
14
- # returned will be in that range. The range can't contain negative numbers
23
+ # returned will be in that range. Note that the range can't contain
24
+ # negative numbers
15
25
  #
16
- # #extract raise a ShellOpts::UserError exception if there's is not enough
26
+ # #extract raise a ShellOpts::Error exception if there's is not enough
17
27
  # elements in the array to satisfy the request
18
28
  #
19
- def extract(count_or_range, message = nil)
29
+ def extract(count_or_range, message = nil)
20
30
  case count_or_range
21
31
  when Range
22
32
  range = count_or_range
@@ -30,16 +40,16 @@ module ShellOpts
30
40
  count.abs <= self.size or inoa(message)
31
41
  start = count >= 0 ? 0 : size + count
32
42
  r = slice!(start, count.abs)
33
- r.size <= 0 ? nil : (r.size == 1 ? r.first : r)
43
+ r.size == 1 ? r.first : r
34
44
  else
35
45
  raise ArgumentError
36
46
  end
37
47
  end
38
48
 
39
49
  # As #extract except it doesn't allow negative counts and that the array is
40
- # expect to be emptied by the operation
50
+ # expected to be emptied by the operation
41
51
  #
42
- # #expect raise a ShellOpts::UserError exception if the array is not emptied
52
+ # #expect raise a ShellOpts::Error exception if the array is not emptied
43
53
  # by the operation
44
54
  #
45
55
  def expect(count_or_range, message = nil)
@@ -54,8 +64,10 @@ module ShellOpts
54
64
  end
55
65
 
56
66
  private
57
- def inoa(message = nil)
58
- raise ArgumentError, message || "Illegal number of arguments"
67
+ def inoa(message = nil)
68
+ message ||= "Illegal number of arguments"
69
+ raise Error, message if @exception
70
+ ::ShellOpts.error(message)
59
71
  end
60
72
  end
61
73
  end
@@ -112,36 +112,33 @@ module ShellOpts
112
112
 
113
113
  section = {
114
114
  Paragraph => "DESCRIPTION",
115
- OptionGroup => "OPTIONS",
116
- Command => "COMMANDS"
115
+ OptionGroup => "OPTION",
116
+ Command => "COMMAND"
117
117
  }
118
118
 
119
+ seen_sections = {}
119
120
  newline = false # True if a newline should be printed before child
120
121
  indent {
121
122
  children.each { |child|
122
- if child.is_a?(Section) # Explicit section
123
- # p :A
124
- puts
125
- indent(-1).puts Ansi.bold child.name
126
- section.delete_if { |_,v| v == child.name }
123
+ klass = child.is_a?(Section) ? section.key(child.name) : child.class
124
+ if s = section[klass] # Implicit section
125
+ section.delete(klass)
127
126
  section.delete(Paragraph)
128
- newline = false
129
- next
130
- elsif s = section[child.class] # Implicit section
131
- # p :B
132
- puts
127
+ if klass <= OptionGroup
128
+ s = s + "S" if options.size > 1
129
+ elsif klass <= Command
130
+ s = s + "S" if commands.size > 1 || commands.first&.commands&.size != 0
131
+ end
132
+ puts
133
133
  indent(-1).puts Ansi.bold s
134
- section.delete(child.class)
135
- section.delete(Paragraph)
136
134
  newline = false
137
- else # Any other node add a newline
138
- # p :C
135
+ next if child.is_a?(Section)
136
+ else # Any other node adds a newline
139
137
  puts if newline
140
138
  newline = true
141
139
  end
142
140
 
143
141
  if child.is_a?(Command)
144
- # prefix = child.parent != self ? nil : child.supercommand&.name
145
142
  prefix = child.supercommand == self ? nil : child.supercommand&.name
146
143
  child.puts_descr(prefix, brief: false, name: :path)
147
144
  newline = true
@@ -151,6 +151,10 @@ module ShellOpts
151
151
  class OptionGroup < Node
152
152
  alias_method :command, :parent
153
153
 
154
+ # Duck typing for compatibility with IdrNode (TODO: maybe just make
155
+ # OptionGroup an IdrNode and be over with it)
156
+ def name() options.first&.name end
157
+
154
158
  # Array of options in declaration order
155
159
  attr_reader :options
156
160
 
@@ -352,6 +356,10 @@ module ShellOpts
352
356
  end
353
357
 
354
358
  class Section < Node
359
+ def initialize(parent, token)
360
+ constrain token.source, *%w(DESCRIPTION OPTION COMMAND)
361
+ super
362
+ end
355
363
  def name() token.source end
356
364
  end
357
365
 
@@ -32,7 +32,7 @@ module ShellOpts
32
32
  end
33
33
  end
34
34
  end
35
- [@expr, Args.new(@args + @argv)]
35
+ [@expr, Args.new(@args + @argv, exception: @exception)]
36
36
  end
37
37
 
38
38
  def self.interpret(grammar, argv, **opts)
@@ -45,7 +45,7 @@ module ShellOpts
45
45
  # Match ArgDescr words (should be at least two characters long)
46
46
  DESCR_RE = /^[^a-z]{2,}$/
47
47
 
48
- SECTIONS = %w(DESCRIPTION OPTIONS COMMANDS)
48
+ SECTIONS = %w(DESCRIPTION OPTION OPTIONS COMMAND COMMANDS)
49
49
 
50
50
  using Ext::Array::ShiftWhile
51
51
 
@@ -109,7 +109,7 @@ module ShellOpts
109
109
 
110
110
  # Sections
111
111
  elsif SECTIONS.include?(line.text)
112
- @tokens << Token.new(:section, line.lineno, line.charno, line.text)
112
+ @tokens << Token.new(:section, line.lineno, line.charno, line.text.sub(/S$/, ""))
113
113
 
114
114
  # Options, commands, usage, arguments, and briefs
115
115
  elsif line =~ DECL_RE
@@ -132,7 +132,8 @@ module ShellOpts
132
132
 
133
133
  option_token = Token.new(:option, 1, 1, "-h,help")
134
134
  brief_token = Token.new(:brief, 1, 1, "Write help text and exit")
135
- paragraph_token = Token.new(:text, 1, 1, "-h prints a brief help text, --help prints a longer man-style description of the command")
135
+ paragraph_token = Token.new(:text, 1, 1,
136
+ "-h prints a brief help text, --help prints a longer man-style description of the command")
136
137
  group = OptionGroup.new(self, option_token)
137
138
  option = Option.parse(group, option_token)
138
139
  brief = Brief.parse(group, brief_token)
@@ -162,14 +163,6 @@ module ShellOpts
162
163
  @nodes = {}
163
164
  end
164
165
 
165
- # def add_stdopts
166
- # version_token = Token.new(:option, 1, 1, "--version")
167
- # version_brief = Token.new(:brief, 1, 1, "Gryf gryf")
168
- # group = Grammar::OptionGroup.new(@program, version_token)
169
- # option = Grammar::Option.parse(group, version_token)
170
- # brief = Grammr::Brief.parse(option, version_brief)
171
- # end
172
-
173
166
  def parse()
174
167
  @program = Grammar::Program.parse(@tokens.shift)
175
168
  oneline = @tokens.first.lineno == @tokens.last.lineno
@@ -1,3 +1,3 @@
1
1
  module ShellOpts
2
- VERSION = "2.0.6"
2
+ VERSION = "2.0.9"
3
3
  end
data/shellopts.gemspec CHANGED
@@ -1,7 +1,5 @@
1
1
 
2
- lib = File.expand_path("../lib", __FILE__)
3
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require "shellopts/version"
2
+ require_relative "lib/shellopts/version"
5
3
 
6
4
  Gem::Specification.new do |spec|
7
5
  spec.name = "shellopts"
@@ -15,15 +13,18 @@ Gem::Specification.new do |spec|
15
13
  and has built-in help and error messages}
16
14
  spec.homepage = "http://github.com/clrgit/shellopts"
17
15
 
18
- spec.files = `git ls-files -z`.split("\x0").reject do |f|
19
- f.match(%r{^(test|spec|features)/})
16
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
17
+ `git ls-files -z`.split("\x0").reject do |f|
18
+ (f == __FILE__) || f.match(%r{\A(?:(?:test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
19
+ end
20
20
  end
21
+
21
22
  spec.bindir = "exe"
22
23
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
24
  spec.require_paths = ["lib"]
24
25
 
25
26
  spec.add_dependency "forward_to"
26
- spec.add_dependency "constrain"
27
+ spec.add_dependency "constrain", "~> 0.5.1"
27
28
  spec.add_dependency "ruby-terminfo"
28
29
  spec.add_dependency "indented_io"
29
30
 
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.6
4
+ version: 2.0.9
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-03-01 00:00:00.000000000 Z
11
+ date: 2022-03-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: forward_to
@@ -28,16 +28,16 @@ dependencies:
28
28
  name: constrain
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ">="
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '0'
33
+ version: 0.5.1
34
34
  type: :runtime
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: '0'
40
+ version: 0.5.1
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: ruby-terminfo
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -132,10 +132,8 @@ executables: []
132
132
  extensions: []
133
133
  extra_rdoc_files: []
134
134
  files:
135
- - ".gitignore"
136
135
  - ".rspec"
137
136
  - ".ruby-version"
138
- - ".travis.yml"
139
137
  - Gemfile
140
138
  - README.md
141
139
  - Rakefile
data/.gitignore DELETED
@@ -1,30 +0,0 @@
1
- /.bundle/
2
- /.yardoc
3
- /_yardoc/
4
- /rdoc/
5
- /pkg/
6
- /spec/reports/
7
- /tmp/
8
-
9
- # rspec failure tracking
10
- .rspec_status
11
-
12
- # simplecov
13
- /coverage/
14
-
15
- # Ignore Gemfile.lock. See https://stackoverflow.com/questions/4151495/should-gemfile-lock-be-included-in-gitignore
16
- /Gemfile.lock
17
-
18
- # Ignore vim files
19
- .*.swp
20
-
21
- # Ignore t.* files
22
- t
23
- t.*
24
- tt
25
- tt.*
26
- s
27
- s.*
28
-
29
- # Ignore temporary directory
30
- /spec/tmpdir/
data/.travis.yml DELETED
@@ -1,5 +0,0 @@
1
- sudo: false
2
- language: ruby
3
- rvm:
4
- - 2.5.1
5
- before_install: gem install bundler -v 1.16.1