thor-exclude_pattern 0.18.0

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