libgems 0.0.1

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.
Files changed (177) hide show
  1. data/ChangeLog +5811 -0
  2. data/History.txt +887 -0
  3. data/LICENSE.txt +51 -0
  4. data/README.md +87 -0
  5. data/Rakefile +113 -0
  6. data/lib/gauntlet_libgems.rb +50 -0
  7. data/lib/libgems.rb +1246 -0
  8. data/lib/libgems/builder.rb +102 -0
  9. data/lib/libgems/command.rb +534 -0
  10. data/lib/libgems/command_manager.rb +182 -0
  11. data/lib/libgems/commands/build_command.rb +53 -0
  12. data/lib/libgems/commands/cert_command.rb +86 -0
  13. data/lib/libgems/commands/check_command.rb +80 -0
  14. data/lib/libgems/commands/cleanup_command.rb +106 -0
  15. data/lib/libgems/commands/contents_command.rb +98 -0
  16. data/lib/libgems/commands/dependency_command.rb +195 -0
  17. data/lib/libgems/commands/environment_command.rb +133 -0
  18. data/lib/libgems/commands/fetch_command.rb +67 -0
  19. data/lib/libgems/commands/generate_index_command.rb +133 -0
  20. data/lib/libgems/commands/help_command.rb +172 -0
  21. data/lib/libgems/commands/install_command.rb +178 -0
  22. data/lib/libgems/commands/list_command.rb +35 -0
  23. data/lib/libgems/commands/lock_command.rb +110 -0
  24. data/lib/libgems/commands/mirror_command.rb +111 -0
  25. data/lib/libgems/commands/outdated_command.rb +33 -0
  26. data/lib/libgems/commands/owner_command.rb +75 -0
  27. data/lib/libgems/commands/pristine_command.rb +93 -0
  28. data/lib/libgems/commands/push_command.rb +56 -0
  29. data/lib/libgems/commands/query_command.rb +280 -0
  30. data/lib/libgems/commands/rdoc_command.rb +91 -0
  31. data/lib/libgems/commands/search_command.rb +31 -0
  32. data/lib/libgems/commands/server_command.rb +86 -0
  33. data/lib/libgems/commands/sources_command.rb +157 -0
  34. data/lib/libgems/commands/specification_command.rb +125 -0
  35. data/lib/libgems/commands/stale_command.rb +27 -0
  36. data/lib/libgems/commands/uninstall_command.rb +83 -0
  37. data/lib/libgems/commands/unpack_command.rb +121 -0
  38. data/lib/libgems/commands/update_command.rb +160 -0
  39. data/lib/libgems/commands/which_command.rb +86 -0
  40. data/lib/libgems/config_file.rb +345 -0
  41. data/lib/libgems/custom_require.rb +44 -0
  42. data/lib/libgems/defaults.rb +101 -0
  43. data/lib/libgems/dependency.rb +227 -0
  44. data/lib/libgems/dependency_installer.rb +286 -0
  45. data/lib/libgems/dependency_list.rb +208 -0
  46. data/lib/libgems/doc_manager.rb +242 -0
  47. data/lib/libgems/errors.rb +35 -0
  48. data/lib/libgems/exceptions.rb +91 -0
  49. data/lib/libgems/ext.rb +18 -0
  50. data/lib/libgems/ext/builder.rb +56 -0
  51. data/lib/libgems/ext/configure_builder.rb +25 -0
  52. data/lib/libgems/ext/ext_conf_builder.rb +24 -0
  53. data/lib/libgems/ext/rake_builder.rb +39 -0
  54. data/lib/libgems/format.rb +81 -0
  55. data/lib/libgems/gem_openssl.rb +92 -0
  56. data/lib/libgems/gem_path_searcher.rb +100 -0
  57. data/lib/libgems/gem_runner.rb +79 -0
  58. data/lib/libgems/gemcutter_utilities.rb +49 -0
  59. data/lib/libgems/indexer.rb +720 -0
  60. data/lib/libgems/install_update_options.rb +125 -0
  61. data/lib/libgems/installer.rb +604 -0
  62. data/lib/libgems/local_remote_options.rb +135 -0
  63. data/lib/libgems/old_format.rb +153 -0
  64. data/lib/libgems/package.rb +97 -0
  65. data/lib/libgems/package/f_sync_dir.rb +23 -0
  66. data/lib/libgems/package/tar_header.rb +266 -0
  67. data/lib/libgems/package/tar_input.rb +222 -0
  68. data/lib/libgems/package/tar_output.rb +144 -0
  69. data/lib/libgems/package/tar_reader.rb +106 -0
  70. data/lib/libgems/package/tar_reader/entry.rb +141 -0
  71. data/lib/libgems/package/tar_writer.rb +241 -0
  72. data/lib/libgems/package_task.rb +126 -0
  73. data/lib/libgems/platform.rb +183 -0
  74. data/lib/libgems/remote_fetcher.rb +414 -0
  75. data/lib/libgems/require_paths_builder.rb +18 -0
  76. data/lib/libgems/requirement.rb +153 -0
  77. data/lib/libgems/security.rb +814 -0
  78. data/lib/libgems/server.rb +872 -0
  79. data/lib/libgems/source_index.rb +597 -0
  80. data/lib/libgems/source_info_cache.rb +395 -0
  81. data/lib/libgems/source_info_cache_entry.rb +56 -0
  82. data/lib/libgems/spec_fetcher.rb +337 -0
  83. data/lib/libgems/specification.rb +1487 -0
  84. data/lib/libgems/test_utilities.rb +147 -0
  85. data/lib/libgems/text.rb +65 -0
  86. data/lib/libgems/uninstaller.rb +278 -0
  87. data/lib/libgems/user_interaction.rb +527 -0
  88. data/lib/libgems/validator.rb +240 -0
  89. data/lib/libgems/version.rb +316 -0
  90. data/lib/libgems/version_option.rb +65 -0
  91. data/lib/rbconfig/datadir.rb +20 -0
  92. data/test/bogussources.rb +8 -0
  93. data/test/data/gem-private_key.pem +27 -0
  94. data/test/data/gem-public_cert.pem +20 -0
  95. data/test/fake_certlib/openssl.rb +7 -0
  96. data/test/foo/discover.rb +0 -0
  97. data/test/gem_installer_test_case.rb +97 -0
  98. data/test/gem_package_tar_test_case.rb +132 -0
  99. data/test/gemutilities.rb +605 -0
  100. data/test/insure_session.rb +43 -0
  101. data/test/mockgemui.rb +56 -0
  102. data/test/plugin/exception/libgems_plugin.rb +2 -0
  103. data/test/plugin/load/libgems_plugin.rb +1 -0
  104. data/test/plugin/standarderror/libgems_plugin.rb +2 -0
  105. data/test/private_key.pem +27 -0
  106. data/test/public_cert.pem +20 -0
  107. data/test/rubygems_plugin.rb +21 -0
  108. data/test/simple_gem.rb +66 -0
  109. data/test/test_config.rb +12 -0
  110. data/test/test_gem.rb +780 -0
  111. data/test/test_gem_builder.rb +27 -0
  112. data/test/test_gem_command.rb +178 -0
  113. data/test/test_gem_command_manager.rb +207 -0
  114. data/test/test_gem_commands_build_command.rb +74 -0
  115. data/test/test_gem_commands_cert_command.rb +124 -0
  116. data/test/test_gem_commands_check_command.rb +18 -0
  117. data/test/test_gem_commands_contents_command.rb +156 -0
  118. data/test/test_gem_commands_dependency_command.rb +216 -0
  119. data/test/test_gem_commands_environment_command.rb +144 -0
  120. data/test/test_gem_commands_fetch_command.rb +76 -0
  121. data/test/test_gem_commands_generate_index_command.rb +135 -0
  122. data/test/test_gem_commands_install_command.rb +315 -0
  123. data/test/test_gem_commands_list_command.rb +36 -0
  124. data/test/test_gem_commands_lock_command.rb +68 -0
  125. data/test/test_gem_commands_mirror_command.rb +60 -0
  126. data/test/test_gem_commands_outdated_command.rb +40 -0
  127. data/test/test_gem_commands_owner_command.rb +105 -0
  128. data/test/test_gem_commands_pristine_command.rb +108 -0
  129. data/test/test_gem_commands_push_command.rb +81 -0
  130. data/test/test_gem_commands_query_command.rb +426 -0
  131. data/test/test_gem_commands_server_command.rb +59 -0
  132. data/test/test_gem_commands_sources_command.rb +209 -0
  133. data/test/test_gem_commands_specification_command.rb +139 -0
  134. data/test/test_gem_commands_stale_command.rb +38 -0
  135. data/test/test_gem_commands_uninstall_command.rb +83 -0
  136. data/test/test_gem_commands_unpack_command.rb +199 -0
  137. data/test/test_gem_commands_update_command.rb +207 -0
  138. data/test/test_gem_commands_which_command.rb +66 -0
  139. data/test/test_gem_config_file.rb +287 -0
  140. data/test/test_gem_dependency.rb +149 -0
  141. data/test/test_gem_dependency_installer.rb +661 -0
  142. data/test/test_gem_dependency_list.rb +230 -0
  143. data/test/test_gem_doc_manager.rb +31 -0
  144. data/test/test_gem_ext_configure_builder.rb +84 -0
  145. data/test/test_gem_ext_ext_conf_builder.rb +173 -0
  146. data/test/test_gem_ext_rake_builder.rb +81 -0
  147. data/test/test_gem_format.rb +70 -0
  148. data/test/test_gem_gem_path_searcher.rb +78 -0
  149. data/test/test_gem_gem_runner.rb +45 -0
  150. data/test/test_gem_gemcutter_utilities.rb +103 -0
  151. data/test/test_gem_indexer.rb +673 -0
  152. data/test/test_gem_install_update_options.rb +68 -0
  153. data/test/test_gem_installer.rb +857 -0
  154. data/test/test_gem_local_remote_options.rb +97 -0
  155. data/test/test_gem_package_tar_header.rb +130 -0
  156. data/test/test_gem_package_tar_input.rb +112 -0
  157. data/test/test_gem_package_tar_output.rb +97 -0
  158. data/test/test_gem_package_tar_reader.rb +46 -0
  159. data/test/test_gem_package_tar_reader_entry.rb +109 -0
  160. data/test/test_gem_package_tar_writer.rb +144 -0
  161. data/test/test_gem_package_task.rb +59 -0
  162. data/test/test_gem_platform.rb +264 -0
  163. data/test/test_gem_remote_fetcher.rb +740 -0
  164. data/test/test_gem_requirement.rb +292 -0
  165. data/test/test_gem_server.rb +356 -0
  166. data/test/test_gem_silent_ui.rb +113 -0
  167. data/test/test_gem_source_index.rb +461 -0
  168. data/test/test_gem_spec_fetcher.rb +410 -0
  169. data/test/test_gem_specification.rb +1334 -0
  170. data/test/test_gem_stream_ui.rb +218 -0
  171. data/test/test_gem_text.rb +43 -0
  172. data/test/test_gem_uninstaller.rb +146 -0
  173. data/test/test_gem_validator.rb +63 -0
  174. data/test/test_gem_version.rb +181 -0
  175. data/test/test_gem_version_option.rb +89 -0
  176. data/test/test_kernel.rb +59 -0
  177. metadata +402 -0
@@ -0,0 +1,597 @@
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
+
7
+ require 'libgems/user_interaction'
8
+ require 'libgems/specification'
9
+
10
+ # :stopdoc:
11
+ module LibGems
12
+ autoload :SpecFetcher, 'libgems/spec_fetcher'
13
+ end
14
+ # :startdoc:
15
+
16
+ ##
17
+ # The SourceIndex object indexes all the gems available from a
18
+ # particular source (e.g. a list of gem directories, or a remote
19
+ # source). A SourceIndex maps a gem full name to a gem
20
+ # specification.
21
+ #
22
+ # NOTE:: The class used to be named Cache, but that became
23
+ # confusing when cached source fetchers where introduced. The
24
+ # constant LibGems::Cache is an alias for this class to allow old
25
+ # YAMLized source index objects to load properly.
26
+
27
+ class LibGems::SourceIndex
28
+
29
+ include Enumerable
30
+
31
+ include LibGems::UserInteraction
32
+
33
+ attr_reader :gems # :nodoc:
34
+
35
+ ##
36
+ # Directories to use to refresh this SourceIndex when calling refresh!
37
+
38
+ attr_accessor :spec_dirs
39
+
40
+ class << self
41
+ include LibGems::UserInteraction
42
+
43
+ ##
44
+ # Factory method to construct a source index instance for a given
45
+ # path.
46
+ #
47
+ # deprecated::
48
+ # If supplied, from_installed_gems will act just like
49
+ # +from_gems_in+. This argument is deprecated and is provided
50
+ # just for backwards compatibility, and should not generally
51
+ # be used.
52
+ #
53
+ # return::
54
+ # SourceIndex instance
55
+
56
+ def from_installed_gems(*deprecated)
57
+ if deprecated.empty?
58
+ from_gems_in(*installed_spec_directories)
59
+ else
60
+ from_gems_in(*deprecated) # HACK warn
61
+ end
62
+ end
63
+
64
+ ##
65
+ # Returns a list of directories from LibGems.path that contain specifications.
66
+
67
+ def installed_spec_directories
68
+ LibGems.path.collect { |dir| File.join(dir, "specifications") }
69
+ end
70
+
71
+ ##
72
+ # Creates a new SourceIndex from the ruby format gem specifications in
73
+ # +spec_dirs+.
74
+
75
+ def from_gems_in(*spec_dirs)
76
+ source_index = new
77
+ source_index.spec_dirs = spec_dirs
78
+ source_index.refresh!
79
+ end
80
+
81
+ ##
82
+ # Loads a ruby-format specification from +file_name+ and returns the
83
+ # loaded spec.
84
+
85
+ def load_specification(file_name)
86
+ return nil unless file_name and File.exist? file_name
87
+
88
+ spec_code = if defined? Encoding then
89
+ File.read file_name, :encoding => 'UTF-8'
90
+ else
91
+ File.read file_name
92
+ end.untaint
93
+
94
+ begin
95
+ gemspec = LibGems.gemspec_eval(spec_code, binding, file_name)
96
+
97
+ if gemspec.is_a?(LibGems::Specification)
98
+ gemspec.loaded_from = file_name
99
+ return gemspec
100
+ end
101
+ alert_warning "File '#{file_name}' does not evaluate to a gem specification"
102
+ rescue SignalException, SystemExit
103
+ raise
104
+ rescue SyntaxError => e
105
+ alert_warning e
106
+ alert_warning spec_code
107
+ rescue Exception => e
108
+ alert_warning "#{e.inspect}\n#{spec_code}"
109
+ alert_warning "Invalid .gemspec format in '#{file_name}'"
110
+ end
111
+
112
+ return nil
113
+ end
114
+
115
+ end
116
+
117
+ ##
118
+ # Constructs a source index instance from the provided specifications, which
119
+ # is a Hash of gem full names and LibGems::Specifications.
120
+ #--
121
+ # TODO merge @gems and @prerelease_gems and provide a separate method
122
+ # #prerelease_gems
123
+
124
+ def initialize(specifications={})
125
+ @gems = {}
126
+ specifications.each{ |full_name, spec| add_spec spec }
127
+ @spec_dirs = nil
128
+ end
129
+
130
+ # TODO: remove method
131
+ def all_gems
132
+ @gems
133
+ end
134
+
135
+ def prerelease_gems
136
+ @gems.reject{ |name, gem| !gem.version.prerelease? }
137
+ end
138
+
139
+ def released_gems
140
+ @gems.reject{ |name, gem| gem.version.prerelease? }
141
+ end
142
+
143
+ ##
144
+ # Reconstruct the source index from the specifications in +spec_dirs+.
145
+
146
+ def load_gems_in(*spec_dirs)
147
+ @gems.clear
148
+
149
+ spec_dirs.reverse_each do |spec_dir|
150
+ spec_files = Dir.glob File.join(spec_dir, '*.gemspec')
151
+
152
+ spec_files.each do |spec_file|
153
+ gemspec = self.class.load_specification spec_file.untaint
154
+ add_spec gemspec if gemspec
155
+ end
156
+ end
157
+
158
+ self
159
+ end
160
+
161
+ ##
162
+ # Returns an Array specifications for the latest released versions
163
+ # of each gem in this index.
164
+
165
+ def latest_specs
166
+ result = Hash.new { |h,k| h[k] = [] }
167
+ latest = {}
168
+
169
+ sort.each do |_, spec|
170
+ name = spec.name
171
+ curr_ver = spec.version
172
+ prev_ver = latest.key?(name) ? latest[name].version : nil
173
+
174
+ next if curr_ver.prerelease?
175
+ next unless prev_ver.nil? or curr_ver >= prev_ver or
176
+ latest[name].platform != LibGems::Platform::RUBY
177
+
178
+ if prev_ver.nil? or
179
+ (curr_ver > prev_ver and spec.platform == LibGems::Platform::RUBY) then
180
+ result[name].clear
181
+ latest[name] = spec
182
+ end
183
+
184
+ if spec.platform != LibGems::Platform::RUBY then
185
+ result[name].delete_if do |result_spec|
186
+ result_spec.platform == spec.platform
187
+ end
188
+ end
189
+
190
+ result[name] << spec
191
+ end
192
+
193
+ # TODO: why is this a hash while @gems is an array? Seems like
194
+ # structural similarity would be good.
195
+ result.values.flatten
196
+ end
197
+
198
+ ##
199
+ # An array including only the prerelease gemspecs
200
+
201
+ def prerelease_specs
202
+ prerelease_gems.values
203
+ end
204
+
205
+ ##
206
+ # An array including only the released gemspecs
207
+
208
+ def released_specs
209
+ released_gems.values
210
+ end
211
+
212
+ ##
213
+ # Add a gem specification to the source index.
214
+
215
+ def add_spec(gem_spec, name = gem_spec.full_name)
216
+ # No idea why, but the Indexer wants to insert them using original_name
217
+ # instead of full_name. So we make it an optional arg.
218
+ @gems[name] = gem_spec
219
+ end
220
+
221
+ ##
222
+ # Add gem specifications to the source index.
223
+
224
+ def add_specs(*gem_specs)
225
+ gem_specs.each do |spec|
226
+ add_spec spec
227
+ end
228
+ end
229
+
230
+ ##
231
+ # Remove a gem specification named +full_name+.
232
+
233
+ def remove_spec(full_name)
234
+ @gems.delete full_name
235
+ end
236
+
237
+ ##
238
+ # Iterate over the specifications in the source index.
239
+
240
+ def each(&block) # :yields: gem.full_name, gem
241
+ @gems.each(&block)
242
+ end
243
+
244
+ ##
245
+ # The gem specification given a full gem spec name.
246
+
247
+ def specification(full_name)
248
+ @gems[full_name]
249
+ end
250
+
251
+ ##
252
+ # The signature for the source index. Changes in the signature indicate a
253
+ # change in the index.
254
+
255
+ def index_signature
256
+ require 'digest'
257
+
258
+ Digest::SHA256.new.hexdigest(@gems.keys.sort.join(',')).to_s
259
+ end
260
+
261
+ ##
262
+ # The signature for the given gem specification.
263
+
264
+ def gem_signature(gem_full_name)
265
+ require 'digest'
266
+
267
+ Digest::SHA256.new.hexdigest(@gems[gem_full_name].to_yaml).to_s
268
+ end
269
+
270
+ def size
271
+ @gems.size
272
+ end
273
+ alias length size
274
+
275
+ ##
276
+ # Find a gem by an exact match on the short name.
277
+
278
+ def find_name(gem_name, version_requirement = LibGems::Requirement.default)
279
+ dep = LibGems::Dependency.new gem_name, version_requirement
280
+ search dep
281
+ end
282
+
283
+ ##
284
+ # Search for a gem by LibGems::Dependency +gem_pattern+. If +only_platform+
285
+ # is true, only gems matching LibGems::Platform.local will be returned. An
286
+ # Array of matching LibGems::Specification objects is returned.
287
+ #
288
+ # For backwards compatibility, a String or Regexp pattern may be passed as
289
+ # +gem_pattern+, and a LibGems::Requirement for +platform_only+. This
290
+ # behavior is deprecated and will be removed.
291
+
292
+ def search(gem_pattern, platform_only = false)
293
+ version_requirement = nil
294
+ only_platform = false
295
+
296
+ # TODO - Remove support and warning for legacy arguments after 2008/11
297
+ unless LibGems::Dependency === gem_pattern
298
+ warn "#{LibGems.location_of_caller.join ':'}:Warning: LibGems::SourceIndex#search support for #{gem_pattern.class} patterns is deprecated, use #find_name"
299
+ end
300
+
301
+ case gem_pattern
302
+ when Regexp then
303
+ version_requirement = platform_only || LibGems::Requirement.default
304
+ when LibGems::Dependency then
305
+ only_platform = platform_only
306
+ version_requirement = gem_pattern.requirement
307
+ gem_pattern = if Regexp === gem_pattern.name then
308
+ gem_pattern.name
309
+ elsif gem_pattern.name.empty? then
310
+ //
311
+ else
312
+ /^#{Regexp.escape gem_pattern.name}$/
313
+ end
314
+ else
315
+ version_requirement = platform_only || LibGems::Requirement.default
316
+ gem_pattern = /#{gem_pattern}/i
317
+ end
318
+
319
+ unless LibGems::Requirement === version_requirement then
320
+ version_requirement = LibGems::Requirement.create version_requirement
321
+ end
322
+
323
+ specs = all_gems.values.select do |spec|
324
+ spec.name =~ gem_pattern and
325
+ version_requirement.satisfied_by? spec.version
326
+ end
327
+
328
+ if only_platform then
329
+ specs = specs.select do |spec|
330
+ LibGems::Platform.match spec.platform
331
+ end
332
+ end
333
+
334
+ specs.sort_by { |s| s.sort_obj }
335
+ end
336
+
337
+ ##
338
+ # Replaces the gems in the source index from specifications in the
339
+ # directories this source index was created from. Raises an exception if
340
+ # this source index wasn't created from a directory (via from_gems_in or
341
+ # from_installed_gems, or having spec_dirs set).
342
+
343
+ def refresh!
344
+ raise 'source index not created from disk' if @spec_dirs.nil?
345
+ load_gems_in(*@spec_dirs)
346
+ end
347
+
348
+ ##
349
+ # Returns an Array of LibGems::Specifications that are not up to date.
350
+
351
+ def outdated
352
+ outdateds = []
353
+
354
+ latest_specs.each do |local|
355
+ dependency = LibGems::Dependency.new local.name, ">= #{local.version}"
356
+
357
+ begin
358
+ fetcher = LibGems::SpecFetcher.fetcher
359
+ remotes = fetcher.find_matching dependency
360
+ remotes = remotes.map { |(name, version,_),_| version }
361
+ rescue LibGems::RemoteFetcher::FetchError => e
362
+ raise unless fetcher.warn_legacy e do
363
+ require 'libgems/source_info_cache'
364
+
365
+ specs = LibGems::SourceInfoCache.search_with_source dependency, true
366
+
367
+ remotes = specs.map { |spec,| spec.version }
368
+ end
369
+ end
370
+
371
+ latest = remotes.sort.last
372
+
373
+ outdateds << local.name if latest and local.version < latest
374
+ end
375
+
376
+ outdateds
377
+ end
378
+
379
+ ##
380
+ # Updates this SourceIndex from +source_uri+. If +all+ is false, only the
381
+ # latest gems are fetched.
382
+
383
+ def update(source_uri, all)
384
+ source_uri = URI.parse source_uri unless URI::Generic === source_uri
385
+ source_uri.path += '/' unless source_uri.path =~ /\/$/
386
+
387
+ use_incremental = false
388
+
389
+ begin
390
+ gem_names = fetch_quick_index source_uri, all
391
+ remove_extra gem_names
392
+ missing_gems = find_missing gem_names
393
+
394
+ return false if missing_gems.size.zero?
395
+
396
+ say "Missing metadata for #{missing_gems.size} gems" if
397
+ missing_gems.size > 0 and LibGems.configuration.really_verbose
398
+
399
+ use_incremental = missing_gems.size <= LibGems.configuration.bulk_threshold
400
+ rescue LibGems::OperationNotSupportedError => ex
401
+ alert_error "Falling back to bulk fetch: #{ex.message}" if
402
+ LibGems.configuration.really_verbose
403
+ use_incremental = false
404
+ end
405
+
406
+ if use_incremental then
407
+ update_with_missing(source_uri, missing_gems)
408
+ else
409
+ new_index = fetch_bulk_index(source_uri)
410
+ @gems.replace(new_index.gems)
411
+ end
412
+
413
+ true
414
+ end
415
+
416
+ def ==(other) # :nodoc:
417
+ self.class === other and @gems == other.gems
418
+ end
419
+
420
+ def dump
421
+ Marshal.dump(self)
422
+ end
423
+
424
+ private
425
+
426
+ def fetcher
427
+ require 'libgems/remote_fetcher'
428
+
429
+ LibGems::RemoteFetcher.fetcher
430
+ end
431
+
432
+ def fetch_index_from(source_uri)
433
+ @fetch_error = nil
434
+
435
+ indexes = %W[
436
+ Marshal.#{LibGems.marshal_version}.Z
437
+ Marshal.#{LibGems.marshal_version}
438
+ yaml.Z
439
+ yaml
440
+ ]
441
+
442
+ indexes.each do |name|
443
+ spec_data = nil
444
+ index = source_uri + name
445
+ begin
446
+ spec_data = fetcher.fetch_path index
447
+ spec_data = unzip(spec_data) if name =~ /\.Z$/
448
+
449
+ if name =~ /Marshal/ then
450
+ return Marshal.load(spec_data)
451
+ else
452
+ return YAML.load(spec_data)
453
+ end
454
+ rescue => e
455
+ if LibGems.configuration.really_verbose then
456
+ alert_error "Unable to fetch #{name}: #{e.message}"
457
+ end
458
+
459
+ @fetch_error = e
460
+ end
461
+ end
462
+
463
+ nil
464
+ end
465
+
466
+ def fetch_bulk_index(source_uri)
467
+ say "Bulk updating LibGems source index for: #{source_uri}" if
468
+ LibGems.configuration.verbose
469
+
470
+ index = fetch_index_from(source_uri)
471
+ if index.nil? then
472
+ raise LibGems::RemoteSourceException,
473
+ "Error fetching remote gem cache: #{@fetch_error}"
474
+ end
475
+ @fetch_error = nil
476
+ index
477
+ end
478
+
479
+ ##
480
+ # Get the quick index needed for incremental updates.
481
+
482
+ def fetch_quick_index(source_uri, all)
483
+ index = all ? 'index' : 'latest_index'
484
+
485
+ zipped_index = fetcher.fetch_path source_uri + "quick/#{index}.rz"
486
+
487
+ unzip(zipped_index).split("\n")
488
+ rescue ::Exception => e
489
+ unless all then
490
+ say "Latest index not found, using quick index" if
491
+ LibGems.configuration.really_verbose
492
+
493
+ fetch_quick_index source_uri, true
494
+ else
495
+ raise LibGems::OperationNotSupportedError,
496
+ "No quick index found: #{e.message}"
497
+ end
498
+ end
499
+
500
+ ##
501
+ # Make a list of full names for all the missing gemspecs.
502
+
503
+ def find_missing(spec_names)
504
+ unless defined? @originals then
505
+ @originals = {}
506
+ each do |full_name, spec|
507
+ @originals[spec.original_name] = spec
508
+ end
509
+ end
510
+
511
+ spec_names.find_all { |full_name|
512
+ @originals[full_name].nil?
513
+ }
514
+ end
515
+
516
+ def remove_extra(spec_names)
517
+ dictionary = spec_names.inject({}) { |h, k| h[k] = true; h }
518
+ each do |name, spec|
519
+ remove_spec name unless dictionary.include? spec.original_name
520
+ end
521
+ end
522
+
523
+ ##
524
+ # Unzip the given string.
525
+
526
+ def unzip(string)
527
+ require 'zlib'
528
+ LibGems.inflate string
529
+ end
530
+
531
+ ##
532
+ # Tries to fetch Marshal representation first, then YAML
533
+
534
+ def fetch_single_spec(source_uri, spec_name)
535
+ @fetch_error = nil
536
+
537
+ begin
538
+ marshal_uri = source_uri + "quick/Marshal.#{LibGems.marshal_version}/#{spec_name}.gemspec.rz"
539
+ zipped = fetcher.fetch_path marshal_uri
540
+ return Marshal.load(unzip(zipped))
541
+ rescue => ex
542
+ @fetch_error = ex
543
+
544
+ if LibGems.configuration.really_verbose then
545
+ say "unable to fetch marshal gemspec #{marshal_uri}: #{ex.class} - #{ex}"
546
+ end
547
+ end
548
+
549
+ begin
550
+ yaml_uri = source_uri + "quick/#{spec_name}.gemspec.rz"
551
+ zipped = fetcher.fetch_path yaml_uri
552
+ return YAML.load(unzip(zipped))
553
+ rescue => ex
554
+ @fetch_error = ex
555
+ if LibGems.configuration.really_verbose then
556
+ say "unable to fetch YAML gemspec #{yaml_uri}: #{ex.class} - #{ex}"
557
+ end
558
+ end
559
+
560
+ nil
561
+ end
562
+
563
+ ##
564
+ # Update the cached source index with the missing names.
565
+
566
+ def update_with_missing(source_uri, missing_names)
567
+ progress = ui.progress_reporter(missing_names.size,
568
+ "Updating metadata for #{missing_names.size} gems from #{source_uri}")
569
+ missing_names.each do |spec_name|
570
+ gemspec = fetch_single_spec(source_uri, spec_name)
571
+ if gemspec.nil? then
572
+ ui.say "Failed to download spec #{spec_name} from #{source_uri}:\n" \
573
+ "\t#{@fetch_error.message}"
574
+ else
575
+ add_spec gemspec
576
+ progress.updated spec_name
577
+ end
578
+ @fetch_error = nil
579
+ end
580
+ progress.done
581
+ progress.count
582
+ end
583
+
584
+ end
585
+
586
+ # :stopdoc:
587
+ module LibGems
588
+
589
+ ##
590
+ # Cache is an alias for SourceIndex to allow older YAMLized source index
591
+ # objects to load properly.
592
+
593
+ Cache = SourceIndex
594
+
595
+ end
596
+ # :startdoc:
597
+