gli 2.5.6 → 2.6.0.rc1

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.
Files changed (46) hide show
  1. data/.travis.yml +1 -0
  2. data/Gemfile +1 -1
  3. data/features/step_definitions/todo_steps.rb +4 -0
  4. data/features/todo.feature +22 -0
  5. data/features/todo_legacy.feature +128 -0
  6. data/lib/gli.rb +3 -1
  7. data/lib/gli/app.rb +10 -2
  8. data/lib/gli/app_support.rb +39 -31
  9. data/lib/gli/command.rb +3 -2
  10. data/lib/gli/command_finder.rb +41 -0
  11. data/lib/gli/command_line_token.rb +0 -4
  12. data/lib/gli/command_support.rb +37 -64
  13. data/lib/gli/commands/doc.rb +12 -2
  14. data/lib/gli/commands/help.rb +3 -0
  15. data/lib/gli/commands/help_modules/command_help_format.rb +22 -5
  16. data/lib/gli/commands/scaffold.rb +1 -1
  17. data/lib/gli/exceptions.rb +17 -5
  18. data/lib/gli/gli_option_block_parser.rb +84 -0
  19. data/lib/gli/gli_option_parser.rb +116 -96
  20. data/lib/gli/option_parser_factory.rb +42 -10
  21. data/lib/gli/option_parsing_result.rb +19 -0
  22. data/lib/gli/version.rb +1 -1
  23. data/test/apps/todo/bin/todo +2 -0
  24. data/test/apps/todo/lib/todo/commands/make.rb +52 -0
  25. data/test/apps/todo_legacy/Gemfile +2 -0
  26. data/test/apps/todo_legacy/README.rdoc +6 -0
  27. data/test/apps/todo_legacy/Rakefile +23 -0
  28. data/test/apps/todo_legacy/bin/todo +61 -0
  29. data/test/apps/todo_legacy/lib/todo/commands/create.rb +24 -0
  30. data/test/apps/todo_legacy/lib/todo/commands/list.rb +63 -0
  31. data/test/apps/todo_legacy/lib/todo/commands/ls.rb +47 -0
  32. data/test/apps/todo_legacy/lib/todo/version.rb +3 -0
  33. data/test/apps/todo_legacy/test/tc_nothing.rb +14 -0
  34. data/test/apps/todo_legacy/todo.gemspec +23 -0
  35. data/test/apps/todo_legacy/todo.rdoc +5 -0
  36. data/test/tc_command.rb +84 -59
  37. data/test/{tc_compount_command.rb → tc_compound_command.rb} +0 -0
  38. data/test/tc_flag.rb +0 -1
  39. data/test/tc_gli.rb +2 -2
  40. data/test/tc_help.rb +11 -3
  41. data/test/tc_subcommand_parsing.rb +104 -0
  42. data/test/tc_subcommands.rb +1 -0
  43. data/test/tc_switch.rb +0 -1
  44. data/test/test_helper.rb +5 -0
  45. metadata +74 -13
  46. data/lib/gli/copy_options_to_aliases.rb +0 -33
@@ -13,10 +13,6 @@ module GLI
13
13
  @name,@aliases,@names = parse_names(names)
14
14
  end
15
15
 
16
- def usage #:nodoc:
17
- all_forms
18
- end
19
-
20
16
  # Sort based on primary name
21
17
  def <=>(other)
22
18
  self.name.to_s <=> other.name.to_s
@@ -50,58 +50,53 @@ module GLI
50
50
  end
51
51
 
52
52
  def flag(*names)
53
- new_flag = if parent.kind_of? Command
54
- parent.flag(*names)
55
- else
56
- super(*names)
57
- end
58
- new_flag.associated_command = self
59
- new_flag
60
- end
61
-
62
- def switch(*names)
63
- new_switch = if parent.kind_of? Command
64
- parent.switch(*names)
53
+ if send_declarations_to_parent?
54
+ new_flag = if parent.kind_of? Command
55
+ super(*names)
56
+ parent.flag(*names)
65
57
  else
66
58
  super(*names)
67
59
  end
68
- new_switch.associated_command = self
69
- new_switch
60
+ new_flag.associated_command = self
61
+ new_flag
62
+ else
63
+ super(*names)
64
+ end
65
+ end
66
+
67
+ def switch(*names)
68
+ if send_declarations_to_parent?
69
+ new_switch = if parent.kind_of? Command
70
+ super(*names)
71
+ parent.switch(*names)
72
+ else
73
+ super(*names)
74
+ end
75
+ new_switch.associated_command = self
76
+ new_switch
77
+ else
78
+ super(*names)
79
+ end
70
80
  end
71
81
 
72
82
  def desc(d)
73
- parent.desc(d) if parent.kind_of? Command
83
+ parent.desc(d) if parent.kind_of?(Command) && send_declarations_to_parent?
74
84
  super(d)
75
85
  end
76
86
 
77
87
  def long_desc(d)
78
- parent.long_desc(d) if parent.kind_of? Command
88
+ parent.long_desc(d) if parent.kind_of?(Command) && send_declarations_to_parent?
79
89
  super(d)
80
90
  end
81
91
 
82
92
  def arg_name(d,options=[])
83
- if parent.kind_of? Command
84
- parent.arg_name(d,options)
85
- else
86
- super(d,options)
87
- end
93
+ parent.arg_name(d,options) if parent.kind_of?(Command) && send_declarations_to_parent?
94
+ super(d,options)
88
95
  end
89
96
 
90
97
  def default_value(d)
91
- if parent.kind_of? Command
92
- parent.default_value(d)
93
- else
94
- super(d)
95
- end
96
- end
97
-
98
- # Get the usage string
99
- # CR: This should probably not be here
100
- def usage
101
- usage = name.to_s
102
- usage += ' [command options]' if !flags.empty? || !switches.empty?
103
- usage += ' ' + @arguments_description if @arguments_description
104
- usage
98
+ parent.default_value(d) if parent.kind_of?(Command) && send_declarations_to_parent?
99
+ super(d)
105
100
  end
106
101
 
107
102
  # Return the flags as a Hash
@@ -123,12 +118,7 @@ module GLI
123
118
 
124
119
  # Executes the command
125
120
  def execute(global_options,options,arguments)
126
- subcommand,arguments = find_subcommand(arguments)
127
- if subcommand
128
- subcommand.execute(global_options,options,arguments)
129
- else
130
- get_action(arguments).call(global_options,options,arguments)
131
- end
121
+ get_action(arguments).call(global_options,options,arguments)
132
122
  end
133
123
 
134
124
  def topmost_ancestor
@@ -151,6 +141,11 @@ module GLI
151
141
 
152
142
  private
153
143
 
144
+ def send_declarations_to_parent?
145
+ app = topmost_ancestor.parent
146
+ app.nil? ? true : (app.subcommand_option_handling_strategy == :legacy)
147
+ end
148
+
154
149
  def get_action(arguments)
155
150
  if @action
156
151
  @action
@@ -168,7 +163,7 @@ module GLI
168
163
  raise BadCommandLine,"Command '#{name}' requires a subcommand"
169
164
  end
170
165
  elsif have_subcommands?
171
- raise BadCommandLine,"Command '#{name}' requires a subcommand"
166
+ raise BadCommandLine,"Command '#{name}' requires a subcommand #{self.commands.keys.join(',')}"
172
167
  else
173
168
  raise "Command '#{name}' has no action block"
174
169
  end
@@ -182,27 +177,5 @@ module GLI
182
177
  def have_subcommands?
183
178
  !self.commands.empty?
184
179
  end
185
-
186
- def find_subcommand(arguments)
187
- subcommand = find_explicit_subcommand(arguments)
188
- if subcommand
189
- [subcommand,arguments[1..-1]]
190
- else
191
- if !@default_command.nil?
192
- [find_explicit_subcommand([@default_command.to_s]),arguments]
193
- else
194
- [false,arguments]
195
- end
196
- end
197
- end
198
-
199
- def find_explicit_subcommand(arguments)
200
- arguments = Array(arguments)
201
- return false if arguments.empty?
202
- subcommand_name = arguments.first
203
- self.commands.values.find { |command|
204
- [command.name,Array(command.aliases)].flatten.map(&:to_s).any? { |name| name == subcommand_name }
205
- }
206
- end
207
180
  end
208
181
  end
@@ -14,6 +14,8 @@ module GLI
14
14
  :skips_pre => true, :skips_post => true, :skips_around => true, :hidden => true)
15
15
 
16
16
  @app = app
17
+ @parent = @app
18
+ @subcommand_option_handling_strategy = @app.subcommand_option_handling_strategy
17
19
 
18
20
  desc 'The format name of the documentation to generate or the class name to use to generate it'
19
21
  default_value 'rdoc'
@@ -171,11 +173,19 @@ module GLI
171
173
  end
172
174
 
173
175
  def command_flags(command)
174
- command.topmost_ancestor.flags.values.select { |flag| flag.associated_command == command }.sort(&by_name)
176
+ if @subcommand_option_handling_strategy == :legacy
177
+ command.topmost_ancestor.flags.values.select { |flag| flag.associated_command == command }.sort(&by_name)
178
+ else
179
+ command.flags.values.sort(&by_name)
180
+ end
175
181
  end
176
182
 
177
183
  def command_switches(command)
178
- command.topmost_ancestor.switches.values.select { |switch| switch.associated_command == command }.sort(&by_name)
184
+ if @subcommand_option_handling_strategy == :legacy
185
+ command.topmost_ancestor.switches.values.select { |switch| switch.associated_command == command }.sort(&by_name)
186
+ else
187
+ command.switches.values.sort(&by_name)
188
+ end
179
189
  end
180
190
 
181
191
  def document_flags_and_switches(document_listener,flags,switches)
@@ -37,9 +37,11 @@ module GLI
37
37
  # Configure help to explicitly skip or not skip the pre block when the help command runs.
38
38
  # This is here because the creation of the help command is outside of the client programmer's control
39
39
  def self.skips_pre=(skips_pre) ; @@skips_pre = skips_pre ; end
40
+
40
41
  # Configure help to explicitly skip or not skip the post block when the help command runs.
41
42
  # This is here because the creation of the help command is outside of the client programmer's control
42
43
  def self.skips_post=(skips_post) ; @@skips_post = skips_post ; end
44
+
43
45
  # Configure help to explicitly skip or not skip the around block when the help command runs.
44
46
  # This is here because the creation of the help command is outside of the client programmer's control
45
47
  def self.skips_around=(skips_around) ; @@skips_around = skips_around ; end
@@ -50,6 +52,7 @@ module GLI
50
52
  :arguments_name => 'command',
51
53
  :long_desc => 'Gets help for the application or its commands. Can also list the commands in a way helpful to creating a bash-style completion function')
52
54
  @app = app
55
+ @parent = app
53
56
  @sorter = SORTERS[@app.help_sort_type]
54
57
  @text_wrapping_class = WRAPPERS[@app.help_text_wrap_type]
55
58
 
@@ -13,10 +13,10 @@ module GLI
13
13
  end
14
14
 
15
15
  def format
16
- command_wrapper = @wrapper_class.new(Terminal.instance.size[0],4 + @command.name.to_s.size + 3)
17
- wrapper = @wrapper_class.new(Terminal.instance.size[0],4)
18
- flags_and_switches = (@command.topmost_ancestor.flags_declaration_order + @command.topmost_ancestor.switches_declaration_order).select { |option| option.associated_command == @command }
19
- options_description = OptionsFormatter.new(flags_and_switches,@sorter,@wrapper_class).format
16
+ command_wrapper = @wrapper_class.new(Terminal.instance.size[0],4 + @command.name.to_s.size + 3)
17
+ wrapper = @wrapper_class.new(Terminal.instance.size[0],4)
18
+
19
+ options_description = OptionsFormatter.new(flags_and_switches(@command,@app),@sorter,@wrapper_class).format
20
20
  commands_description = format_subcommands(@command)
21
21
 
22
22
  synopses = []
@@ -61,7 +61,11 @@ COMMANDS
61
61
 
62
62
  def command_with_subcommand_usage(sub,is_default_command)
63
63
  usage = basic_usage
64
- sub_options = @command.flags.merge(@command.switches).select { |_,o| o.associated_command == sub }
64
+ sub_options = if @app.subcommand_option_handling_strategy == :legacy
65
+ @command.flags.merge(@command.switches).select { |_,o| o.associated_command == sub }
66
+ else
67
+ sub.flags.merge(sub.switches)
68
+ end
65
69
  usage << sub_options.map { |option_name,option|
66
70
  all_names = [option.name,Array(option.aliases)].flatten
67
71
  all_names.map { |_|
@@ -78,6 +82,19 @@ COMMANDS
78
82
  usage
79
83
  end
80
84
 
85
+ def flags_and_switches(command,app)
86
+ if app.subcommand_option_handling_strategy == :legacy
87
+ (
88
+ command.topmost_ancestor.flags_declaration_order +
89
+ command.topmost_ancestor.switches_declaration_order
90
+ ).select { |option| option.associated_command == command }
91
+ else
92
+ (
93
+ command.flags_declaration_order +
94
+ command.switches_declaration_order
95
+ )
96
+ end
97
+ end
81
98
 
82
99
  def basic_usage
83
100
  usage = @basic_invocation.dup
@@ -211,7 +211,7 @@ EOS
211
211
  end
212
212
  puts "Created #{root_dir}/#{project_name}/Rakefile"
213
213
  File.open("#{root_dir}/#{project_name}/Gemfile",'w') do |bundler_file|
214
- bundler_file.puts "source :rubygems"
214
+ bundler_file.puts "source 'https://rubygems.org'"
215
215
  bundler_file.puts "gemspec"
216
216
  end
217
217
  puts "Created #{root_dir}/#{project_name}/Gemfile"
@@ -15,20 +15,32 @@ module GLI
15
15
  class UnknownCommand < BadCommandLine
16
16
  end
17
17
 
18
+ # The command issued partially matches more than one command
19
+ class AmbiguousCommand < BadCommandLine
20
+ end
21
+
18
22
  # Indicates the bad command line was an unknown global argument
19
23
  class UnknownGlobalArgument < BadCommandLine
20
24
  end
21
25
 
22
- # Indicates the bad command line was an unknown command argument
23
- class UnknownCommandArgument < BadCommandLine
26
+ class CommandException < BadCommandLine
24
27
  # The command for which the argument was unknown
25
- attr_reader :command
28
+ attr_reader :command_in_context
26
29
  # +message+:: the error message to show the user
27
30
  # +command+:: the command we were using to parse command-specific options
28
- def initialize(message,command)
31
+ def initialize(message,command_in_context,exit_code=nil)
29
32
  super(message)
30
- @command = command
33
+ @command_in_context = command_in_context
34
+ @exit_code = exit_code
31
35
  end
36
+
37
+ def exit_code
38
+ @exit_code || super
39
+ end
40
+ end
41
+
42
+ # Indicates the bad command line was an unknown command argument
43
+ class UnknownCommandArgument < CommandException
32
44
  end
33
45
 
34
46
  # Raise this if you want to use an exit status that isn't the default
@@ -0,0 +1,84 @@
1
+ module GLI
2
+ # An "option block" is a set of parseable options, starting from the beginning of
3
+ # the argument list, stopping with the first unknown command-line element.
4
+ # This class handles parsing that block
5
+ class GLIOptionBlockParser
6
+
7
+ # Create the parser using the given +OptionParser+ instance and exception handling
8
+ # strategy.
9
+ #
10
+ # option_parser_factory:: An +OptionParserFactory+ instance, configured to parse wherever you are on the command line
11
+ # exception_klass_or_block:: means of handling exceptions from +OptionParser+. One of:
12
+ # an exception class:: will be raised on errors with a message
13
+ # lambda/block:: will be called with a single argument - the error message.
14
+ def initialize(option_parser_factory,exception_klass_or_block)
15
+ @option_parser_factory = option_parser_factory
16
+ @extra_error_context = nil
17
+ @exception_handler = if exception_klass_or_block.kind_of?(Class)
18
+ lambda { |message,extra_error_context|
19
+ raise exception_klass_or_block,message
20
+ }
21
+ else
22
+ exception_klass_or_block
23
+ end
24
+ end
25
+
26
+ # Parse the given argument list, returning the unparsed arguments and options hash of parsed arguments.
27
+ # Exceptions from +OptionParser+ are given to the handler configured in the constructor
28
+ #
29
+ # args:: argument list. This will be mutated
30
+ #
31
+ # Returns unparsed args
32
+ def parse!(args)
33
+ do_parse(args)
34
+ rescue OptionParser::InvalidOption => ex
35
+ @exception_handler.call("Unknown option #{ex.args.join(' ')}",@extra_error_context)
36
+ rescue OptionParser::InvalidArgument => ex
37
+ @exception_handler.call("#{ex.reason}: #{ex.args.join(' ')}",@extra_error_context)
38
+ end
39
+
40
+ protected
41
+
42
+ def do_parse(args)
43
+ first_non_option = nil
44
+ @option_parser_factory.option_parser.order!(args) do |non_option|
45
+ first_non_option = non_option
46
+ break
47
+ end
48
+ args.unshift(first_non_option)
49
+ end
50
+ end
51
+
52
+ class CommandOptionBlockParser < GLIOptionBlockParser
53
+
54
+ def command=(command_being_parsed)
55
+ @extra_error_context = command_being_parsed
56
+ end
57
+
58
+ protected
59
+
60
+ def break_on_non_option?
61
+ true
62
+ end
63
+
64
+ def do_parse(args)
65
+ unknown_options = []
66
+ @option_parser_factory.option_parser.order!(args) do |non_option|
67
+ unknown_options << non_option
68
+ break if break_on_non_option?
69
+ end
70
+ unknown_options.reverse.each do |unknown_option|
71
+ args.unshift(unknown_option)
72
+ end
73
+ args
74
+ end
75
+ end
76
+
77
+ class LegacyCommandOptionBlockParser < CommandOptionBlockParser
78
+
79
+ protected
80
+ def break_on_non_option?
81
+ false
82
+ end
83
+ end
84
+ end
@@ -1,124 +1,144 @@
1
1
  module GLI
2
2
  # Parses the command-line options using an actual +OptionParser+
3
3
  class GLIOptionParser
4
- def initialize(commands,flags,switches,accepts,default_command = nil)
5
- @commands = commands
6
- @flags = flags
7
- @switches = switches
8
- @accepts = accepts
9
- @default_command = default_command
4
+ def initialize(commands,flags,switches,accepts,default_command = nil,subcommand_option_handling_strategy=:legacy)
5
+ command_finder = CommandFinder.new(commands,default_command || "help")
6
+ @global_option_parser = GlobalOptionParser.new(OptionParserFactory.new(flags,switches,accepts),command_finder)
7
+ @accepts = accepts
8
+ @subcommand_option_handling_strategy = subcommand_option_handling_strategy
10
9
  end
11
10
 
12
- # Given the command-line argument array, returns and array of size 4:
13
- #
14
- # 0:: global options
15
- # 1:: command, as a Command
16
- # 2:: command-specific options
17
- # 3:: unparsed arguments
11
+ # Given the command-line argument array, returns an OptionParsingResult
18
12
  def parse_options(args) # :nodoc:
19
- args_clone = args.clone
20
- global_options = {}
21
- command = nil
22
- command_options = {}
23
- remaining_args = nil
24
-
25
- global_options,command_name,args = parse_global_options(OptionParserFactory.new(@flags,@switches,@accepts), args)
26
- @flags.each do |name,flag|
27
- global_options[name] = flag.default_value unless global_options[name]
28
- end
29
- @switches.each do |name,switch|
30
- global_options[name] = switch.default_value if global_options[name].nil?
31
- end
13
+ option_parser_class = self.class.const_get("#{@subcommand_option_handling_strategy.to_s.capitalize}CommandOptionParser")
14
+ OptionParsingResult.new.tap { |parsing_result|
15
+ parsing_result.arguments = args
16
+ parsing_result = @global_option_parser.parse!(parsing_result)
17
+ option_parser_class.new(@accepts).parse!(parsing_result)
18
+ }
19
+ end
20
+
21
+ private
32
22
 
33
- command_name ||= @default_command || :help
34
- command = find_command(command_name)
35
- if Array(command).empty?
36
- raise UnknownCommand.new("Unknown command '#{command_name}'")
37
- elsif command.kind_of? Array
38
- raise UnknownCommand.new("Ambiguous command '#{command_name}'. It matches #{command.sort.join(',')}")
23
+ class GlobalOptionParser
24
+ def initialize(option_parser_factory,command_finder)
25
+ @option_parser_factory = option_parser_factory
26
+ @command_finder = command_finder
39
27
  end
40
28
 
41
- command_options,args = parse_command_options(OptionParserFactory.new(command.flags,command.switches,@accepts),
42
- command,
43
- args)
29
+ def parse!(parsing_result)
30
+ parsing_result.arguments = GLIOptionBlockParser.new(@option_parser_factory,UnknownGlobalArgument).parse!(parsing_result.arguments)
31
+ command_name = parsing_result.arguments.shift
32
+ parsing_result.global_options = @option_parser_factory.options_hash_with_defaults_set!
33
+ parsing_result.command = @command_finder.find_command(command_name)
34
+ parsing_result
35
+ end
36
+ end
44
37
 
45
- command.flags.each do |name,flag|
46
- command_options[name] = flag.default_value unless command_options[name]
38
+ class NormalCommandOptionParser
39
+ def initialize(accepts)
40
+ @accepts = accepts
47
41
  end
48
- command.switches.each do |name,switch|
49
- command_options[name] = switch.default_value if command_options[name].nil?
42
+
43
+ def error_handler
44
+ lambda { |message,extra_error_context|
45
+ raise UnknownCommandArgument.new(message,extra_error_context)
46
+ }
50
47
  end
51
48
 
52
- [global_options,command,command_options,args]
53
- end
49
+ def parse!(parsing_result)
50
+ parsed_command_options = {}
51
+ command = parsing_result.command
52
+ arguments = nil
54
53
 
55
- private
54
+ loop do
55
+ option_parser_factory = OptionParserFactory.for_command(command,@accepts)
56
+ option_block_parser = CommandOptionBlockParser.new(option_parser_factory, self.error_handler)
57
+ option_block_parser.command = command
58
+ arguments = parsing_result.arguments
59
+
60
+ arguments = option_block_parser.parse!(arguments)
56
61
 
57
- def parse_command_options(option_parser_factory,command,args)
58
- option_parser,command_options = option_parser_factory.option_parser
59
- help_args = %w(-h --help).reject { |_| command.has_option?(_) }
60
-
61
- unless help_args.empty?
62
- option_parser.on(*help_args) do
63
- # We need to raise the help exception later in the process, after
64
- # the command-line has been parsed, so we know what command
65
- # to show help for. Unfortunately, we can't just call #action
66
- # on the command to override what to do, so...metaprogramming.
67
- class << command
68
- def execute(*help_args)
69
- exception = BadCommandLine.new(nil)
70
- class << exception
71
- def exit_code; 0; end
72
- end
73
- raise exception
62
+ parsed_command_options[command] = option_parser_factory.options_hash_with_defaults_set!
63
+ command_finder = CommandFinder.new(command.commands,command.get_default_command)
64
+ next_command_name = arguments.shift
65
+
66
+ begin
67
+ command = command_finder.find_command(next_command_name)
68
+ rescue AmbiguousCommand
69
+ arguments.unshift(next_command_name)
70
+ break
71
+ rescue UnknownCommand
72
+ arguments.unshift(next_command_name)
73
+ # Although command finder could certainy know if it should use
74
+ # the default command, it has no way to put the "unknown command"
75
+ # back into the argument stack. UGH.
76
+ unless command.get_default_command.nil?
77
+ command = command_finder.find_command(command.get_default_command)
74
78
  end
79
+ break
75
80
  end
76
81
  end
82
+ parsed_command_options[command] ||= {}
83
+ command_options = parsed_command_options[command]
84
+
85
+ this_command = command.parent
86
+ child_command_options = command_options
87
+
88
+ while this_command.kind_of?(command.class)
89
+ this_command_options = parsed_command_options[this_command] || {}
90
+ child_command_options[GLI::Command::PARENT] = this_command_options
91
+ this_command = this_command.parent
92
+ child_command_options = this_command_options
93
+ end
94
+
95
+ parsing_result.command_options = command_options
96
+ parsing_result.command = command
97
+ parsing_result.arguments = Array(arguments.compact)
98
+ parsing_result
77
99
  end
78
- option_parser.parse!(args)
79
- [command_options,args]
80
- rescue OptionParser::InvalidOption => ex
81
- raise UnknownCommandArgument.new("Unknown option #{ex.args.join(' ')}",command)
82
- rescue OptionParser::InvalidArgument => ex
83
- raise UnknownCommandArgument.new("#{ex.reason}: #{ex.args.join(' ')}",command)
84
100
  end
85
101
 
86
- def parse_global_options(option_parser_factory,args,&error_handler)
87
- if error_handler.nil?
88
- error_handler = lambda { |message|
89
- raise UnknownGlobalArgument.new(message)
90
- }
91
- end
92
- option_parser,global_options = option_parser_factory.option_parser
93
- command = nil
94
- option_parser.order!(args) do |non_option|
95
- command = non_option
96
- break
102
+ class LegacyCommandOptionParser < NormalCommandOptionParser
103
+ def parse!(parsing_result)
104
+ command = parsing_result.command
105
+ option_parser_factory = OptionParserFactory.for_command(command,@accepts)
106
+ option_block_parser = LegacyCommandOptionBlockParser.new(option_parser_factory, self.error_handler)
107
+ option_block_parser.command = command
108
+
109
+ parsing_result.arguments = option_block_parser.parse!(parsing_result.arguments)
110
+ parsing_result.command_options = option_parser_factory.options_hash_with_defaults_set!
111
+
112
+ subcommand,args = find_subcommand(command,parsing_result.arguments)
113
+ parsing_result.command = subcommand
114
+ parsing_result.arguments = args
97
115
  end
98
- [global_options,command,args]
99
- rescue OptionParser::InvalidOption => ex
100
- error_handler.call("Unknown option #{ex.args.join(' ')}")
101
- rescue OptionParser::InvalidArgument => ex
102
- error_handler.call("#{ex.reason}: #{ex.args.join(' ')}")
103
- end
104
116
 
105
- def find_command(name) # :nodoc:
106
- names_to_commands = {}
107
- @commands.each do |command_name,command|
108
- names_to_commands[command_name.to_s] = command
109
- Array(command.aliases).each do |command_alias|
110
- names_to_commands[command_alias.to_s] = command
117
+ private
118
+
119
+ def find_subcommand(command,arguments)
120
+ arguments = Array(arguments)
121
+ command_name = if arguments.empty?
122
+ nil
123
+ else
124
+ arguments.first
125
+ end
126
+
127
+ default_command = command.get_default_command
128
+ finder = CommandFinder.new(command.commands,default_command.to_s)
129
+
130
+ begin
131
+ results = [finder.find_command(command_name),arguments[1..-1]]
132
+ find_subcommand(results[0],results[1])
133
+ rescue UnknownCommand, AmbiguousCommand
134
+ begin
135
+ results = [finder.find_command(default_command.to_s),arguments]
136
+ find_subcommand(results[0],results[1])
137
+ rescue UnknownCommand, AmbiguousCommand
138
+ [command,arguments]
139
+ end
111
140
  end
112
141
  end
113
- names_to_commands.fetch(name.to_s) do |command_to_match|
114
- find_command_by_partial_name(names_to_commands, command_to_match)
115
- end
116
- end
117
-
118
- def find_command_by_partial_name(names_to_commands, command_to_match)
119
- partial_matches = names_to_commands.keys.select { |command_name| command_name =~ /^#{command_to_match}/ }
120
- return names_to_commands[partial_matches[0]] if partial_matches.size == 1
121
- partial_matches
122
142
  end
123
143
  end
124
144
  end