shellopts 2.0.5 → 2.0.8

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: a3a8b18b0a610c14ac4a85f80cd70ff5f85ca7dc868b25615a67a3ad71f737b5
4
- data.tar.gz: 2b0442868d8ddf102a5f8ee1b3d3307ecbea17a95512f5164d4a64a9d3fa9ec7
3
+ metadata.gz: e6c52f313fee54283e1d5e704dd8b75a96239db6cff70584eefe747cf56270c1
4
+ data.tar.gz: 67b50b7b3a993bf77533246c9634ce7850d765891381244fedf2b1ecf3f6bc76
5
5
  SHA512:
6
- metadata.gz: c90ab9e0049ba64e21c2b0fd408b438df84c0265fced52b84d221bdb2b96f92e75624c00ab59526c9caa80f67e2e7d55c5bcf0cb8138b803722b424e08ea9202
7
- data.tar.gz: 372e29956c8f3e27f8f5617215cf610403d40a307257c0c63605e94bd39fffaa8bf9cb165e82fe61497fcc8f0519c4298c1bf4480fa26735f5bf5c01e12fe8e2
6
+ metadata.gz: 3cf5cd91d92bce24b1d04a3a526d6f43f8fe761f760319bf60191e2e638c3301fdbf9c63aa9f465aa8755cd18aa56293f0ae68e01870d6a877928bc1c0eebf5c
7
+ data.tar.gz: 0e1fa649cff6a072d676a33dab6a3cbb038cb6799b59088f7ebff1144e6983843fe16658c80fa75308f057caf24e5b232aea15c92f80290d0969d868e653159e
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
@@ -200,8 +200,8 @@ module ShellOpts
200
200
  # Number of characters between columns in brief output
201
201
  BRIEF_COL_SEP = 2
202
202
 
203
- # Maximum width of first column in brief option and command lists
204
- BRIEF_COL1_MIN_WIDTH = 6
203
+ # Minimum width of first column in brief option and command lists
204
+ BRIEF_COL1_MIN_WIDTH = 20
205
205
 
206
206
  # Maximum width of first column in brief option and command lists
207
207
  BRIEF_COL1_MAX_WIDTH = 40
@@ -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
@@ -122,6 +122,23 @@ module ShellOpts
122
122
  def self.parse(token)
123
123
  super(nil, token)
124
124
  end
125
+
126
+ def add_stdopts
127
+ option_token = Token.new(:option, 1, 1, "--version")
128
+ brief_token = Token.new(:brief, 1, 1, "Write version number and exit")
129
+ group = OptionGroup.new(self, option_token)
130
+ option = Option.parse(group, option_token)
131
+ brief = Brief.parse(group, brief_token)
132
+
133
+ option_token = Token.new(:option, 1, 1, "-h,help")
134
+ brief_token = Token.new(:brief, 1, 1, "Write help text and exit")
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")
137
+ group = OptionGroup.new(self, option_token)
138
+ option = Option.parse(group, option_token)
139
+ brief = Brief.parse(group, brief_token)
140
+ paragraph = Paragraph.parse(group, paragraph_token)
141
+ end
125
142
  end
126
143
 
127
144
  class ArgSpec
@@ -146,6 +163,14 @@ module ShellOpts
146
163
  @nodes = {}
147
164
  end
148
165
 
166
+ # def add_stdopts
167
+ # version_token = Token.new(:option, 1, 1, "--version")
168
+ # version_brief = Token.new(:brief, 1, 1, "Gryf gryf")
169
+ # group = Grammar::OptionGroup.new(@program, version_token)
170
+ # option = Grammar::Option.parse(group, version_token)
171
+ # brief = Grammr::Brief.parse(option, version_brief)
172
+ # end
173
+
149
174
  def parse()
150
175
  @program = Grammar::Program.parse(@tokens.shift)
151
176
  oneline = @tokens.first.lineno == @tokens.last.lineno
@@ -1,3 +1,3 @@
1
1
  module ShellOpts
2
- VERSION = "2.0.5"
2
+ VERSION = "2.0.8"
3
3
  end
data/lib/shellopts.rb CHANGED
@@ -127,16 +127,13 @@ module ShellOpts
127
127
  # Compile source and return grammar object. Also sets #spec and #grammar.
128
128
  # Returns the grammar
129
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
133
130
  handle_exceptions {
134
131
  @oneline = spec.index("\n").nil?
135
132
  @spec = spec.sub(/^\s*\n/, "")
136
133
  @file = find_caller_file
137
134
  @tokens = Lexer.lex(name, @spec, @oneline)
138
135
  ast = Parser.parse(tokens)
139
- # TODO: Add standard and message options and their handlers
136
+ ast.add_stdopts if stdopts
140
137
  @grammar = Analyzer.analyze(ast)
141
138
  }
142
139
  self
@@ -197,7 +194,7 @@ module ShellOpts
197
194
  saved = $stdout
198
195
  $stdout = $stderr
199
196
  $stderr.puts "#{name}: #{message}"
200
- Formatter.usage(program)
197
+ Formatter.usage(grammar)
201
198
  exit 1
202
199
  ensure
203
200
  $stdout = saved
data/main CHANGED
@@ -9,7 +9,9 @@ include ShellOpts
9
9
 
10
10
  VERSION = "1.2.3"
11
11
 
12
- SPEC = "-a"
12
+ SPEC = %(
13
+ -a @ An option
14
+ )
13
15
  opts, args = ShellOpts::process(SPEC, ARGV, version: VERSION)
14
16
  #ShellOpts::ShellOpts.help
15
17
 
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.5
4
+ version: 2.0.8
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