bundler 1.11.2 → 1.12.0.pre.1

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 (129) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +24 -0
  3. data/.gitignore +2 -2
  4. data/.rubocop.yml +17 -2
  5. data/.rubocop_todo.yml +145 -18
  6. data/.travis.yml +9 -2
  7. data/CHANGELOG.md +42 -0
  8. data/Rakefile +20 -13
  9. data/bin/rake +5 -0
  10. data/bin/rspec +5 -0
  11. data/bin/rubocop +7 -1
  12. data/bundler.gemspec +2 -1
  13. data/exe/bundle +10 -2
  14. data/exe/bundle_ruby +2 -1
  15. data/exe/bundler +3 -1
  16. data/lib/bundler.rb +54 -51
  17. data/lib/bundler/capistrano.rb +1 -0
  18. data/lib/bundler/cli.rb +26 -4
  19. data/lib/bundler/cli/binstubs.rb +1 -0
  20. data/lib/bundler/cli/cache.rb +1 -0
  21. data/lib/bundler/cli/check.rb +4 -1
  22. data/lib/bundler/cli/clean.rb +1 -0
  23. data/lib/bundler/cli/common.rb +1 -0
  24. data/lib/bundler/cli/config.rb +5 -5
  25. data/lib/bundler/cli/console.rb +1 -0
  26. data/lib/bundler/cli/exec.rb +4 -9
  27. data/lib/bundler/cli/gem.rb +12 -9
  28. data/lib/bundler/cli/init.rb +1 -0
  29. data/lib/bundler/cli/inject.rb +1 -0
  30. data/lib/bundler/cli/install.rb +8 -5
  31. data/lib/bundler/cli/lock.rb +2 -0
  32. data/lib/bundler/cli/open.rb +1 -0
  33. data/lib/bundler/cli/outdated.rb +36 -9
  34. data/lib/bundler/cli/package.rb +1 -0
  35. data/lib/bundler/cli/platform.rb +4 -1
  36. data/lib/bundler/cli/show.rb +1 -0
  37. data/lib/bundler/cli/update.rb +6 -6
  38. data/lib/bundler/cli/viz.rb +4 -6
  39. data/lib/bundler/constants.rb +1 -0
  40. data/lib/bundler/current_ruby.rb +34 -168
  41. data/lib/bundler/definition.rb +41 -15
  42. data/lib/bundler/dep_proxy.rb +1 -0
  43. data/lib/bundler/dependency.rb +10 -0
  44. data/lib/bundler/deployment.rb +1 -0
  45. data/lib/bundler/deprecate.rb +1 -0
  46. data/lib/bundler/dsl.rb +19 -9
  47. data/lib/bundler/endpoint_specification.rb +37 -8
  48. data/lib/bundler/env.rb +4 -3
  49. data/lib/bundler/environment.rb +1 -0
  50. data/lib/bundler/errors.rb +51 -32
  51. data/lib/bundler/fetcher.rb +44 -30
  52. data/lib/bundler/fetcher/base.rb +3 -2
  53. data/lib/bundler/fetcher/compact_index.rb +98 -0
  54. data/lib/bundler/fetcher/dependency.rb +36 -36
  55. data/lib/bundler/fetcher/downloader.rb +14 -8
  56. data/lib/bundler/fetcher/index.rb +28 -5
  57. data/lib/bundler/friendly_errors.rb +93 -85
  58. data/lib/bundler/gem_helper.rb +20 -21
  59. data/lib/bundler/gem_helpers.rb +9 -2
  60. data/lib/bundler/gem_remote_fetcher.rb +1 -0
  61. data/lib/bundler/gem_tasks.rb +1 -0
  62. data/lib/bundler/graph.rb +16 -17
  63. data/lib/bundler/index.rb +4 -6
  64. data/lib/bundler/injector.rb +1 -0
  65. data/lib/bundler/inline.rb +8 -2
  66. data/lib/bundler/installer.rb +4 -4
  67. data/lib/bundler/installer/gem_installer.rb +1 -0
  68. data/lib/bundler/installer/parallel_installer.rb +3 -2
  69. data/lib/bundler/installer/standalone.rb +5 -1
  70. data/lib/bundler/lazy_specification.rb +5 -2
  71. data/lib/bundler/lockfile_parser.rb +22 -15
  72. data/lib/bundler/match_platform.rb +1 -0
  73. data/lib/bundler/mirror.rb +218 -0
  74. data/lib/bundler/path_preserver.rb +12 -0
  75. data/lib/bundler/psyched_yaml.rb +1 -0
  76. data/lib/bundler/remote_specification.rb +4 -1
  77. data/lib/bundler/resolver.rb +17 -16
  78. data/lib/bundler/retry.rb +1 -0
  79. data/lib/bundler/ruby_dsl.rb +8 -2
  80. data/lib/bundler/ruby_version.rb +58 -61
  81. data/lib/bundler/rubygems_ext.rb +4 -3
  82. data/lib/bundler/rubygems_gem_installer.rb +1 -0
  83. data/lib/bundler/rubygems_integration.rb +9 -14
  84. data/lib/bundler/runtime.rb +17 -22
  85. data/lib/bundler/settings.rb +17 -21
  86. data/lib/bundler/setup.rb +1 -0
  87. data/lib/bundler/shared_helpers.rb +47 -17
  88. data/lib/bundler/similarity_detector.rb +1 -0
  89. data/lib/bundler/source.rb +2 -1
  90. data/lib/bundler/source/git.rb +2 -1
  91. data/lib/bundler/source/git/git_proxy.rb +33 -7
  92. data/lib/bundler/source/path.rb +17 -10
  93. data/lib/bundler/source/path/installer.rb +1 -0
  94. data/lib/bundler/source/rubygems.rb +4 -3
  95. data/lib/bundler/source/rubygems/remote.rb +16 -0
  96. data/lib/bundler/source_list.rb +1 -0
  97. data/lib/bundler/spec_set.rb +1 -0
  98. data/lib/bundler/ssl_certs/certificate_manager.rb +1 -0
  99. data/lib/bundler/stub_specification.rb +1 -0
  100. data/lib/bundler/templates/Executable +1 -0
  101. data/lib/bundler/templates/Gemfile +1 -0
  102. data/lib/bundler/templates/newgem/.travis.yml.tt +1 -0
  103. data/lib/bundler/templates/newgem/newgem.gemspec.tt +2 -2
  104. data/lib/bundler/ui.rb +1 -0
  105. data/lib/bundler/ui/rg_proxy.rb +1 -0
  106. data/lib/bundler/ui/shell.rb +2 -1
  107. data/lib/bundler/ui/silent.rb +1 -0
  108. data/lib/bundler/vendor/compact_index_client/lib/compact_index_client.rb +78 -0
  109. data/lib/bundler/vendor/compact_index_client/lib/compact_index_client/cache.rb +97 -0
  110. data/lib/bundler/vendor/compact_index_client/lib/compact_index_client/updater.rb +55 -0
  111. data/lib/bundler/vendor/compact_index_client/lib/compact_index_client/version.rb +3 -0
  112. data/lib/bundler/vendor/molinillo/lib/molinillo.rb +4 -0
  113. data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb +13 -0
  114. data/lib/bundler/vendor/molinillo/lib/molinillo/errors.rb +5 -0
  115. data/lib/bundler/vendor/molinillo/lib/molinillo/gem_metadata.rb +2 -1
  116. data/lib/bundler/vendor/molinillo/lib/molinillo/modules/ui.rb +2 -1
  117. data/lib/bundler/vendor/molinillo/lib/molinillo/resolution.rb +37 -14
  118. data/lib/bundler/vendor/molinillo/lib/molinillo/resolver.rb +2 -1
  119. data/lib/bundler/vendor/molinillo/lib/molinillo/state.rb +7 -7
  120. data/lib/bundler/vendored_molinillo.rb +1 -0
  121. data/lib/bundler/vendored_persistent.rb +1 -0
  122. data/lib/bundler/vendored_thor.rb +1 -0
  123. data/lib/bundler/version.rb +6 -1
  124. data/lib/bundler/vlad.rb +1 -0
  125. data/lib/bundler/worker.rb +12 -2
  126. data/man/bundle-config.ronn +6 -0
  127. data/man/bundle-gem.ronn +5 -5
  128. metadata +14 -6
  129. data/lib/bundler/gem_path_manipulation.rb +0 -8
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require "erb"
2
3
  require "rubygems/dependency_installer"
3
4
  require "bundler/worker"
@@ -129,12 +130,12 @@ module Bundler
129
130
  def generate_standalone_bundler_executable_stubs(spec)
130
131
  # double-assignment to avoid warnings about variables that will be used by ERB
131
132
  bin_path = Bundler.bin_path
133
+ standalone_path = standalone_path = Bundler.root.join(Bundler.settings[:path]).relative_path_from(bin_path)
132
134
  template = File.read(File.expand_path("../templates/Executable.standalone", __FILE__))
133
135
  ruby_command = ruby_command = Thor::Util.ruby_command
134
136
 
135
137
  spec.executables.each do |executable|
136
138
  next if executable == "bundle"
137
- standalone_path = standalone_path = Pathname(Bundler.settings[:path]).expand_path.relative_path_from(bin_path)
138
139
  executable_path = executable_path = Pathname(spec.full_gem_path).join(spec.bindir, executable).relative_path_from(bin_path)
139
140
  File.open "#{bin_path}/#{executable}", "w", 0755 do |f|
140
141
  f.puts ERB.new(template, nil, "-").result(binding)
@@ -190,9 +191,8 @@ module Bundler
190
191
  end
191
192
  end
192
193
 
193
- unless local
194
- options["local"] ? @definition.resolve_with_cache! : @definition.resolve_remotely!
195
- end
194
+ return if local
195
+ options["local"] ? @definition.resolve_with_cache! : @definition.resolve_remotely!
196
196
  end
197
197
  end
198
198
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Bundler
2
3
  class GemInstaller
3
4
  attr_reader :spec, :standalone, :worker, :force, :installer
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require "bundler/worker"
2
3
  require "bundler/installer/gem_installer"
3
4
 
@@ -45,7 +46,7 @@ class ParallelInstaller
45
46
  @dependencies ||= begin
46
47
  deps = all_dependencies.reject {|dep| ignorable_dependency? dep }
47
48
  missing = deps.reject {|dep| all_spec_names.include? dep.name }
48
- if missing.size > 0
49
+ unless missing.empty?
49
50
  raise Bundler::LockfileError, "Your Gemfile.lock is corrupt. The following #{missing.size > 1 ? "gems are" : "gem is"} missing " \
50
51
  "from the DEPENDENCIES section: '#{missing.map(&:name).join('\' \'')}'"
51
52
  end
@@ -84,7 +85,7 @@ class ParallelInstaller
84
85
  end
85
86
 
86
87
  def worker_pool
87
- @worker_pool ||= Bundler::Worker.new @size, lambda { |spec_install, worker_num|
88
+ @worker_pool ||= Bundler::Worker.new @size, "Parallel Installer", lambda { |spec_install, worker_num|
88
89
  message = Bundler::GemInstaller.new(
89
90
  spec_install.spec, @installer, @standalone, worker_num, @force
90
91
  ).install_from_spec
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Bundler
2
3
  class Standalone
3
4
  def initialize(groups, definition)
@@ -33,7 +34,7 @@ module Bundler
33
34
  end
34
35
 
35
36
  def version_dir
36
- "#{Bundler.ruby_version.engine}/#{RbConfig::CONFIG["ruby_version"]}"
37
+ "#{Bundler::RubyVersion.system.engine}/#{RbConfig::CONFIG["ruby_version"]}"
37
38
  end
38
39
 
39
40
  def bundler_path
@@ -43,6 +44,9 @@ module Bundler
43
44
  def gem_path(path, spec)
44
45
  full_path = Pathname.new(path).absolute? ? path : File.join(spec.full_gem_path, path)
45
46
  Pathname.new(full_path).relative_path_from(Bundler.root.join(bundler_path)).to_s
47
+ rescue TypeError
48
+ error_message = "#{spec.name} #{spec.version} has an invalid gemspec"
49
+ raise Gem::InvalidSpecificationException.new(error_message)
46
50
  end
47
51
  end
48
52
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require "uri"
2
3
  require "rubygems/spec_fetcher"
3
4
  require "bundler/match_platform"
@@ -35,10 +36,12 @@ module Bundler
35
36
  end
36
37
 
37
38
  def to_lock
39
+ out = String.new
40
+
38
41
  if platform == Gem::Platform::RUBY || platform.nil?
39
- out = " #{name} (#{version})\n"
42
+ out << " #{name} (#{version})\n"
40
43
  else
41
- out = " #{name} (#{version}-#{platform})\n"
44
+ out << " #{name} (#{version}-#{platform})\n"
42
45
  end
43
46
 
44
47
  dependencies.sort_by(&:to_s).uniq.each do |dep|
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require "strscan"
2
3
 
3
4
  # Some versions of the Bundler 1.1 RC series introduced corrupted
@@ -12,17 +13,18 @@ require "strscan"
12
13
 
13
14
  module Bundler
14
15
  class LockfileParser
15
- attr_reader :sources, :dependencies, :specs, :platforms, :bundler_version
16
-
17
- BUNDLED = "BUNDLED WITH"
18
- DEPENDENCIES = "DEPENDENCIES"
19
- PLATFORMS = "PLATFORMS"
20
- GIT = "GIT"
21
- GEM = "GEM"
22
- PATH = "PATH"
23
- SPECS = " specs:"
16
+ attr_reader :sources, :dependencies, :specs, :platforms, :bundler_version, :ruby_version
17
+
18
+ BUNDLED = "BUNDLED WITH".freeze
19
+ DEPENDENCIES = "DEPENDENCIES".freeze
20
+ PLATFORMS = "PLATFORMS".freeze
21
+ RUBY = "RUBY VERSION".freeze
22
+ GIT = "GIT".freeze
23
+ GEM = "GEM".freeze
24
+ PATH = "PATH".freeze
25
+ SPECS = " specs:".freeze
24
26
  OPTIONS = /^ ([a-z]+): (.*)$/i
25
- SOURCE = [GIT, GEM, PATH]
27
+ SOURCE = [GIT, GEM, PATH].freeze
26
28
 
27
29
  def initialize(lockfile)
28
30
  @platforms = []
@@ -46,6 +48,8 @@ module Bundler
46
48
  @state = :dependency
47
49
  elsif line == PLATFORMS
48
50
  @state = :platform
51
+ elsif line == RUBY
52
+ @state = :ruby
49
53
  elsif line == BUNDLED
50
54
  @state = :bundled_with
51
55
  elsif line =~ /^[^\s]/
@@ -86,7 +90,7 @@ module Bundler
86
90
  GIT => Bundler::Source::Git,
87
91
  GEM => Bundler::Source::Rubygems,
88
92
  PATH => Bundler::Source::Path,
89
- }
93
+ }.freeze
90
94
 
91
95
  def parse_source(line)
92
96
  case line
@@ -131,7 +135,7 @@ module Bundler
131
135
  end
132
136
  end
133
137
 
134
- NAME_VERSION = '(?! )(.*?)(?: \(([^-]*)(?:-(.*))?\))?'
138
+ NAME_VERSION = '(?! )(.*?)(?: \(([^-]*)(?:-(.*))?\))?'.freeze
135
139
  NAME_VERSION_2 = /^ {2}#{NAME_VERSION}(!)?$/
136
140
  NAME_VERSION_4 = /^ {4}#{NAME_VERSION}$/
137
141
  NAME_VERSION_6 = /^ {6}#{NAME_VERSION}$/
@@ -189,9 +193,12 @@ module Bundler
189
193
 
190
194
  def parse_bundled_with(line)
191
195
  line = line.strip
192
- if Gem::Version.correct?(line)
193
- @bundler_version = Gem::Version.create(line)
194
- end
196
+ return unless Gem::Version.correct?(line)
197
+ @bundler_version = Gem::Version.create(line)
198
+ end
199
+
200
+ def parse_ruby(line)
201
+ @ruby_version = line.strip
195
202
  end
196
203
  end
197
204
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require "bundler/gem_helpers"
2
3
 
3
4
  module Bundler
@@ -0,0 +1,218 @@
1
+ # frozen_string_literal: true
2
+ module Bundler
3
+ class Settings
4
+ # Class used to build the mirror set and then find a mirror for a given URI
5
+ #
6
+ # @param prober [Prober object, nil] by default a TCPSocketProbe, this object
7
+ # will be used to probe the mirror address to validate that the mirror replies.
8
+ class Mirrors
9
+ def initialize(prober = nil)
10
+ @all = Mirror.new
11
+ @prober = prober || TCPSocketProbe.new
12
+ @mirrors = {}
13
+ end
14
+
15
+ # Returns a mirror for the given uri.
16
+ #
17
+ # Depending on the uri having a valid mirror or not, it may be a
18
+ # mirror that points to the provided uri
19
+ def for(uri)
20
+ if @all.validate!(@prober).valid?
21
+ @all
22
+ else
23
+ fetch_valid_mirror_for(Settings.normalize_uri(uri))
24
+ end
25
+ end
26
+
27
+ def each
28
+ @mirrors.each do |k, v|
29
+ yield k, v.uri.to_s
30
+ end
31
+ end
32
+
33
+ def parse(key, value)
34
+ config = MirrorConfig.new(key, value)
35
+ if config.all?
36
+ mirror = @all
37
+ else
38
+ mirror = (@mirrors[config.uri] = @mirrors[config.uri] || Mirror.new)
39
+ end
40
+ config.update_mirror(mirror)
41
+ end
42
+
43
+ private
44
+
45
+ def fetch_valid_mirror_for(uri)
46
+ mirror = (@mirrors[URI(uri.to_s.downcase)] || Mirror.new(uri)).validate!(@prober)
47
+ mirror = Mirror.new(uri) unless mirror.valid?
48
+ mirror
49
+ end
50
+ end
51
+
52
+ # A mirror
53
+ #
54
+ # Contains both the uri that should be used as a mirror and the
55
+ # fallback timeout which will be used for probing if the mirror
56
+ # replies on time or not.
57
+ class Mirror
58
+ DEFAULT_FALLBACK_TIMEOUT = 0.1
59
+
60
+ attr_reader :uri, :fallback_timeout
61
+
62
+ def initialize(uri = nil, fallback_timeout = 0)
63
+ self.uri = uri
64
+ self.fallback_timeout = fallback_timeout
65
+ @valid = nil
66
+ end
67
+
68
+ def uri=(uri)
69
+ @uri = if uri.nil?
70
+ nil
71
+ else
72
+ URI(uri.to_s)
73
+ end
74
+ @valid = nil
75
+ end
76
+
77
+ def fallback_timeout=(timeout)
78
+ case timeout
79
+ when true, "true"
80
+ @fallback_timeout = DEFAULT_FALLBACK_TIMEOUT
81
+ when false, "false"
82
+ @fallback_timeout = 0
83
+ else
84
+ @fallback_timeout = timeout.to_i
85
+ end
86
+ @valid = nil
87
+ end
88
+
89
+ def ==(other)
90
+ !other.nil? && uri == other.uri && fallback_timeout == other.fallback_timeout
91
+ end
92
+
93
+ def valid?
94
+ return false if @uri.nil?
95
+ return @valid unless @valid.nil?
96
+ false
97
+ end
98
+
99
+ def validate!(probe = nil)
100
+ @valid = false if uri.nil?
101
+ if @valid.nil?
102
+ @valid = fallback_timeout == 0 || (probe || TCPSocketProbe.new).replies?(self)
103
+ end
104
+ self
105
+ end
106
+ end
107
+
108
+ # Class used to parse one configuration line
109
+ #
110
+ # Gets the configuration line and the value.
111
+ # This object provides a `update_mirror` method
112
+ # used to setup the given mirror value.
113
+ class MirrorConfig
114
+ attr_accessor :uri, :value
115
+
116
+ def initialize(config_line, value)
117
+ uri, fallback =
118
+ config_line.match(%r{^mirror\.(all|.+?)(\.fallback_timeout)?\/?$}).captures
119
+ @fallback = !fallback.nil?
120
+ @all = false
121
+ if uri == "all"
122
+ @all = true
123
+ else
124
+ @uri = Settings.normalize_uri(uri)
125
+ end
126
+ @value = value
127
+ end
128
+
129
+ def all?
130
+ @all
131
+ end
132
+
133
+ def update_mirror(mirror)
134
+ if @fallback
135
+ mirror.fallback_timeout = @value
136
+ else
137
+ mirror.uri = Settings.normalize_uri(@value)
138
+ end
139
+ end
140
+ end
141
+
142
+ # Class used for probing TCP availability for a given mirror.
143
+ class TCPSocketProbe
144
+ def replies?(mirror)
145
+ MirrorSockets.new(mirror).any? do |socket, address, timeout|
146
+ begin
147
+ socket.connect_nonblock(address)
148
+ rescue Errno::EINPROGRESS
149
+ wait_for_writtable_socket(socket, address, timeout)
150
+ rescue # Connection failed somehow, again
151
+ false
152
+ end
153
+ end
154
+ end
155
+
156
+ private
157
+
158
+ def wait_for_writtable_socket(socket, address, timeout)
159
+ if IO.select(nil, [socket], nil, timeout)
160
+ probe_writtable_socket(socket, address)
161
+ else # TCP Handshake timed out, or there is something dropping packets
162
+ false
163
+ end
164
+ end
165
+
166
+ def probe_writtable_socket(socket, address)
167
+ socket.connect_nonblock(address)
168
+ rescue Errno::EISCONN
169
+ true
170
+ rescue # Connection failed
171
+ false
172
+ end
173
+ end
174
+ end
175
+
176
+ # Class used to build the list of sockets that correspond to
177
+ # a given mirror.
178
+ #
179
+ # One mirror may correspond to many different addresses, both
180
+ # because of it having many dns entries or just because
181
+ # the network interface is both ipv4 and ipv5
182
+ class MirrorSockets
183
+ def initialize(mirror)
184
+ @timeout = mirror.fallback_timeout
185
+ @addresses = Socket.getaddrinfo(mirror.uri.host, mirror.uri.port).map do |address|
186
+ SocketAddress.new(address[0], address[3], address[1])
187
+ end
188
+ end
189
+
190
+ def any?
191
+ @addresses.any? do |address|
192
+ socket = Socket.new(Socket.const_get(address.type), Socket::SOCK_STREAM, 0)
193
+ socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
194
+ value = yield socket, address.to_socket_address, @timeout
195
+ socket.close unless socket.closed?
196
+ value
197
+ end
198
+ end
199
+ end
200
+
201
+ # Socket address builder.
202
+ #
203
+ # Given a socket type, a host and a port,
204
+ # provides a method to build sockaddr string
205
+ class SocketAddress
206
+ attr_reader :type, :host, :port
207
+
208
+ def initialize(type, host, port)
209
+ @type = type
210
+ @host = host
211
+ @port = port
212
+ end
213
+
214
+ def to_socket_address
215
+ Socket.pack_sockaddr_in(@port, @host)
216
+ end
217
+ end
218
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+ module Bundler
3
+ module PathPreserver
4
+ def self.preserve_path_in_environment(env_var, env)
5
+ original_env_var = "_ORIGINAL_#{env_var}"
6
+ original_path = env[original_env_var]
7
+ path = env[env_var]
8
+ env[original_env_var] = path if original_path.nil? || original_path.empty?
9
+ env[env_var] = original_path if path.nil? || path.empty?
10
+ end
11
+ end
12
+ end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  # Psych could be a gem, so try to ask for it
2
3
  begin
3
4
  gem "psych"
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require "uri"
2
3
  require "rubygems/spec_fetcher"
3
4
 
@@ -15,7 +16,7 @@ module Bundler
15
16
 
16
17
  def initialize(name, version, platform, spec_fetcher)
17
18
  @name = name
18
- @version = version
19
+ @version = Gem::Version.create version
19
20
  @platform = platform
20
21
  @spec_fetcher = spec_fetcher
21
22
  end
@@ -69,6 +70,8 @@ module Bundler
69
70
 
70
71
  def _remote_specification
71
72
  @_remote_specification ||= @spec_fetcher.fetch_spec([@name, @version, @platform])
73
+ @_remote_specification || raise(GemspecError, "Gemspec data for #{full_name} was" \
74
+ " missing from the server! Try installing with `--full-index` as a workaround.")
72
75
  end
73
76
 
74
77
  def method_missing(method, *args, &blk)