millisami-thor 0.14.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. data/.autotest +8 -0
  2. data/.gemtest +0 -0
  3. data/.gitignore +9 -0
  4. data/.rspec +1 -0
  5. data/CHANGELOG.rdoc +103 -0
  6. data/Gemfile +11 -0
  7. data/LICENSE.md +20 -0
  8. data/README.md +26 -0
  9. data/Thorfile +13 -0
  10. data/bin/rake2thor +86 -0
  11. data/bin/thor +6 -0
  12. data/lib/thor/actions/create_file.rb +105 -0
  13. data/lib/thor/actions/create_link.rb +57 -0
  14. data/lib/thor/actions/directory.rb +93 -0
  15. data/lib/thor/actions/empty_directory.rb +134 -0
  16. data/lib/thor/actions/file_manipulation.rb +270 -0
  17. data/lib/thor/actions/inject_into_file.rb +109 -0
  18. data/lib/thor/actions.rb +314 -0
  19. data/lib/thor/base.rb +598 -0
  20. data/lib/thor/core_ext/file_binary_read.rb +9 -0
  21. data/lib/thor/core_ext/hash_with_indifferent_access.rb +75 -0
  22. data/lib/thor/core_ext/ordered_hash.rb +100 -0
  23. data/lib/thor/error.rb +30 -0
  24. data/lib/thor/group.rb +276 -0
  25. data/lib/thor/invocation.rb +168 -0
  26. data/lib/thor/parser/argument.rb +67 -0
  27. data/lib/thor/parser/arguments.rb +165 -0
  28. data/lib/thor/parser/option.rb +120 -0
  29. data/lib/thor/parser/options.rb +181 -0
  30. data/lib/thor/parser.rb +4 -0
  31. data/lib/thor/rake_compat.rb +70 -0
  32. data/lib/thor/runner.rb +309 -0
  33. data/lib/thor/shell/basic.rb +302 -0
  34. data/lib/thor/shell/color.rb +108 -0
  35. data/lib/thor/shell/html.rb +121 -0
  36. data/lib/thor/shell.rb +88 -0
  37. data/lib/thor/task.rb +129 -0
  38. data/lib/thor/util.rb +229 -0
  39. data/lib/thor/version.rb +3 -0
  40. data/lib/thor.rb +336 -0
  41. data/spec/actions/create_file_spec.rb +170 -0
  42. data/spec/actions/directory_spec.rb +136 -0
  43. data/spec/actions/empty_directory_spec.rb +98 -0
  44. data/spec/actions/file_manipulation_spec.rb +317 -0
  45. data/spec/actions/inject_into_file_spec.rb +135 -0
  46. data/spec/actions_spec.rb +322 -0
  47. data/spec/base_spec.rb +274 -0
  48. data/spec/core_ext/hash_with_indifferent_access_spec.rb +43 -0
  49. data/spec/core_ext/ordered_hash_spec.rb +115 -0
  50. data/spec/fixtures/application.rb +2 -0
  51. data/spec/fixtures/bundle/execute.rb +6 -0
  52. data/spec/fixtures/bundle/main.thor +1 -0
  53. data/spec/fixtures/doc/%file_name%.rb.tt +1 -0
  54. data/spec/fixtures/doc/README +3 -0
  55. data/spec/fixtures/doc/block_helper.rb +3 -0
  56. data/spec/fixtures/doc/components/.empty_directory +0 -0
  57. data/spec/fixtures/doc/config.rb +1 -0
  58. data/spec/fixtures/doc/config.yaml.tt +1 -0
  59. data/spec/fixtures/group.thor +114 -0
  60. data/spec/fixtures/invoke.thor +112 -0
  61. data/spec/fixtures/path with spaces +0 -0
  62. data/spec/fixtures/script.thor +184 -0
  63. data/spec/fixtures/task.thor +10 -0
  64. data/spec/group_spec.rb +216 -0
  65. data/spec/invocation_spec.rb +100 -0
  66. data/spec/parser/argument_spec.rb +47 -0
  67. data/spec/parser/arguments_spec.rb +65 -0
  68. data/spec/parser/option_spec.rb +202 -0
  69. data/spec/parser/options_spec.rb +329 -0
  70. data/spec/rake_compat_spec.rb +72 -0
  71. data/spec/register_spec.rb +92 -0
  72. data/spec/runner_spec.rb +210 -0
  73. data/spec/shell/basic_spec.rb +223 -0
  74. data/spec/shell/color_spec.rb +41 -0
  75. data/spec/shell/html_spec.rb +27 -0
  76. data/spec/shell_spec.rb +47 -0
  77. data/spec/spec_helper.rb +54 -0
  78. data/spec/task_spec.rb +74 -0
  79. data/spec/thor_spec.rb +362 -0
  80. data/spec/util_spec.rb +163 -0
  81. data/thor.gemspec +25 -0
  82. metadata +241 -0
data/lib/thor/base.rb ADDED
@@ -0,0 +1,598 @@
1
+ require 'thor/core_ext/hash_with_indifferent_access'
2
+ require 'thor/core_ext/ordered_hash'
3
+ require 'thor/error'
4
+ require 'thor/shell'
5
+ require 'thor/invocation'
6
+ require 'thor/parser'
7
+ require 'thor/task'
8
+ require 'thor/util'
9
+
10
+ class Thor
11
+ autoload :Actions, 'thor/actions'
12
+ autoload :RakeCompat, 'thor/rake_compat'
13
+
14
+ # Shortcuts for help.
15
+ HELP_MAPPINGS = %w(-h -? --help -D)
16
+
17
+ # Thor methods that should not be overwritten by the user.
18
+ THOR_RESERVED_WORDS = %w(invoke shell options behavior root destination_root relative_root
19
+ action add_file create_file in_root inside run_cmd run_ruby_script)
20
+
21
+ module Base
22
+ attr_accessor :options, :args
23
+
24
+ # It receives arguments in an Array and two hashes, one for options and
25
+ # other for configuration.
26
+ #
27
+ # Notice that it does not check if all required arguments were supplied.
28
+ # It should be done by the parser.
29
+ #
30
+ # ==== Parameters
31
+ # args<Array[Object]>:: An array of objects. The objects are applied to their
32
+ # respective accessors declared with <tt>argument</tt>.
33
+ #
34
+ # options<Hash>:: An options hash that will be available as self.options.
35
+ # The hash given is converted to a hash with indifferent
36
+ # access, magic predicates (options.skip?) and then frozen.
37
+ #
38
+ # config<Hash>:: Configuration for this Thor class.
39
+ #
40
+ def initialize(args=[], options={}, config={})
41
+ parse_options = self.class.class_options
42
+
43
+ # The start method splits inbound arguments at the first argument
44
+ # that looks like an option (starts with - or --). It then calls
45
+ # new, passing in the two halves of the arguments Array as the
46
+ # first two parameters.
47
+
48
+ if options.is_a?(Array)
49
+ task_options = config.delete(:task_options) # hook for start
50
+ parse_options = parse_options.merge(task_options) if task_options
51
+ array_options, hash_options = options, {}
52
+ else
53
+ # Handle the case where the class was explicitly instantiated
54
+ # with pre-parsed options.
55
+ array_options, hash_options = [], options
56
+ end
57
+
58
+ # Let Thor::Options parse the options first, so it can remove
59
+ # declared options from the array. This will leave us with
60
+ # a list of arguments that weren't declared.
61
+ opts = Thor::Options.new(parse_options, hash_options)
62
+ self.options = opts.parse(array_options)
63
+
64
+ # If unknown options are disallowed, make sure that none of the
65
+ # remaining arguments looks like an option.
66
+ opts.check_unknown! if self.class.check_unknown_options?(config)
67
+
68
+ # Add the remaining arguments from the options parser to the
69
+ # arguments passed in to initialize. Then remove any positional
70
+ # arguments declared using #argument (this is primarily used
71
+ # by Thor::Group). Tis will leave us with the remaining
72
+ # positional arguments.
73
+ thor_args = Thor::Arguments.new(self.class.arguments)
74
+ thor_args.parse(args + opts.remaining).each { |k,v| send("#{k}=", v) }
75
+ args = thor_args.remaining
76
+
77
+ @args = args
78
+ end
79
+
80
+ class << self
81
+ def included(base) #:nodoc:
82
+ base.send :extend, ClassMethods
83
+ base.send :include, Invocation
84
+ base.send :include, Shell
85
+ end
86
+
87
+ # Returns the classes that inherits from Thor or Thor::Group.
88
+ #
89
+ # ==== Returns
90
+ # Array[Class]
91
+ #
92
+ def subclasses
93
+ @subclasses ||= []
94
+ end
95
+
96
+ # Returns the files where the subclasses are kept.
97
+ #
98
+ # ==== Returns
99
+ # Hash[path<String> => Class]
100
+ #
101
+ def subclass_files
102
+ @subclass_files ||= Hash.new{ |h,k| h[k] = [] }
103
+ end
104
+
105
+ # Whenever a class inherits from Thor or Thor::Group, we should track the
106
+ # class and the file on Thor::Base. This is the method responsable for it.
107
+ #
108
+ def register_klass_file(klass) #:nodoc:
109
+ file = caller[1].match(/(.*):\d+/)[1]
110
+ Thor::Base.subclasses << klass unless Thor::Base.subclasses.include?(klass)
111
+
112
+ file_subclasses = Thor::Base.subclass_files[File.expand_path(file)]
113
+ file_subclasses << klass unless file_subclasses.include?(klass)
114
+ end
115
+ end
116
+
117
+ module ClassMethods
118
+ def attr_reader(*) #:nodoc:
119
+ no_tasks { super }
120
+ end
121
+
122
+ def attr_writer(*) #:nodoc:
123
+ no_tasks { super }
124
+ end
125
+
126
+ def attr_accessor(*) #:nodoc:
127
+ no_tasks { super }
128
+ end
129
+
130
+ # If you want to raise an error for unknown options, call check_unknown_options!
131
+ # This is disabled by default to allow dynamic invocations.
132
+ def check_unknown_options!
133
+ @check_unknown_options = true
134
+ end
135
+
136
+ def check_unknown_options #:nodoc:
137
+ @check_unknown_options ||= from_superclass(:check_unknown_options, false)
138
+ end
139
+
140
+ def check_unknown_options?(config) #:nodoc:
141
+ !!check_unknown_options
142
+ end
143
+
144
+ # Adds an argument to the class and creates an attr_accessor for it.
145
+ #
146
+ # Arguments are different from options in several aspects. The first one
147
+ # is how they are parsed from the command line, arguments are retrieved
148
+ # from position:
149
+ #
150
+ # thor task NAME
151
+ #
152
+ # Instead of:
153
+ #
154
+ # thor task --name=NAME
155
+ #
156
+ # Besides, arguments are used inside your code as an accessor (self.argument),
157
+ # while options are all kept in a hash (self.options).
158
+ #
159
+ # Finally, arguments cannot have type :default or :boolean but can be
160
+ # optional (supplying :optional => :true or :required => false), although
161
+ # you cannot have a required argument after a non-required argument. If you
162
+ # try it, an error is raised.
163
+ #
164
+ # ==== Parameters
165
+ # name<Symbol>:: The name of the argument.
166
+ # options<Hash>:: Described below.
167
+ #
168
+ # ==== Options
169
+ # :desc - Description for the argument.
170
+ # :required - If the argument is required or not.
171
+ # :optional - If the argument is optional or not.
172
+ # :type - The type of the argument, can be :string, :hash, :array, :numeric.
173
+ # :default - Default value for this argument. It cannot be required and have default values.
174
+ # :banner - String to show on usage notes.
175
+ #
176
+ # ==== Errors
177
+ # ArgumentError:: Raised if you supply a required argument after a non required one.
178
+ #
179
+ def argument(name, options={})
180
+ is_thor_reserved_word?(name, :argument)
181
+ no_tasks { attr_accessor name }
182
+
183
+ required = if options.key?(:optional)
184
+ !options[:optional]
185
+ elsif options.key?(:required)
186
+ options[:required]
187
+ else
188
+ options[:default].nil?
189
+ end
190
+
191
+ remove_argument name
192
+
193
+ arguments.each do |argument|
194
+ next if argument.required?
195
+ raise ArgumentError, "You cannot have #{name.to_s.inspect} as required argument after " <<
196
+ "the non-required argument #{argument.human_name.inspect}."
197
+ end if required
198
+
199
+ arguments << Thor::Argument.new(name, options[:desc], required, options[:type],
200
+ options[:default], options[:banner])
201
+ end
202
+
203
+ # Returns this class arguments, looking up in the ancestors chain.
204
+ #
205
+ # ==== Returns
206
+ # Array[Thor::Argument]
207
+ #
208
+ def arguments
209
+ @arguments ||= from_superclass(:arguments, [])
210
+ end
211
+
212
+ # Adds a bunch of options to the set of class options.
213
+ #
214
+ # class_options :foo => false, :bar => :required, :baz => :string
215
+ #
216
+ # If you prefer more detailed declaration, check class_option.
217
+ #
218
+ # ==== Parameters
219
+ # Hash[Symbol => Object]
220
+ #
221
+ def class_options(options=nil)
222
+ @class_options ||= from_superclass(:class_options, {})
223
+ build_options(options, @class_options) if options
224
+ @class_options
225
+ end
226
+
227
+ # Adds an option to the set of class options
228
+ #
229
+ # ==== Parameters
230
+ # name<Symbol>:: The name of the argument.
231
+ # options<Hash>:: Described below.
232
+ #
233
+ # ==== Options
234
+ # :desc - Description for the argument.
235
+ # :required - If the argument is required or not.
236
+ # :default - Default value for this argument.
237
+ # :group - The group for this options. Use by class options to output options in different levels.
238
+ # :aliases - Aliases for this option.
239
+ # :type - The type of the argument, can be :string, :hash, :array, :numeric or :boolean.
240
+ # :banner - String to show on usage notes.
241
+ #
242
+ def class_option(name, options={})
243
+ build_option(name, options, class_options)
244
+ end
245
+
246
+ # Removes a previous defined argument. If :undefine is given, undefine
247
+ # accessors as well.
248
+ #
249
+ # ==== Paremeters
250
+ # names<Array>:: Arguments to be removed
251
+ #
252
+ # ==== Examples
253
+ #
254
+ # remove_argument :foo
255
+ # remove_argument :foo, :bar, :baz, :undefine => true
256
+ #
257
+ def remove_argument(*names)
258
+ options = names.last.is_a?(Hash) ? names.pop : {}
259
+
260
+ names.each do |name|
261
+ arguments.delete_if { |a| a.name == name.to_s }
262
+ undef_method name, "#{name}=" if options[:undefine]
263
+ end
264
+ end
265
+
266
+ # Removes a previous defined class option.
267
+ #
268
+ # ==== Paremeters
269
+ # names<Array>:: Class options to be removed
270
+ #
271
+ # ==== Examples
272
+ #
273
+ # remove_class_option :foo
274
+ # remove_class_option :foo, :bar, :baz
275
+ #
276
+ def remove_class_option(*names)
277
+ names.each do |name|
278
+ class_options.delete(name)
279
+ end
280
+ end
281
+
282
+ # Defines the group. This is used when thor list is invoked so you can specify
283
+ # that only tasks from a pre-defined group will be shown. Defaults to standard.
284
+ #
285
+ # ==== Parameters
286
+ # name<String|Symbol>
287
+ #
288
+ def group(name=nil)
289
+ case name
290
+ when nil
291
+ @group ||= from_superclass(:group, 'standard')
292
+ else
293
+ @group = name.to_s
294
+ end
295
+ end
296
+
297
+ # Returns the tasks for this Thor class.
298
+ #
299
+ # ==== Returns
300
+ # OrderedHash:: An ordered hash with tasks names as keys and Thor::Task
301
+ # objects as values.
302
+ #
303
+ def tasks
304
+ @tasks ||= Thor::CoreExt::OrderedHash.new
305
+ end
306
+
307
+ # Returns the tasks for this Thor class and all subclasses.
308
+ #
309
+ # ==== Returns
310
+ # OrderedHash:: An ordered hash with tasks names as keys and Thor::Task
311
+ # objects as values.
312
+ #
313
+ def all_tasks
314
+ @all_tasks ||= from_superclass(:all_tasks, Thor::CoreExt::OrderedHash.new)
315
+ @all_tasks.merge(tasks)
316
+ end
317
+
318
+ # Removes a given task from this Thor class. This is usually done if you
319
+ # are inheriting from another class and don't want it to be available
320
+ # anymore.
321
+ #
322
+ # By default it only remove the mapping to the task. But you can supply
323
+ # :undefine => true to undefine the method from the class as well.
324
+ #
325
+ # ==== Parameters
326
+ # name<Symbol|String>:: The name of the task to be removed
327
+ # options<Hash>:: You can give :undefine => true if you want tasks the method
328
+ # to be undefined from the class as well.
329
+ #
330
+ def remove_task(*names)
331
+ options = names.last.is_a?(Hash) ? names.pop : {}
332
+
333
+ names.each do |name|
334
+ tasks.delete(name.to_s)
335
+ all_tasks.delete(name.to_s)
336
+ undef_method name if options[:undefine]
337
+ end
338
+ end
339
+
340
+ # All methods defined inside the given block are not added as tasks.
341
+ #
342
+ # So you can do:
343
+ #
344
+ # class MyScript < Thor
345
+ # no_tasks do
346
+ # def this_is_not_a_task
347
+ # end
348
+ # end
349
+ # end
350
+ #
351
+ # You can also add the method and remove it from the task list:
352
+ #
353
+ # class MyScript < Thor
354
+ # def this_is_not_a_task
355
+ # end
356
+ # remove_task :this_is_not_a_task
357
+ # end
358
+ #
359
+ def no_tasks
360
+ @no_tasks = true
361
+ yield
362
+ ensure
363
+ @no_tasks = false
364
+ end
365
+
366
+ # Sets the namespace for the Thor or Thor::Group class. By default the
367
+ # namespace is retrieved from the class name. If your Thor class is named
368
+ # Scripts::MyScript, the help method, for example, will be called as:
369
+ #
370
+ # thor scripts:my_script -h
371
+ #
372
+ # If you change the namespace:
373
+ #
374
+ # namespace :my_scripts
375
+ #
376
+ # You change how your tasks are invoked:
377
+ #
378
+ # thor my_scripts -h
379
+ #
380
+ # Finally, if you change your namespace to default:
381
+ #
382
+ # namespace :default
383
+ #
384
+ # Your tasks can be invoked with a shortcut. Instead of:
385
+ #
386
+ # thor :my_task
387
+ #
388
+ def namespace(name=nil)
389
+ case name
390
+ when nil
391
+ @namespace ||= Thor::Util.namespace_from_thor_class(self)
392
+ else
393
+ @namespace = name.to_s
394
+ end
395
+ end
396
+
397
+ # Parses the task and options from the given args, instantiate the class
398
+ # and invoke the task. This method is used when the arguments must be parsed
399
+ # from an array. If you are inside Ruby and want to use a Thor class, you
400
+ # can simply initialize it:
401
+ #
402
+ # script = MyScript.new(args, options, config)
403
+ # script.invoke(:task, first_arg, second_arg, third_arg)
404
+ #
405
+ def start(given_args=ARGV, config={})
406
+ config[:shell] ||= Thor::Base.shell.new
407
+ dispatch(nil, given_args.dup, nil, config)
408
+ rescue Thor::Error => e
409
+ ENV["THOR_DEBUG"] == "1" ? (raise e) : config[:shell].error(e.message)
410
+ exit(1) if exit_on_failure?
411
+ end
412
+
413
+ # Allows to use private methods from parent in child classes as tasks.
414
+ #
415
+ # ==== Paremeters
416
+ # names<Array>:: Method names to be used as tasks
417
+ #
418
+ # ==== Examples
419
+ #
420
+ # public_task :foo
421
+ # public_task :foo, :bar, :baz
422
+ #
423
+ def public_task(*names)
424
+ names.each do |name|
425
+ class_eval "def #{name}(*); super end"
426
+ end
427
+ end
428
+
429
+ def handle_no_task_error(task) #:nodoc:
430
+ if $thor_runner
431
+ raise UndefinedTaskError, "Could not find task #{task.inspect} in #{namespace.inspect} namespace."
432
+ else
433
+ raise UndefinedTaskError, "Could not find task #{task.inspect}."
434
+ end
435
+ end
436
+
437
+ def handle_argument_error(task, error) #:nodoc:
438
+ raise InvocationError, "#{task.name.inspect} was called incorrectly. Call as #{self.banner(task).inspect}."
439
+ end
440
+
441
+ protected
442
+
443
+ # Prints the class options per group. If an option does not belong to
444
+ # any group, it's printed as Class option.
445
+ #
446
+ def class_options_help(shell, groups={}) #:nodoc:
447
+ # Group options by group
448
+ class_options.each do |_, value|
449
+ groups[value.group] ||= []
450
+ groups[value.group] << value
451
+ end
452
+
453
+ # Deal with default group
454
+ global_options = groups.delete(nil) || []
455
+ print_options(shell, global_options)
456
+
457
+ # Print all others
458
+ groups.each do |group_name, options|
459
+ print_options(shell, options, group_name)
460
+ end
461
+ end
462
+
463
+ # Receives a set of options and print them.
464
+ def print_options(shell, options, group_name=nil)
465
+ return if options.empty?
466
+
467
+ list = []
468
+ padding = options.collect{ |o| o.aliases.size }.max.to_i * 4
469
+
470
+ options.each do |option|
471
+ item = [ option.usage(padding) ]
472
+ item.push(option.description ? "# #{option.description}" : "")
473
+
474
+ list << item
475
+ list << [ "", "# Default: #{option.default}" ] if option.show_default?
476
+ end
477
+
478
+ shell.say(group_name ? "#{group_name} options:" : "Options:")
479
+ shell.print_table(list, :ident => 2)
480
+ shell.say ""
481
+ end
482
+
483
+ # Raises an error if the word given is a Thor reserved word.
484
+ def is_thor_reserved_word?(word, type) #:nodoc:
485
+ return false unless THOR_RESERVED_WORDS.include?(word.to_s)
486
+ raise "#{word.inspect} is a Thor reserved word and cannot be defined as #{type}"
487
+ end
488
+
489
+ # Build an option and adds it to the given scope.
490
+ #
491
+ # ==== Parameters
492
+ # name<Symbol>:: The name of the argument.
493
+ # options<Hash>:: Described in both class_option and method_option.
494
+ def build_option(name, options, scope) #:nodoc:
495
+ scope[name] = Thor::Option.new(name, options[:desc], options[:required],
496
+ options[:type], options[:default], options[:banner],
497
+ options[:lazy_default], options[:group], options[:aliases])
498
+ end
499
+
500
+ # Receives a hash of options, parse them and add to the scope. This is a
501
+ # fast way to set a bunch of options:
502
+ #
503
+ # build_options :foo => true, :bar => :required, :baz => :string
504
+ #
505
+ # ==== Parameters
506
+ # Hash[Symbol => Object]
507
+ def build_options(options, scope) #:nodoc:
508
+ options.each do |key, value|
509
+ scope[key] = Thor::Option.parse(key, value)
510
+ end
511
+ end
512
+
513
+ # Finds a task with the given name. If the task belongs to the current
514
+ # class, just return it, otherwise dup it and add the fresh copy to the
515
+ # current task hash.
516
+ def find_and_refresh_task(name) #:nodoc:
517
+ task = if task = tasks[name.to_s]
518
+ task
519
+ elsif task = all_tasks[name.to_s]
520
+ tasks[name.to_s] = task.clone
521
+ else
522
+ raise ArgumentError, "You supplied :for => #{name.inspect}, but the task #{name.inspect} could not be found."
523
+ end
524
+ end
525
+
526
+ # Everytime someone inherits from a Thor class, register the klass
527
+ # and file into baseclass.
528
+ def inherited(klass)
529
+ Thor::Base.register_klass_file(klass)
530
+ klass.instance_variable_set(:@no_tasks, false)
531
+ end
532
+
533
+ # Fire this callback whenever a method is added. Added methods are
534
+ # tracked as tasks by invoking the create_task method.
535
+ def method_added(meth)
536
+ meth = meth.to_s
537
+
538
+ if meth == "initialize"
539
+ initialize_added
540
+ return
541
+ end
542
+
543
+ # Return if it's not a public instance method
544
+ return unless public_instance_methods.include?(meth) ||
545
+ public_instance_methods.include?(meth.to_sym)
546
+
547
+ return if @no_tasks || !create_task(meth)
548
+
549
+ is_thor_reserved_word?(meth, :task)
550
+ Thor::Base.register_klass_file(self)
551
+ end
552
+
553
+ # Retrieves a value from superclass. If it reaches the baseclass,
554
+ # returns default.
555
+ def from_superclass(method, default=nil)
556
+ if self == baseclass || !superclass.respond_to?(method, true)
557
+ default
558
+ else
559
+ value = superclass.send(method)
560
+ value.dup if value
561
+ end
562
+ end
563
+
564
+ # A flag that makes the process exit with status 1 if any error happens.
565
+ def exit_on_failure?
566
+ false
567
+ end
568
+
569
+ #
570
+ # The basename of the program invoking the thor class.
571
+ #
572
+ def basename
573
+ File.basename($0).split(' ').first
574
+ end
575
+
576
+ # SIGNATURE: Sets the baseclass. This is where the superclass lookup
577
+ # finishes.
578
+ def baseclass #:nodoc:
579
+ end
580
+
581
+ # SIGNATURE: Creates a new task if valid_task? is true. This method is
582
+ # called when a new method is added to the class.
583
+ def create_task(meth) #:nodoc:
584
+ end
585
+
586
+ # SIGNATURE: Defines behavior when the initialize method is added to the
587
+ # class.
588
+ def initialize_added #:nodoc:
589
+ end
590
+
591
+ # SIGNATURE: The hook invoked by start.
592
+ def dispatch(task, given_args, given_opts, config) #:nodoc:
593
+ raise NotImplementedError
594
+ end
595
+
596
+ end
597
+ end
598
+ end
@@ -0,0 +1,9 @@
1
+ class File #:nodoc:
2
+
3
+ unless File.respond_to?(:binread)
4
+ def self.binread(file)
5
+ File.open(file, 'rb') { |f| f.read }
6
+ end
7
+ end
8
+
9
+ end
@@ -0,0 +1,75 @@
1
+ class Thor
2
+ module CoreExt #:nodoc:
3
+
4
+ # A hash with indifferent access and magic predicates.
5
+ #
6
+ # hash = Thor::CoreExt::HashWithIndifferentAccess.new 'foo' => 'bar', 'baz' => 'bee', 'force' => true
7
+ #
8
+ # hash[:foo] #=> 'bar'
9
+ # hash['foo'] #=> 'bar'
10
+ # hash.foo? #=> true
11
+ #
12
+ class HashWithIndifferentAccess < ::Hash #:nodoc:
13
+
14
+ def initialize(hash={})
15
+ super()
16
+ hash.each do |key, value|
17
+ self[convert_key(key)] = value
18
+ end
19
+ end
20
+
21
+ def [](key)
22
+ super(convert_key(key))
23
+ end
24
+
25
+ def []=(key, value)
26
+ super(convert_key(key), value)
27
+ end
28
+
29
+ def delete(key)
30
+ super(convert_key(key))
31
+ end
32
+
33
+ def values_at(*indices)
34
+ indices.collect { |key| self[convert_key(key)] }
35
+ end
36
+
37
+ def merge(other)
38
+ dup.merge!(other)
39
+ end
40
+
41
+ def merge!(other)
42
+ other.each do |key, value|
43
+ self[convert_key(key)] = value
44
+ end
45
+ self
46
+ end
47
+
48
+ protected
49
+
50
+ def convert_key(key)
51
+ key.is_a?(Symbol) ? key.to_s : key
52
+ end
53
+
54
+ # Magic predicates. For instance:
55
+ #
56
+ # options.force? # => !!options['force']
57
+ # options.shebang # => "/usr/lib/local/ruby"
58
+ # options.test_framework?(:rspec) # => options[:test_framework] == :rspec
59
+ #
60
+ def method_missing(method, *args, &block)
61
+ method = method.to_s
62
+ if method =~ /^(\w+)\?$/
63
+ if args.empty?
64
+ !!self[$1]
65
+ else
66
+ self[$1] == args.first
67
+ end
68
+ else
69
+ self[method]
70
+ end
71
+ end
72
+
73
+ end
74
+ end
75
+ end