cri 2.0b1 → 2.0rc1

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,7 +1,121 @@
1
1
  Cri
2
2
  ===
3
3
 
4
- Cri is a library for building easy-to-use commandline tools.
4
+ Cri is a library for building easy-to-use commandline tools with support for
5
+ nested commands.
6
+
7
+ Usage
8
+ -----
9
+
10
+ The central concept in Cri is the _command_, which has option definitions as
11
+ well as code for actually executing itself. In Cri, the commandline tool
12
+ itself is a command as well.
13
+
14
+ Here’s a sample command definition:
15
+
16
+ command = Cri::Command.define do
17
+ name 'dostuff'
18
+ usage 'dostuff [options]'
19
+ aliases :ds, :stuff
20
+ summary 'does stuff'
21
+ description 'This command does a lot of stuff. I really mean a lot.'
22
+
23
+ flag :h, :help, 'show help for this command' do |value, cmd|
24
+ puts cmd.help
25
+ exit 0
26
+ end
27
+ flag :m, :more, 'do even more stuff'
28
+ option :s, :stuff, 'specify stuff to do', :argument => :required
29
+
30
+ run do |opts, args, cmd|
31
+ stuff = opts[:stuff] || 'generic stuff'
32
+ puts "Doing #{stuff}!"
33
+
34
+ if opts[:more]
35
+ puts 'Doing it even more!'
36
+ end
37
+ end
38
+ end
39
+
40
+ To run this command, invoke the `#run` method with the raw arguments. For
41
+ example, for a root command (the commandline tool itself), the command could
42
+ be called like this:
43
+
44
+ command.run(ARGS)
45
+
46
+ Each command has automatically generated help. This help can be printed using
47
+ {Cri::Command#help}; something like this will be shown:
48
+
49
+ usage: dostuff [options]
50
+
51
+ does stuff
52
+
53
+ This command does a lot of stuff. I really mean a lot.
54
+
55
+ options:
56
+
57
+ -h --help show help for this command
58
+ -m --more do even more stuff
59
+ -s --stuff specify stuff to do
60
+
61
+ Let’s disect the command definition and start with the first five lines:
62
+
63
+ name 'dostuff'
64
+ usage 'dostuff [options]'
65
+ aliases :ds, :stuff
66
+ summary 'does stuff'
67
+ description 'This command does a lot of stuff. I really mean a lot.'
68
+
69
+ These lines of the command definition specify the name of the command (or the
70
+ commandline tool, if the command is the root command), the usage, a list of
71
+ aliases that can be used to call this command, a one-line summary and a (long)
72
+ description. The usage should not include a “usage:” prefix nor the name of
73
+ the supercommand, because the latter will be automatically prepended.
74
+
75
+ Aliases don’t make sense for root commands, but for subcommands they do.
76
+
77
+ The next few lines contain the command’s option definitions:
78
+
79
+ flag :h, :help, 'show help for this command' do |value, cmd|
80
+ puts cmd.help
81
+ exit 0
82
+ end
83
+ flag :m, :more, 'do even more stuff'
84
+ option :s, :stuff, 'specify stuff to do', :argument => :required
85
+
86
+ Options can be defined using the following methods:
87
+
88
+ * {Cri::CommandDSL#option} or {Cri::CommandDSL#opt}
89
+ * {Cri::CommandDSL#flag} (implies forbidden argument)
90
+ * {Cri::CommandDSL#required} (implies required argument)
91
+ * {Cri::CommandDSL#optional} (implies optional argument)
92
+
93
+ Each of the above methods also take a block, which will be executed when the
94
+ option is found. The argument to the block are the option value (`true` in
95
+ case the option does not have an argument) and the command.
96
+
97
+ The last part of the command defines the execution itself:
98
+
99
+ run do |opts, args, cmd|
100
+ stuff = opts[:stuff] || 'generic stuff'
101
+ puts "Doing #{stuff}!"
102
+
103
+ if opts[:more]
104
+ puts 'Doing it even more!'
105
+ end
106
+ end
107
+
108
+ The {Cri::CommandDSL#run} method takes a block with the actual code to
109
+ execute. This block takes three arguments: the options, any arguments passed
110
+ to the command, and the command itself.
111
+
112
+ Commands can have subcommands. For example, the `git` commandline tool would be represented by a command that has subcommands named `commit`, `add`, and so on. Commands with subcommands do not use a run block; execution will always be dispatched to a subcommand (or none, if no subcommand is found).
113
+
114
+ To add a command as a subcommand to another command, use the {Cri::Command#add_command} method, like this:
115
+
116
+ root_cmd.add_command cmd_add
117
+ root_cmd.add_command cmd_commit
118
+ root.cmd.add_command cmd_init
5
119
 
6
120
  Contributors
7
121
  ------------
data/lib/cri.rb CHANGED
@@ -2,20 +2,22 @@
2
2
 
3
3
  module Cri
4
4
 
5
- # @todo Document
5
+ # A generic error class for all Cri-specific errors.
6
6
  class Error < ::StandardError
7
7
  end
8
8
 
9
- # @todo Document
9
+ # Error that will be raised when an implementation for a method or command
10
+ # is missing. For commands, this may mean that a run block is missing.
10
11
  class NotImplementedError < Error
11
12
  end
12
13
 
13
- # @todo Document
14
+ # Error that will be raised when no help is available because the help
15
+ # command has no supercommand for which to show help.
14
16
  class NoHelpAvailableError < Error
15
17
  end
16
18
 
17
19
  # The current Cri version.
18
- VERSION = '2.0b1'
20
+ VERSION = '2.0rc1'
19
21
 
20
22
  autoload 'Command', 'cri/command'
21
23
  autoload 'CommandDSL', 'cri/command_dsl'
data/lib/cri/command.rb CHANGED
@@ -45,35 +45,49 @@ module Cri
45
45
 
46
46
  end
47
47
 
48
- # @todo Document
48
+ # @return [Cri::Command, nil] This command’s supercommand, or nil if the
49
+ # command has no supercommand
49
50
  attr_accessor :supercommand
50
51
 
51
- # @todo document
52
+ # @return [Set<Cri::Command>] This command’s subcommands
52
53
  attr_accessor :commands
53
54
  alias_method :subcommands, :commands
54
55
 
55
- # @todo Document
56
+ # @return [String] The name
56
57
  attr_accessor :name
57
58
 
58
- # @todo Document
59
+ # @return [Array<String>] A list of aliases for this command that can be
60
+ # used to invoke this command
59
61
  attr_accessor :aliases
60
62
 
61
- # @todo Document
62
- attr_accessor :short_desc
63
+ # @return [String] The short description (“summary”)
64
+ attr_accessor :summary
63
65
 
64
- # @todo Document
65
- attr_accessor :long_desc
66
+ # @return [String] The long description (“description”)
67
+ attr_accessor :description
66
68
 
67
- # @todo Document
69
+ # @return [String] The usage, without the “usage:” prefix and without the
70
+ # supercommands’ names.
68
71
  attr_accessor :usage
69
72
 
70
- # @todo Document
73
+ # @return [Array<Hash>] The list of option definitions
71
74
  attr_accessor :option_definitions
72
75
 
73
- # @todo Document
76
+ # @return [Proc] The block that should be executed when invoking this
77
+ # command (ignored for commands with subcommands)
74
78
  attr_accessor :block
75
79
 
76
- # @todo Document
80
+ # Creates a new command using the DSL. If a string is given, the command
81
+ # will be defined using the string; if a block is given, the block will be
82
+ # used instead.
83
+ #
84
+ # If the block has one parameter, the block will be executed in the same
85
+ # context with the command DSL as its parameter. If the block has no
86
+ # parameters, the block will be executed in the context of the DSL.
87
+ #
88
+ # @param [String, nil] The string containing the command’s definition
89
+ #
90
+ # @return [Cri::Command] The newly defined command
77
91
  def self.define(string=nil, &block)
78
92
  dsl = Cri::CommandDSL.new
79
93
  if string
@@ -86,13 +100,19 @@ module Cri
86
100
  dsl.command
87
101
  end
88
102
 
89
- # @todo Document
103
+ # Returns a new command that has support for the `-h`/`--help` option and
104
+ # also has a `help` subcommand. It is intended to be modified (adding
105
+ # name, summary, description, other subcommands, …)
106
+ #
107
+ # @return [Cri::Command] A basic root command
90
108
  def self.new_basic_root
91
109
  filename = File.dirname(__FILE__) + '/commands/basic_root.rb'
92
110
  self.define(File.read(filename))
93
111
  end
94
112
 
95
- # @todo Document
113
+ # Returns a new command that implements showing help.
114
+ #
115
+ # @return [Cri::Command] A basic help command
96
116
  def self.new_basic_help
97
117
  filename = File.dirname(__FILE__) + '/commands/basic_help.rb'
98
118
  self.define(File.read(filename))
@@ -100,11 +120,17 @@ module Cri
100
120
 
101
121
  def initialize
102
122
  @aliases = Set.new
103
- @commands = Set.new # TODO make this a hash (name -> cmd)
123
+ @commands = Set.new
104
124
  @option_definitions = Set.new
105
125
  end
106
126
 
107
- # @todo Document
127
+ # Modifies the command using the DSL.
128
+ #
129
+ # If the block has one parameter, the block will be executed in the same
130
+ # context with the command DSL as its parameter. If the block has no
131
+ # parameters, the block will be executed in the context of the DSL.
132
+ #
133
+ # @return [Cri::Command] The command itself
108
134
  def modify(&block)
109
135
  dsl = Cri::CommandDSL.new(self)
110
136
  if block.arity == 0
@@ -115,7 +141,8 @@ module Cri
115
141
  self
116
142
  end
117
143
 
118
- # @todo Document
144
+ # @return [Hash] The option definitions for the command itself and all its
145
+ # ancestors
119
146
  def global_option_definitions
120
147
  res = Set.new
121
148
  res.merge(option_definitions)
@@ -123,13 +150,22 @@ module Cri
123
150
  res
124
151
  end
125
152
 
126
- # @todo Document
153
+ # Adds the given command as a subcommand to the current command.
154
+ #
155
+ # @param [Cri::Command] command The command to add as a subcommand
156
+ #
157
+ # @return [void]
127
158
  def add_command(command)
128
159
  @commands << command
129
160
  command.supercommand = self
130
161
  end
131
162
 
132
- # @todo Document
163
+ # Defines a new subcommand for the current command using the DSL.
164
+ #
165
+ # @param [String, nil] name The name of the subcommand, or nil if no name
166
+ # should be set (yet)
167
+ #
168
+ # @return [Cri::Command] The subcommand
133
169
  def define_command(name=nil, &block)
134
170
  # Execute DSL
135
171
  dsl = Cri::CommandDSL.new
@@ -149,7 +185,9 @@ module Cri
149
185
  # Returns the commands that could be referred to with the given name. If
150
186
  # the result contains more than one command, the name is ambiguous.
151
187
  #
152
- # @todo Document
188
+ # @param [String] name The full, partial or aliases name of the command
189
+ #
190
+ # @return [Array<Cri::Command>] A list of commands matching the given name
153
191
  def commands_named(name)
154
192
  # Find by exact name or alias
155
193
  @commands.each do |cmd|
@@ -163,9 +201,15 @@ module Cri
163
201
  end
164
202
  end
165
203
 
166
- # Returns the command with the given name.
204
+ # Returns the command with the given name. This method will display error
205
+ # messages and exit in case of an error (unknown or ambiguous command).
206
+ #
207
+ # The name can be a full command name, a partial command name (e.g. “com”
208
+ # for “commit”) or an aliased command name (e.g. “ci” for “commit”).
167
209
  #
168
- # @todo Document
210
+ # @param [String] name The full, partial or aliases name of the command
211
+ #
212
+ # @return [Cri::Command] The command with the given name
169
213
  def command_named(name)
170
214
  commands = commands_named(name)
171
215
 
@@ -181,7 +225,14 @@ module Cri
181
225
  end
182
226
  end
183
227
 
184
- # @todo Document
228
+ # Runs the command with the given commandline arguments.
229
+ #
230
+ # @param [Array<String>] opts_and_args A list of unparsed arguments
231
+ #
232
+ # @param [Hash] parent_opts A hash of options already handled by the
233
+ # supercommand
234
+ #
235
+ # @return [void]
185
236
  def run(opts_and_args, parent_opts={})
186
237
  if subcommands.empty?
187
238
  # Parse
@@ -240,15 +291,15 @@ module Cri
240
291
  end
241
292
 
242
293
  # Append short description
243
- if short_desc
294
+ if summary
244
295
  text << "\n"
245
- text << short_desc + "\n"
296
+ text << summary + "\n"
246
297
  end
247
298
 
248
299
  # Append long description
249
- if long_desc
300
+ if description
250
301
  text << "\n"
251
- text << long_desc.wrap_and_indent(78, 4) + "\n"
302
+ text << description.wrap_and_indent(78, 4) + "\n"
252
303
  end
253
304
 
254
305
  # Append subcommands
@@ -260,7 +311,7 @@ module Cri
260
311
  self.commands.each do |cmd|
261
312
  text << sprintf(" %-#{length+4}s %s\n",
262
313
  cmd.name,
263
- cmd.short_desc)
314
+ cmd.summary)
264
315
  end
265
316
  end
266
317
 
@@ -2,76 +2,155 @@
2
2
 
3
3
  module Cri
4
4
 
5
- # @todo Document
5
+ # The command DSL is a class that is used for building and modifying
6
+ # commands.
6
7
  class CommandDSL
7
8
 
9
+ # @param [Cri::Command, nil] command The command to modify, or nil if a
10
+ # new command should be created
8
11
  def initialize(command=nil)
9
12
  @command = command || Cri::Command.new
10
13
  end
11
14
 
12
- # @todo Document
15
+ # @return [Cri::Command] The built command
13
16
  def command
14
17
  @command
15
18
  end
16
19
 
17
- # @todo Document
18
- def subcommand(cmd=nil, &block)
19
- if cmd.nil?
20
- cmd = Cri::Command.define(&block)
20
+ # Adds a subcommand to the current command. The command can either be
21
+ # given explicitly, or a block can be given that defines the command.
22
+ #
23
+ # @param [Cri::Command, nil] command The command to add as a subcommand,
24
+ # or nil if the block should be used to define the command that will be
25
+ # added as a subcommand
26
+ #
27
+ # @return [void]
28
+ def subcommand(command=nil, &block)
29
+ if command.nil?
30
+ command = Cri::Command.define(&block)
21
31
  end
22
32
 
23
- @command.add_command(cmd)
33
+ @command.add_command(command)
24
34
  end
25
35
 
26
- # @todo Document
36
+ # Sets the command name.
37
+ #
38
+ # @param [String] arg The new command name
39
+ #
40
+ # @return [void]
27
41
  def name(arg)
28
42
  @command.name = arg
29
43
  end
30
44
 
31
- # @todo Document
45
+ # Sets the command aliases.
46
+ #
47
+ # @param [String, Symbol, Array] args The new command aliases
48
+ #
49
+ # @return [void]
32
50
  def aliases(*args)
33
- @command.aliases = args.flatten
51
+ @command.aliases = args.flatten.map { |a| a.to_s }
34
52
  end
35
53
 
36
- # @todo Document
54
+ # Sets the command summary.
55
+ #
56
+ # @param [String] arg The new command summary
57
+ #
58
+ # @return [void]
37
59
  def summary(arg)
38
- @command.short_desc = arg
60
+ @command.summary = arg
39
61
  end
40
62
 
41
- # @todo Document
63
+ # Sets the command description.
64
+ #
65
+ # @param [String] arg The new command description
66
+ #
67
+ # @return [void]
42
68
  def description(arg)
43
- @command.long_desc = arg
69
+ @command.description = arg
44
70
  end
45
71
 
46
- # @todo Document
72
+ # Sets the command usage. The usage should not include the “usage:”
73
+ # prefix, nor should it include the command names of the supercommand.
74
+ #
75
+ # @param [String] arg The new command usage
76
+ #
77
+ # @return [void]
47
78
  def usage(arg)
48
79
  @command.usage = arg
49
80
  end
50
81
 
51
- # @todo Document
82
+ # Adds a new option to the command. If a block is given, it will be
83
+ # executed when the option is successfully parsed.
84
+ #
85
+ # @param [String, Symbol] short The short option name
86
+ #
87
+ # @param [String, Symbol] long The long option name
88
+ #
89
+ # @param [String] desc The option description
90
+ #
91
+ # @option params [:forbidden, :required, :optional] :argument Whether the
92
+ # argument is forbidden, required or optional
93
+ #
94
+ # @return [void]
52
95
  def option(short, long, desc, params={}, &block)
53
96
  requiredness = params[:argument] || :forbidden
54
97
  self.add_option(short, long, desc, requiredness, block)
55
98
  end
56
99
  alias_method :opt, :option
57
100
 
58
- # @todo Document
101
+ # Adds a new option with a required argument to the command. If a block is
102
+ # given, it will be executed when the option is successfully parsed.
103
+ #
104
+ # @param [String, Symbol] short The short option name
105
+ #
106
+ # @param [String, Symbol] long The long option name
107
+ #
108
+ # @param [String] desc The option description
109
+ #
110
+ # @return [void]
111
+ #
112
+ # @see {#option}
59
113
  def required(short, long, desc, &block)
60
114
  self.add_option(short, long, desc, :required, block)
61
115
  end
62
116
 
63
- # @todo Document
117
+ # Adds a new option with a forbidden argument to the command. If a block
118
+ # is given, it will be executed when the option is successfully parsed.
119
+ #
120
+ # @param [String, Symbol] short The short option name
121
+ #
122
+ # @param [String, Symbol] long The long option name
123
+ #
124
+ # @param [String] desc The option description
125
+ #
126
+ # @return [void]
127
+ #
128
+ # @see {#option}
64
129
  def flag(short, long, desc, &block)
65
130
  self.add_option(short, long, desc, :forbidden, block)
66
131
  end
67
132
  alias_method :forbidden, :flag
68
133
 
69
- # @todo Document
134
+ # Adds a new option with an optional argument to the command. If a block
135
+ # is given, it will be executed when the option is successfully parsed.
136
+ #
137
+ # @param [String, Symbol] short The short option name
138
+ #
139
+ # @param [String, Symbol] long The long option name
140
+ #
141
+ # @param [String] desc The option description
142
+ #
143
+ # @return [void]
144
+ #
145
+ # @see {#option}
70
146
  def optional(short, long, desc, &block)
71
147
  self.add_option(short, long, desc, :optional, block)
72
148
  end
73
149
 
74
- # @todo Document
150
+ # Sets the run block to the given block. The given block should have two
151
+ # or three arguments (options, arguments, and optionally the command).
152
+ #
153
+ # @return [void]
75
154
  def run(&block)
76
155
  unless block.arity != 2 || block.arity != 3
77
156
  raise ArgumentError,
@@ -83,7 +162,6 @@ module Cri
83
162
 
84
163
  protected
85
164
 
86
- # @todo Document
87
165
  def add_option(short, long, desc, argument, block)
88
166
  @command.option_definitions << {
89
167
  :short => short.to_s,
@@ -4,7 +4,9 @@ module Cri::CoreExtensions
4
4
 
5
5
  module String
6
6
 
7
- # @todo Document
7
+ # Extracts individual paragraphs (separated by two newlines).
8
+ #
9
+ # @return [Array<String>] A list of paragraphs in the string
8
10
  def to_paragraphs
9
11
  lines = self.scan(/([^\n]+\n|[^\n]*$)/).map { |s| s[0].strip }
10
12
 
@@ -22,10 +24,13 @@ module Cri::CoreExtensions
22
24
 
23
25
  # Word-wraps and indents the string.
24
26
  #
25
- # +width+:: The maximal width of each line. This also includes indentation,
26
- # i.e. the actual maximal width of the text is width-indentation.
27
+ # @param [Number] width The maximal width of each line. This also includes
28
+ # indentation, i.e. the actual maximal width of the text is
29
+ # `width`-`indentation`.
30
+ #
31
+ # @param [Number] indentation The number of spaces to indent each line.
27
32
  #
28
- # +indentation+:: The number of spaces to indent each wrapped line.
33
+ # @return [String] The word-wrapped and indented string
29
34
  def wrap_and_indent(width, indentation)
30
35
  # Split into paragraphs
31
36
  paragraphs = self.to_paragraphs
@@ -3,6 +3,58 @@
3
3
  module Cri
4
4
 
5
5
  # Cri::OptionParser is used for parsing commandline options.
6
+ #
7
+ # Option definitions are hashes with the keys `:short`, `:long` and
8
+ # `:argument` (optionally `:description` but this is not used by the
9
+ # option parser, only by the help generator). `:short` is the short,
10
+ # one-character option, without the `-` prefix. `:long` is the long,
11
+ # multi-character option, without the `--` prefix. `:argument` can be
12
+ # :required (if an argument should be provided to the option), :optional
13
+ # (if an argument may be provided) or :forbidden (if an argument should
14
+ # not be provided).
15
+ #
16
+ # A sample array of definition hashes could look like this:
17
+ #
18
+ # [
19
+ # { :short => 'a', :long => 'all', :argument => :forbidden },
20
+ # { :short => 'p', :long => 'port', :argument => :required },
21
+ # ]
22
+ #
23
+ # For example, the following commandline options (which should not be
24
+ # passed as a string, but as an array of strings):
25
+ #
26
+ # foo -xyz -a hiss -s -m please --level 50 --father=ani -n luke squeak
27
+ #
28
+ # with the following option definitions:
29
+ #
30
+ # [
31
+ # { :short => 'x', :long => 'xxx', :argument => :forbidden },
32
+ # { :short => 'y', :long => 'yyy', :argument => :forbidden },
33
+ # { :short => 'z', :long => 'zzz', :argument => :forbidden },
34
+ # { :short => 'a', :long => 'all', :argument => :forbidden },
35
+ # { :short => 's', :long => 'stuff', :argument => :optional },
36
+ # { :short => 'm', :long => 'more', :argument => :optional },
37
+ # { :short => 'l', :long => 'level', :argument => :required },
38
+ # { :short => 'f', :long => 'father', :argument => :required },
39
+ # { :short => 'n', :long => 'name', :argument => :required }
40
+ # ]
41
+ #
42
+ # will be translated into:
43
+ #
44
+ # {
45
+ # :arguments => [ 'foo', 'hiss', 'squeak' ],
46
+ # :options => {
47
+ # :xxx => true,
48
+ # :yyy => true,
49
+ # :zzz => true,
50
+ # :all => true,
51
+ # :stuff => true,
52
+ # :more => 'please',
53
+ # :level => '50',
54
+ # :father => 'ani',
55
+ # :name => 'luke'
56
+ # }
57
+ # }
6
58
  class OptionParser
7
59
 
8
60
  # Error that will be raised when an unknown option is encountered.
@@ -49,6 +101,13 @@ module Cri
49
101
 
50
102
  # Parses the commandline arguments. See the instance `parse` method for
51
103
  # details.
104
+ #
105
+ # @param [Array<String>] arguments_and_options An array containing the
106
+ # commandline arguments (will probably be `ARGS` for a root command)
107
+ #
108
+ # @param [Array<Hash>] definitions An array of option definitions
109
+ #
110
+ # @return [Cri::OptionParser] The option parser self
52
111
  def self.parse(arguments_and_options, definitions)
53
112
  self.new(arguments_and_options, definitions).run
54
113
  end
@@ -56,7 +115,7 @@ module Cri
56
115
  # Creates a new parser with the given options/arguments and definitions.
57
116
  #
58
117
  # @param [Array<String>] arguments_and_options An array containing the
59
- # commandline arguments
118
+ # commandline arguments (will probably be `ARGS` for a root command)
60
119
  #
61
120
  # @param [Array<Hash>] definitions An array of option definitions
62
121
  def initialize(arguments_and_options, definitions)
@@ -83,80 +142,17 @@ module Cri
83
142
  @running = false
84
143
  end
85
144
 
86
- # Parses the commandline arguments into options and arguments
87
- #
88
- # +arguments_and_options+ is an array of commandline arguments and
89
- # options. This will usually be +ARGV+.
90
- #
91
- # +definitions+ contains a list of hashes defining which options are
92
- # allowed and how they will be handled. Such a hash has three keys:
93
- #
94
- # :short:: The short name of the option, e.g. +a+. Do not include the '-'
95
- # prefix.
96
- #
97
- # :long:: The long name of the option, e.g. +all+. Do not include the '--'
98
- # prefix.
99
- #
100
- # :argument:: Whether this option's argument is required (:required),
101
- # optional (:optional) or forbidden (:forbidden).
102
- #
103
- # A sample array of definition hashes could look like this:
104
- #
105
- # [
106
- # { :short => 'a', :long => 'all', :argument => :forbidden },
107
- # { :short => 'p', :long => 'port', :argument => :required },
108
- # ]
145
+ # Parses the commandline arguments into options and arguments.
109
146
  #
110
147
  # During parsing, two errors can be raised:
111
148
  #
112
- # IllegalOptionError:: An unrecognised option was encountered, i.e. an
113
- # option that is not present in the list of option
114
- # definitions.
115
- #
116
- # OptionRequiresAnArgumentError:: An option was found that did not have a
117
- # value, even though this value was
118
- # required.
119
- #
120
- # What will be returned, is a hash with two keys, :arguments and :options.
121
- # The :arguments value contains a list of arguments, and the :options
122
- # value contains a hash with key-value pairs for each option. Options
123
- # without values will have a +nil+ value instead.
124
- #
125
- # For example, the following commandline options (which should not be
126
- # passed as a string, but as an array of strings):
149
+ # @raise IllegalOptionError if an unrecognised option was encountered,
150
+ # i.e. an option that is not present in the list of option definitions
127
151
  #
128
- # foo -xyz -a hiss -s -m please --level 50 --father=ani -n luke squeak
152
+ # @raise OptionRequiresAnArgumentError if an option was found that did not
153
+ # have a value, even though this value was required.
129
154
  #
130
- # with the following option definitions:
131
- #
132
- # [
133
- # { :short => 'x', :long => 'xxx', :argument => :forbidden },
134
- # { :short => 'y', :long => 'yyy', :argument => :forbidden },
135
- # { :short => 'z', :long => 'zzz', :argument => :forbidden },
136
- # { :short => 'a', :long => 'all', :argument => :forbidden },
137
- # { :short => 's', :long => 'stuff', :argument => :optional },
138
- # { :short => 'm', :long => 'more', :argument => :optional },
139
- # { :short => 'l', :long => 'level', :argument => :required },
140
- # { :short => 'f', :long => 'father', :argument => :required },
141
- # { :short => 'n', :long => 'name', :argument => :required }
142
- # ]
143
- #
144
- # will be translated into:
145
- #
146
- # {
147
- # :arguments => [ 'foo', 'hiss', 'squeak' ],
148
- # :options => {
149
- # :xxx => true,
150
- # :yyy => true,
151
- # :zzz => true,
152
- # :all => true,
153
- # :stuff => true,
154
- # :more => 'please',
155
- # :level => '50',
156
- # :father => 'ani',
157
- # :name => 'luke'
158
- # }
159
- # }
155
+ # @return [Cri::OptionParser] The option parser self
160
156
  def run
161
157
  @running = true
162
158
 
@@ -241,8 +237,7 @@ module Cri
241
237
  add_argument(e)
242
238
  end
243
239
  end
244
-
245
- { :options => options, :arguments => arguments }
240
+ self
246
241
  ensure
247
242
  @running = false
248
243
  end
@@ -31,8 +31,8 @@ class Cri::CommandDSLTestCase < Cri::TestCase
31
31
  # Check
32
32
  assert_equal 'moo', command.name
33
33
  assert_equal 'dunno whatever', command.usage
34
- assert_equal 'does stuff', command.short_desc
35
- assert_equal 'This command does a lot of stuff.', command.long_desc
34
+ assert_equal 'does stuff', command.summary
35
+ assert_equal 'This command does a lot of stuff.', command.description
36
36
 
37
37
  # Check options
38
38
  expected_option_definitions = Set.new([
@@ -63,4 +63,16 @@ class Cri::CommandDSLTestCase < Cri::TestCase
63
63
  assert_equal 'sub', command.subcommands.to_a[0].name
64
64
  end
65
65
 
66
+ def test_aliases
67
+ # Define
68
+ dsl = Cri::CommandDSL.new
69
+ dsl.instance_eval do
70
+ aliases :moo, :aah
71
+ end
72
+ command = dsl.command
73
+
74
+ # Check
75
+ assert_equal %w( aah moo ), command.aliases.sort
76
+ end
77
+
66
78
  end
@@ -6,10 +6,10 @@ class Cri::OptionParserTestCase < Cri::TestCase
6
6
  input = %w( foo bar baz )
7
7
  definitions = []
8
8
 
9
- result = Cri::OptionParser.parse(input, definitions)
9
+ parser = Cri::OptionParser.parse(input, definitions)
10
10
 
11
- assert_equal({}, result[:options])
12
- assert_equal([ 'foo', 'bar', 'baz' ], result[:arguments])
11
+ assert_equal({}, parser.options)
12
+ assert_equal([ 'foo', 'bar', 'baz' ], parser.arguments)
13
13
  end
14
14
 
15
15
  def test_parse_with_invalid_option
@@ -19,7 +19,7 @@ class Cri::OptionParserTestCase < Cri::TestCase
19
19
  result = nil
20
20
 
21
21
  assert_raises(Cri::OptionParser::IllegalOptionError) do
22
- result = Cri::OptionParser.parse(input, definitions)
22
+ parser = Cri::OptionParser.parse(input, definitions)
23
23
  end
24
24
  end
25
25
 
@@ -29,9 +29,9 @@ class Cri::OptionParserTestCase < Cri::TestCase
29
29
  { :long => 'aaa', :short => 'a', :argument => :forbidden }
30
30
  ]
31
31
 
32
- result = Cri::OptionParser.parse(input, definitions)
32
+ parser = Cri::OptionParser.parse(input, definitions)
33
33
 
34
- assert(!result[:options][:aaa])
34
+ assert(!parser.options[:aaa])
35
35
  end
36
36
 
37
37
  def test_parse_with_long_valueless_option
@@ -40,10 +40,10 @@ class Cri::OptionParserTestCase < Cri::TestCase
40
40
  { :long => 'aaa', :short => 'a', :argument => :forbidden }
41
41
  ]
42
42
 
43
- result = Cri::OptionParser.parse(input, definitions)
43
+ parser = Cri::OptionParser.parse(input, definitions)
44
44
 
45
- assert(result[:options][:aaa])
46
- assert_equal([ 'foo', 'bar' ], result[:arguments])
45
+ assert(parser.options[:aaa])
46
+ assert_equal([ 'foo', 'bar' ], parser.arguments)
47
47
  end
48
48
 
49
49
  def test_parse_with_long_valueful_option
@@ -52,10 +52,10 @@ class Cri::OptionParserTestCase < Cri::TestCase
52
52
  { :long => 'aaa', :short => 'a', :argument => :required }
53
53
  ]
54
54
 
55
- result = Cri::OptionParser.parse(input, definitions)
55
+ parser = Cri::OptionParser.parse(input, definitions)
56
56
 
57
- assert_equal({ :aaa => 'xxx' }, result[:options])
58
- assert_equal([ 'foo', 'bar' ], result[:arguments])
57
+ assert_equal({ :aaa => 'xxx' }, parser.options)
58
+ assert_equal([ 'foo', 'bar' ], parser.arguments)
59
59
  end
60
60
 
61
61
  def test_parse_with_long_valueful_equalsign_option
@@ -64,10 +64,10 @@ class Cri::OptionParserTestCase < Cri::TestCase
64
64
  { :long => 'aaa', :short => 'a', :argument => :required }
65
65
  ]
66
66
 
67
- result = Cri::OptionParser.parse(input, definitions)
67
+ parser = Cri::OptionParser.parse(input, definitions)
68
68
 
69
- assert_equal({ :aaa => 'xxx' }, result[:options])
70
- assert_equal([ 'foo', 'bar' ], result[:arguments])
69
+ assert_equal({ :aaa => 'xxx' }, parser.options)
70
+ assert_equal([ 'foo', 'bar' ], parser.arguments)
71
71
  end
72
72
 
73
73
  def test_parse_with_long_valueful_option_with_missing_value
@@ -79,7 +79,7 @@ class Cri::OptionParserTestCase < Cri::TestCase
79
79
  result = nil
80
80
 
81
81
  assert_raises(Cri::OptionParser::OptionRequiresAnArgumentError) do
82
- result = Cri::OptionParser.parse(input, definitions)
82
+ parser = Cri::OptionParser.parse(input, definitions)
83
83
  end
84
84
  end
85
85
 
@@ -93,7 +93,7 @@ class Cri::OptionParserTestCase < Cri::TestCase
93
93
  result = nil
94
94
 
95
95
  assert_raises(Cri::OptionParser::OptionRequiresAnArgumentError) do
96
- result = Cri::OptionParser.parse(input, definitions)
96
+ parser = Cri::OptionParser.parse(input, definitions)
97
97
  end
98
98
  end
99
99
 
@@ -103,10 +103,10 @@ class Cri::OptionParserTestCase < Cri::TestCase
103
103
  { :long => 'aaa', :short => 'a', :argument => :optional }
104
104
  ]
105
105
 
106
- result = Cri::OptionParser.parse(input, definitions)
106
+ parser = Cri::OptionParser.parse(input, definitions)
107
107
 
108
- assert(result[:options][:aaa])
109
- assert_equal([ 'foo' ], result[:arguments])
108
+ assert(parser.options[:aaa])
109
+ assert_equal([ 'foo' ], parser.arguments)
110
110
  end
111
111
 
112
112
  def test_parse_with_long_valueful_option_with_optional_value
@@ -115,10 +115,10 @@ class Cri::OptionParserTestCase < Cri::TestCase
115
115
  { :long => 'aaa', :short => 'a', :argument => :optional }
116
116
  ]
117
117
 
118
- result = Cri::OptionParser.parse(input, definitions)
118
+ parser = Cri::OptionParser.parse(input, definitions)
119
119
 
120
- assert_equal({ :aaa => 'xxx' }, result[:options])
121
- assert_equal([ 'foo' ], result[:arguments])
120
+ assert_equal({ :aaa => 'xxx' }, parser.options)
121
+ assert_equal([ 'foo' ], parser.arguments)
122
122
  end
123
123
 
124
124
  def test_parse_with_long_valueless_option_with_optional_value_and_more_options
@@ -129,12 +129,12 @@ class Cri::OptionParserTestCase < Cri::TestCase
129
129
  { :long => 'ccc', :short => 'c', :argument => :forbidden }
130
130
  ]
131
131
 
132
- result = Cri::OptionParser.parse(input, definitions)
132
+ parser = Cri::OptionParser.parse(input, definitions)
133
133
 
134
- assert(result[:options][:aaa])
135
- assert(result[:options][:bbb])
136
- assert(result[:options][:ccc])
137
- assert_equal([ 'foo' ], result[:arguments])
134
+ assert(parser.options[:aaa])
135
+ assert(parser.options[:bbb])
136
+ assert(parser.options[:ccc])
137
+ assert_equal([ 'foo' ], parser.arguments)
138
138
  end
139
139
 
140
140
  def test_parse_with_short_valueless_options
@@ -143,10 +143,10 @@ class Cri::OptionParserTestCase < Cri::TestCase
143
143
  { :long => 'aaa', :short => 'a', :argument => :forbidden }
144
144
  ]
145
145
 
146
- result = Cri::OptionParser.parse(input, definitions)
146
+ parser = Cri::OptionParser.parse(input, definitions)
147
147
 
148
- assert(result[:options][:aaa])
149
- assert_equal([ 'foo', 'bar' ], result[:arguments])
148
+ assert(parser.options[:aaa])
149
+ assert_equal([ 'foo', 'bar' ], parser.arguments)
150
150
  end
151
151
 
152
152
  def test_parse_with_short_valueful_option_with_missing_value
@@ -158,7 +158,7 @@ class Cri::OptionParserTestCase < Cri::TestCase
158
158
  result = nil
159
159
 
160
160
  assert_raises(Cri::OptionParser::OptionRequiresAnArgumentError) do
161
- result = Cri::OptionParser.parse(input, definitions)
161
+ parser = Cri::OptionParser.parse(input, definitions)
162
162
  end
163
163
  end
164
164
 
@@ -170,12 +170,12 @@ class Cri::OptionParserTestCase < Cri::TestCase
170
170
  { :long => 'ccc', :short => 'c', :argument => :forbidden }
171
171
  ]
172
172
 
173
- result = Cri::OptionParser.parse(input, definitions)
173
+ parser = Cri::OptionParser.parse(input, definitions)
174
174
 
175
- assert(result[:options][:aaa])
176
- assert(result[:options][:bbb])
177
- assert(result[:options][:ccc])
178
- assert_equal([ 'foo', 'bar' ], result[:arguments])
175
+ assert(parser.options[:aaa])
176
+ assert(parser.options[:bbb])
177
+ assert(parser.options[:ccc])
178
+ assert_equal([ 'foo', 'bar' ], parser.arguments)
179
179
  end
180
180
 
181
181
  def test_parse_with_short_combined_valueful_options_with_missing_value
@@ -189,7 +189,7 @@ class Cri::OptionParserTestCase < Cri::TestCase
189
189
  result = nil
190
190
 
191
191
  assert_raises(Cri::OptionParser::OptionRequiresAnArgumentError) do
192
- result = Cri::OptionParser.parse(input, definitions)
192
+ parser = Cri::OptionParser.parse(input, definitions)
193
193
  end
194
194
  end
195
195
 
@@ -203,7 +203,7 @@ class Cri::OptionParserTestCase < Cri::TestCase
203
203
  result = nil
204
204
 
205
205
  assert_raises(Cri::OptionParser::OptionRequiresAnArgumentError) do
206
- result = Cri::OptionParser.parse(input, definitions)
206
+ parser = Cri::OptionParser.parse(input, definitions)
207
207
  end
208
208
  end
209
209
 
@@ -213,10 +213,10 @@ class Cri::OptionParserTestCase < Cri::TestCase
213
213
  { :long => 'aaa', :short => 'a', :argument => :optional }
214
214
  ]
215
215
 
216
- result = Cri::OptionParser.parse(input, definitions)
216
+ parser = Cri::OptionParser.parse(input, definitions)
217
217
 
218
- assert(result[:options][:aaa])
219
- assert_equal([ 'foo' ], result[:arguments])
218
+ assert(parser.options[:aaa])
219
+ assert_equal([ 'foo' ], parser.arguments)
220
220
  end
221
221
 
222
222
  def test_parse_with_short_valueful_option_with_optional_value
@@ -225,10 +225,10 @@ class Cri::OptionParserTestCase < Cri::TestCase
225
225
  { :long => 'aaa', :short => 'a', :argument => :optional }
226
226
  ]
227
227
 
228
- result = Cri::OptionParser.parse(input, definitions)
228
+ parser = Cri::OptionParser.parse(input, definitions)
229
229
 
230
- assert_equal({ :aaa => 'xxx' }, result[:options])
231
- assert_equal([ 'foo' ], result[:arguments])
230
+ assert_equal({ :aaa => 'xxx' }, parser.options)
231
+ assert_equal([ 'foo' ], parser.arguments)
232
232
  end
233
233
 
234
234
  def test_parse_with_short_valueless_option_with_optional_value_and_more_options
@@ -239,32 +239,32 @@ class Cri::OptionParserTestCase < Cri::TestCase
239
239
  { :long => 'ccc', :short => 'c', :argument => :forbidden }
240
240
  ]
241
241
 
242
- result = Cri::OptionParser.parse(input, definitions)
242
+ parser = Cri::OptionParser.parse(input, definitions)
243
243
 
244
- assert(result[:options][:aaa])
245
- assert(result[:options][:bbb])
246
- assert(result[:options][:ccc])
247
- assert_equal([ 'foo' ], result[:arguments])
244
+ assert(parser.options[:aaa])
245
+ assert(parser.options[:bbb])
246
+ assert(parser.options[:ccc])
247
+ assert_equal([ 'foo' ], parser.arguments)
248
248
  end
249
249
 
250
250
  def test_parse_with_single_hyphen
251
251
  input = %w( foo - bar )
252
252
  definitions = []
253
253
 
254
- result = Cri::OptionParser.parse(input, definitions)
254
+ parser = Cri::OptionParser.parse(input, definitions)
255
255
 
256
- assert_equal({}, result[:options])
257
- assert_equal([ 'foo', '-', 'bar' ], result[:arguments])
256
+ assert_equal({}, parser.options)
257
+ assert_equal([ 'foo', '-', 'bar' ], parser.arguments)
258
258
  end
259
259
 
260
260
  def test_parse_with_end_marker
261
261
  input = %w( foo bar -- -x --yyy -abc )
262
262
  definitions = []
263
263
 
264
- result = Cri::OptionParser.parse(input, definitions)
264
+ parser = Cri::OptionParser.parse(input, definitions)
265
265
 
266
- assert_equal({}, result[:options])
267
- assert_equal([ 'foo', 'bar', '-x', '--yyy', '-abc' ], result[:arguments])
266
+ assert_equal({}, parser.options)
267
+ assert_equal([ 'foo', 'bar', '-x', '--yyy', '-abc' ], parser.arguments)
268
268
  end
269
269
 
270
270
  def test_parse_with_end_marker_between_option_key_and_value
@@ -274,7 +274,7 @@ class Cri::OptionParserTestCase < Cri::TestCase
274
274
  ]
275
275
 
276
276
  assert_raises(Cri::OptionParser::OptionRequiresAnArgumentError) do
277
- result = Cri::OptionParser.parse(input, definitions)
277
+ parser = Cri::OptionParser.parse(input, definitions)
278
278
  end
279
279
  end
280
280
 
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: cri
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease: 3
5
- version: 2.0b1
5
+ version: 2.0rc1
6
6
  platform: ruby
7
7
  authors:
8
8
  - Denis Defreyne
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2011-06-22 00:00:00 Z
13
+ date: 2011-06-26 00:00:00 Z
14
14
  dependencies: []
15
15
 
16
16
  description: Cri allows building easy-to-use commandline interfaces with support for subcommands.