shellopts 2.0.0.pre.1 → 2.0.0.pre.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.
@@ -16,7 +16,7 @@ module ShellOpts
16
16
  end
17
17
 
18
18
  private
19
- # Parse a command line
19
+ # Parse a subcommand line
20
20
  class Parser
21
21
  class Error < RuntimeError; end
22
22
 
@@ -27,24 +27,24 @@ module ShellOpts
27
27
 
28
28
  def call
29
29
  program = Ast::Program.new(@grammar)
30
- parse_command(program)
30
+ parse_subcommand(program)
31
31
  program.arguments = @argv
32
32
  program
33
33
  end
34
34
 
35
35
  private
36
- def parse_command(command)
37
- @seen_options = {} # Every new command resets the seen options
36
+ def parse_subcommand(subcommand)
37
+ @seen_options = {} # Every new subcommand resets the seen options
38
38
  while arg = @argv.first
39
39
  if arg == "--"
40
40
  @argv.shift
41
41
  break
42
42
  elsif arg.start_with?("-")
43
- parse_option(command)
44
- elsif cmd = command.grammar.commands[arg]
43
+ parse_option(subcommand)
44
+ elsif cmd = subcommand.grammar.subcommands[arg]
45
45
  @argv.shift
46
- command.command = Ast::Command.new(cmd, arg)
47
- parse_command(command.command)
46
+ subcommand.subcommand = Ast::Command.new(cmd, arg)
47
+ parse_subcommand(subcommand.subcommand)
48
48
  break
49
49
  else
50
50
  break
@@ -52,7 +52,7 @@ module ShellOpts
52
52
  end
53
53
  end
54
54
 
55
- def parse_option(command)
55
+ def parse_option(subcommand)
56
56
  # Split into name and argument
57
57
  case @argv.first
58
58
  when /^(--.+?)(?:=(.*))?$/
@@ -62,7 +62,7 @@ module ShellOpts
62
62
  end
63
63
  @argv.shift
64
64
 
65
- option = command.grammar.options[name] or raise Error, "Unknown option '#{name}'"
65
+ option = subcommand.grammar.options[name] or raise Error, "Unknown option '#{name}'"
66
66
  !@seen_options.key?(option.key) || option.repeated? or raise Error, "Duplicate option '#{name}'"
67
67
  @seen_options[option.key] = true
68
68
 
@@ -83,7 +83,7 @@ module ShellOpts
83
83
  raise Error, "No argument allowed for option '#{name}'"
84
84
  end
85
85
 
86
- command.options << Ast::Option.new(option, name, arg)
86
+ subcommand.options << Ast::Option.new(option, name, arg)
87
87
  end
88
88
 
89
89
  def parse_arg(option, name, arg)
@@ -1,10 +1,12 @@
1
1
 
2
2
  require "shellopts"
3
3
 
4
+ require "shellopts/args.rb"
5
+
4
6
  # TODO
5
7
  #
6
8
  # PROCESSING
7
- # 1. Compile usage string and yield a grammar
9
+ # 1. Compile spec string and yield a grammar
8
10
  # 2. Parse the options using the grammar and yield an AST
9
11
  # 3. Construct the Program model from the AST
10
12
  # 4. Apply defaults to the model
@@ -15,19 +17,20 @@ require "shellopts"
15
17
  module ShellOpts
16
18
  # The command line processing object
17
19
  class ShellOpts
18
- # One of :key, :name, :option
19
- #
20
- # Option Command
21
- # :key key #command! (no collision)
22
- # :name name #command (possible collision)
23
- # :option --option #command (multihash, no collision) (TODO)
24
- #
25
- DEFAULT_USE = :key
26
-
27
20
  # Name of program
28
- attr_reader :name
21
+ attr_accessor :name
29
22
 
30
- # The grammar compiled from the usage string
23
+ # Usage string. If #usage is nil, the auto-generated default is used
24
+ def usage() @usage || @grammar.usage end
25
+ def usage=(usage) @usage = usage end
26
+
27
+ # Specification of the command
28
+ attr_reader :spec
29
+
30
+ # Original argv argument
31
+ attr_reader :argv
32
+
33
+ # The grammar compiled from the spec string
31
34
  attr_reader :grammar
32
35
 
33
36
  # The AST parsed from the command line arguments
@@ -36,32 +39,33 @@ module ShellOpts
36
39
  # The IDR generated from the Ast
37
40
  attr_reader :idr
38
41
 
39
- # Object for error & fail messages. Default is to write a message on
40
- # standard error and exit with status 1
41
- attr_accessor :messenger
42
-
43
- # Compile a usage string into a grammar and use that to parse command line
42
+ # Compile a spec string into a grammar and use that to parse command line
44
43
  # arguments
45
44
  #
46
- # +usage+ is the usage string, and +argv+ the command line (typically the
45
+ # +spec+ is the spec string, and +argv+ the command line (typically the
47
46
  # global ARGV array). +name+ is the name of the program and defaults to the
48
47
  # basename of the program
49
48
  #
50
- # Syntax errors in the usage string are caused by the developer and raise a
51
- # +ShellOpts::CompilerError+ exception. Errors in the +argv+ arguments are
49
+ # Syntax errors in the spec string are caused by the developer and raise a
50
+ # +ShellOpts::CompilerError+ exception. Errors in the +argv+ arguments are
52
51
  # caused by the user and terminates the program with an error message and a
53
- # short description of its usage
54
- def initialize(usage, argv, name: PROGRAM, messenger: nil)
52
+ # short description of its spec
53
+ #
54
+ # TODO: Change to (name, spec, argv, usage: nil) because
55
+ # ShellOpts::ShellOpts isn't a magician like the ShellOpts module
56
+ def initialize(spec, argv, name: ::ShellOpts.default_name, usage: ::ShellOpts.default_usage)
55
57
  @name = name
58
+ @spec = spec
59
+ @usage = usage
60
+ @argv = argv
56
61
  begin
57
- @grammar = Grammar.compile(name, usage)
58
- @messenger = messenger || Messenger.new(name, @grammar.usage)
59
- @ast = Ast.parse(@grammar, argv)
60
- @idr = Idr.generate(@ast, @messenger)
62
+ @grammar = Grammar.compile(@name, @spec)
63
+ @ast = Ast.parse(@grammar, @argv)
64
+ @idr = Idr.generate(self)
61
65
  rescue Grammar::Compiler::Error => ex
62
66
  raise CompilerError.new(5, ex.message)
63
67
  rescue Ast::Parser::Error => ex
64
- error(ex.message)
68
+ raise UserError.new(ex.message)
65
69
  end
66
70
  end
67
71
 
@@ -73,26 +77,40 @@ module ShellOpts
73
77
  def to_a() idr.to_a end
74
78
 
75
79
  # Return a hash representation of the options. See {ShellOpts::OptionsHash}
76
- def to_h(use: :key, aliases: {}) @idr.to_h(use: use, aliases: aliases) end
80
+ def to_h(key_type: ::ShellOpts.default_key_type, aliases: {})
81
+ @idr.to_h(key_type: :key_type, aliases: aliases)
82
+ end
83
+
84
+ # TODO
85
+ # Return OptionHash object
86
+ # def to_hash(...)
77
87
 
78
88
  # Return a struct representation of the options. See {ShellOpts::OptionStruct}
79
- def to_struct(use: :key, aliases: {}) @idr.to_struct(use: use, aliases: aliases) end
89
+ def to_struct(key_type: ::ShellOpts.default_key_type, aliases: {})
90
+ @idr.to_struct(key_type: key_type, aliases: aliases)
91
+ end
80
92
 
81
- # List of remaining non-option command line arguments. Shorthand for +ast&.arguments+
82
- def args() @ast&.arguments end
93
+ # List of remaining non-option command line arguments. Returns a Argv object
94
+ def args() Args.new(self, ast&.arguments) end
83
95
 
84
96
  # Iterate options and commands as name/value pairs. Same as +to_a.each+
85
97
  def each(&block) to_a.each(&block) end
86
98
 
87
- # Print error messages and usage string and exit with status 1. This method
99
+ # Print error messages and spec string and exit with status 1. This method
88
100
  # should be called in response to user-errors (eg. specifying an illegal
89
101
  # option)
90
- def error(*msgs) @messenger.error(*msgs) end
102
+ def error(*msgs, exit: true)
103
+ msg = "#{name}: #{msgs.join}\n" + (@usage ? usage : "Usage: #{name} #{usage}")
104
+ $stderr.puts msg.rstrip
105
+ exit(1) if exit
106
+ end
91
107
 
92
108
  # Print error message and exit with status 1. This method should called in
93
109
  # response to system errors (like disk full)
94
- def fail(*msgs) @messenger.fail(*msgs) end
110
+ def fail(*msgs, exit: true)
111
+ $stderr.puts "#{name}: #{msgs.join}"
112
+ exit(1) if exit
113
+ end
95
114
  end
96
115
  end
97
116
 
98
-
@@ -1,3 +1,3 @@
1
1
  module Shellopts
2
- VERSION = "2.0.0-1"
2
+ VERSION = "2.0.0.pre.8"
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.0.0.pre.1
4
+ version: 2.0.0.pre.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: 2020-07-27 00:00:00.000000000 Z
11
+ date: 2020-10-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -105,7 +105,9 @@ files:
105
105
  - bin/setup
106
106
  - doc/stylesheet.css
107
107
  - lib/ext/array.rb
108
+ - lib/main.rb
108
109
  - lib/shellopts.rb
110
+ - lib/shellopts/args.rb
109
111
  - lib/shellopts/ast/command.rb
110
112
  - lib/shellopts/ast/node.rb
111
113
  - lib/shellopts/ast/option.rb
@@ -117,13 +119,11 @@ files:
117
119
  - lib/shellopts/grammar/option.rb
118
120
  - lib/shellopts/grammar/program.rb
119
121
  - lib/shellopts/idr.rb
120
- - lib/shellopts/messenger.rb
122
+ - lib/shellopts/main.rb
121
123
  - lib/shellopts/option_struct.rb
122
124
  - lib/shellopts/parser.rb
123
125
  - lib/shellopts/shellopts.rb
124
- - lib/shellopts/utils.rb
125
126
  - lib/shellopts/version.rb
126
- - rs
127
127
  - shellopts.gemspec
128
128
  homepage: http://github.com/clrgit/shellopts
129
129
  licenses: []
@@ -1,71 +0,0 @@
1
-
2
- module ShellOpts
3
- # Service object for output of messages
4
- #
5
- # Messages are using the common command line formats
6
- #
7
- class Messenger
8
- # Name of the program. When assigning to +name+ prefixed and suffixed
9
- # whitespaces are removed
10
- attr_accessor :name
11
-
12
- # :nodoc:
13
- def name=(name) @name = name.strip end
14
- # :nodoc:
15
-
16
- # Usage string. If not nil the usage string is printed by #error. When
17
- # assigning to +usage+ suffixed whitespaces are removed and the format
18
- # automatically set to +:custom+
19
- attr_accessor :usage
20
-
21
- # :nodoc:
22
- def usage=(usage)
23
- @format = :custom
24
- @usage = usage&.rstrip
25
- end
26
- # :nodoc:
27
-
28
- # Format of the usage string: +:default+ prefixes the +usage+ with 'Usage:
29
- # #{name} ' before printing. +:custom+ prints +usage+ as is
30
- attr_accessor :format
31
-
32
- # Initialize a Messenger object. +name+ is the name of the name and +usage+
33
- # is a short description of the options (eg. '-a -b') or a longer multiline
34
- # explanation. The +:format+ option selects bewtween the two: +short+ (the
35
- # default) or :long. Note that
36
- #
37
- def initialize(name, usage, format: :default)
38
- @name = name
39
- @usage = usage
40
- @format = format
41
- end
42
-
43
- # Print error message and usage string and exit with status 1. Output is
44
- # using the following format
45
- #
46
- # <name name>: <message>
47
- # Usage: <name name> <options and arguments>
48
- #
49
- def error(*msgs)
50
- $stderr.print "#{name}: #{msgs.join}\n"
51
- if usage
52
- $stderr.print "Usage: #{name} " if format == :default
53
- $stderr.print "#{usage}\n"
54
- end
55
- exit 1
56
- end
57
-
58
- # Print error message and exit with status 1. It use the current ShellOpts
59
- # object if defined. This method should not be called in response to
60
- # user-errors but system errors (like disk full). Output is using the
61
- # following format:
62
- #
63
- # <name name>: <message>
64
- #
65
- def fail(*msgs)
66
- $stderr.puts "#{name}: #{msgs.join}"
67
- exit 1
68
- end
69
- end
70
- end
71
-
@@ -1,16 +0,0 @@
1
-
2
- module ShellOpts
3
- # Use `include ShellOpts::Utils` to include ShellOpts utility methods in the
4
- # global namespace
5
- module Utils
6
- # Forwards to `ShellOpts.error`
7
- def error(*msgs)
8
- ::ShellOpts.error(*msgs)
9
- end
10
-
11
- # Forwards to `ShellOpts.fail`
12
- def fail(*msgs)
13
- ::ShellOpts.fail(*msgs)
14
- end
15
- end
16
- end
data/rs DELETED
@@ -1,40 +0,0 @@
1
- #!/usr/bin/bash
2
-
3
- PROGRAM=$(basename $0)
4
- USAGE="SOURCE-FILE"
5
-
6
- function error() {
7
- echo "$PROGRAM: $@"
8
- echo "Usage: $PROGRAM $USAGE"
9
- exit 1
10
- } >&2
11
-
12
- [ $# = 1 ] || error "Illegal number of arguments"
13
- SOURCE_NAME=${1%.rb}.rb
14
-
15
- GEM_FILE=$(ls *.gemspec 2>/dev/null)
16
- [ -n "$GEM_FILE" ] || error "Can't find gemspec file"
17
- GEM_NAME=${GEM_FILE%.gemspec}
18
-
19
- if [ -f lib/$SOURCE_NAME ]; then
20
- SOURCE_FILE=lib/$SOURCE_NAME
21
- elif [ -f lib/$GEM_NAME/$SOURCE_NAME ]; then
22
- SOURCE_FILE=lib/$GEM_NAME/$SOURCE_NAME
23
- else
24
- SOURCE_FILE=$(find lib/$GEM_NAME -type f -path $SOURCE_NAME | head -1)
25
- if [ -z "$SOURCE_FILE" ]; then
26
- SOURCE_FILE=lib/$GEM_NAME/$SOURCE_NAME
27
- fi
28
- fi
29
-
30
- SPEC_FILE=spec/${SOURCE_NAME%.rb}_spec.rb
31
- [ -f $SPEC_FILE ] || error "Can't find spec file '$SPEC_FILE'"
32
-
33
- rspec --fail-fast $SPEC_FILE || {
34
- # rcov forgets a newline when rspec fails
35
- status=$?; echo; exit $status;
36
- }
37
-
38
-
39
-
40
-