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.
- data/.travis.yml +1 -0
- data/Gemfile +1 -1
- data/features/step_definitions/todo_steps.rb +4 -0
- data/features/todo.feature +22 -0
- data/features/todo_legacy.feature +128 -0
- data/lib/gli.rb +3 -1
- data/lib/gli/app.rb +10 -2
- data/lib/gli/app_support.rb +39 -31
- data/lib/gli/command.rb +3 -2
- data/lib/gli/command_finder.rb +41 -0
- data/lib/gli/command_line_token.rb +0 -4
- data/lib/gli/command_support.rb +37 -64
- data/lib/gli/commands/doc.rb +12 -2
- data/lib/gli/commands/help.rb +3 -0
- data/lib/gli/commands/help_modules/command_help_format.rb +22 -5
- data/lib/gli/commands/scaffold.rb +1 -1
- data/lib/gli/exceptions.rb +17 -5
- data/lib/gli/gli_option_block_parser.rb +84 -0
- data/lib/gli/gli_option_parser.rb +116 -96
- data/lib/gli/option_parser_factory.rb +42 -10
- data/lib/gli/option_parsing_result.rb +19 -0
- data/lib/gli/version.rb +1 -1
- data/test/apps/todo/bin/todo +2 -0
- data/test/apps/todo/lib/todo/commands/make.rb +52 -0
- data/test/apps/todo_legacy/Gemfile +2 -0
- data/test/apps/todo_legacy/README.rdoc +6 -0
- data/test/apps/todo_legacy/Rakefile +23 -0
- data/test/apps/todo_legacy/bin/todo +61 -0
- data/test/apps/todo_legacy/lib/todo/commands/create.rb +24 -0
- data/test/apps/todo_legacy/lib/todo/commands/list.rb +63 -0
- data/test/apps/todo_legacy/lib/todo/commands/ls.rb +47 -0
- data/test/apps/todo_legacy/lib/todo/version.rb +3 -0
- data/test/apps/todo_legacy/test/tc_nothing.rb +14 -0
- data/test/apps/todo_legacy/todo.gemspec +23 -0
- data/test/apps/todo_legacy/todo.rdoc +5 -0
- data/test/tc_command.rb +84 -59
- data/test/{tc_compount_command.rb → tc_compound_command.rb} +0 -0
- data/test/tc_flag.rb +0 -1
- data/test/tc_gli.rb +2 -2
- data/test/tc_help.rb +11 -3
- data/test/tc_subcommand_parsing.rb +104 -0
- data/test/tc_subcommands.rb +1 -0
- data/test/tc_switch.rb +0 -1
- data/test/test_helper.rb +5 -0
- metadata +74 -13
- data/lib/gli/copy_options_to_aliases.rb +0 -33
data/lib/gli/command_support.rb
CHANGED
@@ -50,58 +50,53 @@ module GLI
|
|
50
50
|
end
|
51
51
|
|
52
52
|
def flag(*names)
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
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
|
-
|
69
|
-
|
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?
|
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?
|
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?
|
84
|
-
|
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?
|
92
|
-
|
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
|
-
|
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
|
data/lib/gli/commands/doc.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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)
|
data/lib/gli/commands/help.rb
CHANGED
@@ -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
|
17
|
-
wrapper
|
18
|
-
|
19
|
-
options_description
|
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 = @
|
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
|
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"
|
data/lib/gli/exceptions.rb
CHANGED
@@ -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
|
-
|
23
|
-
class UnknownCommandArgument < BadCommandLine
|
26
|
+
class CommandException < BadCommandLine
|
24
27
|
# The command for which the argument was unknown
|
25
|
-
attr_reader :
|
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,
|
31
|
+
def initialize(message,command_in_context,exit_code=nil)
|
29
32
|
super(message)
|
30
|
-
@
|
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
|
-
|
6
|
-
@
|
7
|
-
@
|
8
|
-
@
|
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
|
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
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
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
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
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
|
-
|
42
|
-
|
43
|
-
|
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
|
-
|
46
|
-
|
38
|
+
class NormalCommandOptionParser
|
39
|
+
def initialize(accepts)
|
40
|
+
@accepts = accepts
|
47
41
|
end
|
48
|
-
|
49
|
-
|
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
|
-
|
53
|
-
|
49
|
+
def parse!(parsing_result)
|
50
|
+
parsed_command_options = {}
|
51
|
+
command = parsing_result.command
|
52
|
+
arguments = nil
|
54
53
|
|
55
|
-
|
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
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
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
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
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
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
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
|