thor-exclude_pattern 0.18.0

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 (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