shellopts 2.0.0.pre.3 → 2.0.0.pre.11

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)
@@ -6,7 +6,7 @@ require "shellopts/args.rb"
6
6
  # TODO
7
7
  #
8
8
  # PROCESSING
9
- # 1. Compile usage string and yield a grammar
9
+ # 1. Compile spec string and yield a grammar
10
10
  # 2. Parse the options using the grammar and yield an AST
11
11
  # 3. Construct the Program model from the AST
12
12
  # 4. Apply defaults to the model
@@ -17,19 +17,20 @@ require "shellopts/args.rb"
17
17
  module ShellOpts
18
18
  # The command line processing object
19
19
  class ShellOpts
20
- # One of :key, :name, :option
21
- #
22
- # Option Command
23
- # :key key #command! (no collision)
24
- # :name name #command (possible collision)
25
- # :option --option #command (multihash, no collision) (TODO)
26
- #
27
- DEFAULT_USE = :key
28
-
29
20
  # Name of program
30
- attr_reader :name
21
+ attr_accessor :name
22
+
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
31
29
 
32
- # The grammar compiled from the usage string
30
+ # Original argv argument
31
+ attr_reader :argv
32
+
33
+ # The grammar compiled from the spec string
33
34
  attr_reader :grammar
34
35
 
35
36
  # The AST parsed from the command line arguments
@@ -38,33 +39,41 @@ module ShellOpts
38
39
  # The IDR generated from the Ast
39
40
  attr_reader :idr
40
41
 
41
- # Object for error & fail messages. Default is to write a message on
42
- # standard error and exit with status 1
43
- attr_accessor :messenger
44
-
45
- # Compile a usage string into a grammar and use that to parse command line
46
- # arguments
42
+ # Compile a spec string into a grammar
47
43
  #
48
- # +usage+ is the usage string, and +argv+ the command line (typically the
44
+ # +spec+ is the spec string, and +argv+ the command line (typically the
49
45
  # global ARGV array). +name+ is the name of the program and defaults to the
50
46
  # basename of the program
51
47
  #
52
- # Syntax errors in the usage string are caused by the developer and raise a
53
- # +ShellOpts::CompilerError+ exception. Errors in the +argv+ arguments are
54
- # caused by the user and terminates the program with an error message and a
55
- # short description of its usage
56
- def initialize(usage, argv, name: PROGRAM, messenger: nil)
48
+ # Syntax errors in the spec string are caused by the developer and cause
49
+ # #initialize to raise a +ShellOpts::CompilerError+ exception. Errors in
50
+ # the +argv+ arguments are caused by the user and cause #process to raise
51
+ # ShellOpts::UserError exception
52
+ #
53
+ # TODO: Change to (name, spec, argv, usage: nil) because
54
+ # ShellOpts::ShellOpts isn't a magician like the ShellOpts module
55
+ def initialize(spec, argv, name: ::ShellOpts.default_name, usage: ::ShellOpts.default_usage)
57
56
  @name = name
57
+ @spec = spec
58
+ @usage = usage
59
+ @argv = argv
58
60
  begin
59
- @grammar = Grammar.compile(name, usage)
60
- @messenger = messenger || Messenger.new(name, @grammar.usage)
61
- @ast = Ast.parse(@grammar, argv)
62
- @idr = Idr.generate(@ast, @messenger)
61
+ @grammar = Grammar.compile(@name, @spec)
63
62
  rescue Grammar::Compiler::Error => ex
64
63
  raise CompilerError.new(5, ex.message)
64
+ end
65
+ end
66
+
67
+ # Process command line arguments and return self. Raises a
68
+ # ShellOpts::UserError in case of an error
69
+ def process
70
+ begin
71
+ @ast = Ast.parse(@grammar, @argv)
72
+ @idr = Idr.generate(self)
65
73
  rescue Ast::Parser::Error => ex
66
- error(ex.message)
74
+ raise UserError.new(ex.message)
67
75
  end
76
+ self
68
77
  end
69
78
 
70
79
  # Return an array representation of options and commands in the same order
@@ -75,10 +84,18 @@ module ShellOpts
75
84
  def to_a() idr.to_a end
76
85
 
77
86
  # Return a hash representation of the options. See {ShellOpts::OptionsHash}
78
- def to_h(use: :key, aliases: {}) @idr.to_h(use: use, aliases: aliases) end
87
+ def to_h(key_type: ::ShellOpts.default_key_type, aliases: {})
88
+ @idr.to_h(key_type: :key_type, aliases: aliases)
89
+ end
90
+
91
+ # TODO
92
+ # Return OptionHash object
93
+ # def to_hash(...)
79
94
 
80
95
  # Return a struct representation of the options. See {ShellOpts::OptionStruct}
81
- def to_struct(use: :key, aliases: {}) @idr.to_struct(use: use, aliases: aliases) end
96
+ def to_struct(key_type: ::ShellOpts.default_key_type, aliases: {})
97
+ @idr.to_struct(key_type: key_type, aliases: aliases)
98
+ end
82
99
 
83
100
  # List of remaining non-option command line arguments. Returns a Argv object
84
101
  def args() Args.new(self, ast&.arguments) end
@@ -86,15 +103,21 @@ module ShellOpts
86
103
  # Iterate options and commands as name/value pairs. Same as +to_a.each+
87
104
  def each(&block) to_a.each(&block) end
88
105
 
89
- # Print error messages and usage string and exit with status 1. This method
106
+ # Print error messages and spec string and exit with status 1. This method
90
107
  # should be called in response to user-errors (eg. specifying an illegal
91
108
  # option)
92
- def error(*msgs) @messenger.error(*msgs) end
109
+ def error(*msgs, exit: true)
110
+ msg = "#{name}: #{msgs.join}\n" + (@usage ? usage : "Usage: #{name} #{usage}")
111
+ $stderr.puts msg.rstrip
112
+ exit(1) if exit
113
+ end
93
114
 
94
115
  # Print error message and exit with status 1. This method should called in
95
116
  # response to system errors (like disk full)
96
- def fail(*msgs) @messenger.fail(*msgs) end
117
+ def fail(*msgs, exit: true)
118
+ $stderr.puts "#{name}: #{msgs.join}"
119
+ exit(1) if exit
120
+ end
97
121
  end
98
122
  end
99
123
 
100
-
@@ -1,3 +1,3 @@
1
1
  module Shellopts
2
- VERSION = "2.0.0-3"
2
+ VERSION = "2.0.0.pre.11"
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.3
4
+ version: 2.0.0.pre.11
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-28 00:00:00.000000000 Z
11
+ date: 2020-11-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -105,6 +105,7 @@ 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
109
110
  - lib/shellopts/args.rb
110
111
  - lib/shellopts/ast/command.rb
@@ -118,13 +119,11 @@ files:
118
119
  - lib/shellopts/grammar/option.rb
119
120
  - lib/shellopts/grammar/program.rb
120
121
  - lib/shellopts/idr.rb
121
- - lib/shellopts/messenger.rb
122
+ - lib/shellopts/main.rb
122
123
  - lib/shellopts/option_struct.rb
123
124
  - lib/shellopts/parser.rb
124
125
  - lib/shellopts/shellopts.rb
125
- - lib/shellopts/utils.rb
126
126
  - lib/shellopts/version.rb
127
- - rs
128
127
  - shellopts.gemspec
129
128
  homepage: http://github.com/clrgit/shellopts
130
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
-