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

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
-