rkh-bundler 1.2.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (176) hide show
  1. data/.gitignore +22 -0
  2. data/.travis.yml +42 -0
  3. data/CHANGELOG.md +1105 -0
  4. data/ISSUES.md +67 -0
  5. data/LICENSE +23 -0
  6. data/README.md +31 -0
  7. data/Rakefile +208 -0
  8. data/UPGRADING.md +103 -0
  9. data/bin/bundle +31 -0
  10. data/bundler.gemspec +31 -0
  11. data/lib/bundler.rb +353 -0
  12. data/lib/bundler/capistrano.rb +11 -0
  13. data/lib/bundler/cli.rb +693 -0
  14. data/lib/bundler/definition.rb +568 -0
  15. data/lib/bundler/dep_proxy.rb +43 -0
  16. data/lib/bundler/dependency.rb +134 -0
  17. data/lib/bundler/deployment.rb +58 -0
  18. data/lib/bundler/dsl.rb +256 -0
  19. data/lib/bundler/endpoint_specification.rb +78 -0
  20. data/lib/bundler/environment.rb +47 -0
  21. data/lib/bundler/fetcher.rb +225 -0
  22. data/lib/bundler/gem_helper.rb +162 -0
  23. data/lib/bundler/gem_helpers.rb +23 -0
  24. data/lib/bundler/gem_installer.rb +9 -0
  25. data/lib/bundler/gem_tasks.rb +2 -0
  26. data/lib/bundler/graph.rb +148 -0
  27. data/lib/bundler/index.rb +187 -0
  28. data/lib/bundler/installer.rb +190 -0
  29. data/lib/bundler/lazy_specification.rb +79 -0
  30. data/lib/bundler/lockfile_parser.rb +127 -0
  31. data/lib/bundler/match_platform.rb +13 -0
  32. data/lib/bundler/psyched_yaml.rb +15 -0
  33. data/lib/bundler/remote_specification.rb +57 -0
  34. data/lib/bundler/resolver.rb +486 -0
  35. data/lib/bundler/ruby_version.rb +94 -0
  36. data/lib/bundler/rubygems_ext.rb +153 -0
  37. data/lib/bundler/rubygems_integration.rb +394 -0
  38. data/lib/bundler/runtime.rb +233 -0
  39. data/lib/bundler/settings.rb +128 -0
  40. data/lib/bundler/setup.rb +23 -0
  41. data/lib/bundler/shared_helpers.rb +71 -0
  42. data/lib/bundler/source.rb +869 -0
  43. data/lib/bundler/spec_set.rb +137 -0
  44. data/lib/bundler/templates/Executable +16 -0
  45. data/lib/bundler/templates/Executable.standalone +12 -0
  46. data/lib/bundler/templates/Gemfile +4 -0
  47. data/lib/bundler/templates/newgem/Gemfile.tt +4 -0
  48. data/lib/bundler/templates/newgem/LICENSE.tt +22 -0
  49. data/lib/bundler/templates/newgem/README.md.tt +29 -0
  50. data/lib/bundler/templates/newgem/Rakefile.tt +2 -0
  51. data/lib/bundler/templates/newgem/bin/newgem.tt +3 -0
  52. data/lib/bundler/templates/newgem/gitignore.tt +17 -0
  53. data/lib/bundler/templates/newgem/lib/newgem.rb.tt +9 -0
  54. data/lib/bundler/templates/newgem/lib/newgem/version.rb.tt +7 -0
  55. data/lib/bundler/templates/newgem/newgem.gemspec.tt +17 -0
  56. data/lib/bundler/ui.rb +88 -0
  57. data/lib/bundler/vendor/net/http/faster.rb +27 -0
  58. data/lib/bundler/vendor/net/http/persistent.rb +468 -0
  59. data/lib/bundler/vendor/thor.rb +358 -0
  60. data/lib/bundler/vendor/thor/actions.rb +314 -0
  61. data/lib/bundler/vendor/thor/actions/create_file.rb +105 -0
  62. data/lib/bundler/vendor/thor/actions/create_link.rb +57 -0
  63. data/lib/bundler/vendor/thor/actions/directory.rb +93 -0
  64. data/lib/bundler/vendor/thor/actions/empty_directory.rb +134 -0
  65. data/lib/bundler/vendor/thor/actions/file_manipulation.rb +270 -0
  66. data/lib/bundler/vendor/thor/actions/inject_into_file.rb +109 -0
  67. data/lib/bundler/vendor/thor/base.rb +576 -0
  68. data/lib/bundler/vendor/thor/core_ext/file_binary_read.rb +9 -0
  69. data/lib/bundler/vendor/thor/core_ext/hash_with_indifferent_access.rb +75 -0
  70. data/lib/bundler/vendor/thor/core_ext/ordered_hash.rb +100 -0
  71. data/lib/bundler/vendor/thor/error.rb +30 -0
  72. data/lib/bundler/vendor/thor/group.rb +273 -0
  73. data/lib/bundler/vendor/thor/invocation.rb +168 -0
  74. data/lib/bundler/vendor/thor/parser.rb +4 -0
  75. data/lib/bundler/vendor/thor/parser/argument.rb +67 -0
  76. data/lib/bundler/vendor/thor/parser/arguments.rb +161 -0
  77. data/lib/bundler/vendor/thor/parser/option.rb +120 -0
  78. data/lib/bundler/vendor/thor/parser/options.rb +172 -0
  79. data/lib/bundler/vendor/thor/rake_compat.rb +66 -0
  80. data/lib/bundler/vendor/thor/runner.rb +309 -0
  81. data/lib/bundler/vendor/thor/shell.rb +88 -0
  82. data/lib/bundler/vendor/thor/shell/basic.rb +302 -0
  83. data/lib/bundler/vendor/thor/shell/color.rb +108 -0
  84. data/lib/bundler/vendor/thor/shell/html.rb +121 -0
  85. data/lib/bundler/vendor/thor/task.rb +113 -0
  86. data/lib/bundler/vendor/thor/util.rb +229 -0
  87. data/lib/bundler/vendor/thor/version.rb +3 -0
  88. data/lib/bundler/vendored_thor.rb +7 -0
  89. data/lib/bundler/version.rb +6 -0
  90. data/lib/bundler/vlad.rb +11 -0
  91. data/man/bundle-config.ronn +130 -0
  92. data/man/bundle-exec.ronn +111 -0
  93. data/man/bundle-install.ronn +335 -0
  94. data/man/bundle-package.ronn +59 -0
  95. data/man/bundle-update.ronn +176 -0
  96. data/man/bundle.ronn +83 -0
  97. data/man/gemfile.5.ronn +324 -0
  98. data/man/index.txt +6 -0
  99. data/spec/bundler/dsl_spec.rb +48 -0
  100. data/spec/bundler/gem_helper_spec.rb +174 -0
  101. data/spec/bundler/source_spec.rb +25 -0
  102. data/spec/cache/gems_spec.rb +239 -0
  103. data/spec/cache/git_spec.rb +124 -0
  104. data/spec/cache/path_spec.rb +103 -0
  105. data/spec/cache/platform_spec.rb +57 -0
  106. data/spec/install/deploy_spec.rb +211 -0
  107. data/spec/install/gems/c_ext_spec.rb +48 -0
  108. data/spec/install/gems/dependency_api_spec.rb +402 -0
  109. data/spec/install/gems/env_spec.rb +107 -0
  110. data/spec/install/gems/flex_spec.rb +313 -0
  111. data/spec/install/gems/groups_spec.rb +268 -0
  112. data/spec/install/gems/packed_spec.rb +84 -0
  113. data/spec/install/gems/platform_spec.rb +208 -0
  114. data/spec/install/gems/post_install_spec.rb +47 -0
  115. data/spec/install/gems/resolving_spec.rb +72 -0
  116. data/spec/install/gems/simple_case_spec.rb +814 -0
  117. data/spec/install/gems/standalone_spec.rb +260 -0
  118. data/spec/install/gems/sudo_spec.rb +74 -0
  119. data/spec/install/gems/win32_spec.rb +26 -0
  120. data/spec/install/gemspec_spec.rb +170 -0
  121. data/spec/install/git_spec.rb +796 -0
  122. data/spec/install/invalid_spec.rb +35 -0
  123. data/spec/install/path_spec.rb +405 -0
  124. data/spec/install/upgrade_spec.rb +26 -0
  125. data/spec/lock/git_spec.rb +35 -0
  126. data/spec/lock/lockfile_spec.rb +809 -0
  127. data/spec/other/check_spec.rb +265 -0
  128. data/spec/other/clean_spec.rb +492 -0
  129. data/spec/other/config_spec.rb +138 -0
  130. data/spec/other/console_spec.rb +54 -0
  131. data/spec/other/exec_spec.rb +229 -0
  132. data/spec/other/ext_spec.rb +37 -0
  133. data/spec/other/help_spec.rb +39 -0
  134. data/spec/other/init_spec.rb +40 -0
  135. data/spec/other/newgem_spec.rb +87 -0
  136. data/spec/other/open_spec.rb +35 -0
  137. data/spec/other/outdated_spec.rb +93 -0
  138. data/spec/other/platform_spec.rb +881 -0
  139. data/spec/other/show_spec.rb +88 -0
  140. data/spec/quality_spec.rb +62 -0
  141. data/spec/realworld/edgecases_spec.rb +177 -0
  142. data/spec/resolver/basic_spec.rb +20 -0
  143. data/spec/resolver/platform_spec.rb +82 -0
  144. data/spec/runtime/executable_spec.rb +120 -0
  145. data/spec/runtime/load_spec.rb +107 -0
  146. data/spec/runtime/platform_spec.rb +90 -0
  147. data/spec/runtime/require_spec.rb +261 -0
  148. data/spec/runtime/setup_spec.rb +755 -0
  149. data/spec/runtime/with_clean_env_spec.rb +80 -0
  150. data/spec/spec_helper.rb +98 -0
  151. data/spec/support/artifice/endopint_marshal_fail_basic_authentication.rb +13 -0
  152. data/spec/support/artifice/endpoint.rb +54 -0
  153. data/spec/support/artifice/endpoint_500.rb +37 -0
  154. data/spec/support/artifice/endpoint_api_missing.rb +16 -0
  155. data/spec/support/artifice/endpoint_basic_authentication.rb +13 -0
  156. data/spec/support/artifice/endpoint_extra.rb +27 -0
  157. data/spec/support/artifice/endpoint_extra_missing.rb +15 -0
  158. data/spec/support/artifice/endpoint_fallback.rb +18 -0
  159. data/spec/support/artifice/endpoint_marshal_fail.rb +11 -0
  160. data/spec/support/artifice/endpoint_redirect.rb +15 -0
  161. data/spec/support/builders.rb +604 -0
  162. data/spec/support/fakeweb/rack-1.0.0.marshal +2 -0
  163. data/spec/support/fakeweb/windows.rb +23 -0
  164. data/spec/support/helpers.rb +317 -0
  165. data/spec/support/indexes.rb +112 -0
  166. data/spec/support/matchers.rb +77 -0
  167. data/spec/support/path.rb +73 -0
  168. data/spec/support/platforms.rb +86 -0
  169. data/spec/support/ruby_ext.rb +20 -0
  170. data/spec/support/rubygems_ext.rb +37 -0
  171. data/spec/support/rubygems_hax/platform.rb +22 -0
  172. data/spec/support/sudo.rb +21 -0
  173. data/spec/update/gems_spec.rb +134 -0
  174. data/spec/update/git_spec.rb +196 -0
  175. data/spec/update/source_spec.rb +51 -0
  176. metadata +338 -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
@@ -0,0 +1,576 @@
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 run_ruby_script)
20
+
21
+ module Base
22
+ attr_accessor :options
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
+ args = Thor::Arguments.parse(self.class.arguments, args)
42
+ args.each { |key, value| send("#{key}=", value) }
43
+
44
+ parse_options = self.class.class_options
45
+
46
+ if options.is_a?(Array)
47
+ task_options = config.delete(:task_options) # hook for start
48
+ parse_options = parse_options.merge(task_options) if task_options
49
+ array_options, hash_options = options, {}
50
+ else
51
+ array_options, hash_options = [], options
52
+ end
53
+
54
+ opts = Thor::Options.new(parse_options, hash_options)
55
+ self.options = opts.parse(array_options)
56
+ opts.check_unknown! if self.class.check_unknown_options?(config)
57
+ end
58
+
59
+ class << self
60
+ def included(base) #:nodoc:
61
+ base.send :extend, ClassMethods
62
+ base.send :include, Invocation
63
+ base.send :include, Shell
64
+ end
65
+
66
+ # Returns the classes that inherits from Thor or Thor::Group.
67
+ #
68
+ # ==== Returns
69
+ # Array[Class]
70
+ #
71
+ def subclasses
72
+ @subclasses ||= []
73
+ end
74
+
75
+ # Returns the files where the subclasses are kept.
76
+ #
77
+ # ==== Returns
78
+ # Hash[path<String> => Class]
79
+ #
80
+ def subclass_files
81
+ @subclass_files ||= Hash.new{ |h,k| h[k] = [] }
82
+ end
83
+
84
+ # Whenever a class inherits from Thor or Thor::Group, we should track the
85
+ # class and the file on Thor::Base. This is the method responsable for it.
86
+ #
87
+ def register_klass_file(klass) #:nodoc:
88
+ file = caller[1].match(/(.*):\d+/)[1]
89
+ Thor::Base.subclasses << klass unless Thor::Base.subclasses.include?(klass)
90
+
91
+ file_subclasses = Thor::Base.subclass_files[File.expand_path(file)]
92
+ file_subclasses << klass unless file_subclasses.include?(klass)
93
+ end
94
+ end
95
+
96
+ module ClassMethods
97
+ def attr_reader(*) #:nodoc:
98
+ no_tasks { super }
99
+ end
100
+
101
+ def attr_writer(*) #:nodoc:
102
+ no_tasks { super }
103
+ end
104
+
105
+ def attr_accessor(*) #:nodoc:
106
+ no_tasks { super }
107
+ end
108
+
109
+ # If you want to raise an error for unknown options, call check_unknown_options!
110
+ # This is disabled by default to allow dynamic invocations.
111
+ def check_unknown_options!
112
+ @check_unknown_options = true
113
+ end
114
+
115
+ def check_unknown_options #:nodoc:
116
+ @check_unknown_options ||= from_superclass(:check_unknown_options, false)
117
+ end
118
+
119
+ def check_unknown_options?(config) #:nodoc:
120
+ !!check_unknown_options
121
+ end
122
+
123
+ # Adds an argument to the class and creates an attr_accessor for it.
124
+ #
125
+ # Arguments are different from options in several aspects. The first one
126
+ # is how they are parsed from the command line, arguments are retrieved
127
+ # from position:
128
+ #
129
+ # thor task NAME
130
+ #
131
+ # Instead of:
132
+ #
133
+ # thor task --name=NAME
134
+ #
135
+ # Besides, arguments are used inside your code as an accessor (self.argument),
136
+ # while options are all kept in a hash (self.options).
137
+ #
138
+ # Finally, arguments cannot have type :default or :boolean but can be
139
+ # optional (supplying :optional => :true or :required => false), although
140
+ # you cannot have a required argument after a non-required argument. If you
141
+ # try it, an error is raised.
142
+ #
143
+ # ==== Parameters
144
+ # name<Symbol>:: The name of the argument.
145
+ # options<Hash>:: Described below.
146
+ #
147
+ # ==== Options
148
+ # :desc - Description for the argument.
149
+ # :required - If the argument is required or not.
150
+ # :optional - If the argument is optional or not.
151
+ # :type - The type of the argument, can be :string, :hash, :array, :numeric.
152
+ # :default - Default value for this argument. It cannot be required and have default values.
153
+ # :banner - String to show on usage notes.
154
+ #
155
+ # ==== Errors
156
+ # ArgumentError:: Raised if you supply a required argument after a non required one.
157
+ #
158
+ def argument(name, options={})
159
+ is_thor_reserved_word?(name, :argument)
160
+ no_tasks { attr_accessor name }
161
+
162
+ required = if options.key?(:optional)
163
+ !options[:optional]
164
+ elsif options.key?(:required)
165
+ options[:required]
166
+ else
167
+ options[:default].nil?
168
+ end
169
+
170
+ remove_argument name
171
+
172
+ arguments.each do |argument|
173
+ next if argument.required?
174
+ raise ArgumentError, "You cannot have #{name.to_s.inspect} as required argument after " <<
175
+ "the non-required argument #{argument.human_name.inspect}."
176
+ end if required
177
+
178
+ arguments << Thor::Argument.new(name, options[:desc], required, options[:type],
179
+ options[:default], options[:banner])
180
+ end
181
+
182
+ # Returns this class arguments, looking up in the ancestors chain.
183
+ #
184
+ # ==== Returns
185
+ # Array[Thor::Argument]
186
+ #
187
+ def arguments
188
+ @arguments ||= from_superclass(:arguments, [])
189
+ end
190
+
191
+ # Adds a bunch of options to the set of class options.
192
+ #
193
+ # class_options :foo => false, :bar => :required, :baz => :string
194
+ #
195
+ # If you prefer more detailed declaration, check class_option.
196
+ #
197
+ # ==== Parameters
198
+ # Hash[Symbol => Object]
199
+ #
200
+ def class_options(options=nil)
201
+ @class_options ||= from_superclass(:class_options, {})
202
+ build_options(options, @class_options) if options
203
+ @class_options
204
+ end
205
+
206
+ # Adds an option to the set of class options
207
+ #
208
+ # ==== Parameters
209
+ # name<Symbol>:: The name of the argument.
210
+ # options<Hash>:: Described below.
211
+ #
212
+ # ==== Options
213
+ # :desc - Description for the argument.
214
+ # :required - If the argument is required or not.
215
+ # :default - Default value for this argument.
216
+ # :group - The group for this options. Use by class options to output options in different levels.
217
+ # :aliases - Aliases for this option.
218
+ # :type - The type of the argument, can be :string, :hash, :array, :numeric or :boolean.
219
+ # :banner - String to show on usage notes.
220
+ #
221
+ def class_option(name, options={})
222
+ build_option(name, options, class_options)
223
+ end
224
+
225
+ # Removes a previous defined argument. If :undefine is given, undefine
226
+ # accessors as well.
227
+ #
228
+ # ==== Paremeters
229
+ # names<Array>:: Arguments to be removed
230
+ #
231
+ # ==== Examples
232
+ #
233
+ # remove_argument :foo
234
+ # remove_argument :foo, :bar, :baz, :undefine => true
235
+ #
236
+ def remove_argument(*names)
237
+ options = names.last.is_a?(Hash) ? names.pop : {}
238
+
239
+ names.each do |name|
240
+ arguments.delete_if { |a| a.name == name.to_s }
241
+ undef_method name, "#{name}=" if options[:undefine]
242
+ end
243
+ end
244
+
245
+ # Removes a previous defined class option.
246
+ #
247
+ # ==== Paremeters
248
+ # names<Array>:: Class options to be removed
249
+ #
250
+ # ==== Examples
251
+ #
252
+ # remove_class_option :foo
253
+ # remove_class_option :foo, :bar, :baz
254
+ #
255
+ def remove_class_option(*names)
256
+ names.each do |name|
257
+ class_options.delete(name)
258
+ end
259
+ end
260
+
261
+ # Defines the group. This is used when thor list is invoked so you can specify
262
+ # that only tasks from a pre-defined group will be shown. Defaults to standard.
263
+ #
264
+ # ==== Parameters
265
+ # name<String|Symbol>
266
+ #
267
+ def group(name=nil)
268
+ case name
269
+ when nil
270
+ @group ||= from_superclass(:group, 'standard')
271
+ else
272
+ @group = name.to_s
273
+ end
274
+ end
275
+
276
+ # Returns the tasks for this Thor class.
277
+ #
278
+ # ==== Returns
279
+ # OrderedHash:: An ordered hash with tasks names as keys and Thor::Task
280
+ # objects as values.
281
+ #
282
+ def tasks
283
+ @tasks ||= Thor::CoreExt::OrderedHash.new
284
+ end
285
+
286
+ # Returns the tasks for this Thor class and all subclasses.
287
+ #
288
+ # ==== Returns
289
+ # OrderedHash:: An ordered hash with tasks names as keys and Thor::Task
290
+ # objects as values.
291
+ #
292
+ def all_tasks
293
+ @all_tasks ||= from_superclass(:all_tasks, Thor::CoreExt::OrderedHash.new)
294
+ @all_tasks.merge(tasks)
295
+ end
296
+
297
+ # Removes a given task from this Thor class. This is usually done if you
298
+ # are inheriting from another class and don't want it to be available
299
+ # anymore.
300
+ #
301
+ # By default it only remove the mapping to the task. But you can supply
302
+ # :undefine => true to undefine the method from the class as well.
303
+ #
304
+ # ==== Parameters
305
+ # name<Symbol|String>:: The name of the task to be removed
306
+ # options<Hash>:: You can give :undefine => true if you want tasks the method
307
+ # to be undefined from the class as well.
308
+ #
309
+ def remove_task(*names)
310
+ options = names.last.is_a?(Hash) ? names.pop : {}
311
+
312
+ names.each do |name|
313
+ tasks.delete(name.to_s)
314
+ all_tasks.delete(name.to_s)
315
+ undef_method name if options[:undefine]
316
+ end
317
+ end
318
+
319
+ # All methods defined inside the given block are not added as tasks.
320
+ #
321
+ # So you can do:
322
+ #
323
+ # class MyScript < Thor
324
+ # no_tasks do
325
+ # def this_is_not_a_task
326
+ # end
327
+ # end
328
+ # end
329
+ #
330
+ # You can also add the method and remove it from the task list:
331
+ #
332
+ # class MyScript < Thor
333
+ # def this_is_not_a_task
334
+ # end
335
+ # remove_task :this_is_not_a_task
336
+ # end
337
+ #
338
+ def no_tasks
339
+ @no_tasks = true
340
+ yield
341
+ ensure
342
+ @no_tasks = false
343
+ end
344
+
345
+ # Sets the namespace for the Thor or Thor::Group class. By default the
346
+ # namespace is retrieved from the class name. If your Thor class is named
347
+ # Scripts::MyScript, the help method, for example, will be called as:
348
+ #
349
+ # thor scripts:my_script -h
350
+ #
351
+ # If you change the namespace:
352
+ #
353
+ # namespace :my_scripts
354
+ #
355
+ # You change how your tasks are invoked:
356
+ #
357
+ # thor my_scripts -h
358
+ #
359
+ # Finally, if you change your namespace to default:
360
+ #
361
+ # namespace :default
362
+ #
363
+ # Your tasks can be invoked with a shortcut. Instead of:
364
+ #
365
+ # thor :my_task
366
+ #
367
+ def namespace(name=nil)
368
+ case name
369
+ when nil
370
+ @namespace ||= Thor::Util.namespace_from_thor_class(self)
371
+ else
372
+ @namespace = name.to_s
373
+ end
374
+ end
375
+
376
+ # Parses the task and options from the given args, instantiate the class
377
+ # and invoke the task. This method is used when the arguments must be parsed
378
+ # from an array. If you are inside Ruby and want to use a Thor class, you
379
+ # can simply initialize it:
380
+ #
381
+ # script = MyScript.new(args, options, config)
382
+ # script.invoke(:task, first_arg, second_arg, third_arg)
383
+ #
384
+ def start(given_args=ARGV, config={})
385
+ config[:shell] ||= Thor::Base.shell.new
386
+ dispatch(nil, given_args.dup, nil, config)
387
+ rescue Thor::Error => e
388
+ ENV["THOR_DEBUG"] == "1" ? (raise e) : config[:shell].error(e.message)
389
+ exit(1) if exit_on_failure?
390
+ end
391
+
392
+ # Allows to use private methods from parent in child classes as tasks.
393
+ #
394
+ # ==== Paremeters
395
+ # names<Array>:: Method names to be used as tasks
396
+ #
397
+ # ==== Examples
398
+ #
399
+ # public_task :foo
400
+ # public_task :foo, :bar, :baz
401
+ #
402
+ def public_task(*names)
403
+ names.each do |name|
404
+ class_eval "def #{name}(*); super end"
405
+ end
406
+ end
407
+
408
+ def handle_no_task_error(task) #:nodoc:
409
+ if $thor_runner
410
+ raise UndefinedTaskError, "Could not find task #{task.inspect} in #{namespace.inspect} namespace."
411
+ else
412
+ raise UndefinedTaskError, "Could not find task #{task.inspect}."
413
+ end
414
+ end
415
+
416
+ def handle_argument_error(task, error) #:nodoc:
417
+ raise InvocationError, "#{task.name.inspect} was called incorrectly. Call as #{self.banner(task).inspect}."
418
+ end
419
+
420
+ protected
421
+
422
+ # Prints the class options per group. If an option does not belong to
423
+ # any group, it's printed as Class option.
424
+ #
425
+ def class_options_help(shell, groups={}) #:nodoc:
426
+ # Group options by group
427
+ class_options.each do |_, value|
428
+ groups[value.group] ||= []
429
+ groups[value.group] << value
430
+ end
431
+
432
+ # Deal with default group
433
+ global_options = groups.delete(nil) || []
434
+ print_options(shell, global_options)
435
+
436
+ # Print all others
437
+ groups.each do |group_name, options|
438
+ print_options(shell, options, group_name)
439
+ end
440
+ end
441
+
442
+ # Receives a set of options and print them.
443
+ def print_options(shell, options, group_name=nil)
444
+ return if options.empty?
445
+
446
+ list = []
447
+ padding = options.collect{ |o| o.aliases.size }.max.to_i * 4
448
+
449
+ options.each do |option|
450
+ item = [ option.usage(padding) ]
451
+ item.push(option.description ? "# #{option.description}" : "")
452
+
453
+ list << item
454
+ list << [ "", "# Default: #{option.default}" ] if option.show_default?
455
+ end
456
+
457
+ shell.say(group_name ? "#{group_name} options:" : "Options:")
458
+ shell.print_table(list, :ident => 2)
459
+ shell.say ""
460
+ end
461
+
462
+ # Raises an error if the word given is a Thor reserved word.
463
+ def is_thor_reserved_word?(word, type) #:nodoc:
464
+ return false unless THOR_RESERVED_WORDS.include?(word.to_s)
465
+ raise "#{word.inspect} is a Thor reserved word and cannot be defined as #{type}"
466
+ end
467
+
468
+ # Build an option and adds it to the given scope.
469
+ #
470
+ # ==== Parameters
471
+ # name<Symbol>:: The name of the argument.
472
+ # options<Hash>:: Described in both class_option and method_option.
473
+ def build_option(name, options, scope) #:nodoc:
474
+ scope[name] = Thor::Option.new(name, options[:desc], options[:required],
475
+ options[:type], options[:default], options[:banner],
476
+ options[:lazy_default], options[:group], options[:aliases])
477
+ end
478
+
479
+ # Receives a hash of options, parse them and add to the scope. This is a
480
+ # fast way to set a bunch of options:
481
+ #
482
+ # build_options :foo => true, :bar => :required, :baz => :string
483
+ #
484
+ # ==== Parameters
485
+ # Hash[Symbol => Object]
486
+ def build_options(options, scope) #:nodoc:
487
+ options.each do |key, value|
488
+ scope[key] = Thor::Option.parse(key, value)
489
+ end
490
+ end
491
+
492
+ # Finds a task with the given name. If the task belongs to the current
493
+ # class, just return it, otherwise dup it and add the fresh copy to the
494
+ # current task hash.
495
+ def find_and_refresh_task(name) #:nodoc:
496
+ task = if task = tasks[name.to_s]
497
+ task
498
+ elsif task = all_tasks[name.to_s]
499
+ tasks[name.to_s] = task.clone
500
+ else
501
+ raise ArgumentError, "You supplied :for => #{name.inspect}, but the task #{name.inspect} could not be found."
502
+ end
503
+ end
504
+
505
+ # Everytime someone inherits from a Thor class, register the klass
506
+ # and file into baseclass.
507
+ def inherited(klass)
508
+ Thor::Base.register_klass_file(klass)
509
+ end
510
+
511
+ # Fire this callback whenever a method is added. Added methods are
512
+ # tracked as tasks by invoking the create_task method.
513
+ def method_added(meth)
514
+ meth = meth.to_s
515
+
516
+ if meth == "initialize"
517
+ initialize_added
518
+ return
519
+ end
520
+
521
+ # Return if it's not a public instance method
522
+ return unless public_instance_methods.include?(meth) ||
523
+ public_instance_methods.include?(meth.to_sym)
524
+
525
+ return if @no_tasks || !create_task(meth)
526
+
527
+ is_thor_reserved_word?(meth, :task)
528
+ Thor::Base.register_klass_file(self)
529
+ end
530
+
531
+ # Retrieves a value from superclass. If it reaches the baseclass,
532
+ # returns default.
533
+ def from_superclass(method, default=nil)
534
+ if self == baseclass || !superclass.respond_to?(method, true)
535
+ default
536
+ else
537
+ value = superclass.send(method)
538
+ value.dup if value
539
+ end
540
+ end
541
+
542
+ # A flag that makes the process exit with status 1 if any error happens.
543
+ def exit_on_failure?
544
+ false
545
+ end
546
+
547
+ #
548
+ # The basename of the program invoking the thor class.
549
+ #
550
+ def basename
551
+ File.basename($0).split(' ').first
552
+ end
553
+
554
+ # SIGNATURE: Sets the baseclass. This is where the superclass lookup
555
+ # finishes.
556
+ def baseclass #:nodoc:
557
+ end
558
+
559
+ # SIGNATURE: Creates a new task if valid_task? is true. This method is
560
+ # called when a new method is added to the class.
561
+ def create_task(meth) #:nodoc:
562
+ end
563
+
564
+ # SIGNATURE: Defines behavior when the initialize method is added to the
565
+ # class.
566
+ def initialize_added #:nodoc:
567
+ end
568
+
569
+ # SIGNATURE: The hook invoked by start.
570
+ def dispatch(task, given_args, given_opts, config) #:nodoc:
571
+ raise NotImplementedError
572
+ end
573
+
574
+ end
575
+ end
576
+ end