gli 2.1.0 → 2.2.0
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/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)
|