gli 2.1.0 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/features/gli_executable.feature +0 -2
- data/features/step_definitions/todo_steps.rb +11 -0
- data/features/support/env.rb +4 -0
- data/features/todo.feature +94 -2
- data/gli.gemspec +1 -0
- data/gli.rdoc +11 -18
- data/lib/gli/app.rb +34 -3
- data/lib/gli/app_support.rb +34 -4
- data/lib/gli/command.rb +8 -0
- data/lib/gli/command_support.rb +5 -0
- data/lib/gli/commands/doc.rb +22 -8
- data/lib/gli/commands/help.rb +18 -2
- data/lib/gli/commands/help_modules/arg_name_formatter.rb +20 -0
- data/lib/gli/commands/help_modules/command_help_format.rb +9 -18
- data/lib/gli/commands/help_modules/global_help_format.rb +14 -5
- data/lib/gli/commands/help_modules/list_formatter.rb +3 -2
- data/lib/gli/commands/help_modules/no_wrapping_wrapper.rb +18 -0
- data/lib/gli/commands/help_modules/options_formatter.rb +4 -3
- data/lib/gli/commands/help_modules/tty_only_wrapper.rb +23 -0
- data/lib/gli/commands/rdoc_document_listener.rb +13 -9
- data/lib/gli/dsl.rb +2 -0
- data/lib/gli/flag.rb +20 -8
- data/lib/gli/gli_option_parser.rb +19 -0
- data/lib/gli/option_parser_factory.rb +3 -3
- data/lib/gli/terminal.rb +1 -1
- data/lib/gli/version.rb +1 -1
- data/test/apps/todo/bin/todo +4 -0
- data/test/tc_doc.rb +6 -0
- data/test/tc_flag.rb +16 -8
- data/test/test_helper.rb +2 -0
- metadata +258 -364
@@ -2,6 +2,17 @@ Given /^todo's bin directory is in my path/ do
|
|
2
2
|
add_to_path(File.expand_path(File.join(File.dirname(__FILE__),'..','..','test','apps','todo','bin')))
|
3
3
|
end
|
4
4
|
|
5
|
+
Given /^the todo app is coded to avoid sorted help commands$/ do
|
6
|
+
ENV['TODO_SORT_HELP'] = 'manually'
|
7
|
+
end
|
8
|
+
|
9
|
+
Given /^the todo app is coded to avoid wrapping text$/ do
|
10
|
+
ENV['TODO_WRAP_HELP_TEXT'] = 'never'
|
11
|
+
end
|
12
|
+
|
13
|
+
Given /^the todo app is coded to wrap text only for tty$/ do
|
14
|
+
ENV['TODO_WRAP_HELP_TEXT'] = 'tty_only'
|
15
|
+
end
|
5
16
|
|
6
17
|
Given /^a clean home directory$/ do
|
7
18
|
FileUtils.rm_rf File.join(ENV['HOME'],'gli_test_todo.rc')
|
data/features/support/env.rb
CHANGED
@@ -24,6 +24,7 @@ Before do
|
|
24
24
|
FileUtils.rm_rf new_home
|
25
25
|
FileUtils.mkdir new_home
|
26
26
|
ENV['HOME'] = new_home
|
27
|
+
FileUtils.cp 'gli.rdoc','gli.rdoc.orig'
|
27
28
|
end
|
28
29
|
|
29
30
|
After do |scenario|
|
@@ -34,6 +35,9 @@ After do |scenario|
|
|
34
35
|
end
|
35
36
|
ENV['PATH'] = @original_path.join(File::PATH_SEPARATOR)
|
36
37
|
ENV['HOME'] = @original_home
|
38
|
+
ENV['TODO_SORT_HELP'] = nil
|
39
|
+
ENV['TODO_WRAP_HELP_TEXT'] = nil
|
40
|
+
FileUtils.mv 'gli.rdoc.orig','gli.rdoc'
|
37
41
|
end
|
38
42
|
|
39
43
|
def add_to_path(dir)
|
data/features/todo.feature
CHANGED
@@ -15,6 +15,9 @@ Feature: The todo app has a nice user interface
|
|
15
15
|
NAME
|
16
16
|
todo - Manages tasks
|
17
17
|
|
18
|
+
A test program that has a sophisticated UI that can be used to exercise a
|
19
|
+
lot of GLI's power
|
20
|
+
|
18
21
|
SYNOPSIS
|
19
22
|
todo [global options] command [command options] [arguments...]
|
20
23
|
|
@@ -85,8 +88,44 @@ Feature: The todo app has a nice user interface
|
|
85
88
|
contexts
|
86
89
|
"""
|
87
90
|
|
88
|
-
Scenario: Getting Help
|
89
|
-
|
91
|
+
Scenario: Getting Help with self-ordered commands
|
92
|
+
Given the todo app is coded to avoid sorted help commands
|
93
|
+
When I successfully run `todo help`
|
94
|
+
Then the output should contain:
|
95
|
+
"""
|
96
|
+
NAME
|
97
|
+
todo - Manages tasks
|
98
|
+
|
99
|
+
A test program that has a sophisticated UI that can be used to exercise a
|
100
|
+
lot of GLI's power
|
101
|
+
|
102
|
+
SYNOPSIS
|
103
|
+
todo [global options] command [command options] [arguments...]
|
104
|
+
|
105
|
+
VERSION
|
106
|
+
0.0.1
|
107
|
+
|
108
|
+
GLOBAL OPTIONS
|
109
|
+
--flag=arg - (default: none)
|
110
|
+
--help - Show this message
|
111
|
+
--[no-]otherswitch -
|
112
|
+
--[no-]switch -
|
113
|
+
--version -
|
114
|
+
|
115
|
+
COMMANDS
|
116
|
+
help - Shows a list of commands or help for one command
|
117
|
+
initconfig - Initialize the config file using current global options
|
118
|
+
create, new - Create a new task or context
|
119
|
+
list - List things, such as tasks or contexts
|
120
|
+
ls - LS things, such as tasks or contexts
|
121
|
+
first -
|
122
|
+
second -
|
123
|
+
chained -
|
124
|
+
chained2, ch2 -
|
125
|
+
"""
|
126
|
+
|
127
|
+
Scenario Outline: Getting Help for a top level command of todo
|
128
|
+
When I successfully run `todo <help_invocation>`
|
90
129
|
Then the output should contain:
|
91
130
|
"""
|
92
131
|
NAME
|
@@ -111,6 +150,59 @@ Feature: The todo app has a nice user interface
|
|
111
150
|
tasks - List tasks (default)
|
112
151
|
"""
|
113
152
|
|
153
|
+
Examples:
|
154
|
+
| help_invocation |
|
155
|
+
| help list |
|
156
|
+
| list -h |
|
157
|
+
| list --help |
|
158
|
+
|
159
|
+
|
160
|
+
Scenario: Getting Help without wrapping
|
161
|
+
Given the todo app is coded to avoid wrapping text
|
162
|
+
When I successfully run `todo help list`
|
163
|
+
Then the output should contain:
|
164
|
+
"""
|
165
|
+
NAME
|
166
|
+
list - List things, such as tasks or contexts
|
167
|
+
|
168
|
+
SYNOPSIS
|
169
|
+
todo [global options] list [command options] [--flag arg] [-x arg] [tasks]
|
170
|
+
todo [global options] list [command options] [--otherflag arg] [-b] [-f|--foobar] contexts
|
171
|
+
|
172
|
+
DESCRIPTION
|
173
|
+
List a whole lot of things that you might be keeping track of in your overall todo list. This is your go-to place or finding all of the things that you might have stored in your todo databases.
|
174
|
+
|
175
|
+
COMMAND OPTIONS
|
176
|
+
-l, --[no-]long - Show long form
|
177
|
+
|
178
|
+
COMMANDS
|
179
|
+
contexts - List contexts
|
180
|
+
tasks - List tasks (default)
|
181
|
+
"""
|
182
|
+
|
183
|
+
Scenario: Getting Help without wrapping
|
184
|
+
Given the todo app is coded to wrap text only for tty
|
185
|
+
When I successfully run `todo help list`
|
186
|
+
Then the output should contain:
|
187
|
+
"""
|
188
|
+
NAME
|
189
|
+
list - List things, such as tasks or contexts
|
190
|
+
|
191
|
+
SYNOPSIS
|
192
|
+
todo [global options] list [command options] [--flag arg] [-x arg] [tasks]
|
193
|
+
todo [global options] list [command options] [--otherflag arg] [-b] [-f|--foobar] contexts
|
194
|
+
|
195
|
+
DESCRIPTION
|
196
|
+
List a whole lot of things that you might be keeping track of in your overall todo list. This is your go-to place or finding all of the things that you might have stored in your todo databases.
|
197
|
+
|
198
|
+
COMMAND OPTIONS
|
199
|
+
-l, --[no-]long - Show long form
|
200
|
+
|
201
|
+
COMMANDS
|
202
|
+
contexts - List contexts
|
203
|
+
tasks - List tasks (default)
|
204
|
+
"""
|
205
|
+
|
114
206
|
Scenario: Getting Help for a sub command of todo list
|
115
207
|
When I successfully run `todo help list tasks`
|
116
208
|
Then the output should contain:
|
data/gli.gemspec
CHANGED
data/gli.rdoc
CHANGED
@@ -1,13 +1,12 @@
|
|
1
1
|
== gli - create scaffolding for a GLI-powered application
|
2
2
|
|
3
|
-
v2.
|
3
|
+
v2.1.0
|
4
4
|
|
5
5
|
=== Global Options
|
6
|
-
=== -r arg
|
6
|
+
=== -r|--root arg
|
7
7
|
|
8
8
|
Root dir of project
|
9
9
|
|
10
|
-
[Aliases] --root
|
11
10
|
[Default Value] .
|
12
11
|
This is the directory where the project''s directory will be made, so if you
|
13
12
|
specify a project name ''foo'' and the root dir of ''.'', the directory
|
@@ -18,46 +17,43 @@ Show this message
|
|
18
17
|
|
19
18
|
|
20
19
|
|
21
|
-
|
22
20
|
=== -n
|
23
21
|
Dry run; dont change the disk
|
24
22
|
|
25
23
|
|
26
24
|
|
27
|
-
|
28
25
|
=== -v
|
29
26
|
Be verbose
|
30
27
|
|
31
28
|
|
32
29
|
|
33
|
-
|
34
30
|
=== --version
|
35
31
|
|
36
32
|
|
37
33
|
|
38
34
|
|
39
|
-
|
40
35
|
=== Commands
|
41
|
-
==== help
|
36
|
+
==== Command: <tt>help command</tt>
|
42
37
|
Shows a list of commands or help for one command
|
43
38
|
|
44
|
-
|
45
39
|
Gets help for the application or its commands. Can also list the commands in a way helpful to creating a bash-style completion function
|
46
|
-
|
47
|
-
|
40
|
+
===== Options
|
41
|
+
===== -c
|
42
|
+
List commands one per line, to assist with shell completion
|
48
43
|
|
49
|
-
|
44
|
+
|
45
|
+
|
46
|
+
==== Command: <tt>init|scaffold project_name [command[ command]*]</tt>
|
47
|
+
Create a new GLI-based project
|
50
48
|
|
51
49
|
This will create a scaffold command line project that uses GLI
|
52
50
|
for command line processing. Specifically, this will create
|
53
51
|
an executable ready to go, as well as a lib and test directory, all
|
54
52
|
inside the directory named for your project
|
55
53
|
===== Options
|
56
|
-
===== -e
|
54
|
+
===== -e|--[no-]ext
|
57
55
|
Create an ext dir
|
58
56
|
|
59
|
-
[Aliases] --[no-]ext
|
60
|
-
|
61
57
|
|
62
58
|
|
63
59
|
===== --[no-]force
|
@@ -65,16 +61,13 @@ Overwrite/ignore existing files and directories
|
|
65
61
|
|
66
62
|
|
67
63
|
|
68
|
-
|
69
64
|
===== --notest
|
70
65
|
Do not create a test or features dir
|
71
66
|
|
72
67
|
|
73
68
|
|
74
|
-
|
75
69
|
===== --[no-]rvmrc
|
76
70
|
Create an .rvmrc based on your current RVM setup
|
77
71
|
|
78
72
|
|
79
73
|
|
80
|
-
|
data/lib/gli/app.rb
CHANGED
@@ -23,7 +23,7 @@ module GLI
|
|
23
23
|
$LOAD_PATH.each do |load_path|
|
24
24
|
commands_path = File.join(load_path,path)
|
25
25
|
if File.exists? commands_path
|
26
|
-
Dir.entries(commands_path).each do |entry|
|
26
|
+
Dir.entries(commands_path).sort.each do |entry|
|
27
27
|
file = File.join(commands_path,entry)
|
28
28
|
if file =~ /\.rb$/
|
29
29
|
require file
|
@@ -44,6 +44,17 @@ module GLI
|
|
44
44
|
@program_desc
|
45
45
|
end
|
46
46
|
|
47
|
+
# Provide a longer description of the program. This can be as long as needed, and use double-newlines
|
48
|
+
# for paragraphs. This will show up in the help output.
|
49
|
+
#
|
50
|
+
# description:: A String for the description
|
51
|
+
def program_long_desc(description=nil)
|
52
|
+
if description
|
53
|
+
@program_long_desc = description
|
54
|
+
end
|
55
|
+
@program_long_desc
|
56
|
+
end
|
57
|
+
|
47
58
|
# Use this if the following command should not have the pre block executed.
|
48
59
|
# By default, the pre block is executed before each command and can result in
|
49
60
|
# aborting the call. Using this will avoid that behavior for the following command
|
@@ -77,6 +88,7 @@ module GLI
|
|
77
88
|
@config_file = File.join(File.expand_path(ENV['HOME']),filename)
|
78
89
|
end
|
79
90
|
commands[:initconfig] = InitConfig.new(@config_file,commands,flags,switches)
|
91
|
+
@commands_declaration_order << commands[:initconfig]
|
80
92
|
@config_file
|
81
93
|
end
|
82
94
|
|
@@ -195,8 +207,8 @@ module GLI
|
|
195
207
|
|
196
208
|
# Exit now, showing the user help for the command they executed. Use #exit_now! to just show the error message
|
197
209
|
#
|
198
|
-
# message:: message to indicate how the user has messed up the CLI invocation
|
199
|
-
def help_now!(message)
|
210
|
+
# message:: message to indicate how the user has messed up the CLI invocation or nil to just simply show help
|
211
|
+
def help_now!(message=nil)
|
200
212
|
exception = OptionParser::ParseError.new(message)
|
201
213
|
class << exception
|
202
214
|
def exit_code; 64; end
|
@@ -204,6 +216,25 @@ module GLI
|
|
204
216
|
raise exception
|
205
217
|
end
|
206
218
|
|
219
|
+
# Control how help commands are sorted. By default, the commands are sorted alphabetically.
|
220
|
+
#
|
221
|
+
# sort_type:: How you want help commands sorted:
|
222
|
+
# +:manually+:: help commands are ordered in the order declared.
|
223
|
+
# +:alpha+:: sort alphabetically (default)
|
224
|
+
def sort_help(sort_type)
|
225
|
+
@help_sort_type = sort_type
|
226
|
+
end
|
227
|
+
|
228
|
+
# Set how help text is wrapped.
|
229
|
+
#
|
230
|
+
# wrap_type:: Symbol indicating how you'd like text wrapped:
|
231
|
+
# +:to_terminal+:: Wrap text based on the width of the terminal (default)
|
232
|
+
# +:never+:: Do not wrap text at all. This will bring all help content onto one line, removing any newlines
|
233
|
+
# +:tty_only+:: Wrap like +:to_terminal+ if this output is going to a TTY, otherwise don't wrap (like +:never+)
|
234
|
+
def wrap_help_text(wrap_type)
|
235
|
+
@help_text_wrap_type = wrap_type
|
236
|
+
end
|
237
|
+
|
207
238
|
def program_name(override=nil) #:nodoc:
|
208
239
|
warn "#program_name has been deprecated"
|
209
240
|
end
|
data/lib/gli/app_support.rb
CHANGED
@@ -15,6 +15,7 @@ module GLI
|
|
15
15
|
switches.clear
|
16
16
|
flags.clear
|
17
17
|
@commands = nil
|
18
|
+
@commands_declaration_order = []
|
18
19
|
@version = nil
|
19
20
|
@config_file = nil
|
20
21
|
@use_openstruct = false
|
@@ -27,8 +28,13 @@ module GLI
|
|
27
28
|
clear_nexts
|
28
29
|
end
|
29
30
|
|
31
|
+
# Get an array of commands, ordered by when they were declared
|
32
|
+
def commands_declaration_order # :nodoc:
|
33
|
+
@commands_declaration_order
|
34
|
+
end
|
35
|
+
|
30
36
|
# Get the version string
|
31
|
-
def version_string #:nodoc
|
37
|
+
def version_string #:nodoc:
|
32
38
|
@version
|
33
39
|
end
|
34
40
|
|
@@ -119,7 +125,13 @@ module GLI
|
|
119
125
|
end
|
120
126
|
|
121
127
|
def commands # :nodoc:
|
122
|
-
|
128
|
+
if !@commands
|
129
|
+
@commands = { :help => GLI::Commands::Help.new(self), :_doc => GLI::Commands::Doc.new(self) }
|
130
|
+
@commands_declaration_order ||= []
|
131
|
+
@commands_declaration_order << @commands[:help]
|
132
|
+
@commands_declaration_order << @commands[:_doc]
|
133
|
+
end
|
134
|
+
@commands
|
123
135
|
end
|
124
136
|
|
125
137
|
def pre_block
|
@@ -137,6 +149,14 @@ module GLI
|
|
137
149
|
@around_blocks || []
|
138
150
|
end
|
139
151
|
|
152
|
+
def help_sort_type
|
153
|
+
@help_sort_type || :alpha
|
154
|
+
end
|
155
|
+
|
156
|
+
def help_text_wrap_type
|
157
|
+
@help_text_wrap_type || :to_terminal
|
158
|
+
end
|
159
|
+
|
140
160
|
# Sets the default values for flags based on the configuration
|
141
161
|
def override_defaults_based_on_config(config)
|
142
162
|
override_default(flags,config)
|
@@ -167,9 +187,8 @@ module GLI
|
|
167
187
|
|
168
188
|
def handle_exception(ex,command)
|
169
189
|
if regular_error_handling?(ex)
|
170
|
-
|
190
|
+
output_error_message(ex)
|
171
191
|
if ex.kind_of?(OptionParser::ParseError) || ex.kind_of?(BadCommandLine)
|
172
|
-
stderr.puts
|
173
192
|
commands[:help] and commands[:help].execute({},{},command.nil? ? [] : [command.name.to_s])
|
174
193
|
end
|
175
194
|
end
|
@@ -180,6 +199,17 @@ module GLI
|
|
180
199
|
ex.exit_code
|
181
200
|
end
|
182
201
|
|
202
|
+
def output_error_message(ex)
|
203
|
+
stderr.puts error_message(ex) unless no_message_given?(ex)
|
204
|
+
if ex.kind_of?(OptionParser::ParseError) || ex.kind_of?(BadCommandLine)
|
205
|
+
stderr.puts unless no_message_given?(ex)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
def no_message_given?(ex)
|
210
|
+
ex.message == ex.class.name
|
211
|
+
end
|
212
|
+
|
183
213
|
# Possibly returns a copy of the passed-in Hash as an instance of GLI::Option.
|
184
214
|
# By default, it will *not*. However by putting <tt>use_openstruct true</tt>
|
185
215
|
# in your CLI definition, it will
|
data/lib/gli/command.rb
CHANGED
@@ -50,6 +50,7 @@ module GLI
|
|
50
50
|
@skips_pre = options[:skips_pre]
|
51
51
|
@skips_post = options[:skips_post]
|
52
52
|
@skips_around = options[:skips_around]
|
53
|
+
@commands_declaration_order = []
|
53
54
|
clear_nexts
|
54
55
|
end
|
55
56
|
|
@@ -132,6 +133,13 @@ module GLI
|
|
132
133
|
@default_desc = desc
|
133
134
|
end
|
134
135
|
|
136
|
+
# Returns true if this command has the given option defined
|
137
|
+
def has_option?(option) #:nodoc:
|
138
|
+
option = option.gsub(/^\-+/,'')
|
139
|
+
((flags.values.map { |_| [_.name,_.aliases] }) +
|
140
|
+
(switches.values.map { |_| [_.name,_.aliases] })).flatten.map(&:to_s).include?(option)
|
141
|
+
end
|
142
|
+
|
135
143
|
def self.name_as_string(name,negatable=false) #:nodoc:
|
136
144
|
name.to_s
|
137
145
|
end
|
data/lib/gli/command_support.rb
CHANGED
@@ -44,6 +44,11 @@ module GLI
|
|
44
44
|
all_forms
|
45
45
|
end
|
46
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
|
+
|
47
52
|
def flag(*names)
|
48
53
|
new_flag = if parent.kind_of? Command
|
49
54
|
parent.flag(*names)
|
data/lib/gli/commands/doc.rb
CHANGED
@@ -32,7 +32,8 @@ module GLI
|
|
32
32
|
# Generates documentation using the listener
|
33
33
|
def document(document_listener)
|
34
34
|
document_listener.beginning
|
35
|
-
document_listener.program_desc(@app.program_desc)
|
35
|
+
document_listener.program_desc(@app.program_desc) unless @app.program_desc.nil?
|
36
|
+
document_listener.program_long_desc(@app.program_long_desc) unless @app.program_long_desc.nil?
|
36
37
|
document_listener.version(@app.version_string)
|
37
38
|
if any_options?(@app)
|
38
39
|
document_listener.options
|
@@ -66,6 +67,11 @@ module GLI
|
|
66
67
|
abstract!
|
67
68
|
end
|
68
69
|
|
70
|
+
# Gives you the program long description
|
71
|
+
def program_long_desc(desc)
|
72
|
+
abstract!
|
73
|
+
end
|
74
|
+
|
69
75
|
# Gives you the program version
|
70
76
|
def version(version)
|
71
77
|
abstract!
|
@@ -107,7 +113,7 @@ module GLI
|
|
107
113
|
end
|
108
114
|
|
109
115
|
# Gives you a command in the current context and creates a new context of this command
|
110
|
-
def command(name,aliases,desc,long_desc,arg_name)
|
116
|
+
def command(name,aliases,desc,long_desc,arg_name,arg_options)
|
111
117
|
abstract!
|
112
118
|
end
|
113
119
|
|
@@ -136,11 +142,7 @@ module GLI
|
|
136
142
|
|
137
143
|
def document_commands(document_listener,context)
|
138
144
|
context.commands.values.reject {|_| _.nodoc }.sort(&by_name).each do |command|
|
139
|
-
document_listener
|
140
|
-
Array(command.aliases),
|
141
|
-
command.description,
|
142
|
-
command.long_description,
|
143
|
-
command.arguments_description)
|
145
|
+
call_command_method_being_backwards_compatible(document_listener,command)
|
144
146
|
document_listener.options if any_options?(command)
|
145
147
|
document_flags_and_switches(document_listener,command_flags(command),command_switches(command))
|
146
148
|
document_listener.end_options if any_options?(command)
|
@@ -152,6 +154,18 @@ module GLI
|
|
152
154
|
document_listener.default_command(context.get_default_command)
|
153
155
|
end
|
154
156
|
|
157
|
+
def call_command_method_being_backwards_compatible(document_listener,command)
|
158
|
+
command_args = [command.name,
|
159
|
+
Array(command.aliases),
|
160
|
+
command.description,
|
161
|
+
command.long_description,
|
162
|
+
command.arguments_description]
|
163
|
+
if document_listener.method(:command).arity == 6
|
164
|
+
command_args << command.arguments_options
|
165
|
+
end
|
166
|
+
document_listener.command(*command_args)
|
167
|
+
end
|
168
|
+
|
155
169
|
def by_name
|
156
170
|
lambda { |a,b| a.name.to_s <=> b.name.to_s }
|
157
171
|
end
|
@@ -170,7 +184,7 @@ module GLI
|
|
170
184
|
Array(flag.aliases),
|
171
185
|
flag.description,
|
172
186
|
flag.long_description,
|
173
|
-
flag.
|
187
|
+
flag.safe_default_value,
|
174
188
|
flag.argument_name,
|
175
189
|
flag.must_match,
|
176
190
|
flag.type)
|