gli_aziz_light 2.8.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +13 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +17 -0
- data/CONTRIBUTING.md +23 -0
- data/Gemfile +8 -0
- data/LICENSE.txt +201 -0
- data/ObjectModel.graffle +1191 -0
- data/README.rdoc +109 -0
- data/Rakefile +126 -0
- data/bin/gli +59 -0
- data/bin/report_on_rake_results +10 -0
- data/bin/test_all_rubies.sh +6 -0
- data/features/gli_executable.feature +90 -0
- data/features/gli_init.feature +232 -0
- data/features/step_definitions/gli_executable_steps.rb +18 -0
- data/features/step_definitions/gli_init_steps.rb +11 -0
- data/features/step_definitions/todo_steps.rb +88 -0
- data/features/support/env.rb +53 -0
- data/features/todo.feature +413 -0
- data/features/todo_legacy.feature +128 -0
- data/gli.cheat +95 -0
- data/gli.gemspec +34 -0
- data/gli.rdoc +73 -0
- data/lib/gli.rb +35 -0
- data/lib/gli/app.rb +286 -0
- data/lib/gli/app_support.rb +341 -0
- data/lib/gli/command.rb +171 -0
- data/lib/gli/command_finder.rb +41 -0
- data/lib/gli/command_line_option.rb +34 -0
- data/lib/gli/command_line_token.rb +63 -0
- data/lib/gli/command_support.rb +181 -0
- data/lib/gli/commands/compound_command.rb +42 -0
- data/lib/gli/commands/doc.rb +231 -0
- data/lib/gli/commands/help.rb +95 -0
- data/lib/gli/commands/help_modules/arg_name_formatter.rb +20 -0
- data/lib/gli/commands/help_modules/command_finder.rb +60 -0
- data/lib/gli/commands/help_modules/command_help_format.rb +156 -0
- data/lib/gli/commands/help_modules/global_help_format.rb +70 -0
- data/lib/gli/commands/help_modules/help_completion_format.rb +31 -0
- data/lib/gli/commands/help_modules/list_formatter.rb +23 -0
- data/lib/gli/commands/help_modules/one_line_wrapper.rb +18 -0
- data/lib/gli/commands/help_modules/options_formatter.rb +49 -0
- data/lib/gli/commands/help_modules/text_wrapper.rb +53 -0
- data/lib/gli/commands/help_modules/tty_only_wrapper.rb +23 -0
- data/lib/gli/commands/help_modules/verbatim_wrapper.rb +16 -0
- data/lib/gli/commands/initconfig.rb +74 -0
- data/lib/gli/commands/rdoc_document_listener.rb +116 -0
- data/lib/gli/commands/scaffold.rb +401 -0
- data/lib/gli/dsl.rb +226 -0
- data/lib/gli/exceptions.rb +71 -0
- data/lib/gli/flag.rb +68 -0
- data/lib/gli/gli_option_block_parser.rb +84 -0
- data/lib/gli/gli_option_parser.rb +156 -0
- data/lib/gli/option_parser_factory.rb +81 -0
- data/lib/gli/option_parsing_result.rb +21 -0
- data/lib/gli/options.rb +23 -0
- data/lib/gli/switch.rb +35 -0
- data/lib/gli/terminal.rb +101 -0
- data/lib/gli/version.rb +5 -0
- data/test/apps/README.md +2 -0
- data/test/apps/todo/Gemfile +2 -0
- data/test/apps/todo/README.rdoc +6 -0
- data/test/apps/todo/Rakefile +23 -0
- data/test/apps/todo/bin/todo +63 -0
- data/test/apps/todo/lib/todo/commands/create.rb +24 -0
- data/test/apps/todo/lib/todo/commands/list.rb +63 -0
- data/test/apps/todo/lib/todo/commands/ls.rb +47 -0
- data/test/apps/todo/lib/todo/commands/make.rb +52 -0
- data/test/apps/todo/lib/todo/version.rb +3 -0
- data/test/apps/todo/test/tc_nothing.rb +14 -0
- data/test/apps/todo/todo.gemspec +23 -0
- data/test/apps/todo/todo.rdoc +5 -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/apps/todo_plugins/commands/third.rb +1 -0
- data/test/config.yaml +10 -0
- data/test/fake_std_out.rb +30 -0
- data/test/init_simplecov.rb +8 -0
- data/test/option_test_helper.rb +13 -0
- data/test/tc_command.rb +508 -0
- data/test/tc_compound_command.rb +22 -0
- data/test/tc_doc.rb +325 -0
- data/test/tc_flag.rb +62 -0
- data/test/tc_gli.rb +773 -0
- data/test/tc_help.rb +387 -0
- data/test/tc_options.rb +43 -0
- data/test/tc_subcommand_parsing.rb +104 -0
- data/test/tc_subcommands.rb +260 -0
- data/test/tc_switch.rb +55 -0
- data/test/tc_terminal.rb +100 -0
- data/test/tc_verbatim_wrapper.rb +36 -0
- data/test/test_helper.rb +20 -0
- metadata +330 -0
@@ -0,0 +1,41 @@
|
|
1
|
+
module GLI
|
2
|
+
class CommandFinder
|
3
|
+
# Initialize a finder on the given list of commands, using default_command as the default if none found
|
4
|
+
def initialize(commands,default_command)
|
5
|
+
@default_command = default_command
|
6
|
+
@names_to_commands = {}
|
7
|
+
commands.each do |command_name,command|
|
8
|
+
@names_to_commands[command_name.to_s] = command
|
9
|
+
Array(command.aliases).each do |command_alias|
|
10
|
+
@names_to_commands[command_alias.to_s] = command
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Finds the command with the given name, allowing for partial matches. Returns the command named by
|
16
|
+
# the default command if no command with +name+ matched
|
17
|
+
def find_command(name)
|
18
|
+
name ||= @default_command
|
19
|
+
|
20
|
+
raise UnknownCommand.new("No command name given nor default available") if String(name).strip == ''
|
21
|
+
|
22
|
+
command_found = @names_to_commands.fetch(name.to_s) do |command_to_match|
|
23
|
+
find_command_by_partial_name(@names_to_commands, command_to_match)
|
24
|
+
end
|
25
|
+
if Array(command_found).empty?
|
26
|
+
raise UnknownCommand.new("Unknown command '#{name}'")
|
27
|
+
elsif command_found.kind_of? Array
|
28
|
+
raise AmbiguousCommand.new("Ambiguous command '#{name}'. It matches #{command_found.sort.join(',')}")
|
29
|
+
end
|
30
|
+
command_found
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def find_command_by_partial_name(names_to_commands, command_to_match)
|
36
|
+
partial_matches = names_to_commands.keys.select { |command_name| command_name =~ /^#{command_to_match}/ }
|
37
|
+
return names_to_commands[partial_matches[0]] if partial_matches.size == 1
|
38
|
+
partial_matches
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'gli/command_line_token.rb'
|
2
|
+
|
3
|
+
module GLI
|
4
|
+
# An option, not a command or argument, on the command line
|
5
|
+
class CommandLineOption < CommandLineToken #:nodoc:
|
6
|
+
|
7
|
+
attr_accessor :default_value
|
8
|
+
# Command to which this option "belongs", nil if it's a global option
|
9
|
+
attr_accessor :associated_command
|
10
|
+
|
11
|
+
# Creates a new option
|
12
|
+
#
|
13
|
+
# names - Array of symbols or strings representing the names of this switch
|
14
|
+
# options - hash of options:
|
15
|
+
# :desc - the short description
|
16
|
+
# :long_desc - the long description
|
17
|
+
# :default_value - the default value of this option
|
18
|
+
def initialize(names,options = {})
|
19
|
+
super(names,options[:desc],options[:long_desc])
|
20
|
+
@default_value = options[:default_value]
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.name_as_string(name,negatable=true)
|
24
|
+
string = name.to_s
|
25
|
+
if string.length == 1
|
26
|
+
"-#{string}"
|
27
|
+
elsif negatable
|
28
|
+
"--[no-]#{string}"
|
29
|
+
else
|
30
|
+
"--#{string}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module GLI
|
2
|
+
# Abstract base class for a logical element of a command line, mostly so that subclasses can have similar
|
3
|
+
# initialization and interface
|
4
|
+
class CommandLineToken
|
5
|
+
attr_reader :name #:nodoc:
|
6
|
+
attr_reader :aliases #:nodoc:
|
7
|
+
attr_reader :description #:nodoc:
|
8
|
+
attr_reader :long_description #:nodoc:
|
9
|
+
|
10
|
+
def initialize(names,description,long_description=nil) #:nodoc:
|
11
|
+
@description = description
|
12
|
+
@long_description = long_description
|
13
|
+
@name,@aliases,@names = parse_names(names)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Sort based on primary name
|
17
|
+
def <=>(other)
|
18
|
+
self.name.to_s <=> other.name.to_s
|
19
|
+
end
|
20
|
+
|
21
|
+
# Array of the name and aliases, as string
|
22
|
+
def names_and_aliases
|
23
|
+
[self.name,self.aliases].flatten.compact.map(&:to_s)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
# Returns a string of all possible forms
|
28
|
+
# of this flag. Mostly intended for printing
|
29
|
+
# to the user.
|
30
|
+
def all_forms(joiner=', ')
|
31
|
+
forms = all_forms_a
|
32
|
+
forms.join(joiner)
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
# Handles dealing with the "names" param, parsing
|
37
|
+
# it into the primary name and aliases list
|
38
|
+
def parse_names(names)
|
39
|
+
# Allow strings; convert to symbols
|
40
|
+
names = [names].flatten.map { |name| name.to_sym }
|
41
|
+
names_hash = {}
|
42
|
+
names.each do |name|
|
43
|
+
raise ArgumentError.new("#{name} has spaces; they are not allowed") if name.to_s =~ /\s/
|
44
|
+
names_hash[self.class.name_as_string(name)] = true
|
45
|
+
end
|
46
|
+
name = names.shift
|
47
|
+
aliases = names.length > 0 ? names : nil
|
48
|
+
[name,aliases,names_hash]
|
49
|
+
end
|
50
|
+
|
51
|
+
def negatable?
|
52
|
+
false;
|
53
|
+
end
|
54
|
+
|
55
|
+
def all_forms_a
|
56
|
+
forms = [self.class.name_as_string(name,negatable?)]
|
57
|
+
if aliases
|
58
|
+
forms |= aliases.map { |one_alias| self.class.name_as_string(one_alias,negatable?) }.sort { |one,two| one.length <=> two.length }
|
59
|
+
end
|
60
|
+
forms
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,181 @@
|
|
1
|
+
module GLI
|
2
|
+
# Things unrelated to the true public interface of Command that are needed for bookkeeping
|
3
|
+
# and help support. Generally, you shouldn't be calling these methods; they are technically public
|
4
|
+
# but are essentially part of GLI's internal implementation and subject to change
|
5
|
+
module CommandSupport
|
6
|
+
# The parent of this command, either the GLI app, or another command
|
7
|
+
attr_accessor :parent
|
8
|
+
|
9
|
+
def context_description
|
10
|
+
"in the command #{name}"
|
11
|
+
end
|
12
|
+
|
13
|
+
# Return true to avoid including this command in your help strings
|
14
|
+
def nodoc
|
15
|
+
false
|
16
|
+
end
|
17
|
+
|
18
|
+
# Return the arguments description
|
19
|
+
def arguments_description
|
20
|
+
@arguments_description
|
21
|
+
end
|
22
|
+
|
23
|
+
def arguments_options
|
24
|
+
@arguments_options
|
25
|
+
end
|
26
|
+
|
27
|
+
# If true, this command doesn't want the pre block run before it executes
|
28
|
+
def skips_pre
|
29
|
+
@skips_pre
|
30
|
+
end
|
31
|
+
|
32
|
+
# If true, this command doesn't want the post block run before it executes
|
33
|
+
def skips_post
|
34
|
+
@skips_post
|
35
|
+
end
|
36
|
+
|
37
|
+
# If true, this command doesn't want the around block called
|
38
|
+
def skips_around
|
39
|
+
@skips_around
|
40
|
+
end
|
41
|
+
|
42
|
+
# Return the Array of the command's names
|
43
|
+
def names
|
44
|
+
all_forms
|
45
|
+
end
|
46
|
+
|
47
|
+
# Get an array of commands, ordered by when they were declared
|
48
|
+
def commands_declaration_order # :nodoc:
|
49
|
+
@commands_declaration_order
|
50
|
+
end
|
51
|
+
|
52
|
+
def flag(*names)
|
53
|
+
if send_declarations_to_parent?
|
54
|
+
new_flag = if parent.kind_of? Command
|
55
|
+
super(*names)
|
56
|
+
parent.flag(*names)
|
57
|
+
else
|
58
|
+
super(*names)
|
59
|
+
end
|
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
|
80
|
+
end
|
81
|
+
|
82
|
+
def desc(d)
|
83
|
+
parent.desc(d) if parent.kind_of?(Command) && send_declarations_to_parent?
|
84
|
+
super(d)
|
85
|
+
end
|
86
|
+
|
87
|
+
def long_desc(d)
|
88
|
+
parent.long_desc(d) if parent.kind_of?(Command) && send_declarations_to_parent?
|
89
|
+
super(d)
|
90
|
+
end
|
91
|
+
|
92
|
+
def arg_name(d,options=[])
|
93
|
+
parent.arg_name(d,options) if parent.kind_of?(Command) && send_declarations_to_parent?
|
94
|
+
super(d,options)
|
95
|
+
end
|
96
|
+
|
97
|
+
def default_value(d)
|
98
|
+
parent.default_value(d) if parent.kind_of?(Command) && send_declarations_to_parent?
|
99
|
+
super(d)
|
100
|
+
end
|
101
|
+
|
102
|
+
# Return the flags as a Hash
|
103
|
+
def flags
|
104
|
+
@flags ||= {}
|
105
|
+
end
|
106
|
+
# Return the switches as a Hash
|
107
|
+
def switches
|
108
|
+
@switches ||= {}
|
109
|
+
end
|
110
|
+
|
111
|
+
def commands # :nodoc:
|
112
|
+
@commands ||= {}
|
113
|
+
end
|
114
|
+
|
115
|
+
def default_description
|
116
|
+
@default_desc
|
117
|
+
end
|
118
|
+
|
119
|
+
# Executes the command
|
120
|
+
def execute(global_options,options,arguments)
|
121
|
+
get_action(arguments).call(global_options,options,arguments)
|
122
|
+
end
|
123
|
+
|
124
|
+
def topmost_ancestor
|
125
|
+
some_command = self
|
126
|
+
top = some_command
|
127
|
+
while some_command.kind_of? self.class
|
128
|
+
top = some_command
|
129
|
+
some_command = some_command.parent
|
130
|
+
end
|
131
|
+
top
|
132
|
+
end
|
133
|
+
|
134
|
+
def has_action?
|
135
|
+
!!@action
|
136
|
+
end
|
137
|
+
|
138
|
+
def get_default_command
|
139
|
+
@default_command
|
140
|
+
end
|
141
|
+
|
142
|
+
private
|
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
|
+
|
149
|
+
def get_action(arguments)
|
150
|
+
if @action
|
151
|
+
@action
|
152
|
+
else
|
153
|
+
generate_error_action(arguments)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def generate_error_action(arguments)
|
158
|
+
lambda { |global_options,options,arguments|
|
159
|
+
if am_subcommand?
|
160
|
+
if arguments.size > 0
|
161
|
+
raise UnknownCommand,"Unknown command '#{arguments[0]}'"
|
162
|
+
else
|
163
|
+
raise BadCommandLine,"Command '#{name}' requires a subcommand"
|
164
|
+
end
|
165
|
+
elsif have_subcommands?
|
166
|
+
raise BadCommandLine,"Command '#{name}' requires a subcommand #{self.commands.keys.join(',')}"
|
167
|
+
else
|
168
|
+
raise "Command '#{name}' has no action block"
|
169
|
+
end
|
170
|
+
}
|
171
|
+
end
|
172
|
+
|
173
|
+
def am_subcommand?
|
174
|
+
parent.kind_of?(Command)
|
175
|
+
end
|
176
|
+
|
177
|
+
def have_subcommands?
|
178
|
+
!self.commands.empty?
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module GLI
|
2
|
+
module Commands
|
3
|
+
# A command that calls other commands in order
|
4
|
+
class CompoundCommand < Command
|
5
|
+
# base:: object that respondes to +commands+
|
6
|
+
# configuration:: Array of arrays: index 0 is the array of names of this command and index 1
|
7
|
+
# is the names of the compound commands.
|
8
|
+
def initialize(base,configuration,options={})
|
9
|
+
name = configuration.keys.first
|
10
|
+
super(options.merge(:names => [name]))
|
11
|
+
|
12
|
+
command_names = configuration[name]
|
13
|
+
|
14
|
+
check_for_unknown_commands!(base,command_names)
|
15
|
+
|
16
|
+
@wrapped_commands = command_names.map { |name| self.class.find_command(base,name) }
|
17
|
+
end
|
18
|
+
|
19
|
+
def execute(global_options,options,arguments) #:nodoc:
|
20
|
+
@wrapped_commands.each do |command|
|
21
|
+
command.execute(global_options,options,arguments)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def check_for_unknown_commands!(base,command_names)
|
28
|
+
known_commands = base.commands.keys.map(&:to_s)
|
29
|
+
unknown_commands = command_names.map(&:to_s) - known_commands
|
30
|
+
|
31
|
+
unless unknown_commands.empty?
|
32
|
+
raise "Unknown commands #{unknown_commands.join(',')}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.find_command(base,name)
|
37
|
+
base.commands.values.find { |command| command.name == name }
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,231 @@
|
|
1
|
+
module GLI
|
2
|
+
module Commands
|
3
|
+
# Takes a DocListener which will be called with all of the meta-data and documentation
|
4
|
+
# about your app, so as to create documentation in whatever format you want
|
5
|
+
class Doc < Command
|
6
|
+
FORMATS = {
|
7
|
+
'rdoc' => GLI::Commands::RdocDocumentListener,
|
8
|
+
}
|
9
|
+
# Create the Doc generator based on the GLI app passed in
|
10
|
+
def initialize(app)
|
11
|
+
super(:names => "_doc",
|
12
|
+
:description => "Generate documentation of your application's UI",
|
13
|
+
:long_desc => "Introspects your application's UI meta-data to generate documentation in a variety of formats. This is intended to be extensible via the DocumentListener interface, so that you can provide your own documentation formats without them being a part of GLI",
|
14
|
+
:skips_pre => true, :skips_post => true, :skips_around => true, :hidden => true)
|
15
|
+
|
16
|
+
@app = app
|
17
|
+
@parent = @app
|
18
|
+
@subcommand_option_handling_strategy = @app.subcommand_option_handling_strategy
|
19
|
+
|
20
|
+
desc 'The format name of the documentation to generate or the class name to use to generate it'
|
21
|
+
default_value 'rdoc'
|
22
|
+
arg_name 'name_or_class'
|
23
|
+
flag :format
|
24
|
+
|
25
|
+
action do |global_options,options,arguments|
|
26
|
+
self.document(format_class(options[:format]).new(global_options,options,arguments,app))
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def nodoc
|
31
|
+
true
|
32
|
+
end
|
33
|
+
|
34
|
+
# Generates documentation using the listener
|
35
|
+
def document(document_listener)
|
36
|
+
document_listener.beginning
|
37
|
+
document_listener.program_desc(@app.program_desc) unless @app.program_desc.nil?
|
38
|
+
document_listener.program_long_desc(@app.program_long_desc) unless @app.program_long_desc.nil?
|
39
|
+
document_listener.version(@app.version_string)
|
40
|
+
if any_options?(@app)
|
41
|
+
document_listener.options
|
42
|
+
end
|
43
|
+
document_flags_and_switches(document_listener,
|
44
|
+
@app.flags.values.sort(&by_name),
|
45
|
+
@app.switches.values.sort(&by_name))
|
46
|
+
if any_options?(@app)
|
47
|
+
document_listener.end_options
|
48
|
+
end
|
49
|
+
document_listener.commands
|
50
|
+
document_commands(document_listener,@app)
|
51
|
+
document_listener.end_commands
|
52
|
+
document_listener.ending
|
53
|
+
end
|
54
|
+
|
55
|
+
# Interface for a listener that is called during various parts of the doc process
|
56
|
+
class DocumentListener
|
57
|
+
def initialize(global_options,options,arguments,app)
|
58
|
+
@global_options = global_options
|
59
|
+
@options = options
|
60
|
+
@arguments = arguments
|
61
|
+
@app = app
|
62
|
+
end
|
63
|
+
# Called before processing begins
|
64
|
+
def beginning
|
65
|
+
abstract!
|
66
|
+
end
|
67
|
+
|
68
|
+
# Called when processing has completed
|
69
|
+
def ending
|
70
|
+
abstract!
|
71
|
+
end
|
72
|
+
|
73
|
+
# Gives you the program description
|
74
|
+
def program_desc(desc)
|
75
|
+
abstract!
|
76
|
+
end
|
77
|
+
|
78
|
+
# Gives you the program long description
|
79
|
+
def program_long_desc(desc)
|
80
|
+
abstract!
|
81
|
+
end
|
82
|
+
|
83
|
+
# Gives you the program version
|
84
|
+
def version(version)
|
85
|
+
abstract!
|
86
|
+
end
|
87
|
+
|
88
|
+
# Called at the start of options for the current context
|
89
|
+
def options
|
90
|
+
abstract!
|
91
|
+
end
|
92
|
+
|
93
|
+
# Called when all options for the current context have been vended
|
94
|
+
def end_options
|
95
|
+
abstract!
|
96
|
+
end
|
97
|
+
|
98
|
+
# Called at the start of commands for the current context
|
99
|
+
def commands
|
100
|
+
abstract!
|
101
|
+
end
|
102
|
+
|
103
|
+
# Called when all commands for the current context have been vended
|
104
|
+
def end_commands
|
105
|
+
abstract!
|
106
|
+
end
|
107
|
+
|
108
|
+
# Gives you a flag in the current context
|
109
|
+
def flag(name,aliases,desc,long_desc,default_value,arg_name,must_match,type)
|
110
|
+
abstract!
|
111
|
+
end
|
112
|
+
|
113
|
+
# Gives you a switch in the current context
|
114
|
+
def switch(name,aliases,desc,long_desc,negatable)
|
115
|
+
abstract!
|
116
|
+
end
|
117
|
+
|
118
|
+
# Gives you the name of the current command in the current context
|
119
|
+
def default_command(name)
|
120
|
+
abstract!
|
121
|
+
end
|
122
|
+
|
123
|
+
# Gives you a command in the current context and creates a new context of this command
|
124
|
+
def command(name,aliases,desc,long_desc,arg_name,arg_options)
|
125
|
+
abstract!
|
126
|
+
end
|
127
|
+
|
128
|
+
# Ends a command, and "pops" you back up one context
|
129
|
+
def end_command(name)
|
130
|
+
abstract!
|
131
|
+
end
|
132
|
+
|
133
|
+
private
|
134
|
+
def abstract!
|
135
|
+
raise "Subclass must implement"
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
private
|
140
|
+
|
141
|
+
def format_class(format_name)
|
142
|
+
FORMATS.fetch(format_name) {
|
143
|
+
begin
|
144
|
+
return format_name.split(/::/).reduce(Kernel) { |context,part| context.const_get(part) }
|
145
|
+
rescue => ex
|
146
|
+
raise IndexError,"Couldn't find formatter or class named #{format_name}"
|
147
|
+
end
|
148
|
+
}
|
149
|
+
end
|
150
|
+
|
151
|
+
def document_commands(document_listener,context)
|
152
|
+
context.commands.values.reject {|_| _.nodoc }.sort(&by_name).each do |command|
|
153
|
+
call_command_method_being_backwards_compatible(document_listener,command)
|
154
|
+
document_listener.options if any_options?(command)
|
155
|
+
document_flags_and_switches(document_listener,command_flags(command),command_switches(command))
|
156
|
+
document_listener.end_options if any_options?(command)
|
157
|
+
document_listener.commands if any_commands?(command)
|
158
|
+
document_commands(document_listener,command)
|
159
|
+
document_listener.end_commands if any_commands?(command)
|
160
|
+
document_listener.end_command(command.name)
|
161
|
+
end
|
162
|
+
document_listener.default_command(context.get_default_command)
|
163
|
+
end
|
164
|
+
|
165
|
+
def call_command_method_being_backwards_compatible(document_listener,command)
|
166
|
+
command_args = [command.name,
|
167
|
+
Array(command.aliases),
|
168
|
+
command.description,
|
169
|
+
command.long_description,
|
170
|
+
command.arguments_description]
|
171
|
+
if document_listener.method(:command).arity == 6
|
172
|
+
command_args << command.arguments_options
|
173
|
+
end
|
174
|
+
document_listener.command(*command_args)
|
175
|
+
end
|
176
|
+
|
177
|
+
def by_name
|
178
|
+
lambda { |a,b| a.name.to_s <=> b.name.to_s }
|
179
|
+
end
|
180
|
+
|
181
|
+
def command_flags(command)
|
182
|
+
if @subcommand_option_handling_strategy == :legacy
|
183
|
+
command.topmost_ancestor.flags.values.select { |flag| flag.associated_command == command }.sort(&by_name)
|
184
|
+
else
|
185
|
+
command.flags.values.sort(&by_name)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
def command_switches(command)
|
190
|
+
if @subcommand_option_handling_strategy == :legacy
|
191
|
+
command.topmost_ancestor.switches.values.select { |switch| switch.associated_command == command }.sort(&by_name)
|
192
|
+
else
|
193
|
+
command.switches.values.sort(&by_name)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
def document_flags_and_switches(document_listener,flags,switches)
|
198
|
+
flags.each do |flag|
|
199
|
+
document_listener.flag(flag.name,
|
200
|
+
Array(flag.aliases),
|
201
|
+
flag.description,
|
202
|
+
flag.long_description,
|
203
|
+
flag.safe_default_value,
|
204
|
+
flag.argument_name,
|
205
|
+
flag.must_match,
|
206
|
+
flag.type)
|
207
|
+
end
|
208
|
+
switches.each do |switch|
|
209
|
+
document_listener.switch(switch.name,
|
210
|
+
Array(switch.aliases),
|
211
|
+
switch.description,
|
212
|
+
switch.long_description,
|
213
|
+
switch.negatable)
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def any_options?(context)
|
218
|
+
options = if context.kind_of?(Command)
|
219
|
+
command_flags(context) + command_switches(context)
|
220
|
+
else
|
221
|
+
context.flags.values + context.switches.values
|
222
|
+
end
|
223
|
+
!options.empty?
|
224
|
+
end
|
225
|
+
|
226
|
+
def any_commands?(command)
|
227
|
+
!command.commands.empty?
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|