gli 2.8.1 → 2.9.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2fac00c937ac549343211b11935089bcb1277768
4
- data.tar.gz: 0242a96c13a3e5e35ccfd51a65dd6764eb7c375a
3
+ metadata.gz: d1fffe3ab7b8b3aa4881cfb38de3b7cc1e31508b
4
+ data.tar.gz: 057ab9061b13527ed56f448cc20ee3ea54a7472d
5
5
  SHA512:
6
- metadata.gz: 7d4d6b60d31a79295c9a8102c78b8148b71db8b0e2e1f57c1ab1ea36c1c89afd7f0d9f158480699d6e3bc17f90e6b4ce2edf2dfed702711a611ad56191d8cb44
7
- data.tar.gz: 579f2c2504172996d80276540dd0b402ea4f634a2335b246aeb184cf3f3b36b53d0673ff26d34a2200ce20b39c4f6246081fb247aa9862601028e5a576927ac9
6
+ metadata.gz: c61409e0f16a9d07d9e4f704e05890e48868dc359be530dd2c094d3856a841b224968c1f2902b761355ad3ff2574ab8e9a33af626ceb77e897e3a2dfa17a3580
7
+ data.tar.gz: 3f26ab6c6026483c986241c506fdf1742a43408eac52605aa7ba2c5a36076f1d48acba70fbba5031b649fc042043d2cdf112ebc175cf9afeb35b49821fd15979
@@ -3,14 +3,10 @@ notifications:
3
3
  on_success: always
4
4
  script: 'bundle exec rake test features'
5
5
  rvm:
6
- - 1.9.2
7
6
  - 1.9.3
8
7
  - 1.8.7
9
- - ree
10
8
  - 2.0.0
9
+ - 2.1.0
11
10
  branches:
12
11
  only:
13
- - 'master'
14
12
  - 'gli-2'
15
- - 'quick-bugifxes'
16
- - 'fully-nested-subcommands'
@@ -86,3 +86,11 @@ end
86
86
  Given /^the todo app is coded to use verbatim formatting$/ do
87
87
  ENV['TODO_WRAP_HELP_TEXT'] = 'verbatim'
88
88
  end
89
+
90
+ Given(/^my terminal is (\d+) characters wide$/) do |terminal_width|
91
+ ENV['COLUMNS'] = terminal_width.to_s
92
+ end
93
+
94
+ Given(/^my app is configured for "(.*?)" synopses$/) do |synopsis|
95
+ ENV['SYNOPSES'] = synopsis
96
+ end
@@ -15,6 +15,7 @@ GLI_GEMSET = 'gli-testing'
15
15
  TMP_PATH = 'tmp/aruba'
16
16
 
17
17
  Before do
18
+ unset_bundler_env_vars
18
19
  # Not sure how else to get this dynamically
19
20
  @dirs = [TMP_PATH]
20
21
  @aruba_timeout_seconds = 5
@@ -152,8 +152,8 @@ Feature: The todo app has a nice user interface
152
152
  list - List things, such as tasks or contexts
153
153
 
154
154
  SYNOPSIS
155
- todo [global options] list [command options] [--flag arg] [-x arg] [tasks]
156
- todo [global options] list [command options] [--otherflag arg] [-b] [-f|--foobar] contexts
155
+ todo [global options] list [command options] [tasks] [--flag arg] [-x arg]
156
+ todo [global options] list [command options] contexts [--otherflag arg] [-b] [-f|--foobar]
157
157
 
158
158
  DESCRIPTION
159
159
  List a whole lot of things that you might be keeping track of in your
@@ -163,7 +163,8 @@ Feature: The todo app has a nice user interface
163
163
  stored in your todo databases.
164
164
 
165
165
  COMMAND OPTIONS
166
- -l, --[no-]long - Show long form
166
+ -l, --[no-]long - Show long form
167
+ --required_flag=arg - (required, default: none)
167
168
 
168
169
  COMMANDS
169
170
  contexts - List contexts
@@ -198,14 +199,15 @@ Feature: The todo app has a nice user interface
198
199
  list - List things, such as tasks or contexts
199
200
 
200
201
  SYNOPSIS
201
- todo [global options] list [command options] [--flag arg] [-x arg] [tasks]
202
- todo [global options] list [command options] [--otherflag arg] [-b] [-f|--foobar] contexts
202
+ todo [global options] list [command options] [tasks] [--flag arg] [-x arg]
203
+ todo [global options] list [command options] contexts [--otherflag arg] [-b] [-f|--foobar]
203
204
 
204
205
  DESCRIPTION
205
206
  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.
206
207
 
207
208
  COMMAND OPTIONS
208
- -l, --[no-]long - Show long form
209
+ -l, --[no-]long - Show long form
210
+ --required_flag=arg - (required, default: none)
209
211
 
210
212
  COMMANDS
211
213
  contexts - List contexts
@@ -221,8 +223,8 @@ Feature: The todo app has a nice user interface
221
223
  list - List things, such as tasks or contexts
222
224
 
223
225
  SYNOPSIS
224
- todo [global options] list [command options] [--flag arg] [-x arg] [tasks]
225
- todo [global options] list [command options] [--otherflag arg] [-b] [-f|--foobar] contexts
226
+ todo [global options] list [command options] [tasks] [--flag arg] [-x arg]
227
+ todo [global options] list [command options] contexts [--otherflag arg] [-b] [-f|--foobar]
226
228
 
227
229
  DESCRIPTION
228
230
 
@@ -236,7 +238,8 @@ Feature: The todo app has a nice user interface
236
238
 
237
239
 
238
240
  COMMAND OPTIONS
239
- -l, --[no-]long - Show long form
241
+ -l, --[no-]long - Show long form
242
+ --required_flag=arg - (required, default: none)
240
243
 
241
244
  COMMANDS
242
245
  contexts - List contexts
@@ -252,14 +255,15 @@ Feature: The todo app has a nice user interface
252
255
  list - List things, such as tasks or contexts
253
256
 
254
257
  SYNOPSIS
255
- todo [global options] list [command options] [--flag arg] [-x arg] [tasks]
256
- todo [global options] list [command options] [--otherflag arg] [-b] [-f|--foobar] contexts
258
+ todo [global options] list [command options] [tasks] [--flag arg] [-x arg]
259
+ todo [global options] list [command options] contexts [--otherflag arg] [-b] [-f|--foobar]
257
260
 
258
261
  DESCRIPTION
259
262
  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.
260
263
 
261
264
  COMMAND OPTIONS
262
- -l, --[no-]long - Show long form
265
+ -l, --[no-]long - Show long form
266
+ --required_flag=arg - (required, default: none)
263
267
 
264
268
  COMMANDS
265
269
  contexts - List contexts
@@ -275,7 +279,7 @@ Feature: The todo app has a nice user interface
275
279
 
276
280
  SYNOPSIS
277
281
  todo [global options] list tasks [command options]
278
- todo [global options] list tasks [command options] open
282
+ todo [global options] list tasks [command options] open
279
283
 
280
284
  DESCRIPTION
281
285
  Lists all of your tasks that you have, in varying orders, and all that
@@ -299,8 +303,8 @@ Feature: The todo app has a nice user interface
299
303
 
300
304
  SYNOPSIS
301
305
  todo [global options] create
302
- todo [global options] create contexts [context_name]
303
- todo [global options] create tasks task_name[, task_name]*
306
+ todo [global options] create contexts [context_name]
307
+ todo [global options] create tasks task_name[, task_name]*
304
308
 
305
309
  COMMANDS
306
310
  <default> - Makes a new task
@@ -310,11 +314,11 @@ Feature: The todo app has a nice user interface
310
314
  And the output should not contain "COMMAND OPTIONS"
311
315
 
312
316
  Scenario: Running list w/out subcommand performs list tasks by default
313
- When I successfully run `todo list boo yay`
317
+ When I successfully run `todo list --required_flag=blah boo yay`
314
318
  Then the output should contain "list tasks: boo,yay"
315
319
 
316
320
  Scenario: Running list w/out subcommand or any arguments performs list tasks by default
317
- When I successfully run `todo list`
321
+ When I successfully run `todo list --required_flag=blah`
318
322
  Then the output should contain "list tasks:"
319
323
 
320
324
  Scenario: Running chained commands works
@@ -351,8 +355,8 @@ Feature: The todo app has a nice user interface
351
355
  ls - LS things, such as tasks or contexts
352
356
 
353
357
  SYNOPSIS
354
- todo [global options] ls [command options] [-b] [-f|--foobar] contexts
355
- todo [global options] ls [command options] [-x arg] tasks
358
+ todo [global options] ls [command options] contexts [-b] [-f|--foobar]
359
+ todo [global options] ls [command options] tasks [-x arg]
356
360
 
357
361
  DESCRIPTION
358
362
  List a whole lot of things that you might be keeping track of in your
@@ -411,3 +415,35 @@ Feature: The todo app has a nice user interface
411
415
  And a config file that specifies defaults for some commands with subcommands
412
416
  When I successfully run `todo help list contexts`
413
417
  Then I should see the defaults for 'list contexts' from the config file in the help
418
+
419
+ Scenario: A complex SYNOPSIS section gets summarized in terminal mode
420
+ Given my terminal is 50 characters wide
421
+ And my app is configured for "terminal" synopses
422
+ When I run `todo ls`
423
+ Then the exit status should not be 0
424
+ And the stderr should contain "error: Command 'ls' requires a subcommand"
425
+ And the stdout should contain:
426
+ """
427
+ NAME
428
+ ls - LS things, such as tasks or contexts
429
+
430
+ SYNOPSIS
431
+ todo [global options] ls [command options] contexts [subcommand options]
432
+ todo [global options] ls [command options] tasks [subcommand options]
433
+ """
434
+
435
+ Scenario: We can always use a compact SYNOPSIS
436
+ Given my terminal is 500 characters wide
437
+ And my app is configured for "compact" synopses
438
+ When I run `todo ls`
439
+ Then the exit status should not be 0
440
+ And the stderr should contain "error: Command 'ls' requires a subcommand"
441
+ And the stdout should contain:
442
+ """
443
+ NAME
444
+ ls - LS things, such as tasks or contexts
445
+
446
+ SYNOPSIS
447
+ todo [global options] ls [command options] contexts [subcommand options]
448
+ todo [global options] ls [command options] tasks [subcommand options]
449
+ """
@@ -31,8 +31,8 @@ Feature: The todo app is backwards compatible with legacy subcommand parsing
31
31
  list - List things, such as tasks or contexts
32
32
 
33
33
  SYNOPSIS
34
- todo [global options] list [command options] [--flag arg] [-x arg] [tasks]
35
- todo [global options] list [command options] [--otherflag arg] [-b] [-f|--foobar] contexts
34
+ todo [global options] list [command options] [tasks] [--flag arg] [-x arg]
35
+ todo [global options] list [command options] contexts [--otherflag arg] [-b] [-f|--foobar]
36
36
 
37
37
  DESCRIPTION
38
38
  List a whole lot of things that you might be keeping track of in your
@@ -65,7 +65,7 @@ Feature: The todo app is backwards compatible with legacy subcommand parsing
65
65
 
66
66
  SYNOPSIS
67
67
  todo [global options] list tasks [command options]
68
- todo [global options] list tasks [command options] open
68
+ todo [global options] list tasks [command options] open
69
69
 
70
70
  DESCRIPTION
71
71
  Lists all of your tasks that you have, in varying orders, and all that
@@ -89,8 +89,8 @@ Feature: The todo app is backwards compatible with legacy subcommand parsing
89
89
 
90
90
  SYNOPSIS
91
91
  todo [global options] create
92
- todo [global options] create contexts [context_name]
93
- todo [global options] create tasks task_name[, task_name]*
92
+ todo [global options] create contexts [context_name]
93
+ todo [global options] create tasks task_name[, task_name]*
94
94
 
95
95
  COMMANDS
96
96
  <default> - Makes a new task
@@ -109,8 +109,8 @@ Feature: The todo app is backwards compatible with legacy subcommand parsing
109
109
  ls - LS things, such as tasks or contexts
110
110
 
111
111
  SYNOPSIS
112
- todo [global options] ls [command options] [-b] [-f|--foobar] contexts
113
- todo [global options] ls [command options] [-x arg] tasks
112
+ todo [global options] ls [command options] contexts [-b] [-f|--foobar]
113
+ todo [global options] ls [command options] tasks [-x arg]
114
114
 
115
115
  DESCRIPTION
116
116
  List a whole lot of things that you might be keeping track of in your
@@ -245,6 +245,16 @@ module GLI
245
245
  @help_text_wrap_type = wrap_type
246
246
  end
247
247
 
248
+ # Control how the SYNOPSIS is formatted.
249
+ #
250
+ # format:: one of:
251
+ # +:full+:: the default, show subcommand options and flags inline
252
+ # +:terminal+:: if :full would be wider than the terminal, use :compact
253
+ # +:compact+:: use a simpler and shorter SYNOPSIS. Useful if your app has a lot of options and showing them in the SYNOPSIS makes things more confusing
254
+ def synopsis_format(format)
255
+ @synopsis_format_type = format
256
+ end
257
+
248
258
  def program_name(override=nil) #:nodoc:
249
259
  warn "#program_name has been deprecated"
250
260
  end
@@ -166,6 +166,10 @@ module GLI
166
166
  @help_text_wrap_type || :to_terminal
167
167
  end
168
168
 
169
+ def synopsis_format_type
170
+ @synopsis_format_type || :full
171
+ end
172
+
169
173
  # Sets the default values for flags based on the configuration
170
174
  def override_defaults_based_on_config(config)
171
175
  override_default(flags,config)
@@ -12,6 +12,9 @@ require 'gli/commands/help_modules/command_help_format'
12
12
  require 'gli/commands/help_modules/help_completion_format'
13
13
  require 'gli/commands/help_modules/command_finder'
14
14
  require 'gli/commands/help_modules/arg_name_formatter'
15
+ require 'gli/commands/help_modules/full_synopsis_formatter'
16
+ require 'gli/commands/help_modules/compact_synopsis_formatter'
17
+ require 'gli/commands/help_modules/terminal_synopsis_formatter'
15
18
 
16
19
  module GLI
17
20
  module Commands
@@ -28,6 +31,12 @@ module GLI
28
31
  :none => HelpModules::VerbatimWrapper,
29
32
  :verbatim => HelpModules::VerbatimWrapper,
30
33
  }
34
+
35
+ SYNOPSIS_FORMATTERS = {
36
+ :full => HelpModules::FullSynopsisFormatter,
37
+ :compact => HelpModules::CompactSynopsisFormatter,
38
+ :terminal => HelpModules::TerminalSynopsisFormatter,
39
+ }
31
40
  # The help command used for the two-level interactive help system
32
41
  class Help < Command
33
42
  @@skips_pre = true
@@ -55,6 +64,7 @@ module GLI
55
64
  @parent = app
56
65
  @sorter = SORTERS[@app.help_sort_type]
57
66
  @text_wrapping_class = WRAPPERS[@app.help_text_wrap_type]
67
+ @synopsis_formatter_class = SYNOPSIS_FORMATTERS[@app.synopsis_format_type]
58
68
 
59
69
  desc 'List commands one per line, to assist with shell completion'
60
70
  switch :c
@@ -85,7 +95,12 @@ module GLI
85
95
  name = arguments.shift
86
96
  command = command_finder.find_command(name)
87
97
  unless command.nil?
88
- out.puts HelpModules::CommandHelpFormat.new(command,@app,@app.exe_name.to_s,@sorter,@text_wrapping_class).format
98
+ out.puts HelpModules::CommandHelpFormat.new(
99
+ command,
100
+ @app,
101
+ @sorter,
102
+ @synopsis_formatter_class,
103
+ @text_wrapping_class).format
89
104
  end
90
105
  end
91
106
  end
@@ -4,12 +4,12 @@ module GLI
4
4
  module Commands
5
5
  module HelpModules
6
6
  class CommandHelpFormat
7
- def initialize(command,app,basic_invocation,sorter,wrapper_class=TextWrapper)
8
- @basic_invocation = basic_invocation
7
+ def initialize(command,app,sorter,synopsis_formatter_class,wrapper_class=TextWrapper)
9
8
  @app = app
10
9
  @command = command
11
10
  @sorter = sorter
12
11
  @wrapper_class = wrapper_class
12
+ @synopsis_formatter = synopsis_formatter_class.new(@app,flags_and_switches(@command,@app))
13
13
  end
14
14
 
15
15
  def format
@@ -19,19 +19,7 @@ module GLI
19
19
  options_description = OptionsFormatter.new(flags_and_switches(@command,@app),@sorter,@wrapper_class).format
20
20
  commands_description = format_subcommands(@command)
21
21
 
22
- synopses = []
23
- one_line_usage = basic_usage
24
- one_line_usage << @command.arguments_description
25
- if @command.commands.empty?
26
- synopses << one_line_usage
27
- else
28
- synopses = sorted_synopses
29
- if @command.has_action?
30
- synopses.unshift(one_line_usage)
31
- end
32
-
33
- end
34
-
22
+ synopses = @synopsis_formatter.synopses_for_command(@command)
35
23
  COMMAND_HELP.result(binding)
36
24
  end
37
25
 
@@ -59,27 +47,6 @@ COMMANDS
59
47
  <%= commands_description %>
60
48
  <% end %>),nil,'<>')
61
49
 
62
- def command_with_subcommand_usage(sub,is_default_command)
63
- usage = basic_usage
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
69
- usage << sub_options.map { |option_name,option|
70
- option.names_and_aliases.map { |_|
71
- CommandLineOption.name_as_string(_,false) + (option.kind_of?(Flag) ? " #{option.argument_name }" : '')
72
- }.join('|')
73
- }.map { |_| "[#{_}]" }.sort.join(' ')
74
- usage << ' '
75
- if is_default_command
76
- usage << "[#{sub.name}]"
77
- else
78
- usage << sub.name.to_s
79
- end
80
- usage << ArgNameFormatter.new.format(sub.arguments_description,sub.arguments_options)
81
- usage
82
- end
83
50
 
84
51
  def flags_and_switches(command,app)
85
52
  if app.subcommand_option_handling_strategy == :legacy
@@ -95,29 +62,6 @@ COMMANDS
95
62
  end
96
63
  end
97
64
 
98
- def basic_usage
99
- usage = @basic_invocation.dup
100
- usage << " [global options]" unless global_flags_and_switches.empty?
101
- usage << " #{path_to_command}"
102
- usage << " [command options]" unless flags_and_switches(@command,@app).empty?
103
- usage << " "
104
- usage
105
- end
106
-
107
- def path_to_command
108
- path = []
109
- c = @command
110
- while c.kind_of? Command
111
- path.unshift(c.name)
112
- c = c.parent
113
- end
114
- path.join(' ')
115
- end
116
-
117
- def global_flags_and_switches
118
- @app.flags.merge(@app.switches)
119
- end
120
-
121
65
  def format_subcommands(command)
122
66
  commands_array = @sorter.call(command.commands_declaration_order).map { |cmd|
123
67
  if command.get_default_command == cmd.name
@@ -132,24 +76,6 @@ COMMANDS
132
76
  formatter = ListFormatter.new(commands_array,@wrapper_class)
133
77
  StringIO.new.tap { |io| formatter.output(io) }.string
134
78
  end
135
-
136
- def sorted_synopses
137
- synopses_command = {}
138
- @command.commands.each do |name,sub|
139
- default = @command.get_default_command == name
140
- synopsis = command_with_subcommand_usage(sub,default)
141
- synopses_command[synopsis] = sub
142
- end
143
- synopses = synopses_command.keys.sort { |one,two|
144
- if synopses_command[one].name == @command.get_default_command
145
- -1
146
- elsif synopses_command[two].name == @command.get_default_command
147
- 1
148
- else
149
- synopses_command[one] <=> synopses_command[two]
150
- end
151
- }
152
- end
153
79
  end
154
80
  end
155
81
  end
@@ -0,0 +1,20 @@
1
+ module GLI
2
+ module Commands
3
+ module HelpModules
4
+ class CompactSynopsisFormatter < FullSynopsisFormatter
5
+
6
+ protected
7
+
8
+ def sub_options_doc(sub_options)
9
+ if sub_options.empty?
10
+ ''
11
+ else
12
+ '[subcommand options]'
13
+ end
14
+ end
15
+
16
+
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,109 @@
1
+ module GLI
2
+ module Commands
3
+ module HelpModules
4
+ class FullSynopsisFormatter
5
+ def initialize(app,flags_and_switches)
6
+ @app = app
7
+ @basic_invocation = @app.exe_name.to_s
8
+ @flags_and_switches = flags_and_switches
9
+ end
10
+
11
+ def synopses_for_command(command)
12
+ synopses = []
13
+ one_line_usage = basic_usage(command)
14
+ one_line_usage << command.arguments_description
15
+ if command.commands.empty?
16
+ synopses << one_line_usage
17
+ else
18
+ synopses = sorted_synopses(command)
19
+ if command.has_action?
20
+ synopses.unshift(one_line_usage)
21
+ end
22
+ end
23
+ synopses
24
+ end
25
+
26
+ protected
27
+
28
+ def sub_options_doc(sub_options)
29
+ sub_options_doc = sub_options.map { |_,option|
30
+ option.names_and_aliases.map { |name|
31
+ CommandLineOption.name_as_string(name,false) + (option.kind_of?(Flag) ? " #{option.argument_name }" : '')
32
+ }.join('|')
33
+ }.map { |invocations| "[#{invocations}]" }.sort.join(' ').strip
34
+ end
35
+
36
+ private
37
+
38
+ def path_to_command(command)
39
+ path = []
40
+ c = command
41
+ while c.kind_of? Command
42
+ path.unshift(c.name)
43
+ c = c.parent
44
+ end
45
+ path.join(' ')
46
+ end
47
+
48
+
49
+ def basic_usage(command)
50
+ usage = @basic_invocation.dup
51
+ usage << " [global options]" unless global_flags_and_switches.empty?
52
+ usage << " #{path_to_command(command)}"
53
+ usage << " [command options]" unless @flags_and_switches.empty?
54
+ usage << " "
55
+ usage
56
+ end
57
+
58
+
59
+ def command_with_subcommand_usage(command,sub,is_default_command)
60
+ usage = basic_usage(command)
61
+ sub_options = if @app.subcommand_option_handling_strategy == :legacy
62
+ command.flags.merge(command.switches).select { |_,o| o.associated_command == sub }
63
+ else
64
+ sub.flags.merge(sub.switches)
65
+ end
66
+ if is_default_command
67
+ usage << "[#{sub.name}]"
68
+ else
69
+ usage << sub.name.to_s
70
+ end
71
+ sub_options_doc = sub_options_doc(sub_options)
72
+ if sub_options_doc.length > 0
73
+ usage << ' '
74
+ usage << sub_options_doc
75
+ end
76
+ arg_name_doc = ArgNameFormatter.new.format(sub.arguments_description,sub.arguments_options).strip
77
+ if arg_name_doc.length > 0
78
+ usage << ' '
79
+ usage << arg_name_doc
80
+ end
81
+ usage
82
+ end
83
+
84
+ def sorted_synopses(command)
85
+ synopses_command = {}
86
+ command.commands.each do |name,sub|
87
+ default = command.get_default_command == name
88
+ synopsis = command_with_subcommand_usage(command,sub,default)
89
+ synopses_command[synopsis] = sub
90
+ end
91
+ synopses = synopses_command.keys.sort { |one,two|
92
+ if synopses_command[one].name == command.get_default_command
93
+ -1
94
+ elsif synopses_command[two].name == command.get_default_command
95
+ 1
96
+ else
97
+ synopses_command[one] <=> synopses_command[two]
98
+ end
99
+ }
100
+ end
101
+
102
+ def global_flags_and_switches
103
+ @app.flags.merge(@app.switches)
104
+ end
105
+
106
+ end
107
+ end
108
+ end
109
+ end
@@ -24,7 +24,12 @@ module GLI
24
24
 
25
25
  def description_with_default(option)
26
26
  if option.kind_of? Flag
27
- String(option.description) + " (default: #{option.safe_default_value || 'none'})"
27
+ required = if option.required?
28
+ 'required, '
29
+ else
30
+ ''
31
+ end
32
+ String(option.description) + " (#{required}default: #{option.safe_default_value || 'none'})"
28
33
  else
29
34
  String(option.description) + (option.default_value ? " (default: enabled)" : "")
30
35
  end
@@ -0,0 +1,22 @@
1
+ module GLI
2
+ module Commands
3
+ module HelpModules
4
+ class TerminalSynopsisFormatter
5
+ def initialize(app,flags_and_switches)
6
+ @app = app
7
+ @basic_invocation = @app.exe_name.to_s
8
+ @flags_and_switches = flags_and_switches
9
+ end
10
+ def synopses_for_command(command)
11
+ synopses = FullSynopsisFormatter.new(@app,@flags_and_switches).synopses_for_command(command)
12
+ if synopses.any? { |synopsis| synopsis.length > Terminal.instance.size[0] }
13
+ CompactSynopsisFormatter.new(@app,@flags_and_switches).synopses_for_command(command)
14
+
15
+ else
16
+ synopses
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -63,12 +63,7 @@ spec = Gem::Specification.new do |s|
63
63
  s.homepage = 'http://your.website.com'
64
64
  s.platform = Gem::Platform::RUBY
65
65
  s.summary = 'A description of your project'
66
- # Add your other files here if you make them
67
- s.files = %w(
68
- bin/#{project_name}
69
- lib/#{project_name}/version.rb
70
- lib/#{project_name}.rb
71
- )
66
+ s.files = `git ls-files`.split("\n")
72
67
  s.require_paths << 'lib'
73
68
  s.has_rdoc = true
74
69
  s.extra_rdoc_files = ['README.rdoc','#{project_name}.rdoc']
@@ -23,6 +23,7 @@ module GLI
23
23
  # :arg_name:: the name of the flag's argument, default is "arg"
24
24
  # :must_match:: a regexp that the flag's value must match
25
25
  # :type:: a class to convert the value to
26
+ # :required:: if true, this flag must be specified on the command line
26
27
  # :mask:: if true, the default value of this flag will not be output in the help.
27
28
  # This is useful for password flags where you might not want to show it
28
29
  # on the command-line.
@@ -33,8 +34,15 @@ module GLI
33
34
  @must_match = options[:must_match]
34
35
  @type = options[:type]
35
36
  @mask = options[:mask]
37
+ @required = options[:required]
36
38
  end
37
39
 
40
+ # True if this flag is required on the command line
41
+ def required?
42
+ @required
43
+ end
44
+
45
+
38
46
  def safe_default_value
39
47
  if @mask
40
48
  "********"
@@ -3,7 +3,7 @@ module GLI
3
3
  class GLIOptionParser
4
4
  def initialize(commands,flags,switches,accepts,default_command = nil,subcommand_option_handling_strategy=:legacy)
5
5
  command_finder = CommandFinder.new(commands,default_command || "help")
6
- @global_option_parser = GlobalOptionParser.new(OptionParserFactory.new(flags,switches,accepts),command_finder)
6
+ @global_option_parser = GlobalOptionParser.new(OptionParserFactory.new(flags,switches,accepts),command_finder,flags)
7
7
  @accepts = accepts
8
8
  @subcommand_option_handling_strategy = subcommand_option_handling_strategy
9
9
  end
@@ -21,9 +21,10 @@ module GLI
21
21
  private
22
22
 
23
23
  class GlobalOptionParser
24
- def initialize(option_parser_factory,command_finder)
24
+ def initialize(option_parser_factory,command_finder,flags)
25
25
  @option_parser_factory = option_parser_factory
26
26
  @command_finder = command_finder
27
+ @flags = flags
27
28
  end
28
29
 
29
30
  def parse!(parsing_result)
@@ -35,11 +36,29 @@ module GLI
35
36
  parsing_result.arguments.shift
36
37
  end
37
38
  parsing_result.command = @command_finder.find_command(command_name)
39
+ unless command_name == 'help'
40
+ verify_required_options!(@flags,parsing_result.global_options)
41
+ end
38
42
  parsing_result
39
43
  end
44
+
45
+ protected
46
+
47
+ def verify_required_options!(flags,options)
48
+ missing_required_options = flags.values.
49
+ select(&:required?).
50
+ reject { |option|
51
+ options[option.name] != nil
52
+ }
53
+ unless missing_required_options.empty?
54
+ raise BadCommandLine, missing_required_options.map { |option|
55
+ "#{option.name} is required"
56
+ }.join(' ')
57
+ end
58
+ end
40
59
  end
41
60
 
42
- class NormalCommandOptionParser
61
+ class NormalCommandOptionParser < GlobalOptionParser
43
62
  def initialize(accepts)
44
63
  @accepts = accepts
45
64
  end
@@ -67,6 +86,8 @@ module GLI
67
86
  command_finder = CommandFinder.new(command.commands,command.get_default_command)
68
87
  next_command_name = arguments.shift
69
88
 
89
+ verify_required_options!(command.flags,parsed_command_options[command])
90
+
70
91
  begin
71
92
  command = command_finder.find_command(next_command_name)
72
93
  rescue AmbiguousCommand
@@ -101,6 +122,7 @@ module GLI
101
122
  parsing_result.arguments = Array(arguments.compact)
102
123
  parsing_result
103
124
  end
125
+
104
126
  end
105
127
 
106
128
  class LegacyCommandOptionParser < NormalCommandOptionParser
@@ -116,6 +138,7 @@ module GLI
116
138
  subcommand,args = find_subcommand(command,parsing_result.arguments)
117
139
  parsing_result.command = subcommand
118
140
  parsing_result.arguments = args
141
+ verify_required_options!(command.flags,parsing_result.command_options)
119
142
  end
120
143
 
121
144
  private
@@ -1,5 +1,5 @@
1
1
  module GLI
2
2
  unless const_defined? :VERSION
3
- VERSION = '2.8.1'
3
+ VERSION = '2.9.0'
4
4
  end
5
5
  end
@@ -1,7 +1,7 @@
1
1
  require 'rake/clean'
2
2
  require 'rubygems'
3
3
  require 'rake/gempackagetask'
4
- require 'rake/rdoctask'
4
+ require 'rdoc/task'
5
5
 
6
6
  Rake::RDocTask.new do |rd|
7
7
  rd.main = "README.rdoc"
@@ -16,6 +16,7 @@ end
16
16
 
17
17
  sort_help (ENV['TODO_SORT_HELP'] || 'alpha').to_sym
18
18
  wrap_help_text (ENV['TODO_WRAP_HELP_TEXT'] || 'to_terminal').to_sym
19
+ synopsis_format (ENV['SYNOPSES'] || 'full').to_sym
19
20
 
20
21
  subcommand_option_handling :normal
21
22
 
@@ -14,6 +14,8 @@ command [:list] do |c|
14
14
  c.desc "Show long form"
15
15
  c.switch [:l,:long]
16
16
 
17
+ c.flag :required_flag, :required => true
18
+
17
19
  c.desc "List tasks"
18
20
  c.long_desc %(
19
21
  Lists all of your tasks that you have, in varying orders, and
@@ -1,7 +1,7 @@
1
1
  require 'rake/clean'
2
2
  require 'rubygems'
3
3
  require 'rake/gempackagetask'
4
- require 'rake/rdoctask'
4
+ require 'rdoc/task'
5
5
 
6
6
  Rake::RDocTask.new do |rd|
7
7
  rd.main = "README.rdoc"
@@ -71,6 +71,61 @@ class TC_testGLI < Clean::Test::TestCase
71
71
  assert @called, "Expected default command to be executed"
72
72
  end
73
73
 
74
+ def test_command_options_can_be_required
75
+ @app.reset
76
+ @called = false
77
+ @app.command :foo do |c|
78
+ c.flag :flag, :required => true
79
+ c.flag :other_flag, :required => true
80
+ c.action do |global, options, arguments|
81
+ @called = true
82
+ end
83
+ end
84
+ assert_equal 64, @app.run(['foo']), "Expected exit status to be 64"
85
+ assert @fake_stderr.contained?(/flag is required/), @fake_stderr.strings.inspect
86
+ assert @fake_stderr.contained?(/other_flag is required/), @fake_stderr.strings.inspect
87
+ assert !@called
88
+
89
+ assert_equal 0, @app.run(['foo','--flag=bar','--other_flag=blah']), "Expected exit status to be 0 #{@fake_stderr.strings.join(',')}"
90
+ assert @called
91
+
92
+ end
93
+
94
+ def test_global_options_can_be_required
95
+ @app.reset
96
+ @called = false
97
+ @app.flag :flag, :required => true
98
+ @app.flag :other_flag, :required => true
99
+ @app.command :foo do |c|
100
+ c.action do |global, options, arguments|
101
+ @called = true
102
+ end
103
+ end
104
+ assert_equal 64, @app.run(['foo']), "Expected exit status to be 64"
105
+ assert @fake_stderr.contained?(/flag is required/), @fake_stderr.strings.inspect
106
+ assert @fake_stderr.contained?(/other_flag is required/), @fake_stderr.strings.inspect
107
+ assert !@called
108
+
109
+ assert_equal 0, @app.run(['--flag=bar','--other_flag=blah','foo']), "Expected exit status to be 0 #{@fake_stderr.strings.join(',')}"
110
+ assert @called
111
+
112
+ end
113
+
114
+ def test_global_required_options_are_ignored_on_help
115
+ @app.reset
116
+ @called = false
117
+ @app.flag :flag, :required => true
118
+ @app.flag :other_flag, :required => true
119
+ @app.command :foo do |c|
120
+ c.action do |global, options, arguments|
121
+ @called = true
122
+ end
123
+ end
124
+ assert_equal 0, @app.run(['help']), "Expected exit status to be 64"
125
+ assert !@fake_stderr.contained?(/flag is required/), @fake_stderr.strings.inspect
126
+ assert !@fake_stderr.contained?(/other_flag is required/), @fake_stderr.strings.inspect
127
+ end
128
+
74
129
  def test_flag_with_space_barfs
75
130
  @app.reset
76
131
  assert_raises(ArgumentError) { @app.flag ['some flag'] }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gli
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.8.1
4
+ version: 2.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Copeland
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-10-08 00:00:00.000000000 Z
11
+ date: 2014-01-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -185,11 +185,14 @@ files:
185
185
  - lib/gli/commands/help_modules/arg_name_formatter.rb
186
186
  - lib/gli/commands/help_modules/command_finder.rb
187
187
  - lib/gli/commands/help_modules/command_help_format.rb
188
+ - lib/gli/commands/help_modules/compact_synopsis_formatter.rb
189
+ - lib/gli/commands/help_modules/full_synopsis_formatter.rb
188
190
  - lib/gli/commands/help_modules/global_help_format.rb
189
191
  - lib/gli/commands/help_modules/help_completion_format.rb
190
192
  - lib/gli/commands/help_modules/list_formatter.rb
191
193
  - lib/gli/commands/help_modules/one_line_wrapper.rb
192
194
  - lib/gli/commands/help_modules/options_formatter.rb
195
+ - lib/gli/commands/help_modules/terminal_synopsis_formatter.rb
193
196
  - lib/gli/commands/help_modules/text_wrapper.rb
194
197
  - lib/gli/commands/help_modules/tty_only_wrapper.rb
195
198
  - lib/gli/commands/help_modules/verbatim_wrapper.rb
@@ -272,7 +275,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
272
275
  version: '0'
273
276
  requirements: []
274
277
  rubyforge_project: gli
275
- rubygems_version: 2.0.3
278
+ rubygems_version: 2.0.14
276
279
  signing_key:
277
280
  specification_version: 4
278
281
  summary: Build command-suite CLI apps that are awesome.