gli 2.8.1 → 2.9.0

Sign up to get free protection for your applications and to get access to all the features.
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.