choosy 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. data/Gemfile +7 -0
  2. data/Gemfile.lock +25 -0
  3. data/LICENSE +23 -0
  4. data/README.markdown +393 -0
  5. data/Rakefile +57 -0
  6. data/lib/VERSION +1 -0
  7. data/lib/choosy/base_command.rb +59 -0
  8. data/lib/choosy/command.rb +32 -0
  9. data/lib/choosy/converter.rb +115 -0
  10. data/lib/choosy/dsl/base_command_builder.rb +172 -0
  11. data/lib/choosy/dsl/command_builder.rb +43 -0
  12. data/lib/choosy/dsl/option_builder.rb +155 -0
  13. data/lib/choosy/dsl/super_command_builder.rb +41 -0
  14. data/lib/choosy/errors.rb +11 -0
  15. data/lib/choosy/option.rb +22 -0
  16. data/lib/choosy/parse_result.rb +64 -0
  17. data/lib/choosy/parser.rb +184 -0
  18. data/lib/choosy/printing/color.rb +101 -0
  19. data/lib/choosy/printing/erb_printer.rb +23 -0
  20. data/lib/choosy/printing/help_printer.rb +174 -0
  21. data/lib/choosy/super_command.rb +77 -0
  22. data/lib/choosy/super_parser.rb +81 -0
  23. data/lib/choosy/verifier.rb +62 -0
  24. data/lib/choosy/version.rb +12 -0
  25. data/lib/choosy.rb +16 -0
  26. data/spec/choosy/base_command_spec.rb +11 -0
  27. data/spec/choosy/command_spec.rb +51 -0
  28. data/spec/choosy/converter_spec.rb +145 -0
  29. data/spec/choosy/dsl/base_command_builder_spec.rb +328 -0
  30. data/spec/choosy/dsl/commmand_builder_spec.rb +80 -0
  31. data/spec/choosy/dsl/option_builder_spec.rb +386 -0
  32. data/spec/choosy/dsl/super_command_builder_spec.rb +83 -0
  33. data/spec/choosy/parser_spec.rb +275 -0
  34. data/spec/choosy/printing/color_spec.rb +74 -0
  35. data/spec/choosy/printing/help_printer_spec.rb +117 -0
  36. data/spec/choosy/super_command_spec.rb +80 -0
  37. data/spec/choosy/super_parser_spec.rb +106 -0
  38. data/spec/choosy/verifier_spec.rb +180 -0
  39. data/spec/integration/command-A_spec.rb +37 -0
  40. data/spec/integration/supercommand-A_spec.rb +61 -0
  41. data/spec/spec_helpers.rb +30 -0
  42. metadata +150 -0
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source "http://rubygems.org"
2
+
3
+ group :development do
4
+ gem 'rspec', :require => "spec"
5
+ gem 'autotest'
6
+ gem 'autotest-notification'
7
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,25 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ ZenTest (4.4.2)
5
+ autotest (4.4.6)
6
+ ZenTest (>= 4.4.1)
7
+ autotest-notification (2.3.1)
8
+ autotest (~> 4.3)
9
+ diff-lcs (1.1.2)
10
+ rspec (2.5.0)
11
+ rspec-core (~> 2.5.0)
12
+ rspec-expectations (~> 2.5.0)
13
+ rspec-mocks (~> 2.5.0)
14
+ rspec-core (2.5.1)
15
+ rspec-expectations (2.5.0)
16
+ diff-lcs (~> 1.1.2)
17
+ rspec-mocks (2.5.0)
18
+
19
+ PLATFORMS
20
+ ruby
21
+
22
+ DEPENDENCIES
23
+ autotest
24
+ autotest-notification
25
+ rspec
data/LICENSE ADDED
@@ -0,0 +1,23 @@
1
+ The MIT LICENSE
2
+
3
+ Copyright (c) 2011 Gabe McArthur
4
+ Copyright (c) 2010 Gabriel Horner
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining
7
+ a copy of this software and associated documentation files (the
8
+ "Software"), to deal in the Software without restriction, including
9
+ without limitation the rights to use, copy, modify, merge, publish,
10
+ distribute, sublicense, and/or sell copies of the Software, and to
11
+ permit persons to whom the Software is furnished to do so, subject to
12
+ the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be
15
+ included in all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.markdown ADDED
@@ -0,0 +1,393 @@
1
+ # Choosy: Picking your arguments carefully
2
+
3
+ This is a small DSL library for creating command line clients in Ruby. It is largely inspired by the <a href="https://github.com/defunkt/choice">choice</a>, <a href="https://github.com/visionmedia/commander">commander</a>, and <a href="http://furius.ca/optcomplete/">optcomplete.py</a> libraries, though it makes some different design decisions than they do. It is opinionated software.
4
+
5
+ This library should:
6
+
7
+ - Make creating command line clients relatively easy.
8
+ - Make creating supercommands like git, subversion, and gem easier.
9
+ - Allow you to add validation logic for your arguments within the parsing phase.
10
+ - Allowing for dependencies between options, so that you can more easily validate related options (i.e. if the<code>--bold</code> flag requires the <code>--font Arial</code> flag, then you should be able to ask for the <code>--font</code> option to be validated first, and then the <code>--bold</code> option.
11
+ - Allow you to customize its output using your own formatting system.
12
+ - Allow you to customize the output to your specifications.
13
+
14
+ This library should never:
15
+
16
+ - Interact with your execution logic. You can attach executors to commands for convenience, but the execution phase should be delegated to you, not the parsing library. Separation of concerns, people.
17
+ - Rely on display or user interface libraries like Highline, since this is only for parsing command lines.
18
+ - Pollute your namespaces with my DSL function names. (I really, really hate it when libraries do this.)
19
+
20
+ # Examples
21
+
22
+ #!/usr/bin/env ruby
23
+ # foo.rb
24
+
25
+ require 'rubygems'
26
+ require 'choosy'
27
+
28
+ FOO_VERSION = 1.0.1
29
+
30
+ class FooExecutor
31
+ def execute!(options, args)
32
+ puts "BOLDED!!" if options[:bold]
33
+ options[:count].times do
34
+ puts "#{options[:prefix]}#{options[:words].push('foo').join(',')#{options[:suffix}}"
35
+ end
36
+ puts "and #{args.join ' '}"
37
+ end
38
+ end
39
+
40
+ foo_cmd = Choosy::Command.new :foo do |foo|
41
+ # Add a class to do the execution when you call foo_cmd.execute!
42
+ # You can also use a proc that takes the options and the args, like:
43
+ # foo.executor { |opts, args| puts 'Hi!' }
44
+ foo.executor FooExecutor.new
45
+
46
+ # You can add your custom printer by giving the
47
+ # full path to an ERB template file here.
48
+ # The default printer is :standard, but you can
49
+ # also use the builtin printer :compact. The
50
+ # output can be colored or uncolored, though the
51
+ # default is colored.
52
+ foo.printer :standard, :colored => true
53
+
54
+ foo.summary 'Prints out "foo" to the console"
55
+ foo.desc <<HERE
56
+ This is a long description about what 'foo' is
57
+ and how it works. Don't worry your pretty little head
58
+ about the details.
59
+ HERE
60
+
61
+ foo.separator 'Required Options:'
62
+
63
+ # A shorthand for a common option type.
64
+ # It adds the '-p/--prefix PREFIX' infomation for you.
65
+ foo.single :prefix, "A prefix for 'foo'" do |p|
66
+ p.default '<'
67
+ p.required
68
+ end
69
+
70
+ # The long way to do the same thing as above, except with
71
+ # explicitly named dependencies
72
+ foo.option :suffix => [:prefix] do |o|
73
+ o.short '-s'
74
+ o.long '--suffix', 'SUFFIX'
75
+ o.desc 'A suffix for "foo"'
76
+ o.required
77
+
78
+ o.validate do |suffix|
79
+ if suffix == foo[:prefix]
80
+ o.fail "You can't matching prefixes and suffixes, you heathen!"
81
+ end
82
+ end
83
+ end
84
+
85
+ # Just like the 'single' method above, except now it automatically
86
+ # requires/casts the argument to this flag into an integer. These commands
87
+ # also take an optional hash as the last argument, which can be used instead
88
+ # of a block.
89
+ foo.integer :count, 'The number of times to repeat "foo"', :required => true
90
+
91
+ foo.separator
92
+ foo.separator 'Options:'
93
+
94
+ foo.option :words do |o|
95
+ o.short '-w'
96
+ o.long '--words', 'WORDS+' # By default, the '+' at the end
97
+ # means that this takes multiple
98
+ # arguments. You put a '-' at
99
+ # the end of the argument list
100
+ # to stop parsing this option
101
+ # and allow for regular args.
102
+ o.desc "Other fun words to put in quotes"
103
+ o.default [] # The default anyway on multple arg options
104
+
105
+ # Sets the exact count of the number of arguments it accepts.
106
+ # also allowable are the single selectors :zero and :one.
107
+ # By default, the option 'WORDS+' sets the range to be
108
+ # {:at_least => 1, :at_most => 1000 }
109
+ o.count {:at_least => 2, :at_most => 10 }
110
+
111
+ o.validate do |words|
112
+ words.each do |word|
113
+ if word !~ /\w+/
114
+ o.fail "I can't print that: #{word}"
115
+ end
116
+ end
117
+ end
118
+ end
119
+
120
+ # Alternatively, we could have done the following:
121
+ foo.strings :words, "Other fun words to put in quotes" do |w|
122
+ w.count {:at_least => 2, :at_most => 10 }
123
+ w.validate do |words|
124
+ words.each do |word|
125
+ if word !~ /\w+/
126
+ w.fail "I can't print that: #{word}"
127
+ end
128
+ end
129
+ end
130
+ end
131
+
132
+ # Yet another shorthand notation for options, since they
133
+ # are boolean by default
134
+ foo.option :bold => {:long => '--bold', :default => false}
135
+
136
+ options.separator
137
+ # Tail options
138
+
139
+ # When any of the simpler notations are suffixed with a '_'
140
+ # character, the short option is always suppressed.
141
+ foo.boolean_ :debug, "Prints out extra debugging output."
142
+
143
+ # The '_' characters are replaced with '-' in flags, so the
144
+ # following creates a '--no-color' flag.
145
+ foo.boolean_ :no_color, "Turns off coloring in the output" do |o|
146
+ o.validate do
147
+ foo.printer :standard, :colored => false
148
+ end
149
+ end
150
+
151
+ # Adds the standard -h/--help option.
152
+ # Should skip the '-h' flag if already set.
153
+ foo.help
154
+
155
+ # Adds the --version option.
156
+ foo.version "Foo: #{FOO_VERSION}"
157
+
158
+ # Now, add some validation for any addtional arguments
159
+ # that are left over after the parsing.
160
+ foo.arguments do |args|
161
+ if args.empty?
162
+ a.fail "You have to pass in empty arguments that do nothing!"
163
+ end
164
+ if args.count >= 3
165
+ a.fail "Whoa there! You're going argument crazy!"
166
+ end
167
+ end
168
+ end
169
+
170
+ if __FILE__ == $0
171
+ # Parses and validates the options.
172
+ args = ['--prefix', '{',
173
+ '--suffix', '}',
174
+ '--words', 'high', 'there', 'you', '-',
175
+ # The '-' stops parsing this option, so that:
176
+ 'handsom', 'devil',
177
+ 'http://not.posting.here', # will be regular arguments
178
+ '-c, '3', # Count
179
+ '--', # Stops parsing all arguments
180
+ '-h', '--help', '-v', '--version' # Ignored
181
+ ]
182
+ result = foo_cmd.parse!(args)
183
+
184
+ require 'pp'
185
+ pp result[:prefix] # => '{'
186
+ pp result[:suffix] # => '}'
187
+ pp reuslt[:count] # => 3
188
+ pp result[:bold] # => false
189
+ pp reuslt[:words] # => ['high', 'there', 'you']
190
+ pp reuslt.args # => ['handsom', 'devil',
191
+ # 'http://not.posting.here',
192
+ # '-h', '--help', '-v', '--version']
193
+ pp result.options # => {:prefix => '{', :suffix => '}'
194
+ # :count => 3, :bold => false,
195
+ # :words => ['high', 'there', 'you']}
196
+
197
+ # Now, call the command that does the actual work.
198
+ # This passes the foo_cmd.options and the foo_cmd.args
199
+ # as arguments to the executors 'execute!' method.
200
+ #
201
+ # This allows you to easily associate command classes with
202
+ # commands, without resorting to a hash or combining
203
+ # execution logic with command parsing logic.
204
+ foo_cmd.execute!(args) # {high,there,you,foo}
205
+ # {high,there,you,foo}
206
+ # {high,there,you,foo}
207
+ # and handsom devil http://not.posting.here -h --help -v --verbose
208
+
209
+ end
210
+
211
+ ### Super Commands
212
+
213
+ You can also combine multiple choices into an uber-choice, creating
214
+ commands that look a lot like git or subversion.
215
+
216
+ First, we create another command.
217
+
218
+ #!/usr/bin/env ruby
219
+ # bar.rb
220
+
221
+ require 'rubygems'
222
+ require 'choosy'
223
+
224
+ class BarExecutor
225
+ def execute!(options, args)
226
+ if options[:bold]
227
+ puts "BOLDED BAR"
228
+ else
229
+ puts "bar"
230
+ end
231
+ end
232
+ end
233
+
234
+ # Create a new command
235
+ bar_cmd = Choosy::Command.new :bar do |bar|
236
+ bar.executor BarExecutor.new
237
+ bar.summary "Just prints 'bar'"
238
+ bar.desc "A truly unremarkable command"
239
+
240
+ bar.boolean :bold, "Bolds something"
241
+
242
+ # Because there is no bar.arguments call,
243
+ # it is now an error if there are extra
244
+ # command line arguments to this command.
245
+ end
246
+
247
+ We can now create our super command.
248
+
249
+ #!/usr/bin/env ruby
250
+ # superfoo.rb
251
+
252
+ require 'rubygems'
253
+ require 'choosy'
254
+ require 'foo.rb'
255
+ require 'bar.rb'
256
+
257
+ SUPERFOO_VERSION = "1.0.1"
258
+
259
+ superfoo = Choosy::SuperCommand.new :superfoo do |superfoo|
260
+ superfoo.summary "This is a superfoo command."
261
+ superfoo.desc "Say something, dammit!"
262
+
263
+ # You can also add commands after instantiation.
264
+ # Note that, when added, these commands have their
265
+ # -h/--help/--version flags suppressed, so you'll
266
+ # need to add those flags here.
267
+ superfoo.commands bar_cmd
268
+
269
+ # Creates a 'help' command
270
+ superfoo.help do |help|
271
+ help.summary "Prints this help message"
272
+ end
273
+
274
+ # Create some global options that are parsed
275
+ # defore subcommand options
276
+
277
+ superfoo.option :config do |o|
278
+ o.long '--config', 'FILE'
279
+ o.desc "Configure your superfoo with a configuration file."
280
+
281
+ o.validate do |config|
282
+ if !File.exist? config
283
+ o.fail "Unable to find configuration file!"
284
+ end
285
+ end
286
+ end
287
+
288
+ # Adds a global --version flag.
289
+ superfoo.version do
290
+ puts "#{SUPERFOO_VERSION}"
291
+ end
292
+ end
293
+
294
+ # Add a command after the fact.
295
+ superfoo.commands foo_cmd
296
+
297
+ if __FILE__ == $0
298
+ superfoo.parse! ['-c', '5',
299
+ 'foo',
300
+ '--config', '~/.superfoo',
301
+ '--prefix', '{',
302
+ '--suffix', '}',
303
+ 'cruft',
304
+ 'bar',
305
+ '--bold']
306
+
307
+ require 'pp'
308
+ pp superfoo[:config] # => '~/.superfoo'
309
+ pp superfoo.subcommand.name # => :foo
310
+ pp superfoo.subcommand[:prefix] # => '{'
311
+ pp superfoo.subcommand[:suffix] # => '}'
312
+ pp superfoo.subcommand[:count] # => 2
313
+ pp superfoo.subcommand[:bold] # => true
314
+ pp superfoo.subcommand.options # => {:prefix => '{', :suffix => '}'
315
+ # :count => 2,
316
+ # :bold => true,
317
+ # :words => [],
318
+ # :config => '~/.superfoo' }
319
+ pp superfoo.subcommand.args # => ['cruft', 'bar']
320
+
321
+ pp superfoo.options # => {:prefix => '{', :suffix => '}'
322
+ # :count => 2,
323
+ # :bold => true,
324
+ # :words => [],
325
+ # :config => '~/.superfoo' }
326
+ pp superfoo.args # => ['cruft', 'bar']
327
+
328
+ # Now, we can call the subcommand
329
+ superfoo.execute! ## Calls superfoo.subcommand.execute!
330
+ ## Prints:
331
+ # BOLDED!!
332
+ # {foo}
333
+ # {foo}
334
+ # and cruft bar
335
+
336
+ # We got what we wanted, so reset the parser.
337
+ superfoo.reset!
338
+
339
+ # Instead of parsing the 'bar' parameter as an argument to
340
+ # the foo command, so that when the first argument that matches
341
+ # another command name is encountered, it stops parsing the
342
+ # current command and passes the rest of the arguments to the
343
+ # next command.
344
+ #
345
+ # You can also set this inside a SuperChoosy.new {|s| ... }
346
+ # block.
347
+ superfoo.parsimonious
348
+
349
+ superfoo.parse! ['-c', '5',
350
+ 'foo',
351
+ '--config', '~/.superfoo',
352
+ '--prefix', '{',
353
+ '--suffix', '}',
354
+ 'cruft',
355
+ 'bar',
356
+ '--bold']
357
+
358
+ pp superfoo[:config] # => '~/.superfoo'
359
+ pp superfoo.subcommand.name # => :foo
360
+ pp superfoo.subcommands[0].name # => :foo
361
+ pp superfoo.subcommands[0][:prefix] # => '{'
362
+ pp superfoo.subcommands[0][:suffix] # => '}'
363
+ pp superfoo.subcommands[0][:count] # => 2
364
+ pp superfoo.subcommands[0][:bold] # => true
365
+ pp superfoo.subcommands[0].options # => {:prefix => '{', :suffix => '}'
366
+ # :count => 2,
367
+ # :bold => false,
368
+ # :words => [],
369
+ # :config => '~/.superfoo' }
370
+ pp superfoo.subcommands[0].args # => ['cruft']
371
+
372
+ pp superfoo.subcommands[1].name # => :bar
373
+ pp superfoo.subcommands[1][:bold] # => true
374
+ pp superfoo.subcommands[1].options # => {:bold => true,
375
+ # :config => '~/.superfoo'}
376
+ pp superfoo.subcommands[1].args # => []
377
+
378
+ pp superfoo.options # => {:config => '~/.superfoo'}
379
+ pp superfoo.args # => []
380
+
381
+ # Now, execute the subcommands in order
382
+ superfoo.execute! ## Same as:
383
+ # superfoo.subcommands.each do |subcommand|
384
+ # command.execute!
385
+ # end
386
+ ## Prints:
387
+ # {foo}
388
+ # {foo}
389
+ # and cruft
390
+ # BOLDED BAR
391
+ end
392
+
393
+ ### TODO: Output Printing
data/Rakefile ADDED
@@ -0,0 +1,57 @@
1
+ # $LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
2
+ # $LOAD_PATH.unshift File.expand_path("../spec", __FILE__)
3
+
4
+ require 'rubygems'
5
+ require 'rake'
6
+ #require 'rake/rdoctask'
7
+ require 'rspec/core/rake_task'
8
+ require './lib/choosy/version'
9
+
10
+ PACKAGE_NAME = "choosy"
11
+ PACKAGE_VERSION = Choosy::Version
12
+
13
+ desc "Default task"
14
+ task :default => [ :spec ]
15
+
16
+ desc "Build documentation"
17
+ task :doc => [ :rdoc ]
18
+
19
+ #task :rdoc => SOURCE_FILES
20
+
21
+ desc "Run the RSpec tests"
22
+ RSpec::Core::RakeTask.new :spec
23
+
24
+ begin
25
+ require 'jeweler'
26
+ Jeweler::Tasks.new do |gem|
27
+ gem.name = PACKAGE_NAME
28
+ gem.version = PACKAGE_VERSION
29
+ gem.summary = 'Yet another option parsing library.'
30
+ gem.description = 'This is a DSL for creating more complicated command line tools.'
31
+ gem.email = ['madeonamac@gmail.com']
32
+ gem.authors = ['Gabe McArthur']
33
+ gem.homepage = 'http://github.com/gabemc/choosy'
34
+ gem.files = FileList["[A-Z]*", "{bin,lib,spec}/**/*"]
35
+
36
+ gem.add_development_dependency 'rspec', '~> 2.5'
37
+ end
38
+ rescue LoadError
39
+ puts "Jeweler or dependencies are not available. Install it with: gem install jeweler"
40
+ end
41
+
42
+ desc "Cleans the generated files."
43
+ task :clean do
44
+ rm Dir.glob('*.gemspec')
45
+ rm Dir.glob('*.gem')
46
+ rm_rf 'pkg'
47
+ end
48
+
49
+ desc "Deploys the gem to rubygems.org"
50
+ task :gem => :release do
51
+ system("gem build #{PACKAGE_NAME}.gemspec")
52
+ system("gem push #{PACKAGE_NAME}-#{PACKAGE_VERSION}.gem")
53
+ end
54
+
55
+ desc "Does the full release cycle."
56
+ task :deploy => [:gem, :clean] do
57
+ end
data/lib/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,59 @@
1
+ require 'choosy/errors'
2
+
3
+ module Choosy
4
+ class BaseCommand
5
+ attr_accessor :name, :summary, :description, :printer
6
+ attr_reader :builder, :listing, :option_builders
7
+
8
+ def initialize(name)
9
+ @name = name
10
+ @listing = []
11
+ @option_builders = {}
12
+
13
+ @builder = create_builder
14
+ yield @builder if block_given?
15
+ @builder.finalize!
16
+ end
17
+
18
+ def alter(&block)
19
+ yield @builder if block_given?
20
+ @builder.finalize!
21
+ end
22
+
23
+ def options
24
+ @option_builders.values.map {|b| b.option}
25
+ end
26
+
27
+ def parse!(args, propagate=false)
28
+ if propagate
29
+ return parse(args)
30
+ else
31
+ begin
32
+ return parse(args)
33
+ rescue Choosy::ValidationError, Choosy::ConversionError, Choosy::ParseError => e
34
+ $stderr << "#{@name}: #{e.message}\n"
35
+ exit 1
36
+ rescue Choosy::HelpCalled => e
37
+ handle_help(e)
38
+ exit 0
39
+ rescue Choosy::VersionCalled => e
40
+ $stdout << "#{e.message}\n"
41
+ exit 0
42
+ end
43
+ end
44
+ end
45
+
46
+ protected
47
+ def create_builder
48
+ # Override in subclasses
49
+ end
50
+
51
+ def parse(args)
52
+ # Override in subclasses
53
+ end
54
+
55
+ def handle_help(hc)
56
+ # Override in subclasses
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,32 @@
1
+ require 'choosy/errors'
2
+ require 'choosy/base_command'
3
+ require 'choosy/dsl/command_builder'
4
+ require 'choosy/parser'
5
+ require 'choosy/verifier'
6
+
7
+ module Choosy
8
+ class Command < BaseCommand
9
+ attr_accessor :executor, :argument_validation
10
+
11
+ def execute!(args)
12
+ raise Choosy::ConfigurationError.new("No executor given for: #{name}") unless executor
13
+ result = parse!(args)
14
+ executor.call(result.options, result.args)
15
+ end
16
+
17
+ protected
18
+ def create_builder
19
+ Choosy::DSL::CommandBuilder.new(self)
20
+ end
21
+
22
+ def handle_help(hc)
23
+ printer.print!(self)
24
+ end
25
+
26
+ def parse(args)
27
+ parser = Parser.new(self)
28
+ result = parser.parse!(args)
29
+ result.verify!
30
+ end
31
+ end
32
+ end