skylight 0.0.7 → 0.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. checksums.yaml +7 -0
  2. data/bin/skylight +5 -0
  3. data/lib/skylight.rb +2 -0
  4. data/lib/skylight/cli.rb +84 -0
  5. data/lib/skylight/config.rb +53 -6
  6. data/lib/skylight/instrumenter.rb +4 -1
  7. data/lib/skylight/json_proto.rb +0 -3
  8. data/lib/skylight/middleware.rb +1 -0
  9. data/lib/skylight/normalize.rb +30 -5
  10. data/lib/skylight/normalize/process_action.rb +1 -1
  11. data/lib/skylight/normalize/render_collection.rb +2 -6
  12. data/lib/skylight/normalize/render_partial.rb +2 -5
  13. data/lib/skylight/normalize/render_template.rb +2 -5
  14. data/lib/skylight/normalize/sql.rb +2 -4
  15. data/lib/skylight/railtie.rb +30 -33
  16. data/lib/skylight/sanity_checker.rb +73 -0
  17. data/lib/skylight/subscriber.rb +1 -15
  18. data/lib/skylight/trace.rb +30 -5
  19. data/lib/skylight/util/http.rb +97 -0
  20. data/lib/skylight/vendor/highline.rb +1012 -0
  21. data/lib/skylight/vendor/highline/color_scheme.rb +134 -0
  22. data/lib/skylight/vendor/highline/compatibility.rb +16 -0
  23. data/lib/skylight/vendor/highline/import.rb +41 -0
  24. data/lib/skylight/vendor/highline/menu.rb +398 -0
  25. data/lib/skylight/vendor/highline/question.rb +475 -0
  26. data/lib/skylight/vendor/highline/simulate.rb +48 -0
  27. data/lib/skylight/vendor/highline/string_extensions.rb +131 -0
  28. data/lib/skylight/vendor/highline/style.rb +181 -0
  29. data/lib/skylight/vendor/highline/system_extensions.rb +218 -0
  30. data/lib/skylight/vendor/thor.rb +473 -0
  31. data/lib/skylight/vendor/thor/actions.rb +318 -0
  32. data/lib/skylight/vendor/thor/actions/create_file.rb +105 -0
  33. data/lib/skylight/vendor/thor/actions/create_link.rb +60 -0
  34. data/lib/skylight/vendor/thor/actions/directory.rb +119 -0
  35. data/lib/skylight/vendor/thor/actions/empty_directory.rb +137 -0
  36. data/lib/skylight/vendor/thor/actions/file_manipulation.rb +314 -0
  37. data/lib/skylight/vendor/thor/actions/inject_into_file.rb +109 -0
  38. data/lib/skylight/vendor/thor/base.rb +652 -0
  39. data/lib/skylight/vendor/thor/command.rb +136 -0
  40. data/lib/skylight/vendor/thor/core_ext/hash_with_indifferent_access.rb +80 -0
  41. data/lib/skylight/vendor/thor/core_ext/io_binary_read.rb +12 -0
  42. data/lib/skylight/vendor/thor/core_ext/ordered_hash.rb +100 -0
  43. data/lib/skylight/vendor/thor/error.rb +28 -0
  44. data/lib/skylight/vendor/thor/group.rb +282 -0
  45. data/lib/skylight/vendor/thor/invocation.rb +172 -0
  46. data/lib/skylight/vendor/thor/parser.rb +4 -0
  47. data/lib/skylight/vendor/thor/parser/argument.rb +74 -0
  48. data/lib/skylight/vendor/thor/parser/arguments.rb +171 -0
  49. data/lib/skylight/vendor/thor/parser/option.rb +121 -0
  50. data/lib/skylight/vendor/thor/parser/options.rb +218 -0
  51. data/lib/skylight/vendor/thor/rake_compat.rb +72 -0
  52. data/lib/skylight/vendor/thor/runner.rb +322 -0
  53. data/lib/skylight/vendor/thor/shell.rb +88 -0
  54. data/lib/skylight/vendor/thor/shell/basic.rb +393 -0
  55. data/lib/skylight/vendor/thor/shell/color.rb +148 -0
  56. data/lib/skylight/vendor/thor/shell/html.rb +127 -0
  57. data/lib/skylight/vendor/thor/util.rb +270 -0
  58. data/lib/skylight/vendor/thor/version.rb +3 -0
  59. data/lib/skylight/version.rb +1 -1
  60. data/lib/skylight/worker.rb +3 -58
  61. metadata +56 -18
@@ -0,0 +1,109 @@
1
+ require 'thor/actions/empty_directory'
2
+
3
+ class Thor
4
+ module Actions
5
+
6
+ # Injects the given content into a file. Different from gsub_file, this
7
+ # method is reversible.
8
+ #
9
+ # ==== Parameters
10
+ # destination<String>:: Relative path to the destination root
11
+ # data<String>:: Data to add to the file. Can be given as a block.
12
+ # config<Hash>:: give :verbose => false to not log the status and the flag
13
+ # for injection (:after or :before) or :force => true for
14
+ # insert two or more times the same content.
15
+ #
16
+ # ==== Examples
17
+ #
18
+ # insert_into_file "config/environment.rb", "config.gem :thor", :after => "Rails::Initializer.run do |config|\n"
19
+ #
20
+ # insert_into_file "config/environment.rb", :after => "Rails::Initializer.run do |config|\n" do
21
+ # gems = ask "Which gems would you like to add?"
22
+ # gems.split(" ").map{ |gem| " config.gem :#{gem}" }.join("\n")
23
+ # end
24
+ #
25
+ def insert_into_file(destination, *args, &block)
26
+ if block_given?
27
+ data, config = block, args.shift
28
+ else
29
+ data, config = args.shift, args.shift
30
+ end
31
+ action InjectIntoFile.new(self, destination, data, config)
32
+ end
33
+ alias_method :inject_into_file, :insert_into_file
34
+
35
+ class InjectIntoFile < EmptyDirectory #:nodoc:
36
+ attr_reader :replacement, :flag, :behavior
37
+
38
+ def initialize(base, destination, data, config)
39
+ super(base, destination, { :verbose => true }.merge(config))
40
+
41
+ @behavior, @flag = if @config.key?(:after)
42
+ [:after, @config.delete(:after)]
43
+ else
44
+ [:before, @config.delete(:before)]
45
+ end
46
+
47
+ @replacement = data.is_a?(Proc) ? data.call : data
48
+ @flag = Regexp.escape(@flag) unless @flag.is_a?(Regexp)
49
+ end
50
+
51
+ def invoke!
52
+ say_status :invoke
53
+
54
+ content = if @behavior == :after
55
+ '\0' + replacement
56
+ else
57
+ replacement + '\0'
58
+ end
59
+
60
+ replace!(/#{flag}/, content, config[:force])
61
+ end
62
+
63
+ def revoke!
64
+ say_status :revoke
65
+
66
+ regexp = if @behavior == :after
67
+ content = '\1\2'
68
+ /(#{flag})(.*)(#{Regexp.escape(replacement)})/m
69
+ else
70
+ content = '\2\3'
71
+ /(#{Regexp.escape(replacement)})(.*)(#{flag})/m
72
+ end
73
+
74
+ replace!(regexp, content, true)
75
+ end
76
+
77
+ protected
78
+
79
+ def say_status(behavior)
80
+ status = if behavior == :invoke
81
+ if flag == /\A/
82
+ :prepend
83
+ elsif flag == /\z/
84
+ :append
85
+ else
86
+ :insert
87
+ end
88
+ else
89
+ :subtract
90
+ end
91
+
92
+ super(status, config[:verbose])
93
+ end
94
+
95
+ # Adds the content to the file.
96
+ #
97
+ def replace!(regexp, string, force)
98
+ unless base.options[:pretend]
99
+ content = File.binread(destination)
100
+ if force || !content.include?(replacement)
101
+ content.gsub!(regexp, string)
102
+ File.open(destination, 'wb') { |file| file.write(content) }
103
+ end
104
+ end
105
+ end
106
+
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,652 @@
1
+ require 'thor/command'
2
+ require 'thor/core_ext/hash_with_indifferent_access'
3
+ require 'thor/core_ext/ordered_hash'
4
+ require 'thor/error'
5
+ require 'thor/invocation'
6
+ require 'thor/parser'
7
+ require 'thor/shell'
8
+ require 'thor/util'
9
+
10
+ class Thor
11
+ autoload :Actions, 'thor/actions'
12
+ autoload :RakeCompat, 'thor/rake_compat'
13
+ autoload :Group, 'thor/group'
14
+
15
+ # Shortcuts for help.
16
+ HELP_MAPPINGS = %w(-h -? --help -D)
17
+
18
+ # Thor methods that should not be overwritten by the user.
19
+ THOR_RESERVED_WORDS = %w(invoke shell options behavior root destination_root relative_root
20
+ action add_file create_file in_root inside run run_ruby_script)
21
+
22
+ module Base
23
+ attr_accessor :options, :parent_options, :args
24
+
25
+ # It receives arguments in an Array and two hashes, one for options and
26
+ # other for configuration.
27
+ #
28
+ # Notice that it does not check if all required arguments were supplied.
29
+ # It should be done by the parser.
30
+ #
31
+ # ==== Parameters
32
+ # args<Array[Object]>:: An array of objects. The objects are applied to their
33
+ # respective accessors declared with <tt>argument</tt>.
34
+ #
35
+ # options<Hash>:: An options hash that will be available as self.options.
36
+ # The hash given is converted to a hash with indifferent
37
+ # access, magic predicates (options.skip?) and then frozen.
38
+ #
39
+ # config<Hash>:: Configuration for this Thor class.
40
+ #
41
+ def initialize(args=[], options={}, config={})
42
+ parse_options = self.class.class_options
43
+
44
+ # The start method splits inbound arguments at the first argument
45
+ # that looks like an option (starts with - or --). It then calls
46
+ # new, passing in the two halves of the arguments Array as the
47
+ # first two parameters.
48
+
49
+ if options.is_a?(Array)
50
+ command_options = config.delete(:command_options) # hook for start
51
+ parse_options = parse_options.merge(command_options) if command_options
52
+ array_options, hash_options = options, {}
53
+ else
54
+ # Handle the case where the class was explicitly instantiated
55
+ # with pre-parsed options.
56
+ array_options, hash_options = [], options
57
+ end
58
+
59
+ # Let Thor::Options parse the options first, so it can remove
60
+ # declared options from the array. This will leave us with
61
+ # a list of arguments that weren't declared.
62
+ stop_on_unknown = self.class.stop_on_unknown_option? config[:current_command]
63
+ opts = Thor::Options.new(parse_options, hash_options, stop_on_unknown)
64
+ self.options = opts.parse(array_options)
65
+ self.options = config[:class_options].merge(self.options) if config[:class_options]
66
+
67
+ # If unknown options are disallowed, make sure that none of the
68
+ # remaining arguments looks like an option.
69
+ opts.check_unknown! if self.class.check_unknown_options?(config)
70
+
71
+ # Add the remaining arguments from the options parser to the
72
+ # arguments passed in to initialize. Then remove any positional
73
+ # arguments declared using #argument (this is primarily used
74
+ # by Thor::Group). Tis will leave us with the remaining
75
+ # positional arguments.
76
+ to_parse = args
77
+ to_parse += opts.remaining unless self.class.strict_args_position?(config)
78
+
79
+ thor_args = Thor::Arguments.new(self.class.arguments)
80
+ thor_args.parse(to_parse).each { |k,v| __send__("#{k}=", v) }
81
+ @args = thor_args.remaining
82
+ end
83
+
84
+ class << self
85
+ def included(base) #:nodoc:
86
+ base.send :extend, ClassMethods
87
+ base.send :include, Invocation
88
+ base.send :include, Shell
89
+ end
90
+
91
+ # Returns the classes that inherits from Thor or Thor::Group.
92
+ #
93
+ # ==== Returns
94
+ # Array[Class]
95
+ #
96
+ def subclasses
97
+ @subclasses ||= []
98
+ end
99
+
100
+ # Returns the files where the subclasses are kept.
101
+ #
102
+ # ==== Returns
103
+ # Hash[path<String> => Class]
104
+ #
105
+ def subclass_files
106
+ @subclass_files ||= Hash.new{ |h,k| h[k] = [] }
107
+ end
108
+
109
+ # Whenever a class inherits from Thor or Thor::Group, we should track the
110
+ # class and the file on Thor::Base. This is the method responsable for it.
111
+ #
112
+ def register_klass_file(klass) #:nodoc:
113
+ file = caller[1].match(/(.*):\d+/)[1]
114
+ Thor::Base.subclasses << klass unless Thor::Base.subclasses.include?(klass)
115
+
116
+ file_subclasses = Thor::Base.subclass_files[File.expand_path(file)]
117
+ file_subclasses << klass unless file_subclasses.include?(klass)
118
+ end
119
+ end
120
+
121
+ module ClassMethods
122
+ def attr_reader(*) #:nodoc:
123
+ no_commands { super }
124
+ end
125
+
126
+ def attr_writer(*) #:nodoc:
127
+ no_commands { super }
128
+ end
129
+
130
+ def attr_accessor(*) #:nodoc:
131
+ no_commands { super }
132
+ end
133
+
134
+ # If you want to raise an error for unknown options, call check_unknown_options!
135
+ # This is disabled by default to allow dynamic invocations.
136
+ def check_unknown_options!
137
+ @check_unknown_options = true
138
+ end
139
+
140
+ def check_unknown_options #:nodoc:
141
+ @check_unknown_options ||= from_superclass(:check_unknown_options, false)
142
+ end
143
+
144
+ def check_unknown_options?(config) #:nodoc:
145
+ !!check_unknown_options
146
+ end
147
+
148
+ # If true, option parsing is suspended as soon as an unknown option or a
149
+ # regular argument is encountered. All remaining arguments are passed to
150
+ # the command as regular arguments.
151
+ def stop_on_unknown_option?(command_name) #:nodoc:
152
+ false
153
+ end
154
+
155
+ # If you want only strict string args (useful when cascading thor classes),
156
+ # call strict_args_position! This is disabled by default to allow dynamic
157
+ # invocations.
158
+ def strict_args_position!
159
+ @strict_args_position = true
160
+ end
161
+
162
+ def strict_args_position #:nodoc:
163
+ @strict_args_position ||= from_superclass(:strict_args_position, false)
164
+ end
165
+
166
+ def strict_args_position?(config) #:nodoc:
167
+ !!strict_args_position
168
+ end
169
+
170
+ # Adds an argument to the class and creates an attr_accessor for it.
171
+ #
172
+ # Arguments are different from options in several aspects. The first one
173
+ # is how they are parsed from the command line, arguments are retrieved
174
+ # from position:
175
+ #
176
+ # thor command NAME
177
+ #
178
+ # Instead of:
179
+ #
180
+ # thor command --name=NAME
181
+ #
182
+ # Besides, arguments are used inside your code as an accessor (self.argument),
183
+ # while options are all kept in a hash (self.options).
184
+ #
185
+ # Finally, arguments cannot have type :default or :boolean but can be
186
+ # optional (supplying :optional => :true or :required => false), although
187
+ # you cannot have a required argument after a non-required argument. If you
188
+ # try it, an error is raised.
189
+ #
190
+ # ==== Parameters
191
+ # name<Symbol>:: The name of the argument.
192
+ # options<Hash>:: Described below.
193
+ #
194
+ # ==== Options
195
+ # :desc - Description for the argument.
196
+ # :required - If the argument is required or not.
197
+ # :optional - If the argument is optional or not.
198
+ # :type - The type of the argument, can be :string, :hash, :array, :numeric.
199
+ # :default - Default value for this argument. It cannot be required and have default values.
200
+ # :banner - String to show on usage notes.
201
+ #
202
+ # ==== Errors
203
+ # ArgumentError:: Raised if you supply a required argument after a non required one.
204
+ #
205
+ def argument(name, options={})
206
+ is_thor_reserved_word?(name, :argument)
207
+ no_commands { attr_accessor name }
208
+
209
+ required = if options.key?(:optional)
210
+ !options[:optional]
211
+ elsif options.key?(:required)
212
+ options[:required]
213
+ else
214
+ options[:default].nil?
215
+ end
216
+
217
+ remove_argument name
218
+
219
+ arguments.each do |argument|
220
+ next if argument.required?
221
+ raise ArgumentError, "You cannot have #{name.to_s.inspect} as required argument after " <<
222
+ "the non-required argument #{argument.human_name.inspect}."
223
+ end if required
224
+
225
+ options[:required] = required
226
+
227
+ arguments << Thor::Argument.new(name, options)
228
+ end
229
+
230
+ # Returns this class arguments, looking up in the ancestors chain.
231
+ #
232
+ # ==== Returns
233
+ # Array[Thor::Argument]
234
+ #
235
+ def arguments
236
+ @arguments ||= from_superclass(:arguments, [])
237
+ end
238
+
239
+ # Adds a bunch of options to the set of class options.
240
+ #
241
+ # class_options :foo => false, :bar => :required, :baz => :string
242
+ #
243
+ # If you prefer more detailed declaration, check class_option.
244
+ #
245
+ # ==== Parameters
246
+ # Hash[Symbol => Object]
247
+ #
248
+ def class_options(options=nil)
249
+ @class_options ||= from_superclass(:class_options, {})
250
+ build_options(options, @class_options) if options
251
+ @class_options
252
+ end
253
+
254
+ # Adds an option to the set of class options
255
+ #
256
+ # ==== Parameters
257
+ # name<Symbol>:: The name of the argument.
258
+ # options<Hash>:: Described below.
259
+ #
260
+ # ==== Options
261
+ # :desc:: -- Description for the argument.
262
+ # :required:: -- If the argument is required or not.
263
+ # :default:: -- Default value for this argument.
264
+ # :group:: -- The group for this options. Use by class options to output options in different levels.
265
+ # :aliases:: -- Aliases for this option. <b>Note:</b> Thor follows a convention of one-dash-one-letter options. Thus aliases like "-something" wouldn't be parsed; use either "\--something" or "-s" instead.
266
+ # :type:: -- The type of the argument, can be :string, :hash, :array, :numeric or :boolean.
267
+ # :banner:: -- String to show on usage notes.
268
+ # :hide:: -- If you want to hide this option from the help.
269
+ #
270
+ def class_option(name, options={})
271
+ build_option(name, options, class_options)
272
+ end
273
+
274
+ # Removes a previous defined argument. If :undefine is given, undefine
275
+ # accessors as well.
276
+ #
277
+ # ==== Parameters
278
+ # names<Array>:: Arguments to be removed
279
+ #
280
+ # ==== Examples
281
+ #
282
+ # remove_argument :foo
283
+ # remove_argument :foo, :bar, :baz, :undefine => true
284
+ #
285
+ def remove_argument(*names)
286
+ options = names.last.is_a?(Hash) ? names.pop : {}
287
+
288
+ names.each do |name|
289
+ arguments.delete_if { |a| a.name == name.to_s }
290
+ undef_method name, "#{name}=" if options[:undefine]
291
+ end
292
+ end
293
+
294
+ # Removes a previous defined class option.
295
+ #
296
+ # ==== Parameters
297
+ # names<Array>:: Class options to be removed
298
+ #
299
+ # ==== Examples
300
+ #
301
+ # remove_class_option :foo
302
+ # remove_class_option :foo, :bar, :baz
303
+ #
304
+ def remove_class_option(*names)
305
+ names.each do |name|
306
+ class_options.delete(name)
307
+ end
308
+ end
309
+
310
+ # Defines the group. This is used when thor list is invoked so you can specify
311
+ # that only commands from a pre-defined group will be shown. Defaults to standard.
312
+ #
313
+ # ==== Parameters
314
+ # name<String|Symbol>
315
+ #
316
+ def group(name=nil)
317
+ @group = case name
318
+ when nil
319
+ @group || from_superclass(:group, 'standard')
320
+ else
321
+ name.to_s
322
+ end
323
+ end
324
+
325
+ # Returns the commands for this Thor class.
326
+ #
327
+ # ==== Returns
328
+ # OrderedHash:: An ordered hash with commands names as keys and Thor::Command
329
+ # objects as values.
330
+ #
331
+ def commands
332
+ @commands ||= Thor::CoreExt::OrderedHash.new
333
+ end
334
+ alias tasks commands
335
+
336
+ # Returns the commands for this Thor class and all subclasses.
337
+ #
338
+ # ==== Returns
339
+ # OrderedHash:: An ordered hash with commands names as keys and Thor::Command
340
+ # objects as values.
341
+ #
342
+ def all_commands
343
+ @all_commands ||= from_superclass(:all_commands, Thor::CoreExt::OrderedHash.new)
344
+ @all_commands.merge(commands)
345
+ end
346
+ alias all_tasks all_commands
347
+
348
+ # Removes a given command from this Thor class. This is usually done if you
349
+ # are inheriting from another class and don't want it to be available
350
+ # anymore.
351
+ #
352
+ # By default it only remove the mapping to the command. But you can supply
353
+ # :undefine => true to undefine the method from the class as well.
354
+ #
355
+ # ==== Parameters
356
+ # name<Symbol|String>:: The name of the command to be removed
357
+ # options<Hash>:: You can give :undefine => true if you want commands the method
358
+ # to be undefined from the class as well.
359
+ #
360
+ def remove_command(*names)
361
+ options = names.last.is_a?(Hash) ? names.pop : {}
362
+
363
+ names.each do |name|
364
+ commands.delete(name.to_s)
365
+ all_commands.delete(name.to_s)
366
+ undef_method name if options[:undefine]
367
+ end
368
+ end
369
+ alias remove_task remove_command
370
+
371
+ # All methods defined inside the given block are not added as commands.
372
+ #
373
+ # So you can do:
374
+ #
375
+ # class MyScript < Thor
376
+ # no_commands do
377
+ # def this_is_not_a_command
378
+ # end
379
+ # end
380
+ # end
381
+ #
382
+ # You can also add the method and remove it from the command list:
383
+ #
384
+ # class MyScript < Thor
385
+ # def this_is_not_a_command
386
+ # end
387
+ # remove_command :this_is_not_a_command
388
+ # end
389
+ #
390
+ def no_commands
391
+ @no_commands = true
392
+ yield
393
+ ensure
394
+ @no_commands = false
395
+ end
396
+ alias no_tasks no_commands
397
+
398
+ # Sets the namespace for the Thor or Thor::Group class. By default the
399
+ # namespace is retrieved from the class name. If your Thor class is named
400
+ # Scripts::MyScript, the help method, for example, will be called as:
401
+ #
402
+ # thor scripts:my_script -h
403
+ #
404
+ # If you change the namespace:
405
+ #
406
+ # namespace :my_scripts
407
+ #
408
+ # You change how your commands are invoked:
409
+ #
410
+ # thor my_scripts -h
411
+ #
412
+ # Finally, if you change your namespace to default:
413
+ #
414
+ # namespace :default
415
+ #
416
+ # Your commands can be invoked with a shortcut. Instead of:
417
+ #
418
+ # thor :my_command
419
+ #
420
+ def namespace(name=nil)
421
+ @namespace = case name
422
+ when nil
423
+ @namespace || Thor::Util.namespace_from_thor_class(self)
424
+ else
425
+ @namespace = name.to_s
426
+ end
427
+ end
428
+
429
+ # Parses the command and options from the given args, instantiate the class
430
+ # and invoke the command. This method is used when the arguments must be parsed
431
+ # from an array. If you are inside Ruby and want to use a Thor class, you
432
+ # can simply initialize it:
433
+ #
434
+ # script = MyScript.new(args, options, config)
435
+ # script.invoke(:command, first_arg, second_arg, third_arg)
436
+ #
437
+ def start(given_args=ARGV, config={})
438
+ config[:shell] ||= Thor::Base.shell.new
439
+ dispatch(nil, given_args.dup, nil, config)
440
+ rescue Thor::Error => e
441
+ ENV["THOR_DEBUG"] == "1" ? (raise e) : config[:shell].error(e.message)
442
+ exit(1) if exit_on_failure?
443
+ rescue Errno::EPIPE
444
+ # This happens if a thor command is piped to something like `head`,
445
+ # which closes the pipe when it's done reading. This will also
446
+ # mean that if the pipe is closed, further unnecessary
447
+ # computation will not occur.
448
+ exit(0)
449
+ end
450
+
451
+ # Allows to use private methods from parent in child classes as commands.
452
+ #
453
+ # ==== Parameters
454
+ # names<Array>:: Method names to be used as commands
455
+ #
456
+ # ==== Examples
457
+ #
458
+ # public_command :foo
459
+ # public_command :foo, :bar, :baz
460
+ #
461
+ def public_command(*names)
462
+ names.each do |name|
463
+ class_eval "def #{name}(*); super end"
464
+ end
465
+ end
466
+ alias public_task public_command
467
+
468
+ def handle_no_command_error(command, has_namespace = $thor_runner) #:nodoc:
469
+ if has_namespace
470
+ raise UndefinedCommandError, "Could not find command #{command.inspect} in #{namespace.inspect} namespace."
471
+ else
472
+ raise UndefinedCommandError, "Could not find command #{command.inspect}."
473
+ end
474
+ end
475
+ alias handle_no_task_error handle_no_command_error
476
+
477
+ def handle_argument_error(command, error, args, arity) #:nodoc:
478
+ msg = "ERROR: #{basename} #{command.name} was called with "
479
+ msg << 'no arguments' if args.empty?
480
+ msg << 'arguments ' << args.inspect if !args.empty?
481
+ msg << "\nUsage: #{self.banner(command).inspect}."
482
+ raise InvocationError, msg
483
+ end
484
+
485
+ protected
486
+
487
+ # Prints the class options per group. If an option does not belong to
488
+ # any group, it's printed as Class option.
489
+ #
490
+ def class_options_help(shell, groups={}) #:nodoc:
491
+ # Group options by group
492
+ class_options.each do |_, value|
493
+ groups[value.group] ||= []
494
+ groups[value.group] << value
495
+ end
496
+
497
+ # Deal with default group
498
+ global_options = groups.delete(nil) || []
499
+ print_options(shell, global_options)
500
+
501
+ # Print all others
502
+ groups.each do |group_name, options|
503
+ print_options(shell, options, group_name)
504
+ end
505
+ end
506
+
507
+ # Receives a set of options and print them.
508
+ def print_options(shell, options, group_name=nil)
509
+ return if options.empty?
510
+
511
+ list = []
512
+ padding = options.collect{ |o| o.aliases.size }.max.to_i * 4
513
+
514
+ options.each do |option|
515
+ unless option.hide
516
+ item = [ option.usage(padding) ]
517
+ item.push(option.description ? "# #{option.description}" : "")
518
+
519
+ list << item
520
+ list << [ "", "# Default: #{option.default}" ] if option.show_default?
521
+ list << [ "", "# Possible values: #{option.enum.join(', ')}" ] if option.enum
522
+ end
523
+ end
524
+
525
+ shell.say(group_name ? "#{group_name} options:" : "Options:")
526
+ shell.print_table(list, :indent => 2)
527
+ shell.say ""
528
+ end
529
+
530
+ # Raises an error if the word given is a Thor reserved word.
531
+ def is_thor_reserved_word?(word, type) #:nodoc:
532
+ return false unless THOR_RESERVED_WORDS.include?(word.to_s)
533
+ raise "#{word.inspect} is a Thor reserved word and cannot be defined as #{type}"
534
+ end
535
+
536
+ # Build an option and adds it to the given scope.
537
+ #
538
+ # ==== Parameters
539
+ # name<Symbol>:: The name of the argument.
540
+ # options<Hash>:: Described in both class_option and method_option.
541
+ # scope<Hash>:: Options hash that is being built up
542
+ def build_option(name, options, scope) #:nodoc:
543
+ scope[name] = Thor::Option.new(name, options)
544
+ end
545
+
546
+ # Receives a hash of options, parse them and add to the scope. This is a
547
+ # fast way to set a bunch of options:
548
+ #
549
+ # build_options :foo => true, :bar => :required, :baz => :string
550
+ #
551
+ # ==== Parameters
552
+ # Hash[Symbol => Object]
553
+ def build_options(options, scope) #:nodoc:
554
+ options.each do |key, value|
555
+ scope[key] = Thor::Option.parse(key, value)
556
+ end
557
+ end
558
+
559
+ # Finds a command with the given name. If the command belongs to the current
560
+ # class, just return it, otherwise dup it and add the fresh copy to the
561
+ # current command hash.
562
+ def find_and_refresh_command(name) #:nodoc:
563
+ command = if command = commands[name.to_s]
564
+ command
565
+ elsif command = all_commands[name.to_s]
566
+ commands[name.to_s] = command.clone
567
+ else
568
+ raise ArgumentError, "You supplied :for => #{name.inspect}, but the command #{name.inspect} could not be found."
569
+ end
570
+ end
571
+ alias find_and_refresh_task find_and_refresh_command
572
+
573
+ # Everytime someone inherits from a Thor class, register the klass
574
+ # and file into baseclass.
575
+ def inherited(klass)
576
+ Thor::Base.register_klass_file(klass)
577
+ klass.instance_variable_set(:@no_commands, false)
578
+ end
579
+
580
+ # Fire this callback whenever a method is added. Added methods are
581
+ # tracked as commands by invoking the create_command method.
582
+ def method_added(meth)
583
+ meth = meth.to_s
584
+
585
+ if meth == "initialize"
586
+ initialize_added
587
+ return
588
+ end
589
+
590
+ # Return if it's not a public instance method
591
+ return unless public_method_defined?(meth.to_sym)
592
+
593
+ return if @no_commands || !create_command(meth)
594
+
595
+ is_thor_reserved_word?(meth, :command)
596
+ Thor::Base.register_klass_file(self)
597
+ end
598
+
599
+ # Retrieves a value from superclass. If it reaches the baseclass,
600
+ # returns default.
601
+ def from_superclass(method, default=nil)
602
+ if self == baseclass || !superclass.respond_to?(method, true)
603
+ default
604
+ else
605
+ value = superclass.send(method)
606
+
607
+ if value
608
+ if value.is_a?(TrueClass) || value.is_a?(Symbol)
609
+ value
610
+ else
611
+ value.dup
612
+ end
613
+ end
614
+ end
615
+ end
616
+
617
+ # A flag that makes the process exit with status 1 if any error happens.
618
+ def exit_on_failure?
619
+ false
620
+ end
621
+
622
+ #
623
+ # The basename of the program invoking the thor class.
624
+ #
625
+ def basename
626
+ File.basename($0).split(' ').first
627
+ end
628
+
629
+ # SIGNATURE: Sets the baseclass. This is where the superclass lookup
630
+ # finishes.
631
+ def baseclass #:nodoc:
632
+ end
633
+
634
+ # SIGNATURE: Creates a new command if valid_command? is true. This method is
635
+ # called when a new method is added to the class.
636
+ def create_command(meth) #:nodoc:
637
+ end
638
+ alias create_task create_command
639
+
640
+ # SIGNATURE: Defines behavior when the initialize method is added to the
641
+ # class.
642
+ def initialize_added #:nodoc:
643
+ end
644
+
645
+ # SIGNATURE: The hook invoked by start.
646
+ def dispatch(command, given_args, given_opts, config) #:nodoc:
647
+ raise NotImplementedError
648
+ end
649
+
650
+ end
651
+ end
652
+ end