rubygems-update 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of rubygems-update might be problematic. Click here for more details.

Files changed (65) hide show
  1. data.tar.gz.sig +2 -4
  2. data/ChangeLog +155 -0
  3. data/Rakefile +20 -5
  4. data/doc/release_notes/rel_1_3_0.rdoc +125 -0
  5. data/lib/rubygems.rb +107 -15
  6. data/lib/rubygems/commands/contents_command.rb +1 -1
  7. data/lib/rubygems/commands/environment_command.rb +40 -0
  8. data/lib/rubygems/commands/help_command.rb +2 -2
  9. data/lib/rubygems/commands/install_command.rb +15 -0
  10. data/lib/rubygems/commands/lock_command.rb +15 -6
  11. data/lib/rubygems/commands/outdated_command.rb +1 -1
  12. data/lib/rubygems/commands/pristine_command.rb +1 -1
  13. data/lib/rubygems/commands/query_command.rb +14 -9
  14. data/lib/rubygems/commands/rdoc_command.rb +5 -1
  15. data/lib/rubygems/commands/specification_command.rb +2 -2
  16. data/lib/rubygems/commands/unpack_command.rb +1 -1
  17. data/lib/rubygems/commands/update_command.rb +23 -14
  18. data/lib/rubygems/commands/which_command.rb +4 -3
  19. data/lib/rubygems/config_file.rb +25 -3
  20. data/lib/rubygems/defaults.rb +42 -11
  21. data/lib/rubygems/dependency_installer.rb +19 -15
  22. data/lib/rubygems/doc_manager.rb +162 -115
  23. data/lib/rubygems/ext/builder.rb +2 -2
  24. data/lib/rubygems/ext/rake_builder.rb +1 -1
  25. data/lib/rubygems/gem_path_searcher.rb +35 -19
  26. data/lib/rubygems/indexer.rb +15 -6
  27. data/lib/rubygems/install_update_options.rb +7 -0
  28. data/lib/rubygems/installer.rb +85 -9
  29. data/lib/rubygems/local_remote_options.rb +7 -0
  30. data/lib/rubygems/package/tar_reader.rb +7 -7
  31. data/lib/rubygems/platform.rb +1 -18
  32. data/lib/rubygems/remote_fetcher.rb +45 -54
  33. data/lib/rubygems/rubygems_version.rb +1 -1
  34. data/lib/rubygems/source_index.rb +22 -7
  35. data/lib/rubygems/source_info_cache.rb +9 -0
  36. data/lib/rubygems/spec_fetcher.rb +18 -20
  37. data/lib/rubygems/specification.rb +502 -293
  38. data/lib/rubygems/test_utilities.rb +19 -8
  39. data/lib/rubygems/uninstaller.rb +60 -26
  40. data/setup.rb +15 -7
  41. data/test/gemutilities.rb +84 -0
  42. data/test/mockgemui.rb +22 -2
  43. data/test/test_gem.rb +118 -13
  44. data/test/test_gem_commands_dependency_command.rb +1 -1
  45. data/test/test_gem_commands_list_command.rb +37 -0
  46. data/test/test_gem_commands_lock_command.rb +69 -0
  47. data/test/test_gem_commands_query_command.rb +40 -1
  48. data/test/test_gem_commands_uninstall_command.rb +60 -0
  49. data/test/test_gem_config_file.rb +51 -17
  50. data/test/test_gem_ext_configure_builder.rb +9 -9
  51. data/test/test_gem_ext_rake_builder.rb +21 -12
  52. data/test/test_gem_gem_path_searcher.rb +15 -7
  53. data/test/test_gem_indexer.rb +35 -1
  54. data/test/test_gem_install_update_options.rb +26 -5
  55. data/test/test_gem_installer.rb +93 -21
  56. data/test/test_gem_local_remote_options.rb +12 -0
  57. data/test/test_gem_platform.rb +6 -13
  58. data/test/test_gem_remote_fetcher.rb +121 -31
  59. data/test/test_gem_source_index.rb +74 -21
  60. data/test/test_gem_source_info_cache.rb +2 -1
  61. data/test/test_gem_spec_fetcher.rb +13 -3
  62. data/test/test_gem_specification.rb +13 -7
  63. data/test/test_gem_uninstaller.rb +25 -2
  64. metadata +6 -2
  65. metadata.gz.sig +0 -0
@@ -116,7 +116,7 @@ class Gem::Indexer
116
116
  open @specs_index, 'wb' do |io|
117
117
  specs = index.sort.map do |_, spec|
118
118
  platform = spec.original_platform
119
- platform = Gem::Platform::RUBY if platform.nil?
119
+ platform = Gem::Platform::RUBY if platform.nil? or platform.empty?
120
120
  [spec.name, spec.version, platform]
121
121
  end
122
122
 
@@ -129,7 +129,9 @@ class Gem::Indexer
129
129
 
130
130
  open @latest_specs_index, 'wb' do |io|
131
131
  specs = index.latest_specs.sort.map do |spec|
132
- [spec.name, spec.version, spec.original_platform]
132
+ platform = spec.original_platform
133
+ platform = Gem::Platform::RUBY if platform.nil? or platform.empty?
134
+ [spec.name, spec.version, platform]
133
135
  end
134
136
 
135
137
  specs = compact_specs specs
@@ -283,10 +285,7 @@ class Gem::Indexer
283
285
  # Builds and installs indexicies.
284
286
 
285
287
  def generate_index
286
- FileUtils.rm_rf @directory
287
- FileUtils.mkdir_p @directory, :mode => 0700
288
- FileUtils.mkdir_p @quick_marshal_dir
289
-
288
+ make_temp_directories
290
289
  index = collect_specs
291
290
  build_indicies index
292
291
  install_indicies
@@ -322,6 +321,15 @@ class Gem::Indexer
322
321
  end
323
322
  end
324
323
 
324
+ ##
325
+ # Make directories for index generation
326
+
327
+ def make_temp_directories
328
+ FileUtils.rm_rf @directory
329
+ FileUtils.mkdir_p @directory, :mode => 0700
330
+ FileUtils.mkdir_p @quick_marshal_dir
331
+ end
332
+
325
333
  ##
326
334
  # Ensure +path+ and path with +extension+ are identical.
327
335
 
@@ -344,6 +352,7 @@ class Gem::Indexer
344
352
  spec.description = sanitize_string(spec.description)
345
353
  spec.post_install_message = sanitize_string(spec.post_install_message)
346
354
  spec.authors = spec.authors.collect { |a| sanitize_string(a) }
355
+
347
356
  spec
348
357
  end
349
358
 
@@ -90,6 +90,13 @@ module Gem::InstallUpdateOptions
90
90
  options[:format_executable] = value
91
91
  end
92
92
 
93
+ add_option(:"Install/Update", '--[no-]user-install',
94
+ 'Install in user\'s home directory instead',
95
+ 'of GEM_HOME. Defaults to using home directory',
96
+ 'only if GEM_HOME is not writable.') do |value, options|
97
+ options[:user_install] = value
98
+ end
99
+
93
100
  add_option(:"Install/Update", "--development",
94
101
  "Install any additional development",
95
102
  "dependencies") do |value, options|
@@ -20,6 +20,7 @@ require 'rubygems/require_paths_builder'
20
20
  # filesystem including unpacking the gem into its gem dir, installing the
21
21
  # gemspec in the specifications dir, storing the cached gem in the cache dir,
22
22
  # and installing either wrappers or symlinks for executables.
23
+
23
24
  class Gem::Installer
24
25
 
25
26
  ##
@@ -31,8 +32,36 @@ class Gem::Installer
31
32
 
32
33
  include Gem::RequirePathsBuilder
33
34
 
35
+ ##
36
+ # The directory a gem's executables will be installed into
37
+
38
+ attr_reader :bin_dir
39
+
40
+ ##
41
+ # The gem repository the gem will be installed into
42
+
43
+ attr_reader :gem_home
44
+
45
+ ##
46
+ # The Gem::Specification for the gem being installed
47
+
48
+ attr_reader :spec
49
+
50
+ @home_install_warning = false
51
+ @path_warning = false
52
+
34
53
  class << self
35
54
 
55
+ ##
56
+ # True if we've warned about ~/.gems install
57
+
58
+ attr_accessor :home_install_warning
59
+
60
+ ##
61
+ # True if we've warned about PATH not including Gem.bindir
62
+
63
+ attr_accessor :path_warning
64
+
36
65
  attr_writer :exec_format
37
66
 
38
67
  # Defaults to use Ruby's program prefix and suffix.
@@ -61,11 +90,12 @@ class Gem::Installer
61
90
  @gem = gem
62
91
 
63
92
  options = {
64
- :force => false,
65
- :install_dir => Gem.dir,
66
- :exec_format => false,
67
- :env_shebang => false,
68
- :bin_dir => nil
93
+ :bin_dir => nil,
94
+ :env_shebang => false,
95
+ :exec_format => false,
96
+ :force => false,
97
+ :install_dir => Gem.dir,
98
+ :source_index => Gem.source_index,
69
99
  }.merge options
70
100
 
71
101
  @env_shebang = options[:env_shebang]
@@ -78,6 +108,7 @@ class Gem::Installer
78
108
  @wrappers = options[:wrappers]
79
109
  @bin_dir = options[:bin_dir]
80
110
  @development = options[:development]
111
+ @source_index = options[:source_index]
81
112
 
82
113
  begin
83
114
  @format = Gem::Format.from_file_by_path @gem, @security_policy
@@ -85,6 +116,43 @@ class Gem::Installer
85
116
  raise Gem::InstallError, "invalid gem format for #{@gem}"
86
117
  end
87
118
 
119
+ begin
120
+ FileUtils.mkdir_p @gem_home
121
+ rescue Errno::EACCESS, Errno::ENOTDIR
122
+ # We'll divert to ~/.gems below
123
+ end
124
+
125
+ if not File.writable? @gem_home or
126
+ # TODO: Shouldn't have to test for existence of bindir; tests need it.
127
+ (@gem_home.to_s == Gem.dir and File.exist? Gem.bindir and
128
+ not File.writable? Gem.bindir) then
129
+ if options[:user_install] == false then # You don't want to use ~
130
+ raise Gem::FilePermissionError, @gem_home
131
+ elsif options[:user_install].nil? then
132
+ unless self.class.home_install_warning then
133
+ alert_warning "Installing to ~/.gem since #{@gem_home} and\n\t #{Gem.bindir} aren't both writable."
134
+ self.class.home_install_warning = true
135
+ end
136
+ end
137
+ options[:user_install] = true
138
+ end
139
+
140
+ if options[:user_install] then
141
+ @gem_home = Gem.user_dir
142
+
143
+ user_bin_dir = File.join(@gem_home, 'bin')
144
+ unless ENV['PATH'].split(File::PATH_SEPARATOR).include? user_bin_dir then
145
+ unless self.class.path_warning then
146
+ alert_warning "You don't have #{user_bin_dir} in your PATH,\n\t gem executables will not run."
147
+ self.class.path_warning = true
148
+ end
149
+ end
150
+
151
+ FileUtils.mkdir_p @gem_home unless File.directory? @gem_home
152
+ # If it's still not writable, you've got issues.
153
+ raise Gem::FilePermissionError, @gem_home unless File.writable? @gem_home
154
+ end
155
+
88
156
  @spec = @format.spec
89
157
 
90
158
  @gem_dir = File.join(@gem_home, "gems", @spec.full_name).untaint
@@ -131,8 +199,11 @@ class Gem::Installer
131
199
  end
132
200
  end
133
201
 
202
+ Gem.pre_install_hooks.each do |hook|
203
+ hook.call self
204
+ end
205
+
134
206
  FileUtils.mkdir_p @gem_home unless File.directory? @gem_home
135
- raise Gem::FilePermissionError, @gem_home unless File.writable? @gem_home
136
207
 
137
208
  Gem.ensure_gem_subdirectories @gem_home
138
209
 
@@ -156,7 +227,11 @@ class Gem::Installer
156
227
  @spec.loaded_from = File.join(@gem_home, 'specifications',
157
228
  "#{@spec.full_name}.gemspec")
158
229
 
159
- Gem.source_index.add_spec @spec
230
+ @source_index.add_spec @spec
231
+
232
+ Gem.post_install_hooks.each do |hook|
233
+ hook.call self
234
+ end
160
235
 
161
236
  return @spec
162
237
  rescue Zlib::GzipFile::Error
@@ -179,10 +254,10 @@ class Gem::Installer
179
254
  end
180
255
 
181
256
  ##
182
- # True if the gems in Gem.source_index satisfy +dependency+.
257
+ # True if the gems in the source_index satisfy +dependency+.
183
258
 
184
259
  def installation_satisfies_dependency?(dependency)
185
- Gem.source_index.find_name(dependency.name, dependency.version_requirements).size > 0
260
+ @source_index.find_name(dependency.name, dependency.version_requirements).size > 0
186
261
  end
187
262
 
188
263
  ##
@@ -206,6 +281,7 @@ class Gem::Installer
206
281
 
207
282
  file_name = File.join @gem_home, 'specifications',
208
283
  "#{@spec.full_name}.gemspec"
284
+
209
285
  file_name.untaint
210
286
 
211
287
  File.open(file_name, "w") do |file|
@@ -109,6 +109,13 @@ module Gem::LocalRemoteOptions
109
109
  end
110
110
  end
111
111
 
112
+ ##
113
+ # Is fetching of local and remote information enabled?
114
+
115
+ def both?
116
+ options[:domain] == :both
117
+ end
118
+
112
119
  ##
113
120
  # Is local fetching enabled?
114
121
 
@@ -46,17 +46,17 @@ class Gem::Package::TarReader
46
46
  yield entry
47
47
 
48
48
  skip = (512 - (size % 512)) % 512
49
+ pending = size - entry.bytes_read
49
50
 
50
- if @io.respond_to? :seek then
51
+ begin
51
52
  # avoid reading...
52
- @io.seek(size - entry.bytes_read, IO::SEEK_CUR)
53
- else
54
- pending = size - entry.bytes_read
55
-
53
+ @io.seek pending, IO::SEEK_CUR
54
+ pending = 0
55
+ rescue Errno::EINVAL, NameError
56
56
  while pending > 0 do
57
- bread = @io.read([pending, 4096].min).size
57
+ bytes_read = @io.read([pending, 4096].min).size
58
58
  raise UnexpectedEOF if @io.eof?
59
- pending -= bread
59
+ pending -= bytes_read
60
60
  end
61
61
  end
62
62
 
@@ -13,23 +13,6 @@ class Gem::Platform
13
13
 
14
14
  attr_accessor :version
15
15
 
16
- DEPRECATED_CONSTS = [
17
- :DARWIN,
18
- :LINUX_586,
19
- :MSWIN32,
20
- :PPC_DARWIN,
21
- :WIN32,
22
- :X86_LINUX
23
- ]
24
-
25
- def self.const_missing(name) # TODO remove six months from 2007/12
26
- if DEPRECATED_CONSTS.include? name then
27
- raise NameError, "#{name} has been removed, use CURRENT instead"
28
- else
29
- super
30
- end
31
- end
32
-
33
16
  def self.local
34
17
  arch = Gem::ConfigMap[:arch]
35
18
  arch = "#{arch}_60" if arch =~ /mswin32$/
@@ -73,7 +56,7 @@ class Gem::Platform
73
56
  else cpu
74
57
  end
75
58
 
76
- if arch.length == 2 and arch.last =~ /^\d+$/ then # for command-line
59
+ if arch.length == 2 and arch.last =~ /^\d+(\.\d+)?$/ then # for command-line
77
60
  @os, @version = arch
78
61
  return
79
62
  end
@@ -1,5 +1,6 @@
1
1
  require 'net/http'
2
2
  require 'stringio'
3
+ require 'time'
3
4
  require 'uri'
4
5
 
5
6
  require 'rubygems'
@@ -74,7 +75,12 @@ class Gem::RemoteFetcher
74
75
  # always replaced.
75
76
 
76
77
  def download(spec, source_uri, install_dir = Gem.dir)
77
- cache_dir = File.join install_dir, 'cache'
78
+ if File.writable?(install_dir)
79
+ cache_dir = File.join install_dir, 'cache'
80
+ else
81
+ cache_dir = File.join(Gem.user_dir, 'cache')
82
+ end
83
+
78
84
  gem_file_name = "#{spec.full_name}.gem"
79
85
  local_gem_path = File.join cache_dir, gem_file_name
80
86
 
@@ -87,7 +93,7 @@ class Gem::RemoteFetcher
87
93
  scheme = nil if scheme =~ /^[a-z]$/i
88
94
 
89
95
  case scheme
90
- when 'http' then
96
+ when 'http', 'https' then
91
97
  unless File.exist? local_gem_path then
92
98
  begin
93
99
  say "Downloading gem #{gem_file_name}" if
@@ -132,51 +138,27 @@ class Gem::RemoteFetcher
132
138
  ##
133
139
  # Downloads +uri+ and returns it as a String.
134
140
 
135
- def fetch_path(uri)
136
- open_uri_or_path(uri) do |input|
137
- input.read
138
- end
141
+ def fetch_path(uri, mtime = nil, head = false)
142
+ data = open_uri_or_path uri, mtime, head
143
+ data = Gem.gunzip data if data and not head and uri.to_s =~ /gz$/
144
+ data
139
145
  rescue FetchError
140
146
  raise
141
147
  rescue Timeout::Error
142
148
  raise FetchError.new('timed out', uri)
143
149
  rescue IOError, SocketError, SystemCallError => e
144
150
  raise FetchError.new("#{e.class}: #{e}", uri)
145
- rescue => e
146
- raise FetchError.new("#{e.class}: #{e}", uri)
147
151
  end
148
152
 
149
153
  ##
150
154
  # Returns the size of +uri+ in bytes.
151
155
 
152
- def fetch_size(uri)
153
- return File.size(get_file_uri_path(uri)) if file_uri? uri
154
-
155
- uri = URI.parse uri unless URI::Generic === uri
156
-
157
- raise ArgumentError, 'uri is not an HTTP URI' unless URI::HTTP === uri
158
-
159
- response = request uri, Net::HTTP::Head
160
-
161
- case response
162
- when Net::HTTPOK then
163
- else
164
- raise FetchError.new("bad response #{response.message} #{response.code}", uri)
165
- end
166
-
167
- if response['content-length'] then
168
- return response['content-length'].to_i
169
- else
170
- response = http.get uri.request_uri
171
- return response.body.size
172
- end
156
+ def fetch_size(uri) # TODO: phase this out
157
+ response = fetch_path(uri, nil, true)
173
158
 
174
- rescue SocketError, SystemCallError, Timeout::Error => e
175
- raise FetchError.new("#{e.message} (#{e.class})\n\tfetching size", uri)
159
+ response['content-length'].to_i
176
160
  end
177
161
 
178
- private
179
-
180
162
  def escape(str)
181
163
  return unless str
182
164
  URI.escape(str)
@@ -234,8 +216,9 @@ class Gem::RemoteFetcher
234
216
  connection = @connections[connection_id]
235
217
 
236
218
  if uri.scheme == 'https' and not connection.started? then
237
- http_obj.use_ssl = true
238
- http_obj.verify_mode = OpenSSL::SSL::VERIFY_NONE
219
+ require 'net/https'
220
+ connection.use_ssl = true
221
+ connection.verify_mode = OpenSSL::SSL::VERIFY_NONE
239
222
  end
240
223
 
241
224
  connection.start unless connection.started?
@@ -247,24 +230,27 @@ class Gem::RemoteFetcher
247
230
  # Read the data from the (source based) URI, but if it is a file:// URI,
248
231
  # read from the filesystem instead.
249
232
 
250
- def open_uri_or_path(uri, depth = 0, &block)
251
- if file_uri?(uri)
252
- open(get_file_uri_path(uri), &block)
253
- else
254
- uri = URI.parse uri unless URI::Generic === uri
233
+ def open_uri_or_path(uri, last_modified = nil, head = false, depth = 0)
234
+ raise "block is dead" if block_given?
255
235
 
256
- response = request uri
236
+ return open(get_file_uri_path(uri)) if file_uri? uri
257
237
 
258
- case response
259
- when Net::HTTPOK then
260
- block.call(StringIO.new(response.body)) if block
261
- when Net::HTTPRedirection then
262
- raise FetchError.new('too many redirects', uri) if depth > 10
238
+ uri = URI.parse uri unless URI::Generic === uri
239
+ raise ArgumentError, 'uri is not an HTTP URI' unless URI::HTTP === uri
263
240
 
264
- open_uri_or_path(response['Location'], depth + 1, &block)
265
- else
266
- raise FetchError.new("bad response #{response.message} #{response.code}", uri)
267
- end
241
+ fetch_type = head ? Net::HTTP::Head : Net::HTTP::Get
242
+ response = request uri, fetch_type, last_modified
243
+
244
+ case response
245
+ when Net::HTTPOK, Net::HTTPNotModified then
246
+ head ? response : response.body
247
+ when Net::HTTPMovedPermanently, Net::HTTPFound, Net::HTTPSeeOther,
248
+ Net::HTTPTemporaryRedirect then
249
+ raise FetchError.new('too many redirects', uri) if depth > 10
250
+
251
+ open_uri_or_path(response['Location'], last_modified, head, depth + 1)
252
+ else
253
+ raise FetchError.new("bad response #{response.message} #{response.code}", uri)
268
254
  end
269
255
  end
270
256
 
@@ -273,7 +259,7 @@ class Gem::RemoteFetcher
273
259
  # a Net::HTTP response object. request maintains a table of persistent
274
260
  # connections to reduce connect overhead.
275
261
 
276
- def request(uri, request_class = Net::HTTP::Get)
262
+ def request(uri, request_class, last_modified = nil)
277
263
  request = request_class.new uri.request_uri
278
264
 
279
265
  unless uri.nil? || uri.user.nil? || uri.user.empty? then
@@ -289,14 +275,16 @@ class Gem::RemoteFetcher
289
275
  request.add_field 'Connection', 'keep-alive'
290
276
  request.add_field 'Keep-Alive', '30'
291
277
 
278
+ if last_modified then
279
+ last_modified = last_modified.utc
280
+ request.add_field 'If-Modified-Since', last_modified.rfc2822
281
+ end
282
+
292
283
  connection = connection_for uri
293
284
 
294
285
  retried = false
295
286
  bad_response = false
296
287
 
297
- # HACK work around EOFError bug in Net::HTTP
298
- # NOTE Errno::ECONNABORTED raised a lot on Windows, and make impossible
299
- # to install gems.
300
288
  begin
301
289
  @requests[connection.object_id] += 1
302
290
  response = connection.request request
@@ -309,6 +297,9 @@ class Gem::RemoteFetcher
309
297
 
310
298
  bad_response = true
311
299
  retry
300
+ # HACK work around EOFError bug in Net::HTTP
301
+ # NOTE Errno::ECONNABORTED raised a lot on Windows, and make impossible
302
+ # to install gems.
312
303
  rescue EOFError, Errno::ECONNABORTED, Errno::ECONNRESET
313
304
  requests = @requests[connection.object_id]
314
305
  say "connection reset after #{requests} requests, retrying" if