gli_aziz_light 2.8.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (104) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +13 -0
  3. data/.ruby-gemset +1 -0
  4. data/.ruby-version +1 -0
  5. data/.travis.yml +17 -0
  6. data/CONTRIBUTING.md +23 -0
  7. data/Gemfile +8 -0
  8. data/LICENSE.txt +201 -0
  9. data/ObjectModel.graffle +1191 -0
  10. data/README.rdoc +109 -0
  11. data/Rakefile +126 -0
  12. data/bin/gli +59 -0
  13. data/bin/report_on_rake_results +10 -0
  14. data/bin/test_all_rubies.sh +6 -0
  15. data/features/gli_executable.feature +90 -0
  16. data/features/gli_init.feature +232 -0
  17. data/features/step_definitions/gli_executable_steps.rb +18 -0
  18. data/features/step_definitions/gli_init_steps.rb +11 -0
  19. data/features/step_definitions/todo_steps.rb +88 -0
  20. data/features/support/env.rb +53 -0
  21. data/features/todo.feature +413 -0
  22. data/features/todo_legacy.feature +128 -0
  23. data/gli.cheat +95 -0
  24. data/gli.gemspec +34 -0
  25. data/gli.rdoc +73 -0
  26. data/lib/gli.rb +35 -0
  27. data/lib/gli/app.rb +286 -0
  28. data/lib/gli/app_support.rb +341 -0
  29. data/lib/gli/command.rb +171 -0
  30. data/lib/gli/command_finder.rb +41 -0
  31. data/lib/gli/command_line_option.rb +34 -0
  32. data/lib/gli/command_line_token.rb +63 -0
  33. data/lib/gli/command_support.rb +181 -0
  34. data/lib/gli/commands/compound_command.rb +42 -0
  35. data/lib/gli/commands/doc.rb +231 -0
  36. data/lib/gli/commands/help.rb +95 -0
  37. data/lib/gli/commands/help_modules/arg_name_formatter.rb +20 -0
  38. data/lib/gli/commands/help_modules/command_finder.rb +60 -0
  39. data/lib/gli/commands/help_modules/command_help_format.rb +156 -0
  40. data/lib/gli/commands/help_modules/global_help_format.rb +70 -0
  41. data/lib/gli/commands/help_modules/help_completion_format.rb +31 -0
  42. data/lib/gli/commands/help_modules/list_formatter.rb +23 -0
  43. data/lib/gli/commands/help_modules/one_line_wrapper.rb +18 -0
  44. data/lib/gli/commands/help_modules/options_formatter.rb +49 -0
  45. data/lib/gli/commands/help_modules/text_wrapper.rb +53 -0
  46. data/lib/gli/commands/help_modules/tty_only_wrapper.rb +23 -0
  47. data/lib/gli/commands/help_modules/verbatim_wrapper.rb +16 -0
  48. data/lib/gli/commands/initconfig.rb +74 -0
  49. data/lib/gli/commands/rdoc_document_listener.rb +116 -0
  50. data/lib/gli/commands/scaffold.rb +401 -0
  51. data/lib/gli/dsl.rb +226 -0
  52. data/lib/gli/exceptions.rb +71 -0
  53. data/lib/gli/flag.rb +68 -0
  54. data/lib/gli/gli_option_block_parser.rb +84 -0
  55. data/lib/gli/gli_option_parser.rb +156 -0
  56. data/lib/gli/option_parser_factory.rb +81 -0
  57. data/lib/gli/option_parsing_result.rb +21 -0
  58. data/lib/gli/options.rb +23 -0
  59. data/lib/gli/switch.rb +35 -0
  60. data/lib/gli/terminal.rb +101 -0
  61. data/lib/gli/version.rb +5 -0
  62. data/test/apps/README.md +2 -0
  63. data/test/apps/todo/Gemfile +2 -0
  64. data/test/apps/todo/README.rdoc +6 -0
  65. data/test/apps/todo/Rakefile +23 -0
  66. data/test/apps/todo/bin/todo +63 -0
  67. data/test/apps/todo/lib/todo/commands/create.rb +24 -0
  68. data/test/apps/todo/lib/todo/commands/list.rb +63 -0
  69. data/test/apps/todo/lib/todo/commands/ls.rb +47 -0
  70. data/test/apps/todo/lib/todo/commands/make.rb +52 -0
  71. data/test/apps/todo/lib/todo/version.rb +3 -0
  72. data/test/apps/todo/test/tc_nothing.rb +14 -0
  73. data/test/apps/todo/todo.gemspec +23 -0
  74. data/test/apps/todo/todo.rdoc +5 -0
  75. data/test/apps/todo_legacy/Gemfile +2 -0
  76. data/test/apps/todo_legacy/README.rdoc +6 -0
  77. data/test/apps/todo_legacy/Rakefile +23 -0
  78. data/test/apps/todo_legacy/bin/todo +61 -0
  79. data/test/apps/todo_legacy/lib/todo/commands/create.rb +24 -0
  80. data/test/apps/todo_legacy/lib/todo/commands/list.rb +63 -0
  81. data/test/apps/todo_legacy/lib/todo/commands/ls.rb +47 -0
  82. data/test/apps/todo_legacy/lib/todo/version.rb +3 -0
  83. data/test/apps/todo_legacy/test/tc_nothing.rb +14 -0
  84. data/test/apps/todo_legacy/todo.gemspec +23 -0
  85. data/test/apps/todo_legacy/todo.rdoc +5 -0
  86. data/test/apps/todo_plugins/commands/third.rb +1 -0
  87. data/test/config.yaml +10 -0
  88. data/test/fake_std_out.rb +30 -0
  89. data/test/init_simplecov.rb +8 -0
  90. data/test/option_test_helper.rb +13 -0
  91. data/test/tc_command.rb +508 -0
  92. data/test/tc_compound_command.rb +22 -0
  93. data/test/tc_doc.rb +325 -0
  94. data/test/tc_flag.rb +62 -0
  95. data/test/tc_gli.rb +773 -0
  96. data/test/tc_help.rb +387 -0
  97. data/test/tc_options.rb +43 -0
  98. data/test/tc_subcommand_parsing.rb +104 -0
  99. data/test/tc_subcommands.rb +260 -0
  100. data/test/tc_switch.rb +55 -0
  101. data/test/tc_terminal.rb +100 -0
  102. data/test/tc_verbatim_wrapper.rb +36 -0
  103. data/test/test_helper.rb +20 -0
  104. metadata +330 -0
@@ -0,0 +1,341 @@
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 = nil
18
+ @commands_declaration_order = []
19
+ @flags_declaration_order = []
20
+ @switches_declaration_order = []
21
+ @version = nil
22
+ @config_file = nil
23
+ @use_openstruct = false
24
+ @prog_desc = nil
25
+ @error_block = false
26
+ @pre_block = false
27
+ @post_block = false
28
+ @default_command = :help
29
+ @around_block = nil
30
+ @subcommand_option_handling_strategy = :legacy
31
+ clear_nexts
32
+ end
33
+
34
+ def exe_name
35
+ File.basename($0)
36
+ end
37
+
38
+ # Get an array of commands, ordered by when they were declared
39
+ def commands_declaration_order # :nodoc:
40
+ @commands_declaration_order
41
+ end
42
+
43
+ # Get the version string
44
+ def version_string #:nodoc:
45
+ @version
46
+ end
47
+
48
+ # Get the default command for the entire app
49
+ def get_default_command
50
+ @default_command
51
+ end
52
+
53
+ # Runs whatever command is needed based on the arguments.
54
+ #
55
+ # +args+:: the command line ARGV array
56
+ #
57
+ # Returns a number that would be a reasonable exit code
58
+ def run(args) #:nodoc:
59
+ args = args.dup if @preserve_argv
60
+ the_command = nil
61
+ begin
62
+ override_defaults_based_on_config(parse_config)
63
+
64
+ add_help_switch_if_needed(self)
65
+
66
+ gli_option_parser = GLIOptionParser.new(commands,
67
+ flags,
68
+ switches,
69
+ accepts,
70
+ @default_command,
71
+ self.subcommand_option_handling_strategy)
72
+
73
+ parsing_result = gli_option_parser.parse_options(args)
74
+
75
+ self.options = parsing_result.cli_options
76
+ parsing_result.global_options[:cli] = self.options[:global]
77
+ parsing_result.command_options[:cli] = self.options["commands"][parsing_result.command.name]
78
+
79
+ parsing_result.convert_to_openstruct! if @use_openstruct
80
+
81
+ the_command = parsing_result.command
82
+
83
+ if proceed?(parsing_result)
84
+ call_command(parsing_result)
85
+ 0
86
+ else
87
+ raise PreconditionFailed, "preconditions failed"
88
+ end
89
+ rescue Exception => ex
90
+ if the_command.nil? && ex.respond_to?(:command_in_context)
91
+ the_command = ex.command_in_context
92
+ end
93
+ handle_exception(ex,the_command)
94
+ end
95
+ end
96
+
97
+
98
+ # Return the name of the config file; mostly useful for generating help docs
99
+ def config_file_name #:nodoc:
100
+ @config_file
101
+ end
102
+ def accepts #:nodoc:
103
+ @accepts ||= {}
104
+
105
+ end
106
+
107
+ def parse_config # :nodoc:
108
+ config = {
109
+ 'commands' => {},
110
+ }
111
+ if @config_file && File.exist?(@config_file)
112
+ require 'yaml'
113
+ config.merge!(File.open(@config_file) { |file| YAML::load(file) })
114
+ end
115
+ config
116
+ end
117
+
118
+ def clear_nexts # :nodoc:
119
+ super
120
+ @skips_post = false
121
+ @skips_pre = false
122
+ @skips_around = false
123
+ end
124
+
125
+ def stderr
126
+ @stderr ||= STDERR
127
+ end
128
+
129
+ def self.included(klass)
130
+ @stderr = $stderr
131
+ end
132
+
133
+ def flags # :nodoc:
134
+ @flags ||= {}
135
+ end
136
+
137
+ def switches # :nodoc:
138
+ @switches ||= {}
139
+ end
140
+
141
+ def commands # :nodoc:
142
+ if !@commands
143
+ @commands = { :help => GLI::Commands::Help.new(self), :_doc => GLI::Commands::Doc.new(self) }
144
+ @commands_declaration_order ||= []
145
+ @commands_declaration_order << @commands[:help]
146
+ @commands_declaration_order << @commands[:_doc]
147
+ end
148
+ @commands
149
+ end
150
+
151
+ def pre_block
152
+ @pre_block ||= Proc.new do
153
+ true
154
+ end
155
+ end
156
+
157
+ def post_block
158
+ @post_block ||= Proc.new do
159
+ end
160
+ end
161
+
162
+ def around_blocks
163
+ @around_blocks || []
164
+ end
165
+
166
+ def help_sort_type
167
+ @help_sort_type || :alpha
168
+ end
169
+
170
+ def help_text_wrap_type
171
+ @help_text_wrap_type || :to_terminal
172
+ end
173
+
174
+ # Sets the default values for flags based on the configuration
175
+ def override_defaults_based_on_config(config)
176
+ override_default(flags,config)
177
+ override_default(switches,config)
178
+
179
+ override_command_defaults(commands,config)
180
+ end
181
+
182
+ def override_command_defaults(command_list,config)
183
+ command_list.each do |command_name,command|
184
+ next if command_name == :initconfig || command.nil?
185
+ command_config = (config['commands'] || {})[command_name] || {}
186
+
187
+ if @subcommand_option_handling_strategy == :legacy
188
+ override_default(command.topmost_ancestor.flags,command_config)
189
+ override_default(command.topmost_ancestor.switches,command_config)
190
+ else
191
+ override_default(command.flags,command_config)
192
+ override_default(command.switches,command_config)
193
+ end
194
+
195
+ override_command_defaults(command.commands,command_config)
196
+ end
197
+ end
198
+
199
+ def override_default(tokens,config)
200
+ tokens.each do |name,token|
201
+ token.default_value=config[name] if config[name]
202
+ end
203
+ end
204
+
205
+ def subcommand_option_handling_strategy
206
+ @subcommand_option_handling_strategy || :legacy
207
+ end
208
+
209
+ # Sets the @options instance variable if it doesn't exist
210
+ # and returns it. The @options instance variable contains
211
+ # the options passed by the user on the command line.
212
+ def options #:nodoc:
213
+ @options ||= { :global => {}, "commands" => {} }
214
+ end
215
+
216
+ # Sets the @options instance variable, which contains the
217
+ # options passed by the user on the command line.
218
+ # If the opts parameter doesn't have the correct format,
219
+ # the instance variable will be set with the correct format
220
+ # but the command line options will not be added to it.
221
+ # If the GLI Debug mode is enabled, an ArguementError exception
222
+ # will be raised.
223
+ def options=(opts)
224
+ if valid_options_hash_format_for?(opts)
225
+ @options = opts
226
+ else
227
+ if ENV['GLI_DEBUG'] == 'true'
228
+ raise ArgumentError, "The options hash is invalid"
229
+ end
230
+
231
+ @options = { :global => {}, "commands" => {} }
232
+ end
233
+ end
234
+
235
+ private
236
+
237
+ def valid_options_hash_format_for?(opts)
238
+ valid_options_hash = opts.is_a?(Hash)
239
+ valid_global_hash = opts.has_key?(:global) && opts.fetch(:global).is_a?(Hash)
240
+ valid_commands_hash = opts.has_key?("commands") && opts.fetch("commands").is_a?(Hash)
241
+
242
+ valid_options_hash && valid_global_hash && valid_commands_hash
243
+ end
244
+
245
+ def handle_exception(ex,command)
246
+ if regular_error_handling?(ex)
247
+ output_error_message(ex)
248
+ if ex.kind_of?(OptionParser::ParseError) || ex.kind_of?(BadCommandLine)
249
+ if commands[:help]
250
+ command_for_help = command.nil? ? [] : command.name_for_help
251
+ commands[:help].execute({},{},command_for_help)
252
+ end
253
+ end
254
+ elsif ENV['GLI_DEBUG'] == 'true'
255
+ stderr.puts "Custom error handler exited false, skipping normal error handling"
256
+ end
257
+
258
+ raise ex if ENV['GLI_DEBUG'] == 'true'
259
+
260
+ ex.extend(GLI::StandardException)
261
+ ex.exit_code
262
+ end
263
+
264
+ def output_error_message(ex)
265
+ stderr.puts error_message(ex) unless no_message_given?(ex)
266
+ if ex.kind_of?(OptionParser::ParseError) || ex.kind_of?(BadCommandLine)
267
+ stderr.puts unless no_message_given?(ex)
268
+ end
269
+ end
270
+
271
+ def no_message_given?(ex)
272
+ ex.message == ex.class.name
273
+
274
+ end
275
+
276
+ def add_help_switch_if_needed(target)
277
+ help_switch_exists = target.switches.values.find { |switch|
278
+ switch.names_and_aliases.map(&:to_s).find { |an_alias| an_alias == 'help' }
279
+ }
280
+ unless help_switch_exists
281
+ target.desc 'Show this message'
282
+ target.switch :help, :negatable => false
283
+ end
284
+ end
285
+
286
+ # True if we should proceed with executing the command; this calls
287
+ # the pre block if it's defined
288
+ def proceed?(parsing_result) #:nodoc:
289
+ if parsing_result.command && parsing_result.command.skips_pre
290
+ true
291
+ else
292
+ pre_block.call(*parsing_result)
293
+ end
294
+ end
295
+
296
+ # Returns true if we should proceed with GLI's basic error handling.
297
+ # This calls the error block if the user provided one
298
+ def regular_error_handling?(ex) #:nodoc:
299
+ if @error_block
300
+ return true if (ex.respond_to?(:exit_code) && ex.exit_code == 0)
301
+ @error_block.call(ex)
302
+ else
303
+ true
304
+ end
305
+ end
306
+
307
+ # Returns a String of the error message to show the user
308
+ # +ex+:: The exception we caught that launched the error handling routines
309
+ def error_message(ex) #:nodoc:
310
+ "error: #{ex.message}"
311
+ end
312
+
313
+ def call_command(parsing_result)
314
+ command = parsing_result.command
315
+ global_options = parsing_result.global_options
316
+ options = parsing_result.command_options
317
+ arguments = parsing_result.arguments.map { |arg| arg.dup } # unfreeze
318
+
319
+ code = lambda { command.execute(global_options,options,arguments) }
320
+ nested_arounds = unless command.skips_around
321
+ around_blocks.inject do |outer_around, inner_around|
322
+ lambda { |go,c,o,a, code1|
323
+ inner = lambda { inner_around.call(go,c,o,a, code1) }
324
+ outer_around.call(go,c,o,a, inner)
325
+ }
326
+ end
327
+ end
328
+
329
+ if nested_arounds
330
+ nested_arounds.call(global_options,command, options, arguments, code)
331
+ else
332
+ code.call
333
+ end
334
+
335
+ unless command.skips_post
336
+ post_block.call(global_options,command,options,arguments)
337
+ end
338
+ end
339
+
340
+ end
341
+ end
@@ -0,0 +1,171 @@
1
+ require 'gli/command_line_token.rb'
2
+ require 'gli/dsl.rb'
3
+
4
+ module GLI
5
+ # 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::DSL#command. This class mixes in GLI::DSL so all of those methods are available
7
+ # to describe the command, in addition to the methods documented here, most importantly
8
+ # #action.
9
+ #
10
+ # Example:
11
+ #
12
+ # command :list do |c| # <- c is an instance of GLI::Command
13
+ # c.desc 'use long form'
14
+ # c.switch :l
15
+ #
16
+ # c.action do |global,options,args|
17
+ # # list things here
18
+ # end
19
+ #
20
+ # c.command :tasks do |t| # <- t is an instance of GLI::Command
21
+ # # this is a "subcommand" of list
22
+ #
23
+ # t.action do |global,options,args|
24
+ # # do whatever list tasks should do
25
+ # end
26
+ # end
27
+ # end
28
+ #
29
+ class Command < CommandLineToken
30
+ include DSL
31
+ include CommandSupport
32
+
33
+ # Key in an options hash to find the parent's parsed options
34
+ PARENT = Object.new
35
+
36
+ # Create a new command.
37
+ #
38
+ # options:: Keys should be:
39
+ # +names+:: A String, Symbol, or Array of String or Symbol that represents the name(s) of this command (required).
40
+ # +description+:: short description of this command as a String
41
+ # +arguments_name+:: description of the arguments as a String, or nil if this command doesn't take arguments
42
+ # +long_desc+:: a longer description of the command, possibly with multiple lines. A double line-break is treated
43
+ # as a paragraph break. No other formatting is respected, though inner whitespace is maintained.
44
+ # +skips_pre+:: if true, this command advertises that it doesn't want the pre block called first
45
+ # +skips_post+:: if true, this command advertises that it doesn't want the post block called after it
46
+ # +skips_around+:: if true, this command advertises that it doesn't want the around block called
47
+ def initialize(options)
48
+ super(options[:names],options[:description],options[:long_desc])
49
+ @arguments_description = options[:arguments_name] || ''
50
+ @arguments_options = Array(options[:arguments_options]).flatten
51
+ @skips_pre = options[:skips_pre]
52
+ @skips_post = options[:skips_post]
53
+ @skips_around = options[:skips_around]
54
+ @commands_declaration_order = []
55
+ @flags_declaration_order = []
56
+ @switches_declaration_order = []
57
+ clear_nexts
58
+ end
59
+
60
+ # Set the default command if this command has subcommands and the user doesn't
61
+ # provide a subcommand when invoking THIS command. When nil, this will show an error and the help
62
+ # for this command; when set, the command with this name will be executed.
63
+ #
64
+ # +command_name+:: The primary name of the subcommand of this command that should be run by default as a String or Symbol.
65
+ def default_command(command_name)
66
+ @default_command = command_name
67
+ end
68
+
69
+ # Define the action to take when the user executes this command. Every command should either define this
70
+ # action block, or have subcommands (or both).
71
+ #
72
+ # +block+:: A block of code to execute. The block will be given 3 arguments:
73
+ # +global_options+:: A Hash of the _global_ options specified
74
+ # by the user, with defaults set and config file values used (if using a config file, see
75
+ # GLI::App#config_file)
76
+ # +options+:: A Hash of the command-specific options specified by the
77
+ # user, with defaults set and config file values used (if using a config file, see
78
+ # GLI::App#config_file).
79
+ # +arguments+:: An Array of Strings representing the unparsed command line arguments
80
+ # The block's result value is not used; raise an exception or use GLI#exit_now! if you need an early exit based
81
+ # on an error condition
82
+ #
83
+ def action(&block)
84
+ @action = block
85
+ end
86
+
87
+ # Describes this commands action block when it *also* has subcommands.
88
+ # In this case, the GLI::DSL#desc value is the general description of the commands
89
+ # that this command groups, and the value for *this* method documents what
90
+ # will happen if you omit a subcommand.
91
+ #
92
+ # Note that if you omit the action block and specify a subcommand, that subcommand's
93
+ # description will be used to describe what happens by default.
94
+ #
95
+ # desc:: the description of what this command's action block does.
96
+ #
97
+ # Example
98
+ #
99
+ # desc 'list things'
100
+ # command :list do |c|
101
+ #
102
+ # c.desc 'list tasks'
103
+ # c.command :tasks do |t|
104
+ # t.action do |global,options,args|
105
+ # end
106
+ # end
107
+ #
108
+ # c.desc 'list contexts'
109
+ # c.command :contexts do |t|
110
+ # t.action do |global,options,args|
111
+ # end
112
+ # end
113
+ #
114
+ # c.default_desc 'list both tasks and contexts'
115
+ # c.action do |global,options,args|
116
+ # # list everything
117
+ # end
118
+ # end
119
+ #
120
+ #
121
+ # > todo help list
122
+ # NAME
123
+ # list - List things
124
+ #
125
+ # SYNOPSIS
126
+ # todo [global options] list [command options]
127
+ # todo [global options] list [command options] tasks
128
+ # todo [global options] list [command options] contexts
129
+ #
130
+ # COMMANDS
131
+ # <default> - list both tasks and contexts
132
+ # tasks - list tasks
133
+ # contexts - list contexts
134
+ #
135
+ def default_desc(desc)
136
+ @default_desc = desc
137
+ end
138
+
139
+ # Returns true if this command has the given option defined
140
+ def has_option?(option) #:nodoc:
141
+ option = option.gsub(/^\-+/,'')
142
+ ((flags.values.map { |_| [_.name,_.aliases] }) +
143
+ (switches.values.map { |_| [_.name,_.aliases] })).flatten.map(&:to_s).include?(option)
144
+ end
145
+
146
+ # Returns full name for help command including parents
147
+ #
148
+ # Example
149
+ #
150
+ # command :remote do |t|
151
+ # t.command :add do |global,options,args|
152
+ # end
153
+ # end
154
+ #
155
+ # @add_command.name_for_help # => ["remote", "add"]
156
+ #
157
+ def name_for_help
158
+ name_array = [name.to_s]
159
+ command_parent = parent
160
+ while(command_parent.is_a?(GLI::Command)) do
161
+ name_array.unshift(command_parent.name.to_s)
162
+ command_parent = command_parent.parent
163
+ end
164
+ name_array
165
+ end
166
+
167
+ def self.name_as_string(name,negatable=false) #:nodoc:
168
+ name.to_s
169
+ end
170
+ end
171
+ end