rubygems-update 1.1.1 → 1.2.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 (152) hide show
  1. data.tar.gz.sig +0 -0
  2. data/ChangeLog +267 -1
  3. data/Rakefile +13 -7
  4. data/doc/release_notes/rel_1_2_0.rdoc +105 -0
  5. data/lib/rubygems.rb +82 -14
  6. data/lib/rubygems/command.rb +1 -1
  7. data/lib/rubygems/command_manager.rb +3 -2
  8. data/lib/rubygems/commands/cert_command.rb +1 -1
  9. data/lib/rubygems/commands/dependency_command.rb +57 -19
  10. data/lib/rubygems/commands/environment_command.rb +2 -0
  11. data/lib/rubygems/commands/fetch_command.rb +3 -1
  12. data/lib/rubygems/commands/install_command.rb +3 -3
  13. data/lib/rubygems/commands/list_command.rb +30 -28
  14. data/lib/rubygems/commands/lock_command.rb +1 -1
  15. data/lib/rubygems/commands/outdated_command.rb +5 -2
  16. data/lib/rubygems/commands/pristine_command.rb +3 -44
  17. data/lib/rubygems/commands/query_command.rb +80 -21
  18. data/lib/rubygems/commands/sources_command.rb +56 -28
  19. data/lib/rubygems/commands/specification_command.rb +4 -3
  20. data/lib/rubygems/commands/stale_command.rb +27 -0
  21. data/lib/rubygems/commands/update_command.rb +35 -22
  22. data/lib/rubygems/config_file.rb +29 -12
  23. data/lib/rubygems/custom_require.rb +1 -1
  24. data/lib/rubygems/defaults.rb +1 -1
  25. data/lib/rubygems/dependency.rb +63 -9
  26. data/lib/rubygems/dependency_installer.rb +60 -23
  27. data/lib/rubygems/dependency_list.rb +1 -1
  28. data/lib/rubygems/doc_manager.rb +5 -5
  29. data/lib/rubygems/gem_openssl.rb +1 -1
  30. data/lib/rubygems/indexer.rb +269 -84
  31. data/lib/rubygems/install_update_options.rb +6 -0
  32. data/lib/rubygems/installer.rb +35 -12
  33. data/lib/rubygems/local_remote_options.rb +26 -5
  34. data/lib/rubygems/platform.rb +15 -1
  35. data/lib/rubygems/remote_fetcher.rb +158 -90
  36. data/lib/rubygems/requirement.rb +16 -12
  37. data/lib/rubygems/rubygems_version.rb +1 -1
  38. data/lib/rubygems/security.rb +2 -2
  39. data/lib/rubygems/server.rb +239 -110
  40. data/lib/rubygems/source_index.rb +44 -18
  41. data/lib/rubygems/source_info_cache.rb +1 -1
  42. data/lib/rubygems/spec_fetcher.rb +251 -0
  43. data/lib/rubygems/specification.rb +120 -38
  44. data/lib/rubygems/test_utilities.rb +120 -0
  45. data/lib/rubygems/uninstaller.rb +11 -10
  46. data/lib/rubygems/user_interaction.rb +149 -74
  47. data/lib/rubygems/validator.rb +3 -3
  48. data/lib/rubygems/version.rb +23 -21
  49. data/setup.rb +105 -100
  50. data/test/gemutilities.rb +63 -86
  51. data/test/test_config.rb +0 -5
  52. data/test/test_gem.rb +22 -2
  53. data/test/test_gem_command_manager.rb +1 -1
  54. data/test/test_gem_commands_dependency_command.rb +125 -6
  55. data/test/test_gem_commands_environment_command.rb +1 -0
  56. data/test/test_gem_commands_fetch_command.rb +24 -4
  57. data/test/test_gem_commands_install_command.rb +6 -8
  58. data/test/{test_gem_outdated_command.rb → test_gem_commands_outdated_command.rb} +5 -2
  59. data/test/test_gem_commands_pristine_command.rb +13 -4
  60. data/test/test_gem_commands_query_command.rb +113 -37
  61. data/test/test_gem_commands_sources_command.rb +101 -31
  62. data/test/test_gem_commands_specification_command.rb +4 -1
  63. data/test/test_gem_commands_stale_command.rb +39 -0
  64. data/test/test_gem_commands_update_command.rb +17 -27
  65. data/test/test_gem_config_file.rb +38 -1
  66. data/test/test_gem_dependency.rb +51 -0
  67. data/test/test_gem_dependency_installer.rb +133 -25
  68. data/test/test_gem_gem_path_searcher.rb +4 -1
  69. data/test/test_gem_indexer.rb +124 -19
  70. data/test/test_gem_installer.rb +32 -2
  71. data/test/test_gem_local_remote_options.rb +6 -5
  72. data/test/test_gem_remote_fetcher.rb +14 -9
  73. data/test/test_gem_server.rb +207 -21
  74. data/test/test_gem_source_index.rb +203 -63
  75. data/test/test_gem_source_info_cache.rb +8 -6
  76. data/test/test_gem_source_info_cache_entry.rb +11 -9
  77. data/test/test_gem_spec_fetcher.rb +303 -0
  78. data/test/test_gem_specification.rb +91 -7
  79. data/test/test_gem_uninstaller.rb +21 -0
  80. data/test/test_gem_version.rb +14 -5
  81. data/test/test_kernel.rb +1 -1
  82. metadata +10 -73
  83. metadata.gz.sig +0 -0
  84. data/lib/rubygems/indexer/abstract_index_builder.rb +0 -88
  85. data/lib/rubygems/indexer/latest_index_builder.rb +0 -35
  86. data/lib/rubygems/indexer/marshal_index_builder.rb +0 -17
  87. data/lib/rubygems/indexer/master_index_builder.rb +0 -54
  88. data/lib/rubygems/indexer/quick_index_builder.rb +0 -50
  89. data/test/gem_installer_test_case.rbc +0 -0
  90. data/test/gem_package_tar_test_case.rbc +0 -0
  91. data/test/gemutilities.rbc +0 -0
  92. data/test/mockgemui.rbc +0 -0
  93. data/test/simple_gem.rbc +0 -0
  94. data/test/test_config.rbc +0 -0
  95. data/test/test_gem.rbc +0 -0
  96. data/test/test_gem_builder.rbc +0 -0
  97. data/test/test_gem_command.rbc +0 -0
  98. data/test/test_gem_command_manager.rbc +0 -0
  99. data/test/test_gem_commands_build_command.rbc +0 -0
  100. data/test/test_gem_commands_cert_command.rbc +0 -0
  101. data/test/test_gem_commands_check_command.rbc +0 -0
  102. data/test/test_gem_commands_contents_command.rbc +0 -0
  103. data/test/test_gem_commands_dependency_command.rbc +0 -0
  104. data/test/test_gem_commands_environment_command.rbc +0 -0
  105. data/test/test_gem_commands_fetch_command.rbc +0 -0
  106. data/test/test_gem_commands_generate_index_command.rbc +0 -0
  107. data/test/test_gem_commands_install_command.rbc +0 -0
  108. data/test/test_gem_commands_mirror_command.rbc +0 -0
  109. data/test/test_gem_commands_pristine_command.rbc +0 -0
  110. data/test/test_gem_commands_query_command.rbc +0 -0
  111. data/test/test_gem_commands_server_command.rbc +0 -0
  112. data/test/test_gem_commands_sources_command.rbc +0 -0
  113. data/test/test_gem_commands_specification_command.rbc +0 -0
  114. data/test/test_gem_commands_unpack_command.rbc +0 -0
  115. data/test/test_gem_commands_update_command.rbc +0 -0
  116. data/test/test_gem_config_file.rbc +0 -0
  117. data/test/test_gem_dependency.rbc +0 -0
  118. data/test/test_gem_dependency_installer.rbc +0 -0
  119. data/test/test_gem_dependency_list.rbc +0 -0
  120. data/test/test_gem_digest.rbc +0 -0
  121. data/test/test_gem_doc_manager.rbc +0 -0
  122. data/test/test_gem_ext_configure_builder.rbc +0 -0
  123. data/test/test_gem_ext_ext_conf_builder.rbc +0 -0
  124. data/test/test_gem_ext_rake_builder.rbc +0 -0
  125. data/test/test_gem_format.rbc +0 -0
  126. data/test/test_gem_gem_path_searcher.rbc +0 -0
  127. data/test/test_gem_gem_runner.rbc +0 -0
  128. data/test/test_gem_indexer.rbc +0 -0
  129. data/test/test_gem_install_update_options.rbc +0 -0
  130. data/test/test_gem_installer.rbc +0 -0
  131. data/test/test_gem_local_remote_options.rbc +0 -0
  132. data/test/test_gem_outdated_command.rbc +0 -0
  133. data/test/test_gem_package_tar_header.rbc +0 -0
  134. data/test/test_gem_package_tar_input.rbc +0 -0
  135. data/test/test_gem_package_tar_output.rbc +0 -0
  136. data/test/test_gem_package_tar_reader.rbc +0 -0
  137. data/test/test_gem_package_tar_reader_entry.rbc +0 -0
  138. data/test/test_gem_package_tar_writer.rbc +0 -0
  139. data/test/test_gem_platform.rbc +0 -0
  140. data/test/test_gem_remote_fetcher.rbc +0 -0
  141. data/test/test_gem_requirement.rbc +0 -0
  142. data/test/test_gem_server.rbc +0 -0
  143. data/test/test_gem_source_index.rbc +0 -0
  144. data/test/test_gem_source_info_cache.rbc +0 -0
  145. data/test/test_gem_source_info_cache_entry.rbc +0 -0
  146. data/test/test_gem_specification.rbc +0 -0
  147. data/test/test_gem_stream_ui.rbc +0 -0
  148. data/test/test_gem_uninstaller.rbc +0 -0
  149. data/test/test_gem_validator.rbc +0 -0
  150. data/test/test_gem_version.rbc +0 -0
  151. data/test/test_gem_version_option.rbc +0 -0
  152. data/test/test_kernel.rbc +0 -0
@@ -1,9 +1,12 @@
1
1
  require 'rubygems'
2
2
  require 'rubygems/dependency_list'
3
3
  require 'rubygems/installer'
4
- require 'rubygems/source_info_cache'
4
+ require 'rubygems/spec_fetcher'
5
5
  require 'rubygems/user_interaction'
6
6
 
7
+ ##
8
+ # Installs a gem along with all its dependencies from local and remote gems.
9
+
7
10
  class Gem::DependencyInstaller
8
11
 
9
12
  include Gem::UserInteraction
@@ -25,36 +28,50 @@ class Gem::DependencyInstaller
25
28
  # Creates a new installer instance.
26
29
  #
27
30
  # Options are:
28
- # :env_shebang:: See Gem::Installer::new.
31
+ # :cache_dir:: Alternate repository path to store .gem files in.
29
32
  # :domain:: :local, :remote, or :both. :local only searches gems in the
30
33
  # current directory. :remote searches only gems in Gem::sources.
31
34
  # :both searches both.
35
+ # :env_shebang:: See Gem::Installer::new.
32
36
  # :force:: See Gem::Installer#install.
33
37
  # :format_executable:: See Gem::Installer#initialize.
34
- # :ignore_dependencies: Don't install any dependencies.
35
- # :install_dir: See Gem::Installer#install.
36
- # :security_policy: See Gem::Installer::new and Gem::Security.
37
- # :wrappers: See Gem::Installer::new
38
+ # :ignore_dependencies:: Don't install any dependencies.
39
+ # :install_dir:: See Gem::Installer#install.
40
+ # :security_policy:: See Gem::Installer::new and Gem::Security.
41
+ # :wrappers:: See Gem::Installer::new
42
+
38
43
  def initialize(options = {})
39
44
  options = DEFAULT_OPTIONS.merge options
40
- @env_shebang = options[:env_shebang]
45
+
46
+ @bin_dir = options[:bin_dir]
47
+ @development = options[:development]
41
48
  @domain = options[:domain]
49
+ @env_shebang = options[:env_shebang]
42
50
  @force = options[:force]
43
51
  @format_executable = options[:format_executable]
44
52
  @ignore_dependencies = options[:ignore_dependencies]
45
- @install_dir = options[:install_dir] || Gem.dir
46
53
  @security_policy = options[:security_policy]
47
54
  @wrappers = options[:wrappers]
48
- @bin_dir = options[:bin_dir]
49
55
 
50
56
  @installed_gems = []
57
+
58
+ @install_dir = options[:install_dir] || Gem.dir
59
+ @cache_dir = options[:cache_dir] || @install_dir
60
+
61
+ if options[:install_dir] then
62
+ spec_dir = File.join @install_dir, 'specifications'
63
+ @source_index = Gem::SourceIndex.from_gems_in spec_dir
64
+ else
65
+ @source_index = Gem.source_index
66
+ end
51
67
  end
52
68
 
53
69
  ##
54
70
  # Returns a list of pairs of gemspecs and source_uris that match
55
71
  # Gem::Dependency +dep+ from both local (Dir.pwd) and remote (Gem.sources)
56
72
  # sources. Gems are sorted with newer gems prefered over older gems, and
57
- # local gems prefered over remote gems.
73
+ # local gems preferred over remote gems.
74
+
58
75
  def find_gems_with_sources(dep)
59
76
  gems_and_sources = []
60
77
 
@@ -74,8 +91,7 @@ class Gem::DependencyInstaller
74
91
  all = requirements.length > 1 ||
75
92
  (requirements.first != ">=" and requirements.first != ">")
76
93
 
77
- found = Gem::SourceInfoCache.search_with_source dep, true, all
78
-
94
+ found = Gem::SpecFetcher.fetcher.fetch dep, all
79
95
  gems_and_sources.push(*found)
80
96
 
81
97
  rescue Gem::RemoteFetcher::FetchError => e
@@ -95,6 +111,7 @@ class Gem::DependencyInstaller
95
111
  ##
96
112
  # Gathers all dependencies necessary for the installation from local and
97
113
  # remote sources unless the ignore_dependencies was given.
114
+
98
115
  def gather_dependencies
99
116
  specs = @specs_and_sources.map { |spec,_| spec }
100
117
 
@@ -110,14 +127,25 @@ class Gem::DependencyInstaller
110
127
  next if spec.nil? or seen[spec.name]
111
128
  seen[spec.name] = true
112
129
 
113
- spec.dependencies.each do |dep|
114
- results = find_gems_with_sources(dep).reverse # local gems first
130
+ deps = spec.runtime_dependencies
131
+ deps |= spec.development_dependencies if @development
132
+
133
+ deps.each do |dep|
134
+ results = find_gems_with_sources(dep).reverse
135
+
136
+ results.reject! do |dep_spec,|
137
+ to_do.push dep_spec
138
+
139
+ @source_index.any? do |_, installed_spec|
140
+ dep.name == installed_spec.name and
141
+ dep.version_requirements.satisfied_by? installed_spec.version
142
+ end
143
+ end
115
144
 
116
145
  results.each do |dep_spec, source_uri|
117
146
  next if seen[dep_spec.name]
118
147
  @specs_and_sources << [dep_spec, source_uri]
119
148
  dependency_list.add dep_spec
120
- to_do.push dep_spec
121
149
  end
122
150
  end
123
151
  end
@@ -126,6 +154,11 @@ class Gem::DependencyInstaller
126
154
  @gems_to_install = dependency_list.dependency_order.reverse
127
155
  end
128
156
 
157
+ ##
158
+ # Finds a spec and the source_uri it came from for gem +gem_name+ and
159
+ # +version+. Returns an Array of specs and sources required for
160
+ # installation of the gem.
161
+
129
162
  def find_spec_by_name_and_version gem_name, version = Gem::Requirement.default
130
163
  spec_and_source = nil
131
164
 
@@ -160,14 +193,16 @@ class Gem::DependencyInstaller
160
193
 
161
194
  if spec_and_source.nil? then
162
195
  raise Gem::GemNotFoundException,
163
- "could not find #{gem_name} locally or in a repository"
196
+ "could not find gem #{gem_name} locally or in a repository"
164
197
  end
165
198
 
166
199
  @specs_and_sources = [spec_and_source]
167
200
  end
168
201
 
169
202
  ##
170
- # Installs the gem and all its dependencies.
203
+ # Installs the gem and all its dependencies. Returns an Array of installed
204
+ # gems specifications.
205
+
171
206
  def install dep_or_name, version = Gem::Requirement.default
172
207
  if String === dep_or_name then
173
208
  find_spec_by_name_and_version dep_or_name, version
@@ -175,15 +210,14 @@ class Gem::DependencyInstaller
175
210
  @specs_and_sources = [find_gems_with_sources(dep_or_name).last]
176
211
  end
177
212
 
178
- gather_dependencies
213
+ @installed_gems = []
179
214
 
180
- spec_dir = File.join @install_dir, 'specifications'
181
- source_index = Gem::SourceIndex.from_gems_in spec_dir
215
+ gather_dependencies
182
216
 
183
217
  @gems_to_install.each do |spec|
184
218
  last = spec == @gems_to_install.last
185
219
  # HACK is this test for full_name acceptable?
186
- next if source_index.any? { |n,_| n == spec.full_name } and not last
220
+ next if @source_index.any? { |n,_| n == spec.full_name } and not last
187
221
 
188
222
  # TODO: make this sorta_verbose so other users can benefit from it
189
223
  say "Installing gem #{spec.full_name}" if Gem.configuration.really_verbose
@@ -191,7 +225,7 @@ class Gem::DependencyInstaller
191
225
  _, source_uri = @specs_and_sources.assoc spec
192
226
  begin
193
227
  local_gem_path = Gem::RemoteFetcher.fetcher.download spec, source_uri,
194
- @install_dir
228
+ @cache_dir
195
229
  rescue Gem::RemoteFetcher::FetchError
196
230
  next if @force
197
231
  raise
@@ -205,12 +239,15 @@ class Gem::DependencyInstaller
205
239
  :install_dir => @install_dir,
206
240
  :security_policy => @security_policy,
207
241
  :wrappers => @wrappers,
208
- :bin_dir => @bin_dir
242
+ :bin_dir => @bin_dir,
243
+ :development => @development
209
244
 
210
245
  spec = inst.install
211
246
 
212
247
  @installed_gems << spec
213
248
  end
249
+
250
+ @installed_gems
214
251
  end
215
252
 
216
253
  end
@@ -69,7 +69,7 @@ class Gem::DependencyList
69
69
  # Are all the dependencies in the list satisfied?
70
70
  def ok?
71
71
  @specs.all? do |spec|
72
- spec.dependencies.all? do |dep|
72
+ spec.runtime_dependencies.all? do |dep|
73
73
  @specs.find { |s| s.satisfies_requirement? dep }
74
74
  end
75
75
  end
@@ -9,9 +9,9 @@ require 'fileutils'
9
9
  module Gem
10
10
 
11
11
  class DocManager
12
-
12
+
13
13
  include UserInteraction
14
-
14
+
15
15
  # Create a document manager for the given gem spec.
16
16
  #
17
17
  # spec:: The Gem::Specification object representing the gem.
@@ -22,12 +22,12 @@ module Gem
22
22
  @doc_dir = File.join(spec.installation_path, "doc", spec.full_name)
23
23
  @rdoc_args = rdoc_args.nil? ? [] : rdoc_args.split
24
24
  end
25
-
25
+
26
26
  # Is the RDoc documentation installed?
27
27
  def rdoc_installed?
28
28
  return File.exist?(File.join(@doc_dir, "rdoc"))
29
29
  end
30
-
30
+
31
31
  # Generate the RI documents for this gem spec.
32
32
  #
33
33
  # Note that if both RI and RDoc documents are generated from the
@@ -102,7 +102,7 @@ module Gem
102
102
  args << '--quiet'
103
103
  args << @spec.require_paths.clone
104
104
  args << @spec.extra_rdoc_files
105
- args.flatten!
105
+ args = args.flatten.map do |arg| arg.to_s end
106
106
 
107
107
  r = RDoc::RDoc.new
108
108
 
@@ -17,7 +17,7 @@ module Gem
17
17
  @ssl_available
18
18
  end
19
19
 
20
- # Set the value of the ssl_avilable flag.
20
+ # Set the value of the ssl_available flag.
21
21
  attr_writer :ssl_available
22
22
 
23
23
  # Ensure that SSL is available. Throw an exception if it is not.
@@ -1,5 +1,6 @@
1
1
  require 'fileutils'
2
2
  require 'tmpdir'
3
+ require 'zlib'
3
4
 
4
5
  require 'rubygems'
5
6
  require 'rubygems/format'
@@ -40,116 +41,304 @@ class Gem::Indexer
40
41
 
41
42
  marshal_name = "Marshal.#{Gem.marshal_version}"
42
43
 
43
- @master_index = Gem::Indexer::MasterIndexBuilder.new "yaml", @directory
44
- @marshal_index = Gem::Indexer::MarshalIndexBuilder.new marshal_name, @directory
45
- @quick_index = Gem::Indexer::QuickIndexBuilder.new 'index', @directory
44
+ @master_index = File.join @directory, 'yaml'
45
+ @marshal_index = File.join @directory, marshal_name
46
46
 
47
- quick_dir = File.join @directory, 'quick'
48
- @latest_index = Gem::Indexer::LatestIndexBuilder.new 'latest_index', quick_dir
47
+ @quick_dir = File.join @directory, 'quick'
48
+
49
+ @quick_marshal_dir = File.join @quick_dir, marshal_name
50
+
51
+ @quick_index = File.join @quick_dir, 'index'
52
+ @latest_index = File.join @quick_dir, 'latest_index'
53
+
54
+ @specs_index = File.join @directory, "specs.#{Gem.marshal_version}"
55
+ @latest_specs_index = File.join @directory,
56
+ "latest_specs.#{Gem.marshal_version}"
57
+
58
+ files = [
59
+ @specs_index,
60
+ "#{@specs_index}.gz",
61
+ @latest_specs_index,
62
+ "#{@latest_specs_index}.gz",
63
+ @quick_dir,
64
+ @master_index,
65
+ "#{@master_index}.Z",
66
+ @marshal_index,
67
+ "#{@marshal_index}.Z",
68
+ ]
69
+
70
+ @files = files.map do |path|
71
+ path.sub @directory, ''
72
+ end
73
+ end
74
+
75
+ ##
76
+ # Abbreviate the spec for downloading. Abbreviated specs are only used for
77
+ # searching, downloading and related activities and do not need deployment
78
+ # specific information (e.g. list of files). So we abbreviate the spec,
79
+ # making it much smaller for quicker downloads.
80
+
81
+ def abbreviate(spec)
82
+ spec.files = []
83
+ spec.test_files = []
84
+ spec.rdoc_options = []
85
+ spec.extra_rdoc_files = []
86
+ spec.cert_chain = []
87
+ spec
88
+ end
89
+
90
+ ##
91
+ # Build various indicies
92
+
93
+ def build_indicies(index)
94
+ progress = ui.progress_reporter index.size,
95
+ "Generating quick index gemspecs for #{index.size} gems",
96
+ "Complete"
97
+
98
+ index.each do |original_name, spec|
99
+ spec_file_name = "#{original_name}.gemspec.rz"
100
+ yaml_name = File.join @quick_dir, spec_file_name
101
+ marshal_name = File.join @quick_marshal_dir, spec_file_name
102
+
103
+ yaml_zipped = Gem.deflate spec.to_yaml
104
+ open yaml_name, 'wb' do |io| io.write yaml_zipped end
105
+
106
+ marshal_zipped = Gem.deflate Marshal.dump(spec)
107
+ open marshal_name, 'wb' do |io| io.write marshal_zipped end
108
+
109
+ progress.updated original_name
110
+ end
111
+
112
+ progress.done
113
+
114
+ say "Generating specs index"
115
+
116
+ open @specs_index, 'wb' do |io|
117
+ specs = index.sort.map do |_, spec|
118
+ platform = spec.original_platform
119
+ platform = Gem::Platform::RUBY if platform.nil?
120
+ [spec.name, spec.version, platform]
121
+ end
122
+
123
+ specs = compact_specs specs
124
+
125
+ Marshal.dump specs, io
126
+ end
127
+
128
+ say "Generating latest specs index"
129
+
130
+ open @latest_specs_index, 'wb' do |io|
131
+ specs = index.latest_specs.sort.map do |spec|
132
+ [spec.name, spec.version, spec.original_platform]
133
+ end
134
+
135
+ specs = compact_specs specs
136
+
137
+ Marshal.dump specs, io
138
+ end
139
+
140
+ say "Generating quick index"
141
+
142
+ quick_index = File.join @quick_dir, 'index'
143
+ open quick_index, 'wb' do |io|
144
+ io.puts index.sort.map { |_, spec| spec.original_name }
145
+ end
146
+
147
+ say "Generating latest index"
148
+
149
+ latest_index = File.join @quick_dir, 'latest_index'
150
+ open latest_index, 'wb' do |io|
151
+ io.puts index.latest_specs.sort.map { |spec| spec.original_name }
152
+ end
153
+
154
+ say "Generating Marshal master index"
155
+
156
+ open @marshal_index, 'wb' do |io|
157
+ io.write index.dump
158
+ end
159
+
160
+ progress = ui.progress_reporter index.size,
161
+ "Generating YAML master index for #{index.size} gems (this may take a while)",
162
+ "Complete"
163
+
164
+ open @master_index, 'wb' do |io|
165
+ io.puts "--- !ruby/object:#{index.class}"
166
+ io.puts "gems:"
167
+
168
+ gems = index.sort_by { |name, gemspec| gemspec.sort_obj }
169
+ gems.each do |original_name, gemspec|
170
+ yaml = gemspec.to_yaml.gsub(/^/, ' ')
171
+ yaml = yaml.sub(/\A ---/, '') # there's a needed extra ' ' here
172
+ io.print " #{original_name}:"
173
+ io.puts yaml
174
+
175
+ progress.updated original_name
176
+ end
177
+ end
178
+
179
+ progress.done
180
+
181
+ say "Compressing indicies"
182
+ # use gzip for future files.
183
+
184
+ compress quick_index, 'rz'
185
+ paranoid quick_index, 'rz'
186
+
187
+ compress latest_index, 'rz'
188
+ paranoid latest_index, 'rz'
189
+
190
+ compress @marshal_index, 'Z'
191
+ paranoid @marshal_index, 'Z'
192
+
193
+ compress @master_index, 'Z'
194
+ paranoid @master_index, 'Z'
195
+
196
+ gzip @specs_index
197
+ gzip @latest_specs_index
49
198
  end
50
199
 
51
200
  ##
52
- # Build the index.
53
-
54
- def build_index
55
- @master_index.build do
56
- @quick_index.build do
57
- @marshal_index.build do
58
- @latest_index.build do
59
- progress = ui.progress_reporter gem_file_list.size,
60
- "Generating index for #{gem_file_list.size} gems in #{@dest_directory}",
61
- "Loaded all gems"
62
-
63
- gem_file_list.each do |gemfile|
64
- if File.size(gemfile.to_s) == 0 then
65
- alert_warning "Skipping zero-length gem: #{gemfile}"
66
- next
67
- end
68
-
69
- begin
70
- spec = Gem::Format.from_file_by_path(gemfile).spec
71
-
72
- unless gemfile =~ /\/#{Regexp.escape spec.original_name}.*\.gem\z/i then
73
- alert_warning "Skipping misnamed gem: #{gemfile} => #{spec.full_name} (#{spec.original_name})"
74
- next
75
- end
76
-
77
- abbreviate spec
78
- sanitize spec
79
-
80
- @master_index.add spec
81
- @quick_index.add spec
82
- @marshal_index.add spec
83
- @latest_index.add spec
84
-
85
- progress.updated spec.original_name
86
-
87
- rescue SignalException => e
88
- alert_error "Received signal, exiting"
89
- raise
90
- rescue Exception => e
91
- alert_error "Unable to process #{gemfile}\n#{e.message} (#{e.class})\n\t#{e.backtrace.join "\n\t"}"
92
- end
93
- end
94
-
95
- progress.done
96
-
97
- say "Generating master indexes (this may take a while)"
98
- end
201
+ # Collect specifications from .gem files from the gem directory.
202
+
203
+ def collect_specs
204
+ index = Gem::SourceIndex.new
205
+
206
+ progress = ui.progress_reporter gem_file_list.size,
207
+ "Loading #{gem_file_list.size} gems from #{@dest_directory}",
208
+ "Loaded all gems"
209
+
210
+ gem_file_list.each do |gemfile|
211
+ if File.size(gemfile.to_s) == 0 then
212
+ alert_warning "Skipping zero-length gem: #{gemfile}"
213
+ next
214
+ end
215
+
216
+ begin
217
+ spec = Gem::Format.from_file_by_path(gemfile).spec
218
+
219
+ unless gemfile =~ /\/#{Regexp.escape spec.original_name}.*\.gem\z/i then
220
+ alert_warning "Skipping misnamed gem: #{gemfile} => #{spec.full_name} (#{spec.original_name})"
221
+ next
99
222
  end
223
+
224
+ abbreviate spec
225
+ sanitize spec
226
+
227
+ index.gems[spec.original_name] = spec
228
+
229
+ progress.updated spec.original_name
230
+
231
+ rescue SignalException => e
232
+ alert_error "Received signal, exiting"
233
+ raise
234
+ rescue Exception => e
235
+ alert_error "Unable to process #{gemfile}\n#{e.message} (#{e.class})\n\t#{e.backtrace.join "\n\t"}"
100
236
  end
101
237
  end
238
+
239
+ progress.done
240
+
241
+ index
102
242
  end
103
243
 
104
- def install_index
105
- verbose = Gem.configuration.really_verbose
244
+ ##
245
+ # Compacts Marshal output for the specs index data source by using identical
246
+ # objects as much as possible.
106
247
 
107
- say "Moving index into production dir #{@dest_directory}" if verbose
248
+ def compact_specs(specs)
249
+ names = {}
250
+ versions = {}
251
+ platforms = {}
108
252
 
109
- files = @master_index.files + @quick_index.files + @marshal_index.files +
110
- @latest_index.files
253
+ specs.map do |(name, version, platform)|
254
+ names[name] = name unless names.include? name
255
+ versions[version] = version unless versions.include? version
256
+ platforms[platform] = platform unless platforms.include? platform
111
257
 
112
- files.each do |file|
113
- src_name = File.join @directory, file
114
- dst_name = File.join @dest_directory, file
258
+ [names[name], versions[version], platforms[platform]]
259
+ end
260
+ end
115
261
 
116
- FileUtils.rm_rf dst_name, :verbose => verbose
117
- FileUtils.mv src_name, @dest_directory, :verbose => verbose
262
+ ##
263
+ # Compress +filename+ with +extension+.
264
+
265
+ def compress(filename, extension)
266
+ data = Gem.read_binary filename
267
+
268
+ zipped = Gem.deflate data
269
+
270
+ open "#{filename}.#{extension}", 'wb' do |io|
271
+ io.write zipped
118
272
  end
119
273
  end
120
274
 
275
+ ##
276
+ # List of gem file names to index.
277
+
278
+ def gem_file_list
279
+ Dir.glob(File.join(@dest_directory, "gems", "*.gem"))
280
+ end
281
+
282
+ ##
283
+ # Builds and installs indexicies.
284
+
121
285
  def generate_index
122
286
  FileUtils.rm_rf @directory
123
287
  FileUtils.mkdir_p @directory, :mode => 0700
288
+ FileUtils.mkdir_p @quick_marshal_dir
124
289
 
125
- build_index
126
- install_index
290
+ index = collect_specs
291
+ build_indicies index
292
+ install_indicies
127
293
  rescue SignalException
128
294
  ensure
129
295
  FileUtils.rm_rf @directory
130
296
  end
131
297
 
132
- # List of gem file names to index.
133
- def gem_file_list
134
- Dir.glob(File.join(@dest_directory, "gems", "*.gem"))
298
+ ##
299
+ # Zlib::GzipWriter wrapper that gzips +filename+ on disk.
300
+
301
+ def gzip(filename)
302
+ Zlib::GzipWriter.open "#{filename}.gz" do |io|
303
+ io.write Gem.read_binary(filename)
304
+ end
135
305
  end
136
306
 
137
- # Abbreviate the spec for downloading. Abbreviated specs are only
138
- # used for searching, downloading and related activities and do not
139
- # need deployment specific information (e.g. list of files). So we
140
- # abbreviate the spec, making it much smaller for quicker downloads.
141
- def abbreviate(spec)
142
- spec.files = []
143
- spec.test_files = []
144
- spec.rdoc_options = []
145
- spec.extra_rdoc_files = []
146
- spec.cert_chain = []
147
- spec
307
+ ##
308
+ # Install generated indicies into the destination directory.
309
+
310
+ def install_indicies
311
+ verbose = Gem.configuration.really_verbose
312
+
313
+ say "Moving index into production dir #{@dest_directory}" if verbose
314
+
315
+ @files.each do |file|
316
+ src_name = File.join @directory, file
317
+ dst_name = File.join @dest_directory, file
318
+
319
+ FileUtils.rm_rf dst_name, :verbose => verbose
320
+ FileUtils.mv src_name, @dest_directory, :verbose => verbose,
321
+ :force => true
322
+ end
323
+ end
324
+
325
+ ##
326
+ # Ensure +path+ and path with +extension+ are identical.
327
+
328
+ def paranoid(path, extension)
329
+ data = Gem.read_binary path
330
+ compressed_data = Gem.read_binary "#{path}.#{extension}"
331
+
332
+ unless data == Gem.inflate(compressed_data) then
333
+ raise "Compressed file #{compressed_path} does not match uncompressed file #{path}"
334
+ end
148
335
  end
149
336
 
337
+ ##
150
338
  # Sanitize the descriptive fields in the spec. Sometimes non-ASCII
151
339
  # characters will garble the site index. Non-ASCII characters will
152
340
  # be replaced by their XML entity equivalent.
341
+
153
342
  def sanitize(spec)
154
343
  spec.summary = sanitize_string(spec.summary)
155
344
  spec.description = sanitize_string(spec.description)
@@ -158,7 +347,9 @@ class Gem::Indexer
158
347
  spec
159
348
  end
160
349
 
350
+ ##
161
351
  # Sanitize a single string.
352
+
162
353
  def sanitize_string(string)
163
354
  # HACK the #to_s is in here because RSpec has an Array of Arrays of
164
355
  # Strings for authors. Need a way to disallow bad values on gempsec
@@ -168,9 +359,3 @@ class Gem::Indexer
168
359
 
169
360
  end
170
361
 
171
- require 'rubygems/indexer/abstract_index_builder'
172
- require 'rubygems/indexer/master_index_builder'
173
- require 'rubygems/indexer/quick_index_builder'
174
- require 'rubygems/indexer/marshal_index_builder'
175
- require 'rubygems/indexer/latest_index_builder'
176
-