gli 1.6.0 → 2.0.0.rc3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. data/.gitignore +11 -0
  2. data/.rvmrc +1 -0
  3. data/.travis.yml +10 -0
  4. data/Gemfile +8 -0
  5. data/LICENSE.txt +201 -0
  6. data/ObjectModel.graffle +1191 -0
  7. data/README.rdoc +60 -10
  8. data/Rakefile +145 -0
  9. data/bin/gli +12 -30
  10. data/bin/report_on_rake_results +10 -0
  11. data/bin/test_all_rubies.sh +6 -0
  12. data/features/gli_executable.feature +84 -0
  13. data/features/gli_init.feature +219 -0
  14. data/features/step_definitions/gli_executable_steps.rb +12 -0
  15. data/features/step_definitions/gli_init_steps.rb +11 -0
  16. data/features/step_definitions/todo_steps.rb +69 -0
  17. data/features/support/env.rb +49 -0
  18. data/features/todo.feature +182 -0
  19. data/gli.cheat +95 -0
  20. data/gli.gemspec +34 -0
  21. data/lib/gli.rb +11 -571
  22. data/lib/gli/app.rb +184 -0
  23. data/lib/gli/app_support.rb +226 -0
  24. data/lib/gli/command.rb +107 -95
  25. data/lib/gli/command_line_option.rb +34 -0
  26. data/lib/gli/command_line_token.rb +13 -9
  27. data/lib/gli/command_support.rb +200 -0
  28. data/lib/gli/commands/compound_command.rb +42 -0
  29. data/lib/gli/commands/help.rb +63 -0
  30. data/lib/gli/commands/help_modules/command_help_format.rb +134 -0
  31. data/lib/gli/commands/help_modules/global_help_format.rb +61 -0
  32. data/lib/gli/commands/help_modules/list_formatter.rb +22 -0
  33. data/lib/gli/commands/help_modules/options_formatter.rb +50 -0
  34. data/lib/gli/commands/help_modules/text_wrapper.rb +53 -0
  35. data/lib/gli/commands/initconfig.rb +67 -0
  36. data/lib/{support → gli/commands}/scaffold.rb +150 -34
  37. data/lib/gli/dsl.rb +194 -0
  38. data/lib/gli/exceptions.rb +13 -4
  39. data/lib/gli/flag.rb +30 -41
  40. data/lib/gli/gli_option_parser.rb +98 -0
  41. data/lib/gli/option_parser_factory.rb +44 -0
  42. data/lib/gli/options.rb +2 -1
  43. data/lib/gli/switch.rb +19 -51
  44. data/lib/gli/terminal.rb +30 -20
  45. data/lib/gli/version.rb +5 -0
  46. data/test/apps/README.md +2 -0
  47. data/test/apps/todo/Gemfile +2 -0
  48. data/test/apps/todo/README.rdoc +6 -0
  49. data/test/apps/todo/Rakefile +23 -0
  50. data/test/apps/todo/bin/todo +52 -0
  51. data/test/apps/todo/lib/todo/commands/create.rb +22 -0
  52. data/test/apps/todo/lib/todo/commands/list.rb +53 -0
  53. data/test/apps/todo/lib/todo/commands/ls.rb +47 -0
  54. data/test/apps/todo/lib/todo/version.rb +3 -0
  55. data/test/apps/todo/test/tc_nothing.rb +14 -0
  56. data/test/apps/todo/todo.gemspec +23 -0
  57. data/test/apps/todo/todo.rdoc +5 -0
  58. data/test/config.yaml +10 -0
  59. data/test/fake_std_out.rb +30 -0
  60. data/test/gli.reek +122 -0
  61. data/test/init_simplecov.rb +8 -0
  62. data/test/option_test_helper.rb +13 -0
  63. data/test/roodi.yaml +18 -0
  64. data/test/tc_command.rb +260 -0
  65. data/test/tc_compount_command.rb +22 -0
  66. data/test/tc_flag.rb +56 -0
  67. data/test/tc_gli.rb +611 -0
  68. data/test/tc_help.rb +223 -0
  69. data/test/tc_options.rb +31 -0
  70. data/test/tc_subcommands.rb +162 -0
  71. data/test/tc_switch.rb +57 -0
  72. data/test/tc_terminal.rb +97 -0
  73. data/test/test_helper.rb +13 -0
  74. metadata +318 -49
  75. data/lib/gli_version.rb +0 -3
  76. data/lib/support/help.rb +0 -179
  77. data/lib/support/initconfig.rb +0 -34
  78. data/lib/support/rdoc.rb +0 -119
data/lib/gli/app.rb ADDED
@@ -0,0 +1,184 @@
1
+ require 'etc'
2
+ require 'optparse'
3
+ require 'gli/copy_options_to_aliases'
4
+ require 'gli/dsl'
5
+
6
+ module GLI
7
+ # A means to define and parse a command line interface that works as
8
+ # Git's does, in that you specify global options, a command name, command
9
+ # specific options, and then command arguments.
10
+ module App
11
+ include CopyOptionsToAliases
12
+ include DSL
13
+ include AppSupport
14
+
15
+ # Loads ruby files in the load path that start with
16
+ # +path+, which are presumed to be commands for your executable.
17
+ # This is a glorified +require+, but could also be used as a plugin mechanism.
18
+ # You could manipualte the load path at runtime and this call
19
+ # would find those files
20
+ #
21
+ # path:: a path relative to somewhere in the <code>LOAD_PATH</code>, from which all <code>.rb</code> files will be required.
22
+ def commands_from(path)
23
+ $LOAD_PATH.each do |load_path|
24
+ commands_path = File.join(load_path,path)
25
+ if File.exists? commands_path
26
+ Dir.entries(commands_path).each do |entry|
27
+ file = File.join(commands_path,entry)
28
+ if file =~ /\.rb$/
29
+ require file
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ # Describe the overall application/programm. This should be a one-sentence summary
37
+ # of what your program does that will appear in the help output.
38
+ #
39
+ # +description+:: A String of the short description of your program's purpose
40
+ def program_desc(description=nil)
41
+ if description
42
+ @program_desc = description
43
+ end
44
+ @program_desc
45
+ end
46
+
47
+ # Use this if the following command should not have the pre block executed.
48
+ # By default, the pre block is executed before each command and can result in
49
+ # aborting the call. Using this will avoid that behavior for the following command
50
+ def skips_pre
51
+ @skips_pre = true
52
+ end
53
+
54
+ # Use this if the following command should not have the post block executed.
55
+ # By default, the post block is executed after each command.
56
+ # Using this will avoid that behavior for the following command
57
+ def skips_post
58
+ @skips_post = true
59
+ end
60
+
61
+ # Sets that this app uses a config file as well as the name of the config file.
62
+ #
63
+ # +filename+:: A String representing the path to the file to use for the config file. If it's an absolute
64
+ # path, this is treated as the path to the file. If it's *not*, it's treated as relative to the user's home
65
+ # directory as produced by <code>File.expand_path('~')</code>.
66
+ def config_file(filename)
67
+ if filename =~ /^\//
68
+ @config_file = filename
69
+ else
70
+ @config_file = File.join(File.expand_path(ENV['HOME']),filename)
71
+ end
72
+ commands[:initconfig] = InitConfig.new(@config_file,commands,flags,switches)
73
+ @config_file
74
+ end
75
+
76
+ # Define a block to run after command line arguments are parsed
77
+ # but before any command is run. If this block raises an exception
78
+ # the command specified will not be executed.
79
+ # The block will receive the global-options,command,options, and arguments
80
+ # If this block evaluates to true, the program will proceed; otherwise
81
+ # the program will end immediately
82
+ def pre(&a_proc)
83
+ @pre_block = a_proc
84
+ end
85
+
86
+ # Define a block to run after the command was executed, <b>only
87
+ # if there was not an error</b>.
88
+ # The block will receive the global-options,command,options, and arguments
89
+ def post(&a_proc)
90
+ @post_block = a_proc
91
+ end
92
+
93
+ # Define a block to run if an error occurs.
94
+ # The block will receive any Exception that was caught.
95
+ # It should evaluate to false to avoid the built-in error handling (which basically just
96
+ # prints out a message). GLI uses a variety of exceptions that you can use to find out what
97
+ # errors might've occurred during command-line parsing:
98
+ # * GLI::CustomExit
99
+ # * GLI::UnknownCommandArgument
100
+ # * GLI::UnknownGlobalArgument
101
+ # * GLI::UnknownCommand
102
+ # * GLI::BadCommandLine
103
+ def on_error(&a_proc)
104
+ @error_block = a_proc
105
+ end
106
+
107
+ # Indicate the version of your application
108
+ #
109
+ # +version+:: String containing the version of your application.
110
+ def version(version)
111
+ @version = version
112
+ end
113
+
114
+ # Call this with +true+ will cause the +global_options+ and
115
+ # +options+ passed to your code to be wrapped in
116
+ # Options, which is a subclass of +OpenStruct+ that adds
117
+ # <tt>[]</tt> and <tt>[]=</tt> methods.
118
+ #
119
+ # +use_openstruct+:: a Boolean indicating if we should use OpenStruct instead of Hashes
120
+ def use_openstruct(use_openstruct)
121
+ @use_openstruct = use_openstruct
122
+ end
123
+
124
+ # Configure a type conversion not already provided by the underlying OptionParser.
125
+ # This works more or less like the OptionParser version.
126
+ #
127
+ # object:: the class (or whatever) that triggers the type conversion
128
+ # block:: the block that will be given the string argument and is expected
129
+ # to return the converted value
130
+ #
131
+ # Example
132
+ #
133
+ # accept(Hash) do |value|
134
+ # result = {}
135
+ # value.split(/,/) do |pair|
136
+ # k,v = pair.split(/:/)
137
+ # result[k] = v
138
+ # end
139
+ # result
140
+ # end
141
+ #
142
+ # flag :properties, :type => Hash
143
+ def accept(object,&block)
144
+ accepts[object] = block
145
+ end
146
+
147
+ # Simpler means of exiting with a custom exit code. This will
148
+ # raise a CustomExit with the given message and exit code, which will ultimatley
149
+ # cause your application to exit with the given exit_code as its exit status
150
+ # Use #help_now! if you want to show the help in addition to the error message
151
+ #
152
+ # message:: message to show the user
153
+ # exit_code:: exit code to exit as, defaults to 1
154
+ def exit_now!(message,exit_code=1)
155
+ raise CustomExit.new(message,exit_code)
156
+ end
157
+
158
+ # Exit now, showing the user help for the command they executed. Use #exit_now! to just show the error message
159
+ #
160
+ # message:: message to indicate how the user has messed up the CLI invocation
161
+ def help_now!(message)
162
+ exception = OptionParser::ParseError.new(message)
163
+ class << exception
164
+ def exit_code; 64; end
165
+ end
166
+ raise exception
167
+ end
168
+
169
+ def program_name(override=nil) #:nodoc:
170
+ warn "#program_name has been deprecated"
171
+ end
172
+
173
+ # Sets a default command to run when none is specified on the command line. Note that
174
+ # if you use this, you won't be able to pass arguments, flags, or switches
175
+ # to the command when run in default mode. All flags and switches are treated
176
+ # as global, and any argument will be interpretted as the command name and likely
177
+ # fail.
178
+ #
179
+ # +command+:: Command as a Symbol to run as default
180
+ def default_command(command)
181
+ @default_command = command.to_sym
182
+ end
183
+ end
184
+ end
@@ -0,0 +1,226 @@
1
+ module GLI
2
+ # Internals for make App work
3
+ module AppSupport
4
+ # Override the device of stderr; exposed only for testing
5
+ def error_device=(e) #:nodoc:
6
+ @stderr = e
7
+ end
8
+
9
+ def context_description
10
+ "in global context"
11
+ end
12
+
13
+ # Reset the GLI module internal data structures; mostly useful for testing
14
+ def reset # :nodoc:
15
+ switches.clear
16
+ flags.clear
17
+ commands.clear
18
+ @version = nil
19
+ @config_file = nil
20
+ @use_openstruct = false
21
+ @prog_desc = nil
22
+ @error_block = false
23
+ @pre_block = false
24
+ @post_block = false
25
+ @default_command = :help
26
+ clear_nexts
27
+ end
28
+
29
+ # Get the version string
30
+ def version_string #:nodoc
31
+ @version
32
+ end
33
+
34
+ # Runs whatever command is needed based on the arguments.
35
+ #
36
+ # +args+:: the command line ARGV array
37
+ #
38
+ # Returns a number that would be a reasonable exit code
39
+ def run(args) #:nodoc:
40
+ command = nil
41
+ begin
42
+ override_defaults_based_on_config(parse_config)
43
+
44
+ add_help_switch_if_needed(switches)
45
+
46
+ global_options,command,options,arguments = GLIOptionParser.new(commands,flags,switches,accepts).parse_options(args)
47
+
48
+ copy_options_to_aliased_versions(global_options,command,options)
49
+
50
+ global_options = convert_to_openstruct_if_needed(global_options)
51
+ options = convert_to_openstruct_if_needed(options)
52
+
53
+ if proceed?(global_options,command,options,arguments)
54
+ command ||= commands[:help]
55
+ call_command(command,global_options,options,arguments)
56
+ end
57
+ 0
58
+ rescue Exception => ex
59
+ handle_exception(ex,command)
60
+ end
61
+ end
62
+
63
+
64
+ # Return the name of the config file; mostly useful for generating help docs
65
+ def config_file_name #:nodoc:
66
+ @config_file
67
+ end
68
+
69
+ def accepts #:nodoc:
70
+ @accepts ||= {}
71
+ end
72
+
73
+ # Copies all options in both global_options and options to keys for the aliases of those flags.
74
+ # For example, if a flag works with either -f or --flag, this will copy the value from [:f] to [:flag]
75
+ # to allow the user to access the options by any alias
76
+ def copy_options_to_aliased_versions(global_options,command,options) # :nodoc:
77
+ copy_options_to_aliases(global_options)
78
+ command.copy_options_to_aliases(options)
79
+ end
80
+
81
+ def parse_config # :nodoc:
82
+ config = {
83
+ 'commands' => {},
84
+ }
85
+ if @config_file && File.exist?(@config_file)
86
+ require 'yaml'
87
+ config.merge!(File.open(@config_file) { |file| YAML::load(file) })
88
+ end
89
+ config
90
+ end
91
+
92
+ def clear_nexts # :nodoc:
93
+ super
94
+ @skips_post = false
95
+ @skips_pre = false
96
+ end
97
+
98
+ def stderr
99
+ @stderr ||= STDERR
100
+ end
101
+
102
+ def self.included(klass)
103
+ @stderr = $stderr
104
+ end
105
+
106
+ def flags # :nodoc:
107
+ @flags ||= {}
108
+ end
109
+
110
+ def switches # :nodoc:
111
+ @switches ||= {}
112
+ end
113
+
114
+ def commands # :nodoc:
115
+ @commands ||= {:help => GLI::Commands::Help.new(self)}
116
+ end
117
+
118
+ def pre_block
119
+ @pre_block ||= Proc.new do
120
+ true
121
+ end
122
+ end
123
+
124
+ def post_block
125
+ @post_block ||= Proc.new do
126
+ end
127
+ end
128
+
129
+ # Sets the default values for flags based on the configuration
130
+ def override_defaults_based_on_config(config)
131
+ override_default(flags,config)
132
+ override_default(switches,config)
133
+
134
+ override_command_defaults(commands,config)
135
+ end
136
+
137
+ def override_command_defaults(command_list,config)
138
+ command_list.each do |command_name,command|
139
+ next if command_name == :initconfig || command.nil?
140
+ command_config = (config['commands'] || {})[command_name] || {}
141
+
142
+ override_default(command.topmost_ancestor.flags,command_config)
143
+ override_default(command.topmost_ancestor.switches,command_config)
144
+
145
+ override_command_defaults(command.commands,command_config)
146
+ end
147
+ end
148
+
149
+ def override_default(tokens,config)
150
+ tokens.each do |name,token|
151
+ token.default_value=config[name] if config[name]
152
+ end
153
+ end
154
+
155
+ private
156
+
157
+ def handle_exception(ex,command)
158
+ if regular_error_handling?(ex)
159
+ stderr.puts error_message(ex)
160
+ if ex.kind_of?(OptionParser::ParseError) || ex.kind_of?(BadCommandLine)
161
+ stderr.puts
162
+ commands[:help] and commands[:help].execute([],[],command.nil? ? [] : [command.name.to_s])
163
+ end
164
+ end
165
+
166
+ raise ex if ENV['GLI_DEBUG'] == 'true'
167
+
168
+ ex.extend(GLI::StandardException)
169
+ ex.exit_code
170
+ end
171
+
172
+ # Possibly returns a copy of the passed-in Hash as an instance of GLI::Option.
173
+ # By default, it will *not*. However by putting <tt>use_openstruct true</tt>
174
+ # in your CLI definition, it will
175
+ def convert_to_openstruct_if_needed(options) # :nodoc:
176
+ @use_openstruct ? Options.new(options) : options
177
+ end
178
+
179
+ def add_help_switch_if_needed(switches)
180
+ help_switch_exists = switches.values.find { |switch|
181
+ (Array(switch.aliases) + [switch.name]).find { |an_alias|
182
+ an_alias.to_s == 'help'
183
+ }
184
+ }
185
+ unless help_switch_exists
186
+ desc 'Show this message'
187
+ switch :help, :negatable => false
188
+ end
189
+ end
190
+
191
+ # True if we should proceed with executing the command; this calls
192
+ # the pre block if it's defined
193
+ def proceed?(global_options,command,options,arguments) #:nodoc:
194
+ if command && command.skips_pre
195
+ true
196
+ else
197
+ pre_block.call(global_options,command,options,arguments)
198
+ end
199
+ end
200
+
201
+ # Returns true if we should proceed with GLI's basic error handling.
202
+ # This calls the error block if the user provided one
203
+ def regular_error_handling?(ex) #:nodoc:
204
+ if @error_block
205
+ @error_block.call(ex)
206
+ else
207
+ true
208
+ end
209
+ end
210
+
211
+ # Returns a String of the error message to show the user
212
+ # +ex+:: The exception we caught that launched the error handling routines
213
+ def error_message(ex) #:nodoc:
214
+ "error: #{ex.message}"
215
+ end
216
+
217
+ def call_command(command,global_options,options,arguments)
218
+ arguments = arguments.map { |arg| arg.dup } # unfreeze
219
+ command.execute(global_options,options,arguments)
220
+ unless command.skips_post
221
+ post_block.call(global_options,command,options,arguments)
222
+ end
223
+ end
224
+
225
+ end
226
+ end
data/lib/gli/command.rb CHANGED
@@ -1,124 +1,136 @@
1
1
  require 'gli/command_line_token.rb'
2
2
  require 'gli/copy_options_to_aliases.rb'
3
+ require 'gli/dsl.rb'
3
4
 
4
5
  module GLI
5
6
  # A command to be run, in context of global flags and switches. You are given an instance of this class
6
- # to the block you use for GLI#command. You then use the methods described here to describe the
7
- # command-specific command-line arguments, much as you use the methods in GLI to describe the global
8
- # command-line interface
7
+ # to the block you use for GLI::DSL#command. This class mixes in GLI::DSL so all of those methods are available
8
+ # to describe the command, in addition to the methods documented here, most importantly
9
+ # #action.
10
+ #
11
+ # Example:
12
+ #
13
+ # command :list do |c| # <- c is an instance of GLI::Command
14
+ # c.desc 'use long form'
15
+ # c.switch :l
16
+ #
17
+ # c.action do |global,options,args|
18
+ # # list things here
19
+ # end
20
+ #
21
+ # c.command :tasks do |t| # <- t is an instance of GLI::Command
22
+ # # this is a "subcommand" of list
23
+ #
24
+ # t.action do |global,options,args|
25
+ # # do whatever list tasks should do
26
+ # end
27
+ # end
28
+ # end
29
+ #
9
30
  class Command < CommandLineToken
10
31
  include CopyOptionsToAliases
32
+ include DSL
33
+ include CommandSupport
11
34
 
12
- # Create a new command
35
+ # Create a new command.
13
36
  #
14
- # +names+:: A String, Symbol, or Array of String or Symbol that represents the name(s) of this command.
15
- # +description+:: short description of this command as a Strign
16
- # +arguments_name+:: description of the arguments as a String, or nil if this command doesn't take arguments
17
- # +long_desc+:: a longer description of the command, possibly with multiple lines and text formatting
18
- # +skips_pre+:: if true, this command advertises that it doesn't want the pre block called first
19
- # +skips_post+:: if true, this command advertises that it doesn't want the post block called after it
20
- def initialize(names,description,arguments_name=nil,long_desc=nil,skips_pre=false,skips_post=false) # :nodoc:
21
- super(names,description,long_desc)
22
- @arguments_description = arguments_name || ''
23
- @skips_pre = skips_pre
24
- @skips_post = skips_post
37
+ # options:: Keys should be:
38
+ # +names+:: A String, Symbol, or Array of String or Symbol that represents the name(s) of this command (required).
39
+ # +description+:: short description of this command as a String
40
+ # +arguments_name+:: description of the arguments as a String, or nil if this command doesn't take arguments
41
+ # +long_desc+:: a longer description of the command, possibly with multiple lines. A double line-break is treated
42
+ # as a paragraph break. No other formatting is respected, though inner whitespace is maintained.
43
+ # +skips_pre+:: if true, this command advertises that it doesn't want the pre block called first
44
+ # +skips_post+:: if true, this command advertises that it doesn't want the post block called after it
45
+ def initialize(options)
46
+ super(options[:names],options[:description],options[:long_desc])
47
+ @arguments_description = options[:arguments_name] || ''
48
+ @skips_pre = options[:skips_pre]
49
+ @skips_post = options[:skips_post]
25
50
  clear_nexts
26
51
  end
27
52
 
28
- # Return the arguments description
29
- def arguments_description #:nodoc:
30
- @arguments_description
31
- end
32
-
33
- # If true, this command doesn't want the pre block run before it executes
34
- def skips_pre #:nodoc:
35
- @skips_pre
36
- end
37
-
38
- # If true, this command doesn't want the post block run before it executes
39
- def skips_post #:nodoc:
40
- @skips_post
41
- end
42
-
43
- # Return the Array of the command's names
44
- def names #:nodoc:
45
- all_forms
46
- end
47
-
48
- # Get the usage string
49
- # CR: This should probably not be here
50
- def usage #:nodoc:
51
- usage = name.to_s
52
- usage += ' [command options]' if !flags.empty? || !switches.empty?
53
- usage += ' ' + @arguments_description if @arguments_description
54
- usage
55
- end
56
-
57
- # Return the flags as a Hash
58
- def flags #:nodoc:
59
- @flags ||= {}
60
- end
61
- # Return the switches as a Hash
62
- def switches #:nodoc:
63
- @switches ||= {}
64
- end
65
-
66
- # describe the next switch or flag just as GLI#desc does.
67
- def desc(description); @next_desc = description; end
68
- # set the long description of this flag/switch, just as GLI#long_desc does.
69
- def long_desc(long_desc); @next_long_desc = long_desc; end
70
- # describe the argument name of the next flag, just as GLI#arg_name does.
71
- def arg_name(name); @next_arg_name = name; end
72
- # set the default value of the next flag, just as GLI#default_value does.
73
- def default_value(val); @next_default_value = val; end
74
-
75
- # Create a command-specific flag, similar to GLI#flag
76
- def flag(*names)
77
- names = [names].flatten
78
- GLI.verify_unused(names,flags,switches,"in command #{name}")
79
- flag = Flag.new(names,@next_desc,@next_arg_name,@next_default_value,@next_long_desc)
80
- flags[flag.name] = flag
81
- clear_nexts
82
- end
83
-
84
- # Create a command-specific switch, similar to GLI#switch
85
- def switch(*names)
86
- names = [names].flatten
87
- GLI.verify_unused(names,flags,switches,"in command #{name}")
88
- switch = Switch.new(names,@next_desc,@next_long_desc)
89
- switches[switch.name] = switch
90
- clear_nexts
53
+ # Set the default command if this command has subcommands and the user doesn't
54
+ # provide a subcommand when invoking THIS command. When nil, this will show an error and the help
55
+ # for this command; when set, the command with this name will be executed.
56
+ #
57
+ # +command_name+:: The primary name of the subcommand of this command that should be run by default as a String or Symbol.
58
+ def default_command(command_name)
59
+ @default_command = command_name
91
60
  end
92
61
 
93
- # Define the action to take when the user executes this command
62
+ # Define the action to take when the user executes this command. Every command should either define this
63
+ # action block, or have subcommands (or both).
94
64
  #
95
65
  # +block+:: A block of code to execute. The block will be given 3 arguments:
96
- # +global_options+:: A Hash (or Options, see GLI#use_openstruct) of the _global_ options specified
66
+ # +global_options+:: A Hash of the _global_ options specified
97
67
  # by the user, with defaults set and config file values used (if using a config file, see
98
- # GLI#config_file)
99
- # +options+:: A Hash (or Options, see GLI#use_openstruct) of the command-specific options specified by the
100
- # user, with defaults set and config file values used (if using a config file, see GLI#config_file)
68
+ # GLI::App#config_file)
69
+ # +options+:: A Hash of the command-specific options specified by the
70
+ # user, with defaults set and config file values used (if using a config file, see
71
+ # GLI::App#config_file).
101
72
  # +arguments+:: An Array of Strings representing the unparsed command line arguments
102
73
  # The block's result value is not used; raise an exception or use GLI#exit_now! if you need an early exit based
103
74
  # on an error condition
75
+ #
104
76
  def action(&block)
105
77
  @action = block
106
78
  end
107
79
 
108
- def self.name_as_string(name) #:nodoc:
109
- name.to_s
110
- end
111
-
112
- def clear_nexts #:nodoc:
113
- @next_desc = nil
114
- @next_arg_name = nil
115
- @next_default_value = nil
116
- @next_long_desc = nil
80
+ # Describes this commands action block when it *also* has subcommands.
81
+ # In this case, the GLI::DSL#desc value is the general description of the commands
82
+ # that this command groups, and the value for *this* method documents what
83
+ # will happen if you omit a subcommand.
84
+ #
85
+ # Note that if you omit the action block and specify a subcommand, that subcommand's
86
+ # description will be used to describe what happens by default.
87
+ #
88
+ # desc:: the description of what this command's action block does.
89
+ #
90
+ # Example
91
+ #
92
+ # desc 'list things'
93
+ # command :list do |c|
94
+ #
95
+ # c.desc 'list tasks'
96
+ # c.command :tasks do |t|
97
+ # t.action do |global,options,args|
98
+ # end
99
+ # end
100
+ #
101
+ # c.desc 'list contexts'
102
+ # c.command :contexts do |t|
103
+ # t.action do |global,options,args|
104
+ # end
105
+ # end
106
+ #
107
+ # c.default_desc 'list both tasks and contexts'
108
+ # c.action do |global,options,args|
109
+ # # list everything
110
+ # end
111
+ # end
112
+ #
113
+ #
114
+ # > todo help list
115
+ # NAME
116
+ # list - List things
117
+ #
118
+ # SYNOPSIS
119
+ # todo [global options] list [command options]
120
+ # todo [global options] list [command options] tasks
121
+ # todo [global options] list [command options] contexts
122
+ #
123
+ # COMMANDS
124
+ # <default> - list both tasks and contexts
125
+ # tasks - list tasks
126
+ # contexts - list contexts
127
+ #
128
+ def default_desc(desc)
129
+ @default_desc = desc
117
130
  end
118
131
 
119
- # Executes the command
120
- def execute(global_options,options,arguments) #:nodoc:
121
- @action.call(global_options,options,arguments)
132
+ def self.name_as_string(name,negatable=false) #:nodoc:
133
+ name.to_s
122
134
  end
123
135
  end
124
136
  end