bundler 1.0.22 → 1.1.pre

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of bundler might be problematic. Click here for more details.

Files changed (114) hide show
  1. data/CHANGELOG.md +15 -181
  2. data/ISSUES.md +11 -26
  3. data/LICENSE +0 -2
  4. data/Rakefile +67 -116
  5. data/UPGRADING.md +4 -4
  6. data/bin/bundle +10 -10
  7. data/bundler.gemspec +5 -5
  8. data/lib/bundler.rb +15 -25
  9. data/lib/bundler/capistrano.rb +2 -2
  10. data/lib/bundler/cli.rb +41 -72
  11. data/lib/bundler/definition.rb +11 -37
  12. data/lib/bundler/dependency.rb +9 -13
  13. data/lib/bundler/deployment.rb +3 -8
  14. data/lib/bundler/dsl.rb +10 -24
  15. data/lib/bundler/fetcher.rb +101 -0
  16. data/lib/bundler/gem_helper.rb +6 -11
  17. data/lib/bundler/index.rb +8 -15
  18. data/lib/bundler/installer.rb +55 -35
  19. data/lib/bundler/lazy_specification.rb +7 -10
  20. data/lib/bundler/remote_specification.rb +6 -8
  21. data/lib/bundler/resolver.rb +18 -12
  22. data/lib/bundler/rubygems_ext.rb +3 -20
  23. data/lib/bundler/runtime.rb +57 -8
  24. data/lib/bundler/settings.rb +6 -4
  25. data/lib/bundler/setup.rb +5 -12
  26. data/lib/bundler/shared_helpers.rb +100 -4
  27. data/lib/bundler/source.rb +61 -106
  28. data/lib/bundler/spec_set.rb +8 -9
  29. data/lib/bundler/templates/newgem/Rakefile.tt +2 -1
  30. data/lib/bundler/templates/newgem/lib/newgem.rb.tt +0 -2
  31. data/lib/bundler/templates/newgem/newgem.gemspec.tt +3 -6
  32. data/lib/bundler/ui.rb +1 -4
  33. data/lib/bundler/vendor/net/http/faster.rb +27 -0
  34. data/lib/bundler/vendor/net/http/persistent.rb +464 -0
  35. data/lib/bundler/vendor/thor.rb +4 -43
  36. data/lib/bundler/vendor/thor/actions.rb +11 -28
  37. data/lib/bundler/vendor/thor/actions/create_file.rb +2 -2
  38. data/lib/bundler/vendor/thor/actions/directory.rb +2 -2
  39. data/lib/bundler/vendor/thor/actions/empty_directory.rb +0 -0
  40. data/lib/bundler/vendor/thor/actions/file_manipulation.rb +15 -56
  41. data/lib/bundler/vendor/thor/actions/inject_into_file.rb +10 -15
  42. data/lib/bundler/vendor/thor/base.rb +4 -24
  43. data/lib/bundler/vendor/thor/core_ext/file_binary_read.rb +0 -0
  44. data/lib/bundler/vendor/thor/core_ext/hash_with_indifferent_access.rb +0 -0
  45. data/lib/bundler/vendor/thor/core_ext/ordered_hash.rb +0 -0
  46. data/lib/bundler/vendor/thor/error.rb +0 -0
  47. data/lib/bundler/vendor/thor/invocation.rb +0 -0
  48. data/lib/bundler/vendor/thor/parser.rb +0 -0
  49. data/lib/bundler/vendor/thor/parser/argument.rb +0 -0
  50. data/lib/bundler/vendor/thor/parser/arguments.rb +2 -2
  51. data/lib/bundler/vendor/thor/parser/option.rb +1 -1
  52. data/lib/bundler/vendor/thor/parser/options.rb +16 -17
  53. data/lib/bundler/vendor/thor/shell.rb +0 -0
  54. data/lib/bundler/vendor/thor/shell/basic.rb +13 -40
  55. data/lib/bundler/vendor/thor/shell/color.rb +0 -0
  56. data/lib/bundler/vendor/thor/task.rb +4 -3
  57. data/lib/bundler/vendor/thor/util.rb +2 -2
  58. data/lib/bundler/vendor/thor/version.rb +1 -1
  59. data/lib/bundler/version.rb +1 -1
  60. data/lib/bundler/vlad.rb +0 -2
  61. data/man/bundle-install.ronn +3 -6
  62. data/man/gemfile.5.ronn +2 -7
  63. data/spec/cache/gems_spec.rb +0 -11
  64. data/spec/cache/git_spec.rb +2 -5
  65. data/spec/install/deploy_spec.rb +8 -8
  66. data/spec/install/gems/dependency_api_spec.rb +85 -0
  67. data/spec/install/gems/flex_spec.rb +4 -4
  68. data/spec/install/gems/groups_spec.rb +3 -17
  69. data/spec/install/gems/platform_spec.rb +16 -0
  70. data/spec/install/gems/simple_case_spec.rb +24 -88
  71. data/spec/install/gems/standalone_spec.rb +162 -0
  72. data/spec/install/gems/sudo_spec.rb +2 -3
  73. data/spec/install/gemspec_spec.rb +0 -38
  74. data/spec/install/git_spec.rb +4 -3
  75. data/spec/install/invalid_spec.rb +0 -18
  76. data/spec/install/path_spec.rb +1 -53
  77. data/spec/lock/git_spec.rb +1 -1
  78. data/spec/lock/lockfile_spec.rb +16 -72
  79. data/spec/other/check_spec.rb +6 -6
  80. data/spec/other/clean_spec.rb +202 -0
  81. data/spec/other/exec_spec.rb +7 -14
  82. data/spec/other/ext_spec.rb +0 -21
  83. data/spec/{bundler → other}/gem_helper_spec.rb +1 -16
  84. data/spec/other/help_spec.rb +1 -2
  85. data/spec/other/init_spec.rb +3 -3
  86. data/spec/other/newgem_spec.rb +1 -23
  87. data/spec/pack/gems_spec.rb +22 -0
  88. data/spec/quality_spec.rb +2 -6
  89. data/spec/runtime/require_spec.rb +10 -10
  90. data/spec/runtime/setup_spec.rb +12 -54
  91. data/spec/spec_helper.rb +7 -14
  92. data/spec/support/artifice/endpoint.rb +50 -0
  93. data/spec/support/artifice/endpoint_fallback.rb +22 -0
  94. data/spec/support/artifice/endpoint_marshal_fail.rb +11 -0
  95. data/spec/support/artifice/endpoint_redirect.rb +11 -0
  96. data/spec/support/builders.rb +2 -25
  97. data/spec/support/fakeweb/rack-1.0.0.marshal +2 -0
  98. data/spec/support/fakeweb/windows.rb +23 -0
  99. data/spec/support/helpers.rb +12 -5
  100. data/spec/support/matchers.rb +16 -4
  101. data/spec/support/path.rb +2 -0
  102. data/spec/support/rubygems_ext.rb +2 -6
  103. data/spec/update/gems_spec.rb +2 -3
  104. data/spec/update/git_spec.rb +2 -2
  105. metadata +41 -24
  106. data/.travis.yml +0 -31
  107. data/lib/bundler/gem_installer.rb +0 -9
  108. data/lib/bundler/gem_tasks.rb +0 -2
  109. data/lib/bundler/rubygems_integration.rb +0 -344
  110. data/lib/bundler/vendor/thor/actions/create_link.rb +0 -57
  111. data/lib/bundler/vendor/thor/group.rb +0 -273
  112. data/lib/bundler/vendor/thor/rake_compat.rb +0 -66
  113. data/lib/bundler/vendor/thor/runner.rb +0 -309
  114. data/lib/bundler/vendored_thor.rb +0 -7
@@ -1,31 +0,0 @@
1
- before_script:
2
- - sudo apt-get install groff -y
3
- - rake spec:deps
4
-
5
- script: rake spec:travis
6
-
7
- rvm:
8
- - 1.8.7
9
- - 1.9.2
10
-
11
- env:
12
- - RGV=v1.3.6
13
- - RGV=v1.3.7
14
- - RGV=v1.4.2
15
- - RGV=v1.5.3
16
- - RGV=v1.6.2
17
- - RGV=v1.7.2
18
- - RGV=v1.8.10
19
-
20
- matrix:
21
- exclude:
22
- - rvm: 1.9.2
23
- env: RGV=v1.3.6
24
- - rvm: 1.9.2
25
- env: RGV=v1.3.7
26
- - rvm: 1.9.2
27
- env: RGV=v1.4.2
28
-
29
- notifications:
30
- email:
31
- - travis-ci@andrearko.com
@@ -1,9 +0,0 @@
1
- require 'rubygems/installer'
2
-
3
- module Bundler
4
- class GemInstaller < Gem::Installer
5
- def check_executable_overwrite(filename)
6
- # Bundler needs to install gems regardless of binstub overwriting
7
- end
8
- end
9
- end
@@ -1,2 +0,0 @@
1
- require 'bundler/gem_helper'
2
- Bundler::GemHelper.install_tasks
@@ -1,344 +0,0 @@
1
- module Bundler
2
- class RubygemsIntegration
3
- def initialize
4
- # Work around a RubyGems bug
5
- configuration
6
- end
7
-
8
- def loaded_specs(name)
9
- Gem.loaded_specs[name]
10
- end
11
-
12
- def mark_loaded(spec)
13
- Gem.loaded_specs[spec.name] = spec
14
- end
15
-
16
- def path(obj)
17
- obj.to_s
18
- end
19
-
20
- def platforms
21
- Gem.platforms
22
- end
23
-
24
- def configuration
25
- Gem.configuration
26
- end
27
-
28
- def ruby_engine
29
- Gem.ruby_engine
30
- end
31
-
32
- def read_binary(path)
33
- Gem.read_binary(path)
34
- end
35
-
36
- def inflate(obj)
37
- Gem.inflate(obj)
38
- end
39
-
40
- def sources=(val)
41
- Gem.sources = val
42
- end
43
-
44
- def sources
45
- Gem.sources
46
- end
47
-
48
- def gem_dir
49
- Gem.dir
50
- end
51
-
52
- def gem_bindir
53
- Gem.bindir
54
- end
55
-
56
- def user_home
57
- Gem.user_home
58
- end
59
-
60
- def gem_path
61
- Gem.path
62
- end
63
-
64
- def marshal_spec_dir
65
- Gem::MARSHAL_SPEC_DIR
66
- end
67
-
68
- def clear_paths
69
- Gem.clear_paths
70
- end
71
-
72
- def bin_path(gem, bin, ver)
73
- Gem.bin_path(gem, bin, ver)
74
- end
75
-
76
- def preserve_paths
77
- # this is a no-op outside of Rubygems 1.8
78
- yield
79
- end
80
-
81
- def ui=(obj)
82
- Gem::DefaultUserInteraction.ui = obj
83
- end
84
-
85
- def fetch_specs(all, pre, &blk)
86
- Gem::SpecFetcher.new.list(all, pre).each(&blk)
87
- end
88
-
89
- def with_build_args(args)
90
- old_args = Gem::Command.build_args
91
- begin
92
- Gem::Command.build_args = args
93
- yield
94
- ensure
95
- Gem::Command.build_args = old_args
96
- end
97
- end
98
-
99
- def spec_from_gem(path)
100
- Gem::Format.from_file_by_path(path).spec
101
- end
102
-
103
- def download_gem(spec, uri, path)
104
- Gem::RemoteFetcher.fetcher.download(spec, uri, path)
105
- end
106
-
107
- def reverse_rubygems_kernel_mixin
108
- # Disable rubygems' gem activation system
109
- ::Kernel.class_eval do
110
- if private_method_defined?(:gem_original_require)
111
- alias rubygems_require require
112
- alias require gem_original_require
113
- end
114
-
115
- undef gem
116
- end
117
- end
118
-
119
- def replace_gem(specs)
120
- executables = specs.map { |s| s.executables }.flatten
121
-
122
- ::Kernel.send(:define_method, :gem) do |dep, *reqs|
123
- if executables.include? File.basename(caller.first.split(':').first)
124
- return
125
- end
126
- reqs.pop if reqs.last.is_a?(Hash)
127
-
128
- unless dep.respond_to?(:name) && dep.respond_to?(:requirement)
129
- dep = Gem::Dependency.new(dep, reqs)
130
- end
131
-
132
- spec = specs.find { |s| s.name == dep.name }
133
-
134
- if spec.nil?
135
-
136
- e = Gem::LoadError.new "#{dep.name} is not part of the bundle. Add it to Gemfile."
137
- e.name = dep.name
138
- if e.respond_to?(:requirement=)
139
- e.requirement = dep.requirement
140
- else
141
- e.version_requirement = dep.requirement
142
- end
143
- raise e
144
- elsif dep !~ spec
145
- e = Gem::LoadError.new "can't activate #{dep}, already activated #{spec.full_name}. " \
146
- "Make sure all dependencies are added to Gemfile."
147
- e.name = dep.name
148
- if e.respond_to?(:requirement=)
149
- e.requirement = dep.requirement
150
- else
151
- e.version_requirement = dep.requirement
152
- end
153
- raise e
154
- end
155
-
156
- true
157
- end
158
- end
159
-
160
- if defined? ::Deprecate
161
- Deprecate = ::Deprecate
162
- elsif defined? Gem::Deprecate
163
- Deprecate = Gem::Deprecate
164
- else
165
- class Deprecate
166
- def skip_during; yield; end
167
- end
168
- end
169
-
170
- def stub_source_index137(specs)
171
- # Rubygems versions lower than 1.7 use SourceIndex#from_gems_in
172
- source_index_class = (class << Gem::SourceIndex ; self ; end)
173
- source_index_class.send(:remove_method, :from_gems_in)
174
- source_index_class.send(:define_method, :from_gems_in) do |*args|
175
- source_index = Gem::SourceIndex.new
176
- source_index.spec_dirs = *args
177
- source_index.add_specs(*specs)
178
- source_index
179
- end
180
- end
181
-
182
- def stub_source_index170(specs)
183
- Gem::SourceIndex.send(:alias_method, :old_initialize, :initialize)
184
- Gem::SourceIndex.send(:define_method, :initialize) do |*args|
185
- @gems = {}
186
- # You're looking at this thinking: Oh! This is how I make those
187
- # rubygems deprecations go away!
188
- #
189
- # You'd be correct BUT using of this method in production code
190
- # must be approved by the rubygems team itself!
191
- #
192
- # This is your warning. If you use this and don't have approval
193
- # we can't protect you.
194
- #
195
- Deprecate.skip_during do
196
- self.spec_dirs = *args
197
- add_specs(*specs)
198
- end
199
- end
200
- end
201
-
202
- # Used to make bin stubs that are not created by bundler work
203
- # under bundler. The new Gem.bin_path only considers gems in
204
- # +specs+
205
- def replace_bin_path(specs)
206
- gem_class = (class << Gem ; self ; end)
207
- gem_class.send(:remove_method, :bin_path)
208
- gem_class.send(:define_method, :bin_path) do |name, *args|
209
- exec_name = args.first
210
-
211
- if exec_name == 'bundle'
212
- return ENV['BUNDLE_BIN_PATH']
213
- end
214
-
215
- spec = nil
216
-
217
- if exec_name
218
- spec = specs.find { |s| s.executables.include?(exec_name) }
219
- spec or raise Gem::Exception, "can't find executable #{exec_name}"
220
- else
221
- spec = specs.find { |s| s.name == name }
222
- exec_name = spec.default_executable or raise Gem::Exception, "no default executable for #{spec.full_name}"
223
- end
224
-
225
- gem_bin = File.join(spec.full_gem_path, spec.bindir, exec_name)
226
- gem_from_path_bin = File.join(File.dirname(spec.loaded_from), spec.bindir, exec_name)
227
- File.exist?(gem_bin) ? gem_bin : gem_from_path_bin
228
- end
229
- end
230
-
231
- # Because Bundler has a static view of what specs are available,
232
- # we don't #reflesh, so stub it out.
233
- def replace_refresh
234
- gem_class = (class << Gem ; self ; end)
235
- gem_class.send(:remove_method, :refresh)
236
- gem_class.send(:define_method, :refresh) { }
237
- end
238
-
239
- # Replace or hook into Rubygems to provide a bundlerized view
240
- # of the world.
241
- def replace_entrypoints(specs)
242
- reverse_rubygems_kernel_mixin
243
-
244
- replace_gem(specs)
245
-
246
- stub_rubygems(specs)
247
-
248
- replace_bin_path(specs)
249
- replace_refresh
250
-
251
- Gem.clear_paths
252
- end
253
-
254
- # This backports the correct segment generation code from Rubygems 1.4+
255
- # by monkeypatching it into the method in Rubygems 1.3.6 and 1.3.7.
256
- def backport_segment_generation
257
- Gem::Version.send(:define_method, :segments) do
258
- @segments ||= @version.scan(/[0-9]+|[a-z]+/i).map do |s|
259
- /^\d+$/ =~ s ? s.to_i : s
260
- end
261
- end
262
- end
263
-
264
- # Rubygems 1.4 through 1.6
265
- class Legacy < RubygemsIntegration
266
- def stub_rubygems(specs)
267
- stub_source_index137(specs)
268
- end
269
-
270
- def all_specs
271
- Gem.source_index.gems.values
272
- end
273
-
274
- def find_name(name)
275
- Gem.source_index.find_name(name)
276
- end
277
- end
278
-
279
- # Rubygems versions 1.3.6 and 1.3.7
280
- class Ancient < Legacy
281
- def initialize
282
- super
283
- backport_segment_generation
284
- end
285
- end
286
-
287
- # Rubygems 1.7
288
- class Transitional < Legacy
289
- def stub_rubygems(specs)
290
- stub_source_index170(specs)
291
- end
292
- end
293
-
294
- # Rubygems 1.8.5
295
- class Modern < RubygemsIntegration
296
- def stub_rubygems(specs)
297
- Gem::Specification.all = specs
298
-
299
- Gem.post_reset {
300
- Gem::Specification.all = specs
301
- }
302
-
303
- stub_source_index170(specs)
304
- end
305
-
306
- def all_specs
307
- Gem::Specification.to_a
308
- end
309
-
310
- def find_name(name)
311
- Gem::Specification.find_all_by_name name
312
- end
313
- end
314
-
315
- # Rubygems 1.8.0 to 1.8.4
316
- class AlmostModern < Modern
317
- # Rubygems [>= 1.8.0, < 1.8.5] has a bug that changes Gem.dir whenever
318
- # you call Gem::Installer#install with an :install_dir set. We have to
319
- # change it back for our sudo mode to work.
320
- def preserve_paths
321
- old_dir, old_path = gem_dir, gem_path
322
- yield
323
- Gem.use_paths(old_dir, old_path)
324
- end
325
- end
326
-
327
- end
328
-
329
- if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.8.5')
330
- @rubygems = RubygemsIntegration::Modern.new
331
- elsif Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.8.0')
332
- @rubygems = RubygemsIntegration::AlmostModern.new
333
- elsif Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.7.0')
334
- @rubygems = RubygemsIntegration::Transitional.new
335
- elsif Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.4.0')
336
- @rubygems = RubygemsIntegration::Legacy.new
337
- else # Rubygems 1.3.6 and 1.3.7
338
- @rubygems = RubygemsIntegration::Ancient.new
339
- end
340
-
341
- class << self
342
- attr_reader :rubygems
343
- end
344
- end
@@ -1,57 +0,0 @@
1
- require 'thor/actions/create_file'
2
-
3
- class Thor
4
- module Actions
5
-
6
- # Create a new file relative to the destination root from the given source.
7
- #
8
- # ==== Parameters
9
- # destination<String>:: the relative path to the destination root.
10
- # source<String|NilClass>:: the relative path to the source root.
11
- # config<Hash>:: give :verbose => false to not log the status.
12
- # :: give :symbolic => false for hard link.
13
- #
14
- # ==== Examples
15
- #
16
- # create_link "config/apache.conf", "/etc/apache.conf"
17
- #
18
- def create_link(destination, *args, &block)
19
- config = args.last.is_a?(Hash) ? args.pop : {}
20
- source = args.first
21
- action CreateLink.new(self, destination, source, config)
22
- end
23
- alias :add_link :create_link
24
-
25
- # CreateLink is a subset of CreateFile, which instead of taking a block of
26
- # data, just takes a source string from the user.
27
- #
28
- class CreateLink < CreateFile #:nodoc:
29
- attr_reader :data
30
-
31
- # Checks if the content of the file at the destination is identical to the rendered result.
32
- #
33
- # ==== Returns
34
- # Boolean:: true if it is identical, false otherwise.
35
- #
36
- def identical?
37
- exists? && File.identical?(render, destination)
38
- end
39
-
40
- def invoke!
41
- invoke_with_conflict_check do
42
- FileUtils.mkdir_p(File.dirname(destination))
43
- # Create a symlink by default
44
- config[:symbolic] ||= true
45
- File.unlink(destination) if exists?
46
- if config[:symbolic]
47
- File.symlink(render, destination)
48
- else
49
- File.link(render, destination)
50
- end
51
- end
52
- given_destination
53
- end
54
-
55
- end
56
- end
57
- end
@@ -1,273 +0,0 @@
1
- require 'thor/base'
2
-
3
- # Thor has a special class called Thor::Group. The main difference to Thor class
4
- # is that it invokes all tasks at once. It also include some methods that allows
5
- # invocations to be done at the class method, which are not available to Thor
6
- # tasks.
7
- class Thor::Group
8
- class << self
9
- # The description for this Thor::Group. If none is provided, but a source root
10
- # exists, tries to find the USAGE one folder above it, otherwise searches
11
- # in the superclass.
12
- #
13
- # ==== Parameters
14
- # description<String>:: The description for this Thor::Group.
15
- #
16
- def desc(description=nil)
17
- case description
18
- when nil
19
- @desc ||= from_superclass(:desc, nil)
20
- else
21
- @desc = description
22
- end
23
- end
24
-
25
- # Prints help information.
26
- #
27
- # ==== Options
28
- # short:: When true, shows only usage.
29
- #
30
- def help(shell)
31
- shell.say "Usage:"
32
- shell.say " #{banner}\n"
33
- shell.say
34
- class_options_help(shell)
35
- shell.say self.desc if self.desc
36
- end
37
-
38
- # Stores invocations for this class merging with superclass values.
39
- #
40
- def invocations #:nodoc:
41
- @invocations ||= from_superclass(:invocations, {})
42
- end
43
-
44
- # Stores invocation blocks used on invoke_from_option.
45
- #
46
- def invocation_blocks #:nodoc:
47
- @invocation_blocks ||= from_superclass(:invocation_blocks, {})
48
- end
49
-
50
- # Invoke the given namespace or class given. It adds an instance
51
- # method that will invoke the klass and task. You can give a block to
52
- # configure how it will be invoked.
53
- #
54
- # The namespace/class given will have its options showed on the help
55
- # usage. Check invoke_from_option for more information.
56
- #
57
- def invoke(*names, &block)
58
- options = names.last.is_a?(Hash) ? names.pop : {}
59
- verbose = options.fetch(:verbose, true)
60
-
61
- names.each do |name|
62
- invocations[name] = false
63
- invocation_blocks[name] = block if block_given?
64
-
65
- class_eval <<-METHOD, __FILE__, __LINE__
66
- def _invoke_#{name.to_s.gsub(/\W/, '_')}
67
- klass, task = self.class.prepare_for_invocation(nil, #{name.inspect})
68
-
69
- if klass
70
- say_status :invoke, #{name.inspect}, #{verbose.inspect}
71
- block = self.class.invocation_blocks[#{name.inspect}]
72
- _invoke_for_class_method klass, task, &block
73
- else
74
- say_status :error, %(#{name.inspect} [not found]), :red
75
- end
76
- end
77
- METHOD
78
- end
79
- end
80
-
81
- # Invoke a thor class based on the value supplied by the user to the
82
- # given option named "name". A class option must be created before this
83
- # method is invoked for each name given.
84
- #
85
- # ==== Examples
86
- #
87
- # class GemGenerator < Thor::Group
88
- # class_option :test_framework, :type => :string
89
- # invoke_from_option :test_framework
90
- # end
91
- #
92
- # ==== Boolean options
93
- #
94
- # In some cases, you want to invoke a thor class if some option is true or
95
- # false. This is automatically handled by invoke_from_option. Then the
96
- # option name is used to invoke the generator.
97
- #
98
- # ==== Preparing for invocation
99
- #
100
- # In some cases you want to customize how a specified hook is going to be
101
- # invoked. You can do that by overwriting the class method
102
- # prepare_for_invocation. The class method must necessarily return a klass
103
- # and an optional task.
104
- #
105
- # ==== Custom invocations
106
- #
107
- # You can also supply a block to customize how the option is giong to be
108
- # invoked. The block receives two parameters, an instance of the current
109
- # class and the klass to be invoked.
110
- #
111
- def invoke_from_option(*names, &block)
112
- options = names.last.is_a?(Hash) ? names.pop : {}
113
- verbose = options.fetch(:verbose, :white)
114
-
115
- names.each do |name|
116
- unless class_options.key?(name)
117
- raise ArgumentError, "You have to define the option #{name.inspect} " <<
118
- "before setting invoke_from_option."
119
- end
120
-
121
- invocations[name] = true
122
- invocation_blocks[name] = block if block_given?
123
-
124
- class_eval <<-METHOD, __FILE__, __LINE__
125
- def _invoke_from_option_#{name.to_s.gsub(/\W/, '_')}
126
- return unless options[#{name.inspect}]
127
-
128
- value = options[#{name.inspect}]
129
- value = #{name.inspect} if TrueClass === value
130
- klass, task = self.class.prepare_for_invocation(#{name.inspect}, value)
131
-
132
- if klass
133
- say_status :invoke, value, #{verbose.inspect}
134
- block = self.class.invocation_blocks[#{name.inspect}]
135
- _invoke_for_class_method klass, task, &block
136
- else
137
- say_status :error, %(\#{value} [not found]), :red
138
- end
139
- end
140
- METHOD
141
- end
142
- end
143
-
144
- # Remove a previously added invocation.
145
- #
146
- # ==== Examples
147
- #
148
- # remove_invocation :test_framework
149
- #
150
- def remove_invocation(*names)
151
- names.each do |name|
152
- remove_task(name)
153
- remove_class_option(name)
154
- invocations.delete(name)
155
- invocation_blocks.delete(name)
156
- end
157
- end
158
-
159
- # Overwrite class options help to allow invoked generators options to be
160
- # shown recursively when invoking a generator.
161
- #
162
- def class_options_help(shell, groups={}) #:nodoc:
163
- get_options_from_invocations(groups, class_options) do |klass|
164
- klass.send(:get_options_from_invocations, groups, class_options)
165
- end
166
- super(shell, groups)
167
- end
168
-
169
- # Get invocations array and merge options from invocations. Those
170
- # options are added to group_options hash. Options that already exists
171
- # in base_options are not added twice.
172
- #
173
- def get_options_from_invocations(group_options, base_options) #:nodoc:
174
- invocations.each do |name, from_option|
175
- value = if from_option
176
- option = class_options[name]
177
- option.type == :boolean ? name : option.default
178
- else
179
- name
180
- end
181
- next unless value
182
-
183
- klass, task = prepare_for_invocation(name, value)
184
- next unless klass && klass.respond_to?(:class_options)
185
-
186
- value = value.to_s
187
- human_name = value.respond_to?(:classify) ? value.classify : value
188
-
189
- group_options[human_name] ||= []
190
- group_options[human_name] += klass.class_options.values.select do |option|
191
- base_options[option.name.to_sym].nil? && option.group.nil? &&
192
- !group_options.values.flatten.any? { |i| i.name == option.name }
193
- end
194
-
195
- yield klass if block_given?
196
- end
197
- end
198
-
199
- # Returns tasks ready to be printed.
200
- def printable_tasks(*)
201
- item = []
202
- item << banner
203
- item << (desc ? "# #{desc.gsub(/\s+/m,' ')}" : "")
204
- [item]
205
- end
206
-
207
- def handle_argument_error(task, error) #:nodoc:
208
- raise error, "#{task.name.inspect} was called incorrectly. Are you sure it has arity equals to 0?"
209
- end
210
-
211
- protected
212
-
213
- # The method responsible for dispatching given the args.
214
- def dispatch(task, given_args, given_opts, config) #:nodoc:
215
- if Thor::HELP_MAPPINGS.include?(given_args.first)
216
- help(config[:shell])
217
- return
218
- end
219
-
220
- args, opts = Thor::Options.split(given_args)
221
- opts = given_opts || opts
222
-
223
- if task
224
- new(args, opts, config).invoke_task(all_tasks[task])
225
- else
226
- new(args, opts, config).invoke_all
227
- end
228
- end
229
-
230
- # The banner for this class. You can customize it if you are invoking the
231
- # thor class by another ways which is not the Thor::Runner.
232
- def banner
233
- "#{basename} #{self_task.formatted_usage(self, false)}"
234
- end
235
-
236
- # Represents the whole class as a task.
237
- def self_task #:nodoc:
238
- Thor::DynamicTask.new(self.namespace, class_options)
239
- end
240
-
241
- def baseclass #:nodoc:
242
- Thor::Group
243
- end
244
-
245
- def create_task(meth) #:nodoc:
246
- tasks[meth.to_s] = Thor::Task.new(meth, nil, nil, nil, nil)
247
- true
248
- end
249
- end
250
-
251
- include Thor::Base
252
-
253
- protected
254
-
255
- # Shortcut to invoke with padding and block handling. Use internally by
256
- # invoke and invoke_from_option class methods.
257
- def _invoke_for_class_method(klass, task=nil, *args, &block) #:nodoc:
258
- with_padding do
259
- if block
260
- case block.arity
261
- when 3
262
- block.call(self, klass, task)
263
- when 2
264
- block.call(self, klass)
265
- when 1
266
- instance_exec(klass, &block)
267
- end
268
- else
269
- invoke klass, task, *args
270
- end
271
- end
272
- end
273
- end