rubygems-update 0.8.3

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 (96) hide show
  1. data/ChangeLog +2335 -0
  2. data/README +54 -0
  3. data/Rakefile +293 -0
  4. data/Releases +98 -0
  5. data/TODO +7 -0
  6. data/bin/gem +11 -0
  7. data/bin/gem_server +111 -0
  8. data/bin/generate_yaml_index.rb +58 -0
  9. data/bin/update_rubygems +18 -0
  10. data/doc/doc.css +73 -0
  11. data/doc/makedoc.rb +4 -0
  12. data/examples/application/an-app.gemspec +26 -0
  13. data/examples/application/bin/myapp +3 -0
  14. data/examples/application/lib/somefunctionality.rb +3 -0
  15. data/gemspecs/README +4 -0
  16. data/gemspecs/cgikit-1.1.0.gemspec +18 -0
  17. data/gemspecs/jabber4r.gemspec +26 -0
  18. data/gemspecs/linguistics.gemspec +22 -0
  19. data/gemspecs/ook.gemspec +21 -0
  20. data/gemspecs/progressbar.gemspec +22 -0
  21. data/gemspecs/redcloth.gemspec +22 -0
  22. data/gemspecs/rublog.gemspec +23 -0
  23. data/gemspecs/ruby-doom.gemspec +21 -0
  24. data/gemspecs/rubyjdwp.gemspec +21 -0
  25. data/gemspecs/statistics.gemspec +21 -0
  26. data/lib/rubygems.rb +353 -0
  27. data/lib/rubygems/builder.rb +54 -0
  28. data/lib/rubygems/cmd_manager.rb +127 -0
  29. data/lib/rubygems/command.rb +191 -0
  30. data/lib/rubygems/config_file.rb +57 -0
  31. data/lib/rubygems/doc_manager.rb +94 -0
  32. data/lib/rubygems/format.rb +65 -0
  33. data/lib/rubygems/gem_commands.rb +925 -0
  34. data/lib/rubygems/gem_runner.rb +23 -0
  35. data/lib/rubygems/installer.rb +621 -0
  36. data/lib/rubygems/loadpath_manager.rb +108 -0
  37. data/lib/rubygems/old_format.rb +150 -0
  38. data/lib/rubygems/open-uri.rb +604 -0
  39. data/lib/rubygems/package.rb +740 -0
  40. data/lib/rubygems/remote_installer.rb +499 -0
  41. data/lib/rubygems/rubygems_version.rb +6 -0
  42. data/lib/rubygems/source_index.rb +130 -0
  43. data/lib/rubygems/specification.rb +613 -0
  44. data/lib/rubygems/user_interaction.rb +176 -0
  45. data/lib/rubygems/validator.rb +148 -0
  46. data/lib/rubygems/version.rb +279 -0
  47. data/lib/ubygems.rb +4 -0
  48. data/pkgs/sources/lib/sources.rb +6 -0
  49. data/pkgs/sources/sources.gemspec +14 -0
  50. data/post-install.rb +75 -0
  51. data/redist/session.gem +433 -0
  52. data/scripts/buildtests.rb +25 -0
  53. data/scripts/gemdoc.rb +62 -0
  54. data/scripts/runtest.rb +17 -0
  55. data/scripts/specdoc.rb +164 -0
  56. data/setup.rb +1360 -0
  57. data/test/bogussources.rb +5 -0
  58. data/test/data/legacy/keyedlist-0.4.0.ruby +11 -0
  59. data/test/data/legacy/keyedlist-0.4.0.yaml +16 -0
  60. data/test/data/lib/code.rb +1 -0
  61. data/test/data/one/README.one +1 -0
  62. data/test/data/one/lib/one.rb +3 -0
  63. data/test/data/one/one.gemspec +17 -0
  64. data/test/data/one/one.yaml +40 -0
  65. data/test/functional.rb +145 -0
  66. data/test/gemenvironment.rb +45 -0
  67. data/test/gemutilities.rb +18 -0
  68. data/test/insure_session.rb +46 -0
  69. data/test/mock/gems/gems/sources-0.0.1/lib/sources.rb +5 -0
  70. data/test/mock/gems/specifications/sources-0.0.1.gemspec +8 -0
  71. data/test/mockgemui.rb +45 -0
  72. data/test/onegem.rb +23 -0
  73. data/test/simple_gem.rb +66 -0
  74. data/test/test_builder.rb +13 -0
  75. data/test/test_cached_fetcher.rb +60 -0
  76. data/test/test_check_command.rb +28 -0
  77. data/test/test_command.rb +130 -0
  78. data/test/test_configfile.rb +36 -0
  79. data/test/test_format.rb +70 -0
  80. data/test/test_gemloadpaths.rb +45 -0
  81. data/test/test_gempaths.rb +115 -0
  82. data/test/test_loadmanager.rb +40 -0
  83. data/test/test_local_cache.rb +157 -0
  84. data/test/test_package.rb +600 -0
  85. data/test/test_parse_commands.rb +179 -0
  86. data/test/test_process_commands.rb +21 -0
  87. data/test/test_remote_fetcher.rb +162 -0
  88. data/test/test_remote_installer.rb +154 -0
  89. data/test/test_source_index.rb +58 -0
  90. data/test/test_specification.rb +286 -0
  91. data/test/test_validator.rb +53 -0
  92. data/test/test_version_comparison.rb +204 -0
  93. data/test/testgem.rc +6 -0
  94. data/test/user_capture.rb +1 -0
  95. data/test/yaml_data.rb +59 -0
  96. metadata +151 -0
@@ -0,0 +1,499 @@
1
+ require 'rubygems'
2
+ require 'socket'
3
+ require 'fileutils'
4
+
5
+ module Gem
6
+ class DependencyError < Gem::Exception; end
7
+ class RemoteSourceException < Gem::Exception; end
8
+ class GemNotFoundException < Gem::Exception; end
9
+ class RemoteInstallationCancelled < Gem::Exception; end
10
+
11
+ # RemoteSourceFetcher handles the details of fetching gems and gem
12
+ # information from a remote source.
13
+ class RemoteSourceFetcher
14
+ include UserInteraction
15
+
16
+ # Initialize a remote fetcher using the source URI (and possible
17
+ # proxy information).
18
+ def initialize(source_uri, proxy)
19
+ @uri = source_uri
20
+ @http_proxy = proxy
21
+ if @http_proxy == true
22
+ @http_proxy = ENV['http_proxy'] || ENV['HTTP_PROXY']
23
+ end
24
+ end
25
+
26
+ # The uncompressed +size+ of the source's directory (e.g. source
27
+ # info).
28
+ def size
29
+ @size ||= get_size("/yaml")
30
+ end
31
+
32
+ # Fetch the data from the source at the given path.
33
+ def fetch_path(path="")
34
+ read_data(@uri + path)
35
+ end
36
+
37
+ # Get the source index from the gem source. The source index is a
38
+ # directory of the gems available on the source, formatted as a
39
+ # Gem::Cache object. The cache object allows easy searching for
40
+ # gems by name and version requirement.
41
+ #
42
+ # Notice that the gem specs in the cache are adequate for searches
43
+ # and queries, but may have some information elided (hence
44
+ # "abbreviated").
45
+ def source_index
46
+ say "Updating Gem source index for: #{@uri}"
47
+ begin
48
+ require 'zlib'
49
+ yaml_spec = fetch_path("/yaml.Z")
50
+ yaml_spec = Zlib::Inflate.inflate(yaml_spec)
51
+ rescue
52
+ yaml_spec = nil
53
+ end
54
+ begin
55
+ yaml_spec = fetch_path("/yaml") unless yaml_spec
56
+ r = convert_spec(yaml_spec)
57
+ rescue SocketError => e
58
+ raise RemoteSourceException.new("Error fetching remote gem cache: #{e.to_s}")
59
+ end
60
+ end
61
+
62
+ private
63
+
64
+ # Connect to the source host/port, using a proxy if needed.
65
+ def connect_to(host, port)
66
+ if @http_proxy
67
+ proxy_uri = URI.parse(@http_proxy)
68
+ Net::HTTP::Proxy(proxy_uri.host, proxy_uri.port).new(host, port)
69
+ else
70
+ Net::HTTP.new(host, port)
71
+ end
72
+ end
73
+
74
+ # Get the size of the (non-compressed) data from the source at the
75
+ # given path.
76
+ def get_size(path)
77
+ read_size(@uri + path)
78
+ end
79
+
80
+ # Read the size of the (source based) URI using an HTTP HEAD
81
+ # command.
82
+ def read_size(uri)
83
+ require 'net/http'
84
+ require 'uri'
85
+ u = URI.parse(uri)
86
+ http = connect_to(u.host, u.port)
87
+ path = (u.path == "") ? "/" : u.path
88
+ resp = http.head(path)
89
+ fail RemoteSourceException, "HTTP Response #{resp.code}" if resp.code !~ /^2/
90
+ resp['content-length'].to_i
91
+ end
92
+
93
+ # Read the data from the (source based) URI.
94
+ def read_data(uri)
95
+ require 'rubygems/open-uri'
96
+ open(uri,
97
+ "User-Agent" => "RubyGems/#{Gem::RubyGemsVersion}",
98
+ :proxy => @http_proxy
99
+ ) do |input|
100
+ input.read
101
+ end
102
+ end
103
+
104
+ # Convert the yamlized string spec into a real spec (actually,
105
+ # these are hashes of specs.).
106
+ def convert_spec(yaml_spec)
107
+ YAML.load(reduce_spec(yaml_spec)) or
108
+ raise "Didn't get a valid YAML document"
109
+ end
110
+
111
+ # This reduces the source spec in size so that YAML bugs with
112
+ # large data sets will be dodged. Obviously this is a workaround,
113
+ # but it allows Gems to continue to work until the YAML bug is
114
+ # fixed.
115
+ def reduce_spec(yaml_spec)
116
+ result = ""
117
+ state = :copy
118
+ yaml_spec.each do |line|
119
+ if state == :copy && line =~ /^\s+files:\s*$/
120
+ state = :skip
121
+ result << line.sub(/$/, " []")
122
+ elsif state == :skip
123
+ if line !~ /^\s+-/
124
+ state = :copy
125
+ end
126
+ end
127
+ result << line if state == :copy
128
+ end
129
+ result
130
+ end
131
+
132
+ class << self
133
+ # Sent by the client when it is done with all the sources,
134
+ # allowing any cleanup activity to take place.
135
+ def finish
136
+ # Nothing to do
137
+ end
138
+ end
139
+ end
140
+
141
+ # LocalSourceInfoCache implements the cache management policy on
142
+ # where the source info is stored on local file system. There are
143
+ # two possible cache locations: (1) the system wide cache, and (2)
144
+ # the user specific cache.
145
+ #
146
+ # * The system cache is prefered if it is writable (or can be
147
+ # created).
148
+ # * The user cache is used if the system cache is not writable (or
149
+ # can not be created).
150
+ #
151
+ # Once a cache is selected, it will be used for all operations. It
152
+ # will not switch between cache files dynamically.
153
+ #
154
+ class LocalSourceInfoCache
155
+
156
+ # The most recent cache data.
157
+ def cache_data
158
+ @dirty = false
159
+ @cache_data ||= read_cache
160
+ end
161
+
162
+ # Write data to the proper cache.
163
+ def write_cache
164
+ data = cache_data
165
+ open(writable_file, "w") do |f|
166
+ f.puts data.to_yaml
167
+ end
168
+ end
169
+
170
+ # The name of the system cache file.
171
+ def system_cache_file
172
+ @sysetm_cache ||= File.join(Gem.dir, "source_cache")
173
+ end
174
+
175
+ # The name of the user cache file.
176
+ def user_cache_file
177
+ @user_cache ||=
178
+ ENV['GEMCACHE'] || File.join(Gem.user_home, ".gem/source_cache")
179
+ end
180
+
181
+ # Mark the cache as updated (i.e. dirty).
182
+ def update
183
+ @dirty = true
184
+ end
185
+
186
+ # Write the cache to a local file (if it is dirty).
187
+ def flush
188
+ write_cache if @dirty
189
+ @dirty = false
190
+ end
191
+
192
+ private
193
+
194
+ # Find a writable cache file.
195
+ def writable_file
196
+ @cache_file
197
+ end
198
+
199
+ # Read the most current cache data.
200
+ def read_cache
201
+ @cache_file = select_cache_file
202
+ open(@cache_file) { |f| YAML.load(f) } || {}
203
+ end
204
+
205
+ # Select a writable cache file
206
+ def select_cache_file
207
+ try_file(system_cache_file) or
208
+ try_file(user_cache_file) or
209
+ fail "Unable to locate a writable cache file."
210
+ end
211
+
212
+ # Determine if +fn+ is a candidate for a cache file. Return fn if
213
+ # it is. Return nil if it is not.
214
+ def try_file(fn)
215
+ return fn if File.writable?(fn)
216
+ return nil if File.exist?(fn)
217
+ dir = File.dirname(fn)
218
+ if ! File.exist? dir
219
+ begin
220
+ FileUtils.mkdir_p(dir)
221
+ rescue RuntimeError
222
+ return nil
223
+ end
224
+ end
225
+ if File.writable?(dir)
226
+ FileUtils.touch fn
227
+ return fn
228
+ end
229
+ nil
230
+ end
231
+ end
232
+
233
+
234
+ # CachedFetcher is a decorator that adds local file caching to
235
+ # RemoteSourceFetcher objects.
236
+ class CachedFetcher
237
+
238
+ # Create a cached fetcher (based on a RemoteSourceFetcher) for the
239
+ # source at +source_uri+ (through the proxy +proxy+).
240
+ def initialize(source_uri, proxy)
241
+ @source_uri = source_uri
242
+ @fetcher = RemoteSourceFetcher.new(source_uri, proxy)
243
+ end
244
+
245
+ # The uncompressed +size+ of the source's directory (e.g. source
246
+ # info).
247
+ def size
248
+ @fetcher.size
249
+ end
250
+
251
+ # Fetch the data from the source at the given path.
252
+ def fetch_path(path="")
253
+ @fetcher.fetch_path(path)
254
+ end
255
+
256
+ # Get the source index from the gem source. The source index is a
257
+ # directory of the gems available on the source, formatted as a
258
+ # Gem::Cache object. The cache object allows easy searching for
259
+ # gems by name and version requirement.
260
+ #
261
+ # Notice that the gem specs in the cache are adequate for searches
262
+ # and queries, but may have some information elided (hence
263
+ # "abbreviated").
264
+ def source_index
265
+ cache = manager.cache_data[@source_uri]
266
+ if cache && cache['size'] == @fetcher.size
267
+ cache['cache']
268
+ else
269
+ result = @fetcher.source_index
270
+ manager.cache_data[@source_uri] = {
271
+ 'size' => @fetcher.size,
272
+ 'cache' => result,
273
+ }
274
+ manager.update
275
+ result
276
+ end
277
+ end
278
+
279
+ # Flush the cache to a local file, if needed.
280
+ def flush
281
+ manager.flush
282
+ end
283
+
284
+ private
285
+
286
+ # The cache manager for this cached source.
287
+ def manager
288
+ self.class.manager
289
+ end
290
+
291
+ # The cache is shared between all caching fetchers, so the cache
292
+ # is put in the class object.
293
+ class << self
294
+
295
+ # The Cache manager for all instances of this class.
296
+ def manager
297
+ @manager ||= LocalSourceInfoCache.new
298
+ end
299
+
300
+
301
+ # Sent by the client when it is done with all the sources,
302
+ # allowing any cleanup activity to take place.
303
+ def finish
304
+ manager.flush
305
+ end
306
+ end
307
+
308
+ end
309
+
310
+ class RemoteInstaller
311
+ include UserInteraction
312
+
313
+ # <tt>http_proxy</tt>::
314
+ # * [String]: explicit specification of proxy; overrides any
315
+ # environment variable setting
316
+ # * nil: respect environment variables
317
+ # * <tt>:no_proxy</tt>: ignore environment variables and _don't_
318
+ # use a proxy
319
+ #
320
+ def initialize(http_proxy=nil)
321
+ # Ensure http_proxy env vars are used if no proxy explicitly supplied.
322
+ @http_proxy =
323
+ case http_proxy
324
+ when :no_proxy
325
+ false
326
+ when nil
327
+ true
328
+ else
329
+ http_proxy.to_str
330
+ end
331
+ @fetcher_class = CachedFetcher
332
+ end
333
+
334
+ # This method will install package_name onto the local system.
335
+ #
336
+ # gem_name::
337
+ # [String] Name of the Gem to install
338
+ #
339
+ # version_requirement::
340
+ # [default = "> 0.0.0"] Gem version requirement to install
341
+ #
342
+ # Returns::
343
+ # an array of Gem::Specification objects, one for each gem installed.
344
+ #
345
+ def install(gem_name,
346
+ version_requirement = "> 0.0.0",
347
+ force=false,
348
+ install_dir=Gem.dir,
349
+ install_stub=true)
350
+ unless version_requirement.respond_to?(:satisfied_by?)
351
+ version_requirement = Version::Requirement.new(version_requirement)
352
+ end
353
+ installed_gems = []
354
+ caches = source_index_hash
355
+ spec, source = find_gem_to_install(gem_name, version_requirement, caches)
356
+ dependencies = find_dependencies_not_installed(spec.dependencies)
357
+ installed_gems << install_dependencies(dependencies, force, install_dir)
358
+ cache_dir = File.join(install_dir, "cache")
359
+ destination_file = File.join(cache_dir, spec.full_name + ".gem")
360
+ download_gem(destination_file, source, spec)
361
+ installer = new_installer(destination_file)
362
+ installed_gems.unshift installer.install(force, install_dir, install_stub)
363
+ installed_gems.flatten
364
+ end
365
+
366
+ # Search Gem repository for a gem by specifying all or part of
367
+ # the Gem's name
368
+ def search(pattern_to_match)
369
+ results = []
370
+ caches = source_index_hash
371
+ caches.each do |cache|
372
+ results << cache[1].search(pattern_to_match)
373
+ end
374
+ results
375
+ end
376
+
377
+ # Return a list of the sources that we can download gems from
378
+ def sources
379
+ unless @sources
380
+ require_gem("sources")
381
+ @sources = Gem.sources
382
+ end
383
+ @sources
384
+ end
385
+
386
+ # Return a hash mapping the available source names to the source
387
+ # index of that source.
388
+ def source_index_hash
389
+ result = {}
390
+ sources.each do |source|
391
+ result[source] = fetch_source(source)
392
+ end
393
+ @fetcher_class.finish
394
+ result
395
+ end
396
+
397
+ # Return the source info for the given source. The
398
+ def fetch_source(source)
399
+ rsf = @fetcher_class.new(source, @http_proxy)
400
+ rsf.source_index
401
+ end
402
+
403
+ # Find a gem to be installed by interacting with the user.
404
+ def find_gem_to_install(gem_name, version_requirement, caches)
405
+ max_version = Version.new("0.0.0")
406
+ specs_n_sources = []
407
+ caches.each do |source, cache|
408
+ cache.each do |name, spec|
409
+ if (/#{gem_name}/i === name &&
410
+ version_requirement.satisfied_by?(spec.version))
411
+ specs_n_sources << [spec, source]
412
+ end
413
+ end
414
+ end
415
+ if specs_n_sources.size == 0
416
+ raise GemNotFoundException.new(
417
+ "Could not find #{gem_name} (#{version_requirement}) in the repository")
418
+ end
419
+ specs_n_sources = specs_n_sources.sort_by { |x| x[0].version }.reverse
420
+ if specs_n_sources.reject { |item|
421
+ item[0].platform.nil? || item[0].platform==Platform::RUBY
422
+ }.size == 0
423
+ # only non-binary gems...return latest
424
+ return specs_n_sources.first
425
+ end
426
+ list = specs_n_sources.collect {|item|
427
+ "#{item[0].name} #{item[0].version} (#{item[0].platform.to_s})"
428
+ }
429
+ list << "Cancel installation"
430
+ string, index = choose_from_list(
431
+ "Select which gem to install for your platform (#{RUBY_PLATFORM})",
432
+ list)
433
+ raise RemoteInstallationCancelled.new("Installation of #{gem_name} cancelled.") if
434
+ index == (list.size - 1)
435
+ specs_n_sources[index]
436
+ end
437
+
438
+ def find_dependencies_not_installed(dependencies)
439
+ to_install = []
440
+ dependencies.each do |dependency|
441
+ begin
442
+ require_gem(dependency.name, *dependency.requirement_list)
443
+ rescue LoadError => e
444
+ to_install.push dependency
445
+ end
446
+ end
447
+ to_install
448
+ end
449
+
450
+ # Install all the given dependencies. Returns an array of
451
+ # Gem::Specification objects, one for each dependency installed.
452
+ #
453
+ # TODO: For now, we recursively install, but this is not the right
454
+ # way to do things (e.g. if a package fails to download, we
455
+ # shouldn't install anything).
456
+ def install_dependencies(dependencies, force, install_dir)
457
+ installed_gems = []
458
+ dependencies.each do |dependency|
459
+ if ask_yes_no("Install required dependency #{dependency.name}?", true)
460
+ remote_installer = RemoteInstaller.new(
461
+ if @http_proxy == false
462
+ :no_proxy
463
+ elsif @http_proxy == true
464
+ else
465
+ @http_proxy
466
+ end
467
+ )
468
+ installed_gems << remote_installer.install(
469
+ dependency.name,
470
+ dependency.version_requirements,
471
+ force,
472
+ install_dir)
473
+ else
474
+ raise DependencyError.new("Required dependency #{dependency.name} not installed")
475
+ end
476
+ end
477
+ installed_gems
478
+ end
479
+
480
+ def download_gem(destination_file, source, spec)
481
+ rsf = @fetcher_class.new(source, @http_proxy)
482
+ path = "/gems/#{spec.full_name}.gem"
483
+ response = rsf.fetch_path(path)
484
+ write_gem_to_file(response, destination_file)
485
+ end
486
+
487
+ def write_gem_to_file(body, destination_file)
488
+ FileUtils.mkdir_p(File.dirname(destination_file)) unless File.exist?(destination_file)
489
+ File.open(destination_file, 'wb') do |out|
490
+ out.write(body)
491
+ end
492
+ end
493
+
494
+ def new_installer(gem)
495
+ return Installer.new(gem)
496
+ end
497
+ end
498
+
499
+ end