rubygems-update 0.8.3

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

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