bundler 1.2.5 → 1.3.0.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.
- data/.gitignore +10 -7
- data/.travis.yml +12 -3
- data/CHANGELOG.md +26 -19
- data/CONTRIBUTE.md +97 -0
- data/README.md +4 -2
- data/Rakefile +17 -59
- data/bundler.gemspec +2 -1
- data/lib/bundler.rb +23 -20
- data/lib/bundler/cli.rb +68 -22
- data/lib/bundler/definition.rb +3 -2
- data/lib/bundler/deprecate.rb +15 -0
- data/lib/bundler/dsl.rb +14 -16
- data/lib/bundler/environment.rb +0 -5
- data/lib/bundler/fetcher.rb +23 -78
- data/lib/bundler/friendly_errors.rb +4 -5
- data/lib/bundler/gem_helper.rb +14 -16
- data/lib/bundler/injector.rb +64 -0
- data/lib/bundler/installer.rb +1 -7
- data/lib/bundler/lazy_specification.rb +6 -3
- data/lib/bundler/lockfile_parser.rb +25 -13
- data/lib/bundler/resolver.rb +0 -1
- data/lib/bundler/rubygems_integration.rb +83 -17
- data/lib/bundler/settings.rb +4 -2
- data/lib/bundler/similarity_detector.rb +63 -0
- data/lib/bundler/source.rb +3 -886
- data/lib/bundler/source/git.rb +267 -0
- data/lib/bundler/source/git/git_proxy.rb +142 -0
- data/lib/bundler/source/path.rb +209 -0
- data/lib/bundler/source/path/installer.rb +33 -0
- data/lib/bundler/source/rubygems.rb +261 -0
- data/lib/bundler/templates/newgem/newgem.gemspec.tt +3 -0
- data/lib/bundler/templates/newgem/rspec.tt +2 -0
- data/lib/bundler/templates/newgem/spec/newgem_spec.rb.tt +9 -0
- data/lib/bundler/templates/newgem/spec/spec_helper.rb.tt +2 -0
- data/lib/bundler/templates/newgem/test/minitest_helper.rb.tt +4 -0
- data/lib/bundler/templates/newgem/test/test_newgem.rb.tt +11 -0
- data/lib/bundler/ui.rb +20 -5
- data/lib/bundler/vendor/.document +0 -0
- data/lib/bundler/vendor/thor.rb +74 -5
- data/lib/bundler/vendor/thor/actions.rb +5 -5
- data/lib/bundler/vendor/thor/actions/directory.rb +1 -0
- data/lib/bundler/vendor/thor/actions/file_manipulation.rb +7 -1
- data/lib/bundler/vendor/thor/base.rb +44 -11
- data/lib/bundler/vendor/thor/core_ext/hash_with_indifferent_access.rb +5 -0
- data/lib/bundler/vendor/thor/parser/argument.rb +14 -7
- data/lib/bundler/vendor/thor/parser/arguments.rb +7 -1
- data/lib/bundler/vendor/thor/parser/option.rb +8 -8
- data/lib/bundler/vendor/thor/parser/options.rb +62 -24
- data/lib/bundler/vendor/thor/runner.rb +1 -1
- data/lib/bundler/vendor/thor/shell/basic.rb +2 -2
- data/lib/bundler/vendor/thor/task.rb +2 -2
- data/lib/bundler/vendor/thor/version.rb +1 -1
- data/lib/bundler/vendored_persistent.rb +3 -15
- data/lib/bundler/version.rb +1 -1
- data/man/bundle-exec.ronn +1 -1
- data/man/bundle-update.ronn +1 -1
- data/man/bundle.ronn +4 -1
- data/spec/bundler/bundler_spec.rb +2 -28
- data/spec/bundler/cli_rspec.rb +9 -0
- data/spec/bundler/definition_spec.rb +1 -1
- data/spec/bundler/dsl_spec.rb +15 -8
- data/spec/bundler/gem_helper_spec.rb +38 -21
- data/spec/bundler/psyched_yaml_spec.rb +1 -0
- data/spec/bundler/source_spec.rb +3 -3
- data/spec/cache/gems_spec.rb +24 -24
- data/spec/cache/git_spec.rb +21 -23
- data/spec/cache/path_spec.rb +11 -11
- data/spec/cache/platform_spec.rb +6 -6
- data/spec/install/deploy_spec.rb +38 -38
- data/spec/install/gems/c_ext_spec.rb +2 -2
- data/spec/install/gems/dependency_api_spec.rb +23 -116
- data/spec/install/gems/env_spec.rb +1 -1
- data/spec/install/gems/flex_spec.rb +7 -8
- data/spec/install/gems/groups_spec.rb +10 -10
- data/spec/install/gems/packed_spec.rb +4 -4
- data/spec/install/gems/platform_spec.rb +3 -3
- data/spec/install/gems/post_install_spec.rb +9 -9
- data/spec/install/gems/resolving_spec.rb +2 -2
- data/spec/install/gems/simple_case_spec.rb +50 -53
- data/spec/install/gems/standalone_spec.rb +19 -19
- data/spec/install/gems/sudo_spec.rb +31 -16
- data/spec/install/gems/win32_spec.rb +1 -1
- data/spec/install/gemspec_spec.rb +6 -6
- data/spec/install/git_spec.rb +34 -34
- data/spec/install/invalid_spec.rb +3 -3
- data/spec/install/path_spec.rb +71 -8
- data/spec/install/upgrade_spec.rb +2 -2
- data/spec/integration/inject.rb +78 -0
- data/spec/lock/git_spec.rb +2 -2
- data/spec/lock/lockfile_spec.rb +14 -14
- data/spec/other/check_spec.rb +29 -29
- data/spec/other/clean_spec.rb +47 -48
- data/spec/other/config_spec.rb +20 -20
- data/spec/other/console_spec.rb +5 -5
- data/spec/other/exec_spec.rb +48 -28
- data/spec/other/ext_spec.rb +3 -3
- data/spec/other/help_spec.rb +6 -6
- data/spec/other/init_spec.rb +8 -8
- data/spec/other/newgem_spec.rb +95 -15
- data/spec/other/open_spec.rb +10 -5
- data/spec/other/outdated_spec.rb +8 -8
- data/spec/other/platform_spec.rb +45 -45
- data/spec/other/show_spec.rb +10 -10
- data/spec/quality_spec.rb +2 -2
- data/spec/realworld/dependency_api_spec.rb +61 -0
- data/spec/realworld/edgecases_spec.rb +8 -8
- data/spec/runtime/executable_spec.rb +13 -13
- data/spec/runtime/load_spec.rb +12 -12
- data/spec/runtime/platform_spec.rb +1 -1
- data/spec/runtime/require_spec.rb +24 -24
- data/spec/runtime/setup_spec.rb +113 -56
- data/spec/runtime/with_clean_env_spec.rb +11 -13
- data/spec/spec_helper.rb +6 -0
- data/spec/support/artifice/endpoint.rb +28 -13
- data/spec/support/artifice/endpoint_extra.rb +4 -0
- data/spec/support/builders.rb +1 -1
- data/spec/support/helpers.rb +2 -7
- data/spec/support/indexes.rb +3 -3
- data/spec/support/matchers.rb +6 -6
- data/spec/update/gems_spec.rb +19 -8
- data/spec/update/git_spec.rb +10 -10
- data/spec/update/source_spec.rb +1 -1
- metadata +86 -55
- data/.rspec +0 -2
@@ -0,0 +1,33 @@
|
|
1
|
+
module Bundler
|
2
|
+
module Source
|
3
|
+
class Path
|
4
|
+
|
5
|
+
class Installer < Bundler::GemInstaller
|
6
|
+
def initialize(spec, options = {})
|
7
|
+
@spec = spec
|
8
|
+
@bin_dir = Bundler.requires_sudo? ? "#{Bundler.tmp}/bin" : "#{Bundler.rubygems.gem_dir}/bin"
|
9
|
+
@gem_dir = Bundler.rubygems.path(spec.full_gem_path)
|
10
|
+
@wrappers = options[:wrappers] || true
|
11
|
+
@env_shebang = options[:env_shebang] || true
|
12
|
+
@format_executable = options[:format_executable] || false
|
13
|
+
end
|
14
|
+
|
15
|
+
def generate_bin
|
16
|
+
return if spec.executables.nil? || spec.executables.empty?
|
17
|
+
|
18
|
+
if Bundler.requires_sudo?
|
19
|
+
FileUtils.mkdir_p("#{Bundler.tmp}/bin") unless File.exist?("#{Bundler.tmp}/bin")
|
20
|
+
end
|
21
|
+
super
|
22
|
+
if Bundler.requires_sudo?
|
23
|
+
Bundler.mkdir_p "#{Bundler.rubygems.gem_dir}/bin"
|
24
|
+
spec.executables.each do |exe|
|
25
|
+
Bundler.sudo "cp -R #{Bundler.tmp}/bin/#{exe} #{Bundler.rubygems.gem_dir}/bin/"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,261 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'rubygems/user_interaction'
|
3
|
+
require 'rubygems/spec_fetcher'
|
4
|
+
|
5
|
+
module Bundler
|
6
|
+
module Source
|
7
|
+
# TODO: Refactor this class
|
8
|
+
class Rubygems
|
9
|
+
FORCE_MODERN_INDEX_LIMIT = 100 # threshold for switching back to the modern index instead of fetching every spec
|
10
|
+
|
11
|
+
attr_reader :remotes, :caches
|
12
|
+
attr_accessor :dependency_names
|
13
|
+
|
14
|
+
def initialize(options = {})
|
15
|
+
@options = options
|
16
|
+
@remotes = (options["remotes"] || []).map { |r| normalize_uri(r) }
|
17
|
+
@fetchers = {}
|
18
|
+
@allow_remote = false
|
19
|
+
@allow_cached = false
|
20
|
+
|
21
|
+
@caches = [ Bundler.app_cache ] +
|
22
|
+
Bundler.rubygems.gem_path.map{|p| File.expand_path("#{p}/cache") }
|
23
|
+
end
|
24
|
+
|
25
|
+
def remote!
|
26
|
+
@allow_remote = true
|
27
|
+
end
|
28
|
+
|
29
|
+
def cached!
|
30
|
+
@allow_cached = true
|
31
|
+
end
|
32
|
+
|
33
|
+
def hash
|
34
|
+
Rubygems.hash
|
35
|
+
end
|
36
|
+
|
37
|
+
def eql?(o)
|
38
|
+
Rubygems === o
|
39
|
+
end
|
40
|
+
|
41
|
+
alias == eql?
|
42
|
+
|
43
|
+
def options
|
44
|
+
{ "remotes" => @remotes.map { |r| r.to_s } }
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.from_lock(options)
|
48
|
+
s = new(options)
|
49
|
+
Array(options["remote"]).each { |r| s.add_remote(r) }
|
50
|
+
s
|
51
|
+
end
|
52
|
+
|
53
|
+
def to_lock
|
54
|
+
out = "GEM\n"
|
55
|
+
out << remotes.map {|r| " remote: #{r}\n" }.join
|
56
|
+
out << " specs:\n"
|
57
|
+
end
|
58
|
+
|
59
|
+
def to_s
|
60
|
+
remote_names = self.remotes.map { |r| r.to_s }.join(', ')
|
61
|
+
"rubygems repository #{remote_names}"
|
62
|
+
end
|
63
|
+
alias_method :name, :to_s
|
64
|
+
|
65
|
+
def specs
|
66
|
+
@specs ||= fetch_specs
|
67
|
+
end
|
68
|
+
|
69
|
+
def install(spec)
|
70
|
+
if installed_specs[spec].any?
|
71
|
+
Bundler.ui.info "Using #{spec.name} (#{spec.version}) "
|
72
|
+
return
|
73
|
+
end
|
74
|
+
|
75
|
+
Bundler.ui.info "Installing #{spec.name} (#{spec.version}) "
|
76
|
+
path = cached_gem(spec)
|
77
|
+
if Bundler.requires_sudo?
|
78
|
+
install_path = Bundler.tmp
|
79
|
+
bin_path = install_path.join("bin")
|
80
|
+
else
|
81
|
+
install_path = Bundler.rubygems.gem_dir
|
82
|
+
bin_path = Bundler.system_bindir
|
83
|
+
end
|
84
|
+
|
85
|
+
installed_spec = nil
|
86
|
+
Bundler.rubygems.preserve_paths do
|
87
|
+
installed_spec = Bundler::GemInstaller.new(path,
|
88
|
+
:install_dir => install_path.to_s,
|
89
|
+
:bin_dir => bin_path.to_s,
|
90
|
+
:ignore_dependencies => true,
|
91
|
+
:wrappers => true,
|
92
|
+
:env_shebang => true
|
93
|
+
).install
|
94
|
+
end
|
95
|
+
|
96
|
+
if spec.post_install_message
|
97
|
+
Installer.post_install_messages[spec.name] = spec.post_install_message
|
98
|
+
end
|
99
|
+
|
100
|
+
# SUDO HAX
|
101
|
+
if Bundler.requires_sudo?
|
102
|
+
Bundler.mkdir_p "#{Bundler.rubygems.gem_dir}/gems"
|
103
|
+
Bundler.mkdir_p "#{Bundler.rubygems.gem_dir}/specifications"
|
104
|
+
Bundler.sudo "cp -R #{Bundler.tmp}/gems/#{spec.full_name} #{Bundler.rubygems.gem_dir}/gems/"
|
105
|
+
Bundler.sudo "cp -R #{Bundler.tmp}/specifications/#{spec.full_name}.gemspec #{Bundler.rubygems.gem_dir}/specifications/"
|
106
|
+
spec.executables.each do |exe|
|
107
|
+
Bundler.mkdir_p Bundler.system_bindir
|
108
|
+
Bundler.sudo "cp -R #{Bundler.tmp}/bin/#{exe} #{Bundler.system_bindir}"
|
109
|
+
end
|
110
|
+
end
|
111
|
+
installed_spec.loaded_from = "#{Bundler.rubygems.gem_dir}/specifications/#{spec.full_name}.gemspec"
|
112
|
+
spec.loaded_from = "#{Bundler.rubygems.gem_dir}/specifications/#{spec.full_name}.gemspec"
|
113
|
+
end
|
114
|
+
|
115
|
+
def cache(spec)
|
116
|
+
cached_path = cached_gem(spec)
|
117
|
+
raise GemNotFound, "Missing gem file '#{spec.full_name}.gem'." unless cached_path
|
118
|
+
return if File.dirname(cached_path) == Bundler.app_cache.to_s
|
119
|
+
Bundler.ui.info " * #{File.basename(cached_path)}"
|
120
|
+
FileUtils.cp(cached_path, Bundler.app_cache)
|
121
|
+
end
|
122
|
+
|
123
|
+
def add_remote(source)
|
124
|
+
@remotes << normalize_uri(source)
|
125
|
+
end
|
126
|
+
|
127
|
+
def replace_remotes(source)
|
128
|
+
return false if source.remotes == @remotes
|
129
|
+
|
130
|
+
@remotes = []
|
131
|
+
source.remotes.each do |r|
|
132
|
+
add_remote r.to_s
|
133
|
+
end
|
134
|
+
|
135
|
+
true
|
136
|
+
end
|
137
|
+
|
138
|
+
private
|
139
|
+
|
140
|
+
def cached_gem(spec)
|
141
|
+
possibilities = @caches.map { |p| "#{p}/#{spec.file_name}" }
|
142
|
+
cached_gem = possibilities.find { |p| File.exist?(p) }
|
143
|
+
unless cached_gem
|
144
|
+
raise Bundler::GemNotFound, "Could not find #{spec.file_name} for installation"
|
145
|
+
end
|
146
|
+
cached_gem
|
147
|
+
end
|
148
|
+
|
149
|
+
def normalize_uri(uri)
|
150
|
+
uri = uri.to_s
|
151
|
+
uri = "#{uri}/" unless uri =~ %r'/$'
|
152
|
+
uri = URI(uri)
|
153
|
+
raise ArgumentError, "The source must be an absolute URI" unless uri.absolute?
|
154
|
+
uri
|
155
|
+
end
|
156
|
+
|
157
|
+
def fetch_specs
|
158
|
+
# remote_specs usually generates a way larger Index than the other
|
159
|
+
# sources, and large_idx.use small_idx is way faster than
|
160
|
+
# small_idx.use large_idx.
|
161
|
+
if @allow_remote
|
162
|
+
idx = remote_specs.dup
|
163
|
+
else
|
164
|
+
idx = Index.new
|
165
|
+
end
|
166
|
+
idx.use(cached_specs, :override_dupes) if @allow_cached || @allow_remote
|
167
|
+
idx.use(installed_specs, :override_dupes)
|
168
|
+
idx
|
169
|
+
end
|
170
|
+
|
171
|
+
def installed_specs
|
172
|
+
@installed_specs ||= begin
|
173
|
+
idx = Index.new
|
174
|
+
have_bundler = false
|
175
|
+
Bundler.rubygems.all_specs.reverse.each do |spec|
|
176
|
+
next if spec.name == 'bundler' && spec.version.to_s != VERSION
|
177
|
+
have_bundler = true if spec.name == 'bundler'
|
178
|
+
spec.source = self
|
179
|
+
idx << spec
|
180
|
+
end
|
181
|
+
|
182
|
+
# Always have bundler locally
|
183
|
+
unless have_bundler
|
184
|
+
# We're running bundler directly from the source
|
185
|
+
# so, let's create a fake gemspec for it (it's a path)
|
186
|
+
# gemspec
|
187
|
+
bundler = Gem::Specification.new do |s|
|
188
|
+
s.name = 'bundler'
|
189
|
+
s.version = VERSION
|
190
|
+
s.platform = Gem::Platform::RUBY
|
191
|
+
s.source = self
|
192
|
+
s.authors = ["bundler team"]
|
193
|
+
s.loaded_from = File.expand_path("..", __FILE__)
|
194
|
+
end
|
195
|
+
idx << bundler
|
196
|
+
end
|
197
|
+
idx
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
def cached_specs
|
202
|
+
@cached_specs ||= begin
|
203
|
+
idx = installed_specs.dup
|
204
|
+
|
205
|
+
path = Bundler.app_cache
|
206
|
+
Dir["#{path}/*.gem"].each do |gemfile|
|
207
|
+
next if gemfile =~ /^bundler\-[\d\.]+?\.gem/
|
208
|
+
s ||= Bundler.rubygems.spec_from_gem(gemfile)
|
209
|
+
s.source = self
|
210
|
+
idx << s
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
idx
|
215
|
+
end
|
216
|
+
|
217
|
+
def remote_specs
|
218
|
+
@remote_specs ||= begin
|
219
|
+
idx = Index.new
|
220
|
+
old = Bundler.rubygems.sources
|
221
|
+
|
222
|
+
sources = {}
|
223
|
+
remotes.each do |uri|
|
224
|
+
fetcher = Bundler::Fetcher.new(uri)
|
225
|
+
specs = fetcher.specs(dependency_names, self)
|
226
|
+
sources[fetcher] = specs.size
|
227
|
+
|
228
|
+
idx.use specs
|
229
|
+
end
|
230
|
+
|
231
|
+
# don't need to fetch all specifications for every gem/version on
|
232
|
+
# the rubygems repo if there's no api endpoints to search over
|
233
|
+
# or it has too many specs to fetch
|
234
|
+
fetchers = sources.keys
|
235
|
+
api_fetchers = fetchers.select {|fetcher| fetcher.has_api }
|
236
|
+
modern_index_fetchers = fetchers - api_fetchers
|
237
|
+
if api_fetchers.any? && modern_index_fetchers.all? {|fetcher| sources[fetcher] < FORCE_MODERN_INDEX_LIMIT }
|
238
|
+
# this will fetch all the specifications on the rubygems repo
|
239
|
+
unmet_dependency_names = idx.unmet_dependency_names
|
240
|
+
unmet_dependency_names -= ['bundler'] # bundler will always be unmet
|
241
|
+
|
242
|
+
Bundler.ui.debug "Unmet Dependencies: #{unmet_dependency_names}"
|
243
|
+
if unmet_dependency_names.any?
|
244
|
+
api_fetchers.each do |fetcher|
|
245
|
+
idx.use fetcher.specs(unmet_dependency_names, self)
|
246
|
+
end
|
247
|
+
end
|
248
|
+
else
|
249
|
+
Bundler::Fetcher.disable_endpoint = true
|
250
|
+
api_fetchers.each {|fetcher| idx.use fetcher.specs([], self) }
|
251
|
+
end
|
252
|
+
|
253
|
+
idx
|
254
|
+
ensure
|
255
|
+
Bundler.rubygems.sources = old
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
end
|
261
|
+
end
|
@@ -11,9 +11,12 @@ Gem::Specification.new do |gem|
|
|
11
11
|
gem.description = %q{TODO: Write a gem description}
|
12
12
|
gem.summary = %q{TODO: Write a gem summary}
|
13
13
|
gem.homepage = ""
|
14
|
+
gem.license = "MIT"
|
14
15
|
|
15
16
|
gem.files = `git ls-files`.split($/)
|
16
17
|
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
18
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
19
|
gem.require_paths = ["lib"]
|
20
|
+
|
21
|
+
gem.add_development_dependency "rake"
|
19
22
|
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require './minitest_helper'
|
2
|
+
|
3
|
+
class Test<%= config[:constant_name] %> < MiniTest::Unit::TestCase
|
4
|
+
def test_that_it_has_a_version_number
|
5
|
+
refute_nil ::<%= config[:constant_name] %>::VERSION
|
6
|
+
end
|
7
|
+
|
8
|
+
def test_it_does_something_useful
|
9
|
+
assert false
|
10
|
+
end
|
11
|
+
end
|
data/lib/bundler/ui.rb
CHANGED
@@ -8,6 +8,9 @@ module Bundler
|
|
8
8
|
def debug(message, newline = nil)
|
9
9
|
end
|
10
10
|
|
11
|
+
def trace(message, newline = nil)
|
12
|
+
end
|
13
|
+
|
11
14
|
def error(message, newline = nil)
|
12
15
|
end
|
13
16
|
|
@@ -22,13 +25,16 @@ module Bundler
|
|
22
25
|
end
|
23
26
|
|
24
27
|
class Shell < UI
|
25
|
-
attr_reader :quiet
|
26
28
|
attr_writer :shell
|
27
29
|
|
28
|
-
def initialize(
|
29
|
-
|
30
|
+
def initialize(options = {})
|
31
|
+
if options["no-color"] || !STDOUT.tty?
|
32
|
+
Thor::Base.shell = Thor::Shell::Basic
|
33
|
+
end
|
34
|
+
@shell = Thor::Base.shell.new
|
30
35
|
@quiet = false
|
31
36
|
@debug = ENV['DEBUG']
|
37
|
+
@trace = ENV['TRACE']
|
32
38
|
end
|
33
39
|
|
34
40
|
def info(msg, newline = nil)
|
@@ -47,8 +53,8 @@ module Bundler
|
|
47
53
|
tell_me(msg, :red, newline)
|
48
54
|
end
|
49
55
|
|
50
|
-
def
|
51
|
-
@quiet =
|
56
|
+
def be_quiet!
|
57
|
+
@quiet = true
|
52
58
|
end
|
53
59
|
|
54
60
|
def debug?
|
@@ -64,6 +70,15 @@ module Bundler
|
|
64
70
|
tell_me(msg, nil, newline) if debug?
|
65
71
|
end
|
66
72
|
|
73
|
+
def trace(e, newline = nil)
|
74
|
+
msg = ["#{e.class}: #{e.message}", *e.backtrace].join("\n")
|
75
|
+
if debug?
|
76
|
+
tell_me(msg, nil, newline)
|
77
|
+
elsif @trace
|
78
|
+
STDERR.puts "#{msg}#{newline}"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
67
82
|
private
|
68
83
|
# valimism
|
69
84
|
def tell_me(msg, color = nil, newline = nil)
|
File without changes
|
data/lib/bundler/vendor/thor.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'set'
|
1
2
|
require 'thor/base'
|
2
3
|
|
3
4
|
class Thor
|
@@ -210,7 +211,7 @@ class Thor
|
|
210
211
|
|
211
212
|
define_method(subcommand) do |*args|
|
212
213
|
args, opts = Thor::Arguments.split(args)
|
213
|
-
invoke subcommand_class, args, opts
|
214
|
+
invoke subcommand_class, args, opts, :invoked_via_subcommand => true
|
214
215
|
end
|
215
216
|
end
|
216
217
|
|
@@ -251,15 +252,83 @@ class Thor
|
|
251
252
|
end
|
252
253
|
end
|
253
254
|
|
255
|
+
# Stop parsing of options as soon as an unknown option or a regular
|
256
|
+
# argument is encountered. All remaining arguments are passed to the task.
|
257
|
+
# This is useful if you have a task that can receive arbitrary additional
|
258
|
+
# options, and where those additional options should not be handled by
|
259
|
+
# Thor.
|
260
|
+
#
|
261
|
+
# ==== Example
|
262
|
+
#
|
263
|
+
# To better understand how this is useful, let's consider a task that calls
|
264
|
+
# an external command. A user may want to pass arbitrary options and
|
265
|
+
# arguments to that command. The task itself also accepts some options,
|
266
|
+
# which should be handled by Thor.
|
267
|
+
#
|
268
|
+
# class_option "verbose", :type => :boolean
|
269
|
+
# stop_on_unknown_option! :exec
|
270
|
+
# check_unknown_options! :except => :exec
|
271
|
+
#
|
272
|
+
# desc "exec", "Run a shell command"
|
273
|
+
# def exec(*args)
|
274
|
+
# puts "diagnostic output" if options[:verbose]
|
275
|
+
# Kernel.exec(*args)
|
276
|
+
# end
|
277
|
+
#
|
278
|
+
# Here +exec+ can be called with +--verbose+ to get diagnostic output,
|
279
|
+
# e.g.:
|
280
|
+
#
|
281
|
+
# $ thor exec --verbose echo foo
|
282
|
+
# diagnostic output
|
283
|
+
# foo
|
284
|
+
#
|
285
|
+
# But if +--verbose+ is given after +echo+, it is passed to +echo+ instead:
|
286
|
+
#
|
287
|
+
# $ thor exec echo --verbose foo
|
288
|
+
# --verbose foo
|
289
|
+
#
|
290
|
+
# ==== Parameters
|
291
|
+
# Symbol ...:: A list of tasks that should be affected.
|
292
|
+
def stop_on_unknown_option!(*task_names)
|
293
|
+
@stop_on_unknown_option ||= Set.new
|
294
|
+
@stop_on_unknown_option.merge(task_names)
|
295
|
+
end
|
296
|
+
|
297
|
+
def stop_on_unknown_option?(task) #:nodoc:
|
298
|
+
!!@stop_on_unknown_option && @stop_on_unknown_option.include?(task.name.to_sym)
|
299
|
+
end
|
300
|
+
|
254
301
|
protected
|
255
302
|
|
256
303
|
# The method responsible for dispatching given the args.
|
257
304
|
def dispatch(meth, given_args, given_opts, config) #:nodoc:
|
258
|
-
|
259
|
-
task
|
305
|
+
# There is an edge case when dispatching from a subcommand.
|
306
|
+
# A problem occurs invoking the default task. This case occurs
|
307
|
+
# when arguments are passed and a default task is defined, and
|
308
|
+
# the first given_args does not match the default task.
|
309
|
+
# Thor use "help" by default so we skip that case.
|
310
|
+
# Note the call to retrieve_task_name. It's called with
|
311
|
+
# given_args.dup since that method calls args.shift. Then lookup
|
312
|
+
# the task normally. If the first item in given_args is not
|
313
|
+
# a task then use the default task. The given_args will be
|
314
|
+
# intact later since dup was used.
|
315
|
+
if config[:invoked_via_subcommand] && given_args.size >= 1 && default_task != "help" && given_args.first != default_task
|
316
|
+
meth ||= retrieve_task_name(given_args.dup)
|
317
|
+
task = all_tasks[normalize_task_name(meth)]
|
318
|
+
task ||= all_tasks[normalize_task_name(default_task)]
|
319
|
+
else
|
320
|
+
meth ||= retrieve_task_name(given_args)
|
321
|
+
task = all_tasks[normalize_task_name(meth)]
|
322
|
+
end
|
260
323
|
|
261
324
|
if task
|
262
325
|
args, opts = Thor::Options.split(given_args)
|
326
|
+
if stop_on_unknown_option?(task) && !args.empty?
|
327
|
+
# given_args starts with a non-option, so we treat everything as
|
328
|
+
# ordinary arguments
|
329
|
+
args.concat opts
|
330
|
+
opts.clear
|
331
|
+
end
|
263
332
|
else
|
264
333
|
args, opts = given_args, nil
|
265
334
|
task = Thor::DynamicTask.new(meth)
|
@@ -321,7 +390,7 @@ class Thor
|
|
321
390
|
|
322
391
|
# receives a (possibly nil) task name and returns a name that is in
|
323
392
|
# the tasks hash. In addition to normalizing aliases, this logic
|
324
|
-
# will determine if a shortened command is an unambiguous
|
393
|
+
# will determine if a shortened command is an unambiguous substring of
|
325
394
|
# a task or alias.
|
326
395
|
#
|
327
396
|
# +normalize_task_name+ also converts names like +animal-prison+
|
@@ -344,7 +413,7 @@ class Thor
|
|
344
413
|
end
|
345
414
|
|
346
415
|
# this is the logic that takes the task name passed in by the user
|
347
|
-
# and determines whether it is an unambiguous
|
416
|
+
# and determines whether it is an unambiguous substrings of a task or
|
348
417
|
# alias name.
|
349
418
|
def find_task_possibilities(meth)
|
350
419
|
len = meth.to_s.length
|