rubygems-update 0.8.11 → 0.9.0

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 (114) hide show
  1. data/ChangeLog +199 -1
  2. data/README +6 -4
  3. data/Rakefile +46 -14
  4. data/bin/gem +6 -0
  5. data/bin/gem_mirror +7 -1
  6. data/bin/gem_server +3 -5
  7. data/bin/gemlock +127 -0
  8. data/bin/gemri +24 -0
  9. data/bin/gemwhich +39 -11
  10. data/bin/index_gem_repository.rb +280 -0
  11. data/bin/update_rubygems +6 -0
  12. data/doc/makedoc.rb +7 -1
  13. data/examples/application/lib/somefunctionality.rb +6 -0
  14. data/lib/gemconfigure.rb +6 -0
  15. data/lib/rbconfig/datadir.rb +24 -0
  16. data/lib/rubygems.rb +90 -24
  17. data/lib/rubygems/builder.rb +6 -0
  18. data/lib/rubygems/cmd_manager.rb +36 -30
  19. data/lib/rubygems/command.rb +90 -29
  20. data/lib/rubygems/config_file.rb +45 -2
  21. data/lib/rubygems/custom_require.rb +12 -6
  22. data/lib/rubygems/dependency_list.rb +6 -0
  23. data/lib/rubygems/doc_manager.rb +79 -34
  24. data/lib/rubygems/format.rb +6 -0
  25. data/lib/rubygems/gem_commands.rb +209 -88
  26. data/lib/rubygems/gem_openssl.rb +12 -0
  27. data/lib/rubygems/gem_runner.rb +25 -10
  28. data/lib/rubygems/incremental_fetcher.rb +136 -0
  29. data/lib/rubygems/installer.rb +103 -28
  30. data/lib/rubygems/loadpath_manager.rb +6 -0
  31. data/lib/rubygems/old_format.rb +6 -0
  32. data/lib/rubygems/open-uri.rb +231 -79
  33. data/lib/rubygems/package.rb +5 -3
  34. data/lib/rubygems/remote_installer.rb +89 -52
  35. data/lib/rubygems/rubygems_version.rb +1 -1
  36. data/lib/rubygems/security.rb +6 -0
  37. data/lib/rubygems/source_index.rb +115 -52
  38. data/lib/rubygems/specification.rb +92 -76
  39. data/lib/rubygems/timer.rb +6 -0
  40. data/lib/rubygems/user_interaction.rb +88 -4
  41. data/lib/rubygems/validator.rb +6 -0
  42. data/lib/rubygems/version.rb +6 -0
  43. data/lib/ubygems.rb +6 -0
  44. data/post-install.rb +6 -0
  45. data/scripts/buildtests.rb +6 -0
  46. data/scripts/gemdoc.rb +6 -0
  47. data/scripts/runtest.rb +6 -0
  48. data/scripts/specdoc.rb +6 -0
  49. data/scripts/upload_gemdoc.rb +6 -0
  50. data/test/bogussources.rb +6 -2
  51. data/test/data/PostMessage-0.0.1.gem +0 -0
  52. data/test/data/a-0.0.1.gem +0 -0
  53. data/test/data/a-0.0.2.gem +0 -0
  54. data/test/data/b-0.0.2.gem +0 -0
  55. data/test/data/c-1.2.gem +0 -0
  56. data/test/data/gemhome/cache/a-0.0.1.gem +0 -0
  57. data/test/data/gemhome/cache/a-0.0.2.gem +0 -0
  58. data/test/data/gemhome/cache/b-0.0.2.gem +0 -0
  59. data/test/data/gemhome/cache/c-1.2.gem +0 -0
  60. data/test/data/gemhome/gems/a-0.0.1/lib/code.rb +6 -0
  61. data/test/data/gemhome/gems/a-0.0.2/lib/code.rb +6 -0
  62. data/test/data/gemhome/gems/b-0.0.2/lib/code.rb +6 -0
  63. data/test/data/gemhome/gems/c-1.2/lib/code.rb +6 -0
  64. data/test/data/gemhome/specifications/a-0.0.1.gemspec +1 -1
  65. data/test/data/gemhome/specifications/a-0.0.2.gemspec +1 -1
  66. data/test/data/gemhome/specifications/b-0.0.2.gemspec +1 -1
  67. data/test/data/gemhome/specifications/c-1.2.gemspec +1 -1
  68. data/test/data/lib/code.rb +6 -0
  69. data/test/data/one/lib/one.rb +6 -0
  70. data/test/data/one/one-0.0.1.gem +0 -0
  71. data/test/data/post_install.gemspec +19 -0
  72. data/test/fake_certlib/openssl.rb +6 -0
  73. data/test/functional.rb +15 -1
  74. data/test/functional_generate_yaml_index.rb +101 -0
  75. data/test/gemenvironment.rb +8 -0
  76. data/test/gemutilities.rb +18 -2
  77. data/test/insure_session.rb +6 -0
  78. data/test/mock/gems/gems/sources-0.0.1/lib/sources.rb +6 -0
  79. data/test/mockgemui.rb +6 -0
  80. data/test/onegem.rb +12 -0
  81. data/test/simple_gem.rb +6 -0
  82. data/test/test_builder.rb +6 -0
  83. data/test/test_cached_fetcher.rb +6 -0
  84. data/test/test_check_command.rb +6 -0
  85. data/test/test_command.rb +6 -0
  86. data/test/test_configfile.rb +6 -0
  87. data/test/test_datadir.rb +32 -0
  88. data/test/test_dependency_list.rb +6 -0
  89. data/test/test_file_list.rb +6 -0
  90. data/test/test_format.rb +6 -0
  91. data/test/test_gemloadpaths.rb +6 -0
  92. data/test/test_gempaths.rb +11 -1
  93. data/test/test_incremental_fetcher.rb +175 -0
  94. data/test/test_installer.rb +30 -3
  95. data/test/test_loadmanager.rb +6 -0
  96. data/test/test_local_cache.rb +8 -2
  97. data/test/test_package.rb +6 -0
  98. data/test/test_parse_commands.rb +8 -2
  99. data/test/test_process_commands.rb +31 -0
  100. data/test/test_remote_fetcher.rb +47 -5
  101. data/test/test_remote_installer.rb +6 -0
  102. data/test/test_require_gem.rb +6 -0
  103. data/test/test_source_index.rb +27 -0
  104. data/test/test_specific_extras.rb +6 -0
  105. data/test/test_specification.rb +30 -0
  106. data/test/test_validator.rb +6 -0
  107. data/test/test_version_comparison.rb +6 -0
  108. data/test/testgem.rc +1 -0
  109. data/test/user_capture.rb +6 -0
  110. data/test/yaml_data.rb +8 -4
  111. metadata +187 -173
  112. data/bin/generate_yaml_index.rb +0 -78
  113. data/lib/rubygems/deployment.rb +0 -267
  114. data/test/test_deployment.rb +0 -93
@@ -506,10 +506,8 @@ class TarInput
506
506
  gzis.close
507
507
  end
508
508
  when 'metadata.gz.sig'
509
- Gem.ensure_ssl_available
510
509
  meta_sig = entry.read
511
510
  when 'data.tar.gz.sig'
512
- Gem.ensure_ssl_available
513
511
  data_sig = entry.read
514
512
  when 'data.tar.gz'
515
513
  if security_policy
@@ -703,7 +701,11 @@ class TarOutput
703
701
 
704
702
  TarWriter.new(os) do |inner_tar_stream|
705
703
  klass = class <<inner_tar_stream; self end
706
- klass.send(:define_method, :metadata=, &set_meta)
704
+ if RUBY_VERSION >= "1.9" then
705
+ klass.funcall(:define_method, :metadata=, &set_meta)
706
+ else
707
+ klass.send(:define_method, :metadata=, &set_meta)
708
+ end
707
709
  block.call inner_tar_stream
708
710
  end
709
711
  ensure
@@ -1,3 +1,9 @@
1
+ #--
2
+ # Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
3
+ # All rights reserved.
4
+ # See LICENSE.txt for permissions.
5
+ #++
6
+
1
7
  require 'rubygems'
2
8
  require 'socket'
3
9
  require 'fileutils'
@@ -8,18 +14,37 @@ module Gem
8
14
  class GemNotFoundException < Gem::Exception; end
9
15
  class RemoteInstallationCancelled < Gem::Exception; end
10
16
 
17
+ ####################################################################
11
18
  # RemoteSourceFetcher handles the details of fetching gems and gem
12
19
  # information from a remote source.
13
20
  class RemoteSourceFetcher
14
21
  include UserInteraction
15
22
 
16
23
  # Initialize a remote fetcher using the source URI (and possible
17
- # proxy information).
24
+ # proxy information).
25
+ # +proxy+
26
+ # * [String]: explicit specification of proxy; overrides any
27
+ # environment variable setting
28
+ # * nil: respect environment variables (HTTP_PROXY, HTTP_PROXY_USER, HTTP_PROXY_PASS)
29
+ # * <tt>:no_proxy</tt>: ignore environment variables and _don't_
30
+ # use a proxy
18
31
  def initialize(source_uri, proxy)
19
32
  @uri = normalize_uri(source_uri)
20
- @http_proxy = proxy
21
- if @http_proxy == true
22
- @http_proxy = ENV['http_proxy'] || ENV['HTTP_PROXY']
33
+ @proxy_uri =
34
+ case proxy
35
+ when :no_proxy
36
+ nil
37
+ when nil
38
+ env_proxy = ENV['http_proxy'] || ENV['HTTP_PROXY']
39
+ uri = env_proxy ? URI.parse(env_proxy) : nil
40
+ if uri and uri.user.nil? and uri.password.nil?
41
+ #Probably we have http_proxy_* variables?
42
+ uri.user = ENV['http_proxy_user'] || ENV['HTTP_PROXY_USER']
43
+ uri.password = ENV['http_proxy_pass'] || ENV['HTTP_PROXY_PASS']
44
+ end
45
+ uri
46
+ else
47
+ URI.parse(proxy.to_str)
23
48
  end
24
49
  end
25
50
 
@@ -43,7 +68,7 @@ module Gem
43
68
  # and queries, but may have some information elided (hence
44
69
  # "abbreviated").
45
70
  def source_index
46
- say "Updating Gem source index for: #{@uri}"
71
+ say "Bulk updating Gem source index for: #{@uri}"
47
72
  begin
48
73
  require 'zlib'
49
74
  yaml_spec = fetch_path("/yaml.Z")
@@ -53,7 +78,7 @@ module Gem
53
78
  end
54
79
  begin
55
80
  yaml_spec = fetch_path("/yaml") unless yaml_spec
56
- r = convert_spec(yaml_spec)
81
+ convert_spec(yaml_spec)
57
82
  rescue SocketError => e
58
83
  raise RemoteSourceException.new("Error fetching remote gem cache: #{e.to_s}")
59
84
  end
@@ -68,9 +93,8 @@ module Gem
68
93
 
69
94
  # Connect to the source host/port, using a proxy if needed.
70
95
  def connect_to(host, port)
71
- if @http_proxy
72
- proxy_uri = URI.parse(@http_proxy)
73
- Net::HTTP::Proxy(proxy_uri.host, proxy_uri.port).new(host, port)
96
+ if @proxy_uri
97
+ Net::HTTP::Proxy(@proxy_uri.host, @proxy_uri.port, @proxy_uri.user, @proxy_uri.password).new(host, port)
74
98
  else
75
99
  Net::HTTP.new(host, port)
76
100
  end
@@ -86,7 +110,6 @@ module Gem
86
110
  # command.
87
111
  def read_size(uri)
88
112
  return File.size(get_file_uri_path(uri)) if is_file_uri(uri)
89
-
90
113
  require 'net/http'
91
114
  require 'uri'
92
115
  u = URI.parse(uri)
@@ -118,10 +141,13 @@ module Gem
118
141
  if is_file_uri(uri)
119
142
  open(get_file_uri_path(uri), &block)
120
143
  else
121
- open(uri,
122
- "User-Agent" => "RubyGems/#{Gem::RubyGemsVersion}",
123
- :proxy => @http_proxy,
124
- &block)
144
+ connection_options = {"User-Agent" => "RubyGems/#{Gem::RubyGemsVersion}"}
145
+ if @proxy_uri
146
+ http_proxy_url = "#{@proxy_uri.scheme}://#{@proxy_uri.host}:#{@proxy_uri.port}"
147
+ connection_options[:proxy_http_basic_authentication] = [http_proxy_url, @proxy_uri.user||'', @proxy_uri.password||'']
148
+ end
149
+
150
+ open(uri, connection_options, &block)
125
151
  end
126
152
  end
127
153
 
@@ -139,7 +165,7 @@ module Gem
139
165
  # these are hashes of specs.).
140
166
  def convert_spec(yaml_spec)
141
167
  YAML.load(reduce_spec(yaml_spec)) or
142
- raise "Didn't get a valid YAML document"
168
+ fail "Didn't get a valid YAML document"
143
169
  end
144
170
 
145
171
  # This reduces the source spec in size so that YAML bugs with
@@ -172,10 +198,33 @@ module Gem
172
198
  end
173
199
  end
174
200
 
175
- # LocalSourceInfoCache implements the cache management policy on
176
- # where the source info is stored on local file system. There are
177
- # two possible cache locations: (1) the system wide cache, and (2)
178
- # the user specific cache.
201
+ ####################################################################
202
+ # Entrys held by a SourceInfoCache.
203
+ class SourceInfoCacheEntry
204
+ # The source index for this cache entry.
205
+ attr_reader :source_index
206
+
207
+ # The size of the of the source entry. Used to determine if the
208
+ # source index has changed.
209
+ attr_reader :size
210
+
211
+ # Create a cache entry.
212
+ def initialize(si, size)
213
+ replace_source_index(si, size)
214
+ end
215
+
216
+ # Replace the source index and the index size with given values.
217
+ def replace_source_index(si, size)
218
+ @source_index = si || SourceIndex.new({})
219
+ @size = size
220
+ end
221
+ end
222
+
223
+ ####################################################################
224
+ # SourceInfoCache implements the cache management policy on where
225
+ # the source info is stored on local file system. There are two
226
+ # possible cache locations: (1) the system wide cache, and (2) the
227
+ # user specific cache.
179
228
  #
180
229
  # * The system cache is prefered if it is writable (or can be
181
230
  # created).
@@ -185,7 +234,10 @@ module Gem
185
234
  # Once a cache is selected, it will be used for all operations. It
186
235
  # will not switch between cache files dynamically.
187
236
  #
188
- class LocalSourceInfoCache
237
+ # Cache data is a simple hash indexed by the source URI. Retrieving
238
+ # and entry from the cache data will return a SourceInfoCacheEntry.
239
+ #
240
+ class SourceInfoCache
189
241
 
190
242
  # The most recent cache data.
191
243
  def cache_data
@@ -197,7 +249,7 @@ module Gem
197
249
  def write_cache
198
250
  data = cache_data
199
251
  open(writable_file, "wb") do |f|
200
- f.puts Marshal.dump(data)
252
+ f.write Marshal.dump(data)
201
253
  end
202
254
  end
203
255
 
@@ -242,8 +294,6 @@ module Gem
242
294
 
243
295
  def load_local_cache(f)
244
296
  Marshal.load(f)
245
- rescue StandardError => ex
246
- {}
247
297
  end
248
298
 
249
299
  # Select a writable cache file
@@ -274,7 +324,7 @@ module Gem
274
324
  end
275
325
  end
276
326
 
277
-
327
+ ####################################################################
278
328
  # CachedFetcher is a decorator that adds local file caching to
279
329
  # RemoteSourceFetcher objects.
280
330
  class CachedFetcher
@@ -282,8 +332,10 @@ module Gem
282
332
  # Create a cached fetcher (based on a RemoteSourceFetcher) for the
283
333
  # source at +source_uri+ (through the proxy +proxy+).
284
334
  def initialize(source_uri, proxy)
335
+ require 'rubygems/incremental_fetcher'
285
336
  @source_uri = source_uri
286
- @fetcher = RemoteSourceFetcher.new(source_uri, proxy)
337
+ rsf = RemoteSourceFetcher.new(source_uri, proxy)
338
+ @fetcher = IncrementalFetcher.new(source_uri, rsf, manager)
287
339
  end
288
340
 
289
341
  # The uncompressed +size+ of the source's directory (e.g. source
@@ -307,14 +359,11 @@ module Gem
307
359
  # "abbreviated").
308
360
  def source_index
309
361
  cache = manager.cache_data[@source_uri]
310
- if cache && cache['size'] == @fetcher.size
311
- cache['cache']
362
+ if cache && cache.size == @fetcher.size
363
+ cache.source_index
312
364
  else
313
365
  result = @fetcher.source_index
314
- manager.cache_data[@source_uri] = {
315
- 'size' => @fetcher.size,
316
- 'cache' => result,
317
- }
366
+ manager.cache_data[@source_uri] = SourceInfoCacheEntry.new(result, @fetcher.size)
318
367
  manager.update
319
368
  result
320
369
  end
@@ -338,10 +387,9 @@ module Gem
338
387
 
339
388
  # The Cache manager for all instances of this class.
340
389
  def manager
341
- @manager ||= LocalSourceInfoCache.new
390
+ @manager ||= SourceInfoCache.new
342
391
  end
343
392
 
344
-
345
393
  # Sent by the client when it is done with all the sources,
346
394
  # allowing any cleanup activity to take place.
347
395
  def finish
@@ -354,25 +402,18 @@ module Gem
354
402
  class RemoteInstaller
355
403
  include UserInteraction
356
404
 
357
- # <tt>http_proxy</tt>::
405
+ # <tt>options[:http_proxy]</tt>::
358
406
  # * [String]: explicit specification of proxy; overrides any
359
407
  # environment variable setting
360
- # * nil: respect environment variables
408
+ # * nil: respect environment variables (HTTP_PROXY, HTTP_PROXY_USER, HTTP_PROXY_PASS)
361
409
  # * <tt>:no_proxy</tt>: ignore environment variables and _don't_
362
410
  # use a proxy
363
411
  #
364
412
  def initialize(options={})
413
+ require 'uri'
414
+
365
415
  # Ensure http_proxy env vars are used if no proxy explicitly supplied.
366
416
  @options = options
367
- @http_proxy =
368
- case @options[:http_proxy]
369
- when :no_proxy
370
- false
371
- when nil
372
- true
373
- else
374
- @options[:http_proxy].to_str
375
- end
376
417
  @fetcher_class = CachedFetcher
377
418
  end
378
419
 
@@ -387,11 +428,7 @@ module Gem
387
428
  # Returns::
388
429
  # an array of Gem::Specification objects, one for each gem installed.
389
430
  #
390
- def install(gem_name,
391
- version_requirement = "> 0.0.0",
392
- force=false,
393
- install_dir=Gem.dir,
394
- install_stub=true)
431
+ def install(gem_name, version_requirement = "> 0.0.0", force=false, install_dir=Gem.dir, install_stub=true)
395
432
  unless version_requirement.respond_to?(:satisfied_by?)
396
433
  version_requirement = Version::Requirement.new(version_requirement)
397
434
  end
@@ -422,7 +459,7 @@ module Gem
422
459
  # Return a list of the sources that we can download gems from
423
460
  def sources
424
461
  unless @sources
425
- require_gem("sources")
462
+ require 'sources'
426
463
  @sources = Gem.sources
427
464
  end
428
465
  @sources
@@ -441,7 +478,7 @@ module Gem
441
478
 
442
479
  # Return the source info for the given source. The
443
480
  def fetch_source(source)
444
- rsf = @fetcher_class.new(source, @http_proxy)
481
+ rsf = @fetcher_class.new(source, @options[:http_proxy])
445
482
  rsf.source_index
446
483
  end
447
484
 
@@ -524,7 +561,7 @@ module Gem
524
561
  end
525
562
 
526
563
  def download_gem(destination_file, source, spec)
527
- rsf = @fetcher_class.new(source, @http_proxy)
564
+ rsf = @fetcher_class.new(source, @proxy_uri)
528
565
  path = "/gems/#{spec.full_name}.gem"
529
566
  response = rsf.fetch_path(path)
530
567
  write_gem_to_file(response, destination_file)
@@ -2,5 +2,5 @@
2
2
  # This file is auto-generated by build scripts.
3
3
  # See: rake update_version
4
4
  module Gem
5
- RubyGemsVersion = '0.8.11'
5
+ RubyGemsVersion = '0.9.0'
6
6
  end
@@ -1,4 +1,10 @@
1
1
  #!/usr/bin/env ruby
2
+ #--
3
+ # Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
4
+ # All rights reserved.
5
+ # See LICENSE.txt for permissions.
6
+ #++
7
+
2
8
 
3
9
  require 'rubygems/gem_openssl'
4
10
 
@@ -1,6 +1,14 @@
1
+ #--
2
+ # Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
3
+ # All rights reserved.
4
+ # See LICENSE.txt for permissions.
5
+ #++
6
+
1
7
  require 'rubygems/user_interaction'
2
8
 
3
9
  require 'forwardable'
10
+ require 'digest/sha2'
11
+ require 'time'
4
12
 
5
13
  module Gem
6
14
 
@@ -10,18 +18,91 @@ module Gem
10
18
  # specification.
11
19
  #
12
20
  # NOTE:: The class used to be named Cache, but that became
13
- # confusing when cached source fetchers where introduced.
14
- # The constant Gem::Cache is an alias for this class to allow
15
- # old YAMLized source index objects to load properly.
21
+ # confusing when cached source fetchers where introduced. The
22
+ # constant Gem::Cache is an alias for this class to allow old
23
+ # YAMLized source index objects to load properly.
16
24
  #
17
25
  class SourceIndex
18
26
  extend Forwardable
19
27
  include Enumerable
20
28
 
29
+ # Class Methods. -------------------------------------------------
21
30
  class << self
22
31
  include Gem::UserInteraction
23
- end
24
32
 
33
+ # Factory method to construct a source index instance for a given
34
+ # path.
35
+ #
36
+ # deprecated::
37
+ # If supplied, from_installed_gems will act just like
38
+ # +from_gems_in+. This argument is deprecated and is provided
39
+ # just for backwards compatibility, and should not generally
40
+ # be used.
41
+ #
42
+ # return::
43
+ # SourceIndex instance
44
+ #
45
+ def from_installed_gems(*deprecated)
46
+ if deprecated.empty?
47
+ from_gems_in(*installed_spec_directories)
48
+ else
49
+ from_gems_in(*deprecated)
50
+ end
51
+ end
52
+
53
+ # Return a list of directories in the current gem path that
54
+ # contain specifications.
55
+ #
56
+ # return::
57
+ # List of directory paths (all ending in "../specifications").
58
+ #
59
+ def installed_spec_directories
60
+ Gem.path.collect { |dir| File.join(dir, "specifications") }
61
+ end
62
+
63
+ # Factory method to construct a source index instance for a
64
+ # given path.
65
+ #
66
+ # spec_dirs::
67
+ # List of directories to search for specifications. Each
68
+ # directory should have a "specifications" subdirectory
69
+ # containing the gem specifications.
70
+ #
71
+ # return::
72
+ # SourceIndex instance
73
+ #
74
+ def from_gems_in(*spec_dirs)
75
+ self.new.load_gems_in(*spec_dirs)
76
+ end
77
+
78
+ # Load a specification from a file (eval'd Ruby code)
79
+ #
80
+ # file_name:: [String] The .gemspec file
81
+ # return:: Specification instance or nil if an error occurs
82
+ #
83
+ def load_specification(file_name)
84
+ begin
85
+ spec_code = File.read(file_name).untaint
86
+ gemspec = eval(spec_code)
87
+ if gemspec.is_a?(Gem::Specification)
88
+ gemspec.loaded_from = file_name
89
+ return gemspec
90
+ end
91
+ alert_warning "File '#{file_name}' does not evaluate to a gem specification"
92
+ rescue SyntaxError => e
93
+ alert_warning e
94
+ alert_warning spec_code
95
+ rescue Exception => e
96
+ alert_warning(e.inspect.to_s + "\n" + spec_code)
97
+ alert_warning "Invalid .gemspec format in '#{file_name}'"
98
+ end
99
+ return nil
100
+ end
101
+
102
+ end
103
+
104
+ # Instance Methods -----------------------------------------------
105
+
25
106
  # Constructs a source index instance from the provided
26
107
  # specifications
27
108
  #
@@ -32,69 +113,51 @@ module Gem
32
113
  @gems = specifications
33
114
  end
34
115
 
35
- # Reconstruct this source index from the list of source
36
- # directories. If the list is empty, then use the directories in
37
- # Gem.path.
38
- #
39
- def from_installed_gems(*spec_dirs)
116
+ # Reconstruct the source index from the list of source
117
+ # directories.
118
+ def load_gems_in(*spec_dirs)
40
119
  @gems.clear
41
- if spec_dirs.empty?
42
- spec_dirs = Gem.path.collect { |dir| File.join(dir, "specifications") }
43
- end
44
120
  Dir.glob("{#{spec_dirs.join(',')}}/*.gemspec").each do |file_name|
45
- gemspec = self.class.load_specification(file_name)
46
- @gems[gemspec.full_name] = gemspec if gemspec
121
+ gemspec = self.class.load_specification(file_name.untaint)
122
+ add_spec(gemspec) if gemspec
47
123
  end
48
124
  self
49
125
  end
50
126
 
51
- # Factory method to construct a source index instance for a given
52
- # path.
53
- #
54
- # source_dirs::
55
- # List of gem directories to search for specifications. The
56
- # default is the "specification" directories under each
57
- # directory in Gem.path.
58
- #
59
- # return::
60
- # SourceIndex instance
61
- #
62
- def self.from_installed_gems(*spec_dirs)
63
- self.new.from_installed_gems(*spec_dirs)
127
+ # Add a gem specification to the source index.
128
+ def add_spec(gem_spec)
129
+ @gems[gem_spec.full_name] = gem_spec
64
130
  end
65
131
 
66
- # Load a specification from a file (eval'd Ruby code)
67
- #
68
- # file_name:: [String] The .gemspec file
69
- # return:: Specification instance or nil if an error occurs
70
- #
71
- def self.load_specification(file_name)
72
- begin
73
- spec_code = File.read(file_name)
74
- gemspec = eval(spec_code)
75
- if gemspec.is_a?(Gem::Specification)
76
- gemspec.loaded_from = file_name
77
- return gemspec
78
- end
79
- alert_warning "File '#{file_name}' does not evaluate to a gem specification"
80
- rescue SyntaxError => e
81
- alert_warning e
82
- alert_warning spec_code
83
- rescue Exception => e
84
- alert_warning(e.inspect.to_s + "\n" + spec_code)
85
- alert_warning "Invalid .gemspec format in '#{file_name}'"
86
- end
87
- return nil
132
+ # Remove a gem specification named +full_name+.
133
+ def remove_spec(full_name)
134
+ @gems.delete(full_name)
88
135
  end
89
-
136
+
90
137
  # Iterate over the specifications in the source index.
91
138
  #
92
- # &block:: [yields gem.long_name, Gem::Specification]
139
+ # &block:: [yields gem.full_name, Gem::Specification]
93
140
  #
94
141
  def each(&block)
95
142
  @gems.each(&block)
96
143
  end
97
144
 
145
+ # The gem specification given a full gem spec name.
146
+ def specification(full_name)
147
+ @gems[full_name]
148
+ end
149
+
150
+ # The signature for the source index. Changes in the signature
151
+ # indicate a change in the index.
152
+ def index_signature
153
+ Digest::SHA256.new(@gems.keys.sort.join(',')).to_s
154
+ end
155
+
156
+ # The signature for the given gem specification.
157
+ def gem_signature(gem_full_name)
158
+ Digest::SHA256.new(@gems[gem_full_name].to_yaml).to_s
159
+ end
160
+
98
161
  def_delegators :@gems, :size, :length
99
162
 
100
163
  # Find a gem by an exact match on the short name.
@@ -132,7 +195,7 @@ module Gem
132
195
  # return:: Returns a pointer to itself.
133
196
  #
134
197
  def refresh!
135
- from_installed_gems
198
+ load_gems_in(self.class.installed_spec_directories)
136
199
  end
137
200
 
138
201
  end