honeybadger 5.0.2 → 5.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (115) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +713 -701
  3. data/LICENSE +19 -19
  4. data/README.md +57 -57
  5. data/TROUBLESHOOTING.md +3 -3
  6. data/bin/honeybadger +5 -5
  7. data/lib/honeybadger/agent.rb +488 -488
  8. data/lib/honeybadger/backend/base.rb +116 -116
  9. data/lib/honeybadger/backend/debug.rb +22 -22
  10. data/lib/honeybadger/backend/null.rb +29 -29
  11. data/lib/honeybadger/backend/server.rb +62 -62
  12. data/lib/honeybadger/backend/test.rb +46 -46
  13. data/lib/honeybadger/backend.rb +27 -27
  14. data/lib/honeybadger/backtrace.rb +181 -181
  15. data/lib/honeybadger/breadcrumbs/active_support.rb +119 -119
  16. data/lib/honeybadger/breadcrumbs/breadcrumb.rb +53 -53
  17. data/lib/honeybadger/breadcrumbs/collector.rb +82 -82
  18. data/lib/honeybadger/breadcrumbs/logging.rb +51 -51
  19. data/lib/honeybadger/breadcrumbs/ring_buffer.rb +44 -44
  20. data/lib/honeybadger/breadcrumbs.rb +8 -8
  21. data/lib/honeybadger/cli/deploy.rb +43 -43
  22. data/lib/honeybadger/cli/exec.rb +143 -143
  23. data/lib/honeybadger/cli/helpers.rb +28 -28
  24. data/lib/honeybadger/cli/heroku.rb +129 -129
  25. data/lib/honeybadger/cli/install.rb +101 -101
  26. data/lib/honeybadger/cli/main.rb +237 -237
  27. data/lib/honeybadger/cli/notify.rb +67 -67
  28. data/lib/honeybadger/cli/test.rb +267 -267
  29. data/lib/honeybadger/cli.rb +14 -14
  30. data/lib/honeybadger/config/defaults.rb +336 -333
  31. data/lib/honeybadger/config/env.rb +42 -42
  32. data/lib/honeybadger/config/ruby.rb +146 -146
  33. data/lib/honeybadger/config/yaml.rb +76 -76
  34. data/lib/honeybadger/config.rb +413 -413
  35. data/lib/honeybadger/const.rb +20 -20
  36. data/lib/honeybadger/context_manager.rb +55 -55
  37. data/lib/honeybadger/conversions.rb +16 -16
  38. data/lib/honeybadger/init/rails.rb +38 -38
  39. data/lib/honeybadger/init/rake.rb +66 -66
  40. data/lib/honeybadger/init/ruby.rb +11 -11
  41. data/lib/honeybadger/init/sinatra.rb +51 -51
  42. data/lib/honeybadger/logging.rb +177 -177
  43. data/lib/honeybadger/notice.rb +579 -568
  44. data/lib/honeybadger/plugin.rb +210 -210
  45. data/lib/honeybadger/plugins/breadcrumbs.rb +111 -111
  46. data/lib/honeybadger/plugins/delayed_job/plugin.rb +56 -56
  47. data/lib/honeybadger/plugins/delayed_job.rb +22 -22
  48. data/lib/honeybadger/plugins/faktory.rb +52 -52
  49. data/lib/honeybadger/plugins/lambda.rb +71 -71
  50. data/lib/honeybadger/plugins/local_variables.rb +44 -44
  51. data/lib/honeybadger/plugins/passenger.rb +23 -23
  52. data/lib/honeybadger/plugins/rails.rb +72 -63
  53. data/lib/honeybadger/plugins/resque.rb +72 -72
  54. data/lib/honeybadger/plugins/shoryuken.rb +52 -52
  55. data/lib/honeybadger/plugins/sidekiq.rb +71 -62
  56. data/lib/honeybadger/plugins/sucker_punch.rb +18 -18
  57. data/lib/honeybadger/plugins/thor.rb +32 -32
  58. data/lib/honeybadger/plugins/warden.rb +19 -19
  59. data/lib/honeybadger/rack/error_notifier.rb +92 -92
  60. data/lib/honeybadger/rack/user_feedback.rb +88 -88
  61. data/lib/honeybadger/rack/user_informer.rb +45 -45
  62. data/lib/honeybadger/ruby.rb +2 -2
  63. data/lib/honeybadger/singleton.rb +103 -103
  64. data/lib/honeybadger/tasks.rb +22 -22
  65. data/lib/honeybadger/templates/feedback_form.erb +84 -84
  66. data/lib/honeybadger/util/http.rb +92 -92
  67. data/lib/honeybadger/util/lambda.rb +32 -32
  68. data/lib/honeybadger/util/request_hash.rb +73 -73
  69. data/lib/honeybadger/util/request_payload.rb +41 -41
  70. data/lib/honeybadger/util/revision.rb +39 -39
  71. data/lib/honeybadger/util/sanitizer.rb +214 -214
  72. data/lib/honeybadger/util/sql.rb +34 -34
  73. data/lib/honeybadger/util/stats.rb +50 -50
  74. data/lib/honeybadger/version.rb +4 -4
  75. data/lib/honeybadger/worker.rb +253 -253
  76. data/lib/honeybadger.rb +11 -11
  77. data/resources/ca-bundle.crt +3376 -3376
  78. data/vendor/capistrano-honeybadger/lib/capistrano/honeybadger.rb +5 -5
  79. data/vendor/capistrano-honeybadger/lib/capistrano/tasks/deploy.cap +89 -89
  80. data/vendor/capistrano-honeybadger/lib/honeybadger/capistrano/legacy.rb +47 -47
  81. data/vendor/capistrano-honeybadger/lib/honeybadger/capistrano.rb +2 -2
  82. data/vendor/cli/inifile.rb +628 -628
  83. data/vendor/cli/thor/actions/create_file.rb +103 -103
  84. data/vendor/cli/thor/actions/create_link.rb +59 -59
  85. data/vendor/cli/thor/actions/directory.rb +118 -118
  86. data/vendor/cli/thor/actions/empty_directory.rb +135 -135
  87. data/vendor/cli/thor/actions/file_manipulation.rb +316 -316
  88. data/vendor/cli/thor/actions/inject_into_file.rb +107 -107
  89. data/vendor/cli/thor/actions.rb +319 -319
  90. data/vendor/cli/thor/base.rb +656 -656
  91. data/vendor/cli/thor/command.rb +133 -133
  92. data/vendor/cli/thor/core_ext/hash_with_indifferent_access.rb +77 -77
  93. data/vendor/cli/thor/core_ext/io_binary_read.rb +10 -10
  94. data/vendor/cli/thor/core_ext/ordered_hash.rb +98 -98
  95. data/vendor/cli/thor/error.rb +32 -32
  96. data/vendor/cli/thor/group.rb +281 -281
  97. data/vendor/cli/thor/invocation.rb +178 -178
  98. data/vendor/cli/thor/line_editor/basic.rb +35 -35
  99. data/vendor/cli/thor/line_editor/readline.rb +88 -88
  100. data/vendor/cli/thor/line_editor.rb +17 -17
  101. data/vendor/cli/thor/parser/argument.rb +73 -73
  102. data/vendor/cli/thor/parser/arguments.rb +175 -175
  103. data/vendor/cli/thor/parser/option.rb +125 -125
  104. data/vendor/cli/thor/parser/options.rb +218 -218
  105. data/vendor/cli/thor/parser.rb +4 -4
  106. data/vendor/cli/thor/rake_compat.rb +71 -71
  107. data/vendor/cli/thor/runner.rb +322 -322
  108. data/vendor/cli/thor/shell/basic.rb +421 -421
  109. data/vendor/cli/thor/shell/color.rb +149 -149
  110. data/vendor/cli/thor/shell/html.rb +126 -126
  111. data/vendor/cli/thor/shell.rb +81 -81
  112. data/vendor/cli/thor/util.rb +267 -267
  113. data/vendor/cli/thor/version.rb +3 -3
  114. data/vendor/cli/thor.rb +484 -484
  115. metadata +10 -5
data/vendor/cli/thor.rb CHANGED
@@ -1,484 +1,484 @@
1
- require "set"
2
- require "thor/base"
3
-
4
- class Thor # rubocop:disable ClassLength
5
- class << self
6
- # Allows for custom "Command" package naming.
7
- #
8
- # === Parameters
9
- # name<String>
10
- # options<Hash>
11
- #
12
- def package_name(name, options = {})
13
- @package_name = name.nil? || name == "" ? nil : name
14
- end
15
-
16
- # Sets the default command when thor is executed without an explicit command to be called.
17
- #
18
- # ==== Parameters
19
- # meth<Symbol>:: name of the default command
20
- #
21
- def default_command(meth = nil)
22
- if meth
23
- @default_command = meth == :none ? "help" : meth.to_s
24
- else
25
- @default_command ||= from_superclass(:default_command, "help")
26
- end
27
- end
28
- alias_method :default_task, :default_command
29
-
30
- # Registers another Thor subclass as a command.
31
- #
32
- # ==== Parameters
33
- # klass<Class>:: Thor subclass to register
34
- # command<String>:: Subcommand name to use
35
- # usage<String>:: Short usage for the subcommand
36
- # description<String>:: Description for the subcommand
37
- def register(klass, subcommand_name, usage, description, options = {})
38
- if klass <= Thor::Group
39
- desc usage, description, options
40
- define_method(subcommand_name) { |*args| invoke(klass, args) }
41
- else
42
- desc usage, description, options
43
- subcommand subcommand_name, klass
44
- end
45
- end
46
-
47
- # Defines the usage and the description of the next command.
48
- #
49
- # ==== Parameters
50
- # usage<String>
51
- # description<String>
52
- # options<String>
53
- #
54
- def desc(usage, description, options = {})
55
- if options[:for]
56
- command = find_and_refresh_command(options[:for])
57
- command.usage = usage if usage
58
- command.description = description if description
59
- else
60
- @usage, @desc, @hide = usage, description, options[:hide] || false
61
- end
62
- end
63
-
64
- # Defines the long description of the next command.
65
- #
66
- # ==== Parameters
67
- # long description<String>
68
- #
69
- def long_desc(long_description, options = {})
70
- if options[:for]
71
- command = find_and_refresh_command(options[:for])
72
- command.long_description = long_description if long_description
73
- else
74
- @long_desc = long_description
75
- end
76
- end
77
-
78
- # Maps an input to a command. If you define:
79
- #
80
- # map "-T" => "list"
81
- #
82
- # Running:
83
- #
84
- # thor -T
85
- #
86
- # Will invoke the list command.
87
- #
88
- # ==== Parameters
89
- # Hash[String|Array => Symbol]:: Maps the string or the strings in the array to the given command.
90
- #
91
- def map(mappings = nil)
92
- @map ||= from_superclass(:map, {})
93
-
94
- if mappings
95
- mappings.each do |key, value|
96
- if key.respond_to?(:each)
97
- key.each { |subkey| @map[subkey] = value }
98
- else
99
- @map[key] = value
100
- end
101
- end
102
- end
103
-
104
- @map
105
- end
106
-
107
- # Declares the options for the next command to be declared.
108
- #
109
- # ==== Parameters
110
- # Hash[Symbol => Object]:: The hash key is the name of the option and the value
111
- # is the type of the option. Can be :string, :array, :hash, :boolean, :numeric
112
- # or :required (string). If you give a value, the type of the value is used.
113
- #
114
- def method_options(options = nil)
115
- @method_options ||= {}
116
- build_options(options, @method_options) if options
117
- @method_options
118
- end
119
-
120
- alias_method :options, :method_options
121
-
122
- # Adds an option to the set of method options. If :for is given as option,
123
- # it allows you to change the options from a previous defined command.
124
- #
125
- # def previous_command
126
- # # magic
127
- # end
128
- #
129
- # method_option :foo => :bar, :for => :previous_command
130
- #
131
- # def next_command
132
- # # magic
133
- # end
134
- #
135
- # ==== Parameters
136
- # name<Symbol>:: The name of the argument.
137
- # options<Hash>:: Described below.
138
- #
139
- # ==== Options
140
- # :desc - Description for the argument.
141
- # :required - If the argument is required or not.
142
- # :default - Default value for this argument. It cannot be required and have default values.
143
- # :aliases - Aliases for this option.
144
- # :type - The type of the argument, can be :string, :hash, :array, :numeric or :boolean.
145
- # :banner - String to show on usage notes.
146
- # :hide - If you want to hide this option from the help.
147
- #
148
- def method_option(name, options = {})
149
- scope = if options[:for]
150
- find_and_refresh_command(options[:for]).options
151
- else
152
- method_options
153
- end
154
-
155
- build_option(name, options, scope)
156
- end
157
- alias_method :option, :method_option
158
-
159
- # Prints help information for the given command.
160
- #
161
- # ==== Parameters
162
- # shell<Thor::Shell>
163
- # command_name<String>
164
- #
165
- def command_help(shell, command_name)
166
- meth = normalize_command_name(command_name)
167
- command = all_commands[meth]
168
- handle_no_command_error(meth) unless command
169
-
170
- shell.say "Usage:"
171
- shell.say " #{banner(command)}"
172
- shell.say
173
- class_options_help(shell, nil => command.options.map { |_, o| o })
174
- if command.long_description
175
- shell.say "Description:"
176
- shell.print_wrapped(command.long_description, :indent => 2)
177
- else
178
- shell.say command.description
179
- end
180
- end
181
- alias_method :task_help, :command_help
182
-
183
- # Prints help information for this class.
184
- #
185
- # ==== Parameters
186
- # shell<Thor::Shell>
187
- #
188
- def help(shell, subcommand = false)
189
- list = printable_commands(true, subcommand)
190
- Thor::Util.thor_classes_in(self).each do |klass|
191
- list += klass.printable_commands(false)
192
- end
193
- list.sort! { |a, b| a[0] <=> b[0] }
194
-
195
- if defined?(@package_name) && @package_name
196
- shell.say "#{@package_name} commands:"
197
- else
198
- shell.say "Commands:"
199
- end
200
-
201
- shell.print_table(list, :indent => 2, :truncate => true)
202
- shell.say
203
- class_options_help(shell)
204
- end
205
-
206
- # Returns commands ready to be printed.
207
- def printable_commands(all = true, subcommand = false)
208
- (all ? all_commands : commands).map do |_, command|
209
- next if command.hidden?
210
- item = []
211
- item << banner(command, false, subcommand)
212
- item << (command.description ? "# #{command.description.gsub(/\s+/m, ' ')}" : "")
213
- item
214
- end.compact
215
- end
216
- alias_method :printable_tasks, :printable_commands
217
-
218
- def subcommands
219
- @subcommands ||= from_superclass(:subcommands, [])
220
- end
221
- alias_method :subtasks, :subcommands
222
-
223
- def subcommand_classes
224
- @subcommand_classes ||= {}
225
- end
226
-
227
- def subcommand(subcommand, subcommand_class)
228
- subcommands << subcommand.to_s
229
- subcommand_class.subcommand_help subcommand
230
- subcommand_classes[subcommand.to_s] = subcommand_class
231
-
232
- define_method(subcommand) do |*args|
233
- args, opts = Thor::Arguments.split(args)
234
- args.unshift("help") if opts.include? "--help" or opts.include? "-h"
235
- invoke subcommand_class, args, opts, :invoked_via_subcommand => true, :class_options => options
236
- end
237
- end
238
- alias_method :subtask, :subcommand
239
-
240
- # Extend check unknown options to accept a hash of conditions.
241
- #
242
- # === Parameters
243
- # options<Hash>: A hash containing :only and/or :except keys
244
- def check_unknown_options!(options = {})
245
- @check_unknown_options ||= {}
246
- options.each do |key, value|
247
- if value
248
- @check_unknown_options[key] = Array(value)
249
- else
250
- @check_unknown_options.delete(key)
251
- end
252
- end
253
- @check_unknown_options
254
- end
255
-
256
- # Overwrite check_unknown_options? to take subcommands and options into account.
257
- def check_unknown_options?(config) #:nodoc:
258
- options = check_unknown_options
259
- return false unless options
260
-
261
- command = config[:current_command]
262
- return true unless command
263
-
264
- name = command.name
265
-
266
- if subcommands.include?(name)
267
- false
268
- elsif options[:except]
269
- !options[:except].include?(name.to_sym)
270
- elsif options[:only]
271
- options[:only].include?(name.to_sym)
272
- else
273
- true
274
- end
275
- end
276
-
277
- # Stop parsing of options as soon as an unknown option or a regular
278
- # argument is encountered. All remaining arguments are passed to the command.
279
- # This is useful if you have a command that can receive arbitrary additional
280
- # options, and where those additional options should not be handled by
281
- # Thor.
282
- #
283
- # ==== Example
284
- #
285
- # To better understand how this is useful, let's consider a command that calls
286
- # an external command. A user may want to pass arbitrary options and
287
- # arguments to that command. The command itself also accepts some options,
288
- # which should be handled by Thor.
289
- #
290
- # class_option "verbose", :type => :boolean
291
- # stop_on_unknown_option! :exec
292
- # check_unknown_options! :except => :exec
293
- #
294
- # desc "exec", "Run a shell command"
295
- # def exec(*args)
296
- # puts "diagnostic output" if options[:verbose]
297
- # Kernel.exec(*args)
298
- # end
299
- #
300
- # Here +exec+ can be called with +--verbose+ to get diagnostic output,
301
- # e.g.:
302
- #
303
- # $ thor exec --verbose echo foo
304
- # diagnostic output
305
- # foo
306
- #
307
- # But if +--verbose+ is given after +echo+, it is passed to +echo+ instead:
308
- #
309
- # $ thor exec echo --verbose foo
310
- # --verbose foo
311
- #
312
- # ==== Parameters
313
- # Symbol ...:: A list of commands that should be affected.
314
- def stop_on_unknown_option!(*command_names)
315
- stop_on_unknown_option.merge(command_names)
316
- end
317
-
318
- def stop_on_unknown_option?(command) #:nodoc:
319
- command && stop_on_unknown_option.include?(command.name.to_sym)
320
- end
321
-
322
- protected
323
- def stop_on_unknown_option #:nodoc:
324
- @stop_on_unknown_option ||= Set.new
325
- end
326
-
327
- # The method responsible for dispatching given the args.
328
- def dispatch(meth, given_args, given_opts, config) #:nodoc: # rubocop:disable MethodLength
329
- meth ||= retrieve_command_name(given_args)
330
- command = all_commands[normalize_command_name(meth)]
331
-
332
- if !command && config[:invoked_via_subcommand]
333
- # We're a subcommand and our first argument didn't match any of our
334
- # commands. So we put it back and call our default command.
335
- given_args.unshift(meth)
336
- command = all_commands[normalize_command_name(default_command)]
337
- end
338
-
339
- if command
340
- args, opts = Thor::Options.split(given_args)
341
- if stop_on_unknown_option?(command) && !args.empty?
342
- # given_args starts with a non-option, so we treat everything as
343
- # ordinary arguments
344
- args.concat opts
345
- opts.clear
346
- end
347
- else
348
- args, opts = given_args, nil
349
- command = dynamic_command_class.new(meth)
350
- end
351
-
352
- opts = given_opts || opts || []
353
- config.merge!(:current_command => command, :command_options => command.options)
354
-
355
- instance = new(args, opts, config)
356
- yield instance if block_given?
357
- args = instance.args
358
- trailing = args[Range.new(arguments.size, -1)]
359
- instance.invoke_command(command, trailing || [])
360
- end
361
-
362
- # The banner for this class. You can customize it if you are invoking the
363
- # thor class by another ways which is not the Thor::Runner. It receives
364
- # the command that is going to be invoked and a boolean which indicates if
365
- # the namespace should be displayed as arguments.
366
- #
367
- def banner(command, namespace = nil, subcommand = false)
368
- "#{basename} #{command.formatted_usage(self, $thor_runner, subcommand)}"
369
- end
370
-
371
- def baseclass #:nodoc:
372
- Thor
373
- end
374
-
375
- def dynamic_command_class #:nodoc:
376
- Thor::DynamicCommand
377
- end
378
-
379
- def create_command(meth) #:nodoc:
380
- @usage ||= nil
381
- @desc ||= nil
382
- @long_desc ||= nil
383
-
384
- if @usage && @desc
385
- base_class = @hide ? Thor::HiddenCommand : Thor::Command
386
- commands[meth] = base_class.new(meth, @desc, @long_desc, @usage, method_options)
387
- @usage, @desc, @long_desc, @method_options, @hide = nil
388
- true
389
- elsif all_commands[meth] || meth == "method_missing"
390
- true
391
- else
392
- puts "[WARNING] Attempted to create command #{meth.inspect} without usage or description. " <<
393
- "Call desc if you want this method to be available as command or declare it inside a " <<
394
- "no_commands{} block. Invoked from #{caller[1].inspect}."
395
- false
396
- end
397
- end
398
- alias_method :create_task, :create_command
399
-
400
- def initialize_added #:nodoc:
401
- class_options.merge!(method_options)
402
- @method_options = nil
403
- end
404
-
405
- # Retrieve the command name from given args.
406
- def retrieve_command_name(args) #:nodoc:
407
- meth = args.first.to_s unless args.empty?
408
- if meth && (map[meth] || meth !~ /^\-/)
409
- args.shift
410
- else
411
- nil
412
- end
413
- end
414
- alias_method :retrieve_task_name, :retrieve_command_name
415
-
416
- # receives a (possibly nil) command name and returns a name that is in
417
- # the commands hash. In addition to normalizing aliases, this logic
418
- # will determine if a shortened command is an unambiguous substring of
419
- # a command or alias.
420
- #
421
- # +normalize_command_name+ also converts names like +animal-prison+
422
- # into +animal_prison+.
423
- def normalize_command_name(meth) #:nodoc:
424
- return default_command.to_s.gsub("-", "_") unless meth
425
-
426
- possibilities = find_command_possibilities(meth)
427
- if possibilities.size > 1
428
- fail AmbiguousTaskError, "Ambiguous command #{meth} matches [#{possibilities.join(', ')}]"
429
- elsif possibilities.size < 1
430
- meth = meth || default_command
431
- elsif map[meth]
432
- meth = map[meth]
433
- else
434
- meth = possibilities.first
435
- end
436
-
437
- meth.to_s.gsub("-", "_") # treat foo-bar as foo_bar
438
- end
439
- alias_method :normalize_task_name, :normalize_command_name
440
-
441
- # this is the logic that takes the command name passed in by the user
442
- # and determines whether it is an unambiguous substrings of a command or
443
- # alias name.
444
- def find_command_possibilities(meth)
445
- len = meth.to_s.length
446
- possibilities = all_commands.merge(map).keys.select { |n| meth == n[0, len] }.sort
447
- unique_possibilities = possibilities.map { |k| map[k] || k }.uniq
448
-
449
- if possibilities.include?(meth)
450
- [meth]
451
- elsif unique_possibilities.size == 1
452
- unique_possibilities
453
- else
454
- possibilities
455
- end
456
- end
457
- alias_method :find_task_possibilities, :find_command_possibilities
458
-
459
- def subcommand_help(cmd)
460
- desc "help [COMMAND]", "Describe subcommands or one specific subcommand"
461
- class_eval "
462
- def help(command = nil, subcommand = true); super; end
463
- "
464
- end
465
- alias_method :subtask_help, :subcommand_help
466
- end
467
-
468
- include Thor::Base
469
-
470
- map HELP_MAPPINGS => :help
471
-
472
- desc "help [COMMAND]", "Describe available commands or one specific command"
473
- def help(command = nil, subcommand = false)
474
- if command
475
- if self.class.subcommands.include? command
476
- self.class.subcommand_classes[command].help(shell, true)
477
- else
478
- self.class.command_help(shell, command)
479
- end
480
- else
481
- self.class.help(shell, subcommand)
482
- end
483
- end
484
- end
1
+ require "set"
2
+ require "thor/base"
3
+
4
+ class Thor # rubocop:disable ClassLength
5
+ class << self
6
+ # Allows for custom "Command" package naming.
7
+ #
8
+ # === Parameters
9
+ # name<String>
10
+ # options<Hash>
11
+ #
12
+ def package_name(name, options = {})
13
+ @package_name = name.nil? || name == "" ? nil : name
14
+ end
15
+
16
+ # Sets the default command when thor is executed without an explicit command to be called.
17
+ #
18
+ # ==== Parameters
19
+ # meth<Symbol>:: name of the default command
20
+ #
21
+ def default_command(meth = nil)
22
+ if meth
23
+ @default_command = meth == :none ? "help" : meth.to_s
24
+ else
25
+ @default_command ||= from_superclass(:default_command, "help")
26
+ end
27
+ end
28
+ alias_method :default_task, :default_command
29
+
30
+ # Registers another Thor subclass as a command.
31
+ #
32
+ # ==== Parameters
33
+ # klass<Class>:: Thor subclass to register
34
+ # command<String>:: Subcommand name to use
35
+ # usage<String>:: Short usage for the subcommand
36
+ # description<String>:: Description for the subcommand
37
+ def register(klass, subcommand_name, usage, description, options = {})
38
+ if klass <= Thor::Group
39
+ desc usage, description, options
40
+ define_method(subcommand_name) { |*args| invoke(klass, args) }
41
+ else
42
+ desc usage, description, options
43
+ subcommand subcommand_name, klass
44
+ end
45
+ end
46
+
47
+ # Defines the usage and the description of the next command.
48
+ #
49
+ # ==== Parameters
50
+ # usage<String>
51
+ # description<String>
52
+ # options<String>
53
+ #
54
+ def desc(usage, description, options = {})
55
+ if options[:for]
56
+ command = find_and_refresh_command(options[:for])
57
+ command.usage = usage if usage
58
+ command.description = description if description
59
+ else
60
+ @usage, @desc, @hide = usage, description, options[:hide] || false
61
+ end
62
+ end
63
+
64
+ # Defines the long description of the next command.
65
+ #
66
+ # ==== Parameters
67
+ # long description<String>
68
+ #
69
+ def long_desc(long_description, options = {})
70
+ if options[:for]
71
+ command = find_and_refresh_command(options[:for])
72
+ command.long_description = long_description if long_description
73
+ else
74
+ @long_desc = long_description
75
+ end
76
+ end
77
+
78
+ # Maps an input to a command. If you define:
79
+ #
80
+ # map "-T" => "list"
81
+ #
82
+ # Running:
83
+ #
84
+ # thor -T
85
+ #
86
+ # Will invoke the list command.
87
+ #
88
+ # ==== Parameters
89
+ # Hash[String|Array => Symbol]:: Maps the string or the strings in the array to the given command.
90
+ #
91
+ def map(mappings = nil)
92
+ @map ||= from_superclass(:map, {})
93
+
94
+ if mappings
95
+ mappings.each do |key, value|
96
+ if key.respond_to?(:each)
97
+ key.each { |subkey| @map[subkey] = value }
98
+ else
99
+ @map[key] = value
100
+ end
101
+ end
102
+ end
103
+
104
+ @map
105
+ end
106
+
107
+ # Declares the options for the next command to be declared.
108
+ #
109
+ # ==== Parameters
110
+ # Hash[Symbol => Object]:: The hash key is the name of the option and the value
111
+ # is the type of the option. Can be :string, :array, :hash, :boolean, :numeric
112
+ # or :required (string). If you give a value, the type of the value is used.
113
+ #
114
+ def method_options(options = nil)
115
+ @method_options ||= {}
116
+ build_options(options, @method_options) if options
117
+ @method_options
118
+ end
119
+
120
+ alias_method :options, :method_options
121
+
122
+ # Adds an option to the set of method options. If :for is given as option,
123
+ # it allows you to change the options from a previous defined command.
124
+ #
125
+ # def previous_command
126
+ # # magic
127
+ # end
128
+ #
129
+ # method_option :foo => :bar, :for => :previous_command
130
+ #
131
+ # def next_command
132
+ # # magic
133
+ # end
134
+ #
135
+ # ==== Parameters
136
+ # name<Symbol>:: The name of the argument.
137
+ # options<Hash>:: Described below.
138
+ #
139
+ # ==== Options
140
+ # :desc - Description for the argument.
141
+ # :required - If the argument is required or not.
142
+ # :default - Default value for this argument. It cannot be required and have default values.
143
+ # :aliases - Aliases for this option.
144
+ # :type - The type of the argument, can be :string, :hash, :array, :numeric or :boolean.
145
+ # :banner - String to show on usage notes.
146
+ # :hide - If you want to hide this option from the help.
147
+ #
148
+ def method_option(name, options = {})
149
+ scope = if options[:for]
150
+ find_and_refresh_command(options[:for]).options
151
+ else
152
+ method_options
153
+ end
154
+
155
+ build_option(name, options, scope)
156
+ end
157
+ alias_method :option, :method_option
158
+
159
+ # Prints help information for the given command.
160
+ #
161
+ # ==== Parameters
162
+ # shell<Thor::Shell>
163
+ # command_name<String>
164
+ #
165
+ def command_help(shell, command_name)
166
+ meth = normalize_command_name(command_name)
167
+ command = all_commands[meth]
168
+ handle_no_command_error(meth) unless command
169
+
170
+ shell.say "Usage:"
171
+ shell.say " #{banner(command)}"
172
+ shell.say
173
+ class_options_help(shell, nil => command.options.map { |_, o| o })
174
+ if command.long_description
175
+ shell.say "Description:"
176
+ shell.print_wrapped(command.long_description, :indent => 2)
177
+ else
178
+ shell.say command.description
179
+ end
180
+ end
181
+ alias_method :task_help, :command_help
182
+
183
+ # Prints help information for this class.
184
+ #
185
+ # ==== Parameters
186
+ # shell<Thor::Shell>
187
+ #
188
+ def help(shell, subcommand = false)
189
+ list = printable_commands(true, subcommand)
190
+ Thor::Util.thor_classes_in(self).each do |klass|
191
+ list += klass.printable_commands(false)
192
+ end
193
+ list.sort! { |a, b| a[0] <=> b[0] }
194
+
195
+ if defined?(@package_name) && @package_name
196
+ shell.say "#{@package_name} commands:"
197
+ else
198
+ shell.say "Commands:"
199
+ end
200
+
201
+ shell.print_table(list, :indent => 2, :truncate => true)
202
+ shell.say
203
+ class_options_help(shell)
204
+ end
205
+
206
+ # Returns commands ready to be printed.
207
+ def printable_commands(all = true, subcommand = false)
208
+ (all ? all_commands : commands).map do |_, command|
209
+ next if command.hidden?
210
+ item = []
211
+ item << banner(command, false, subcommand)
212
+ item << (command.description ? "# #{command.description.gsub(/\s+/m, ' ')}" : "")
213
+ item
214
+ end.compact
215
+ end
216
+ alias_method :printable_tasks, :printable_commands
217
+
218
+ def subcommands
219
+ @subcommands ||= from_superclass(:subcommands, [])
220
+ end
221
+ alias_method :subtasks, :subcommands
222
+
223
+ def subcommand_classes
224
+ @subcommand_classes ||= {}
225
+ end
226
+
227
+ def subcommand(subcommand, subcommand_class)
228
+ subcommands << subcommand.to_s
229
+ subcommand_class.subcommand_help subcommand
230
+ subcommand_classes[subcommand.to_s] = subcommand_class
231
+
232
+ define_method(subcommand) do |*args|
233
+ args, opts = Thor::Arguments.split(args)
234
+ args.unshift("help") if opts.include? "--help" or opts.include? "-h"
235
+ invoke subcommand_class, args, opts, :invoked_via_subcommand => true, :class_options => options
236
+ end
237
+ end
238
+ alias_method :subtask, :subcommand
239
+
240
+ # Extend check unknown options to accept a hash of conditions.
241
+ #
242
+ # === Parameters
243
+ # options<Hash>: A hash containing :only and/or :except keys
244
+ def check_unknown_options!(options = {})
245
+ @check_unknown_options ||= {}
246
+ options.each do |key, value|
247
+ if value
248
+ @check_unknown_options[key] = Array(value)
249
+ else
250
+ @check_unknown_options.delete(key)
251
+ end
252
+ end
253
+ @check_unknown_options
254
+ end
255
+
256
+ # Overwrite check_unknown_options? to take subcommands and options into account.
257
+ def check_unknown_options?(config) #:nodoc:
258
+ options = check_unknown_options
259
+ return false unless options
260
+
261
+ command = config[:current_command]
262
+ return true unless command
263
+
264
+ name = command.name
265
+
266
+ if subcommands.include?(name)
267
+ false
268
+ elsif options[:except]
269
+ !options[:except].include?(name.to_sym)
270
+ elsif options[:only]
271
+ options[:only].include?(name.to_sym)
272
+ else
273
+ true
274
+ end
275
+ end
276
+
277
+ # Stop parsing of options as soon as an unknown option or a regular
278
+ # argument is encountered. All remaining arguments are passed to the command.
279
+ # This is useful if you have a command that can receive arbitrary additional
280
+ # options, and where those additional options should not be handled by
281
+ # Thor.
282
+ #
283
+ # ==== Example
284
+ #
285
+ # To better understand how this is useful, let's consider a command that calls
286
+ # an external command. A user may want to pass arbitrary options and
287
+ # arguments to that command. The command itself also accepts some options,
288
+ # which should be handled by Thor.
289
+ #
290
+ # class_option "verbose", :type => :boolean
291
+ # stop_on_unknown_option! :exec
292
+ # check_unknown_options! :except => :exec
293
+ #
294
+ # desc "exec", "Run a shell command"
295
+ # def exec(*args)
296
+ # puts "diagnostic output" if options[:verbose]
297
+ # Kernel.exec(*args)
298
+ # end
299
+ #
300
+ # Here +exec+ can be called with +--verbose+ to get diagnostic output,
301
+ # e.g.:
302
+ #
303
+ # $ thor exec --verbose echo foo
304
+ # diagnostic output
305
+ # foo
306
+ #
307
+ # But if +--verbose+ is given after +echo+, it is passed to +echo+ instead:
308
+ #
309
+ # $ thor exec echo --verbose foo
310
+ # --verbose foo
311
+ #
312
+ # ==== Parameters
313
+ # Symbol ...:: A list of commands that should be affected.
314
+ def stop_on_unknown_option!(*command_names)
315
+ stop_on_unknown_option.merge(command_names)
316
+ end
317
+
318
+ def stop_on_unknown_option?(command) #:nodoc:
319
+ command && stop_on_unknown_option.include?(command.name.to_sym)
320
+ end
321
+
322
+ protected
323
+ def stop_on_unknown_option #:nodoc:
324
+ @stop_on_unknown_option ||= Set.new
325
+ end
326
+
327
+ # The method responsible for dispatching given the args.
328
+ def dispatch(meth, given_args, given_opts, config) #:nodoc: # rubocop:disable MethodLength
329
+ meth ||= retrieve_command_name(given_args)
330
+ command = all_commands[normalize_command_name(meth)]
331
+
332
+ if !command && config[:invoked_via_subcommand]
333
+ # We're a subcommand and our first argument didn't match any of our
334
+ # commands. So we put it back and call our default command.
335
+ given_args.unshift(meth)
336
+ command = all_commands[normalize_command_name(default_command)]
337
+ end
338
+
339
+ if command
340
+ args, opts = Thor::Options.split(given_args)
341
+ if stop_on_unknown_option?(command) && !args.empty?
342
+ # given_args starts with a non-option, so we treat everything as
343
+ # ordinary arguments
344
+ args.concat opts
345
+ opts.clear
346
+ end
347
+ else
348
+ args, opts = given_args, nil
349
+ command = dynamic_command_class.new(meth)
350
+ end
351
+
352
+ opts = given_opts || opts || []
353
+ config.merge!(:current_command => command, :command_options => command.options)
354
+
355
+ instance = new(args, opts, config)
356
+ yield instance if block_given?
357
+ args = instance.args
358
+ trailing = args[Range.new(arguments.size, -1)]
359
+ instance.invoke_command(command, trailing || [])
360
+ end
361
+
362
+ # The banner for this class. You can customize it if you are invoking the
363
+ # thor class by another ways which is not the Thor::Runner. It receives
364
+ # the command that is going to be invoked and a boolean which indicates if
365
+ # the namespace should be displayed as arguments.
366
+ #
367
+ def banner(command, namespace = nil, subcommand = false)
368
+ "#{basename} #{command.formatted_usage(self, $thor_runner, subcommand)}"
369
+ end
370
+
371
+ def baseclass #:nodoc:
372
+ Thor
373
+ end
374
+
375
+ def dynamic_command_class #:nodoc:
376
+ Thor::DynamicCommand
377
+ end
378
+
379
+ def create_command(meth) #:nodoc:
380
+ @usage ||= nil
381
+ @desc ||= nil
382
+ @long_desc ||= nil
383
+
384
+ if @usage && @desc
385
+ base_class = @hide ? Thor::HiddenCommand : Thor::Command
386
+ commands[meth] = base_class.new(meth, @desc, @long_desc, @usage, method_options)
387
+ @usage, @desc, @long_desc, @method_options, @hide = nil
388
+ true
389
+ elsif all_commands[meth] || meth == "method_missing"
390
+ true
391
+ else
392
+ puts "[WARNING] Attempted to create command #{meth.inspect} without usage or description. " <<
393
+ "Call desc if you want this method to be available as command or declare it inside a " <<
394
+ "no_commands{} block. Invoked from #{caller[1].inspect}."
395
+ false
396
+ end
397
+ end
398
+ alias_method :create_task, :create_command
399
+
400
+ def initialize_added #:nodoc:
401
+ class_options.merge!(method_options)
402
+ @method_options = nil
403
+ end
404
+
405
+ # Retrieve the command name from given args.
406
+ def retrieve_command_name(args) #:nodoc:
407
+ meth = args.first.to_s unless args.empty?
408
+ if meth && (map[meth] || meth !~ /^\-/)
409
+ args.shift
410
+ else
411
+ nil
412
+ end
413
+ end
414
+ alias_method :retrieve_task_name, :retrieve_command_name
415
+
416
+ # receives a (possibly nil) command name and returns a name that is in
417
+ # the commands hash. In addition to normalizing aliases, this logic
418
+ # will determine if a shortened command is an unambiguous substring of
419
+ # a command or alias.
420
+ #
421
+ # +normalize_command_name+ also converts names like +animal-prison+
422
+ # into +animal_prison+.
423
+ def normalize_command_name(meth) #:nodoc:
424
+ return default_command.to_s.gsub("-", "_") unless meth
425
+
426
+ possibilities = find_command_possibilities(meth)
427
+ if possibilities.size > 1
428
+ fail AmbiguousTaskError, "Ambiguous command #{meth} matches [#{possibilities.join(', ')}]"
429
+ elsif possibilities.size < 1
430
+ meth = meth || default_command
431
+ elsif map[meth]
432
+ meth = map[meth]
433
+ else
434
+ meth = possibilities.first
435
+ end
436
+
437
+ meth.to_s.gsub("-", "_") # treat foo-bar as foo_bar
438
+ end
439
+ alias_method :normalize_task_name, :normalize_command_name
440
+
441
+ # this is the logic that takes the command name passed in by the user
442
+ # and determines whether it is an unambiguous substrings of a command or
443
+ # alias name.
444
+ def find_command_possibilities(meth)
445
+ len = meth.to_s.length
446
+ possibilities = all_commands.merge(map).keys.select { |n| meth == n[0, len] }.sort
447
+ unique_possibilities = possibilities.map { |k| map[k] || k }.uniq
448
+
449
+ if possibilities.include?(meth)
450
+ [meth]
451
+ elsif unique_possibilities.size == 1
452
+ unique_possibilities
453
+ else
454
+ possibilities
455
+ end
456
+ end
457
+ alias_method :find_task_possibilities, :find_command_possibilities
458
+
459
+ def subcommand_help(cmd)
460
+ desc "help [COMMAND]", "Describe subcommands or one specific subcommand"
461
+ class_eval "
462
+ def help(command = nil, subcommand = true); super; end
463
+ "
464
+ end
465
+ alias_method :subtask_help, :subcommand_help
466
+ end
467
+
468
+ include Thor::Base
469
+
470
+ map HELP_MAPPINGS => :help
471
+
472
+ desc "help [COMMAND]", "Describe available commands or one specific command"
473
+ def help(command = nil, subcommand = false)
474
+ if command
475
+ if self.class.subcommands.include? command
476
+ self.class.subcommand_classes[command].help(shell, true)
477
+ else
478
+ self.class.command_help(shell, command)
479
+ end
480
+ else
481
+ self.class.help(shell, subcommand)
482
+ end
483
+ end
484
+ end