slimgems 1.3.8

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 (185) 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 +120 -0
  6. data/bin/gem +25 -0
  7. data/bin/update_slimgems +35 -0
  8. data/bootstrap/Rakefile +4 -0
  9. data/hide_lib_for_update/note.txt +5 -0
  10. data/lib/gauntlet_rubygems.rb +50 -0
  11. data/lib/rbconfig/datadir.rb +20 -0
  12. data/lib/rubygems.rb +1220 -0
  13. data/lib/rubygems/builder.rb +102 -0
  14. data/lib/rubygems/command.rb +534 -0
  15. data/lib/rubygems/command_manager.rb +182 -0
  16. data/lib/rubygems/commands/build_command.rb +53 -0
  17. data/lib/rubygems/commands/cert_command.rb +86 -0
  18. data/lib/rubygems/commands/check_command.rb +80 -0
  19. data/lib/rubygems/commands/cleanup_command.rb +106 -0
  20. data/lib/rubygems/commands/contents_command.rb +98 -0
  21. data/lib/rubygems/commands/dependency_command.rb +195 -0
  22. data/lib/rubygems/commands/environment_command.rb +132 -0
  23. data/lib/rubygems/commands/fetch_command.rb +67 -0
  24. data/lib/rubygems/commands/generate_index_command.rb +133 -0
  25. data/lib/rubygems/commands/help_command.rb +172 -0
  26. data/lib/rubygems/commands/install_command.rb +178 -0
  27. data/lib/rubygems/commands/list_command.rb +35 -0
  28. data/lib/rubygems/commands/lock_command.rb +110 -0
  29. data/lib/rubygems/commands/mirror_command.rb +111 -0
  30. data/lib/rubygems/commands/outdated_command.rb +33 -0
  31. data/lib/rubygems/commands/owner_command.rb +75 -0
  32. data/lib/rubygems/commands/pristine_command.rb +93 -0
  33. data/lib/rubygems/commands/push_command.rb +56 -0
  34. data/lib/rubygems/commands/query_command.rb +280 -0
  35. data/lib/rubygems/commands/rdoc_command.rb +91 -0
  36. data/lib/rubygems/commands/search_command.rb +31 -0
  37. data/lib/rubygems/commands/server_command.rb +86 -0
  38. data/lib/rubygems/commands/setup_command.rb +387 -0
  39. data/lib/rubygems/commands/sources_command.rb +157 -0
  40. data/lib/rubygems/commands/specification_command.rb +125 -0
  41. data/lib/rubygems/commands/stale_command.rb +27 -0
  42. data/lib/rubygems/commands/uninstall_command.rb +83 -0
  43. data/lib/rubygems/commands/unpack_command.rb +121 -0
  44. data/lib/rubygems/commands/update_command.rb +212 -0
  45. data/lib/rubygems/commands/which_command.rb +86 -0
  46. data/lib/rubygems/config_file.rb +345 -0
  47. data/lib/rubygems/custom_require.rb +44 -0
  48. data/lib/rubygems/defaults.rb +101 -0
  49. data/lib/rubygems/dependency.rb +227 -0
  50. data/lib/rubygems/dependency_installer.rb +286 -0
  51. data/lib/rubygems/dependency_list.rb +208 -0
  52. data/lib/rubygems/doc_manager.rb +242 -0
  53. data/lib/rubygems/errors.rb +35 -0
  54. data/lib/rubygems/exceptions.rb +91 -0
  55. data/lib/rubygems/ext.rb +18 -0
  56. data/lib/rubygems/ext/builder.rb +56 -0
  57. data/lib/rubygems/ext/configure_builder.rb +25 -0
  58. data/lib/rubygems/ext/ext_conf_builder.rb +24 -0
  59. data/lib/rubygems/ext/rake_builder.rb +39 -0
  60. data/lib/rubygems/format.rb +81 -0
  61. data/lib/rubygems/gem_openssl.rb +92 -0
  62. data/lib/rubygems/gem_path_searcher.rb +100 -0
  63. data/lib/rubygems/gem_runner.rb +79 -0
  64. data/lib/rubygems/gemcutter_utilities.rb +49 -0
  65. data/lib/rubygems/indexer.rb +720 -0
  66. data/lib/rubygems/install_update_options.rb +125 -0
  67. data/lib/rubygems/installer.rb +604 -0
  68. data/lib/rubygems/local_remote_options.rb +135 -0
  69. data/lib/rubygems/old_format.rb +153 -0
  70. data/lib/rubygems/package.rb +97 -0
  71. data/lib/rubygems/package/f_sync_dir.rb +23 -0
  72. data/lib/rubygems/package/tar_header.rb +266 -0
  73. data/lib/rubygems/package/tar_input.rb +222 -0
  74. data/lib/rubygems/package/tar_output.rb +144 -0
  75. data/lib/rubygems/package/tar_reader.rb +106 -0
  76. data/lib/rubygems/package/tar_reader/entry.rb +141 -0
  77. data/lib/rubygems/package/tar_writer.rb +241 -0
  78. data/lib/rubygems/package_task.rb +126 -0
  79. data/lib/rubygems/platform.rb +183 -0
  80. data/lib/rubygems/remote_fetcher.rb +414 -0
  81. data/lib/rubygems/require_paths_builder.rb +18 -0
  82. data/lib/rubygems/requirement.rb +153 -0
  83. data/lib/rubygems/security.rb +814 -0
  84. data/lib/rubygems/server.rb +872 -0
  85. data/lib/rubygems/source_index.rb +597 -0
  86. data/lib/rubygems/source_info_cache.rb +395 -0
  87. data/lib/rubygems/source_info_cache_entry.rb +56 -0
  88. data/lib/rubygems/spec_fetcher.rb +337 -0
  89. data/lib/rubygems/specification.rb +1486 -0
  90. data/lib/rubygems/test_utilities.rb +147 -0
  91. data/lib/rubygems/text.rb +65 -0
  92. data/lib/rubygems/uninstaller.rb +278 -0
  93. data/lib/rubygems/user_interaction.rb +527 -0
  94. data/lib/rubygems/validator.rb +240 -0
  95. data/lib/rubygems/version.rb +316 -0
  96. data/lib/rubygems/version_option.rb +65 -0
  97. data/lib/ubygems.rb +10 -0
  98. data/setup.rb +42 -0
  99. data/test/bogussources.rb +8 -0
  100. data/test/data/gem-private_key.pem +27 -0
  101. data/test/data/gem-public_cert.pem +20 -0
  102. data/test/fake_certlib/openssl.rb +7 -0
  103. data/test/foo/discover.rb +0 -0
  104. data/test/functional.rb +92 -0
  105. data/test/gem_installer_test_case.rb +97 -0
  106. data/test/gem_package_tar_test_case.rb +132 -0
  107. data/test/gemutilities.rb +605 -0
  108. data/test/insure_session.rb +43 -0
  109. data/test/mockgemui.rb +56 -0
  110. data/test/plugin/exception/rubygems_plugin.rb +2 -0
  111. data/test/plugin/load/rubygems_plugin.rb +1 -0
  112. data/test/plugin/standarderror/rubygems_plugin.rb +2 -0
  113. data/test/private_key.pem +27 -0
  114. data/test/public_cert.pem +20 -0
  115. data/test/rubygems_plugin.rb +21 -0
  116. data/test/simple_gem.rb +66 -0
  117. data/test/test_config.rb +12 -0
  118. data/test/test_gem.rb +766 -0
  119. data/test/test_gem_builder.rb +27 -0
  120. data/test/test_gem_command.rb +178 -0
  121. data/test/test_gem_command_manager.rb +207 -0
  122. data/test/test_gem_commands_build_command.rb +74 -0
  123. data/test/test_gem_commands_cert_command.rb +124 -0
  124. data/test/test_gem_commands_check_command.rb +18 -0
  125. data/test/test_gem_commands_contents_command.rb +156 -0
  126. data/test/test_gem_commands_dependency_command.rb +216 -0
  127. data/test/test_gem_commands_environment_command.rb +144 -0
  128. data/test/test_gem_commands_fetch_command.rb +76 -0
  129. data/test/test_gem_commands_generate_index_command.rb +135 -0
  130. data/test/test_gem_commands_install_command.rb +315 -0
  131. data/test/test_gem_commands_list_command.rb +36 -0
  132. data/test/test_gem_commands_lock_command.rb +68 -0
  133. data/test/test_gem_commands_mirror_command.rb +60 -0
  134. data/test/test_gem_commands_outdated_command.rb +40 -0
  135. data/test/test_gem_commands_owner_command.rb +105 -0
  136. data/test/test_gem_commands_pristine_command.rb +108 -0
  137. data/test/test_gem_commands_push_command.rb +81 -0
  138. data/test/test_gem_commands_query_command.rb +426 -0
  139. data/test/test_gem_commands_server_command.rb +59 -0
  140. data/test/test_gem_commands_sources_command.rb +209 -0
  141. data/test/test_gem_commands_specification_command.rb +139 -0
  142. data/test/test_gem_commands_stale_command.rb +38 -0
  143. data/test/test_gem_commands_uninstall_command.rb +83 -0
  144. data/test/test_gem_commands_unpack_command.rb +199 -0
  145. data/test/test_gem_commands_update_command.rb +353 -0
  146. data/test/test_gem_commands_which_command.rb +66 -0
  147. data/test/test_gem_config_file.rb +287 -0
  148. data/test/test_gem_dependency.rb +149 -0
  149. data/test/test_gem_dependency_installer.rb +661 -0
  150. data/test/test_gem_dependency_list.rb +230 -0
  151. data/test/test_gem_doc_manager.rb +31 -0
  152. data/test/test_gem_ext_configure_builder.rb +84 -0
  153. data/test/test_gem_ext_ext_conf_builder.rb +173 -0
  154. data/test/test_gem_ext_rake_builder.rb +81 -0
  155. data/test/test_gem_format.rb +70 -0
  156. data/test/test_gem_gem_path_searcher.rb +78 -0
  157. data/test/test_gem_gem_runner.rb +45 -0
  158. data/test/test_gem_gemcutter_utilities.rb +103 -0
  159. data/test/test_gem_indexer.rb +673 -0
  160. data/test/test_gem_install_update_options.rb +68 -0
  161. data/test/test_gem_installer.rb +857 -0
  162. data/test/test_gem_local_remote_options.rb +97 -0
  163. data/test/test_gem_package_tar_header.rb +130 -0
  164. data/test/test_gem_package_tar_input.rb +112 -0
  165. data/test/test_gem_package_tar_output.rb +97 -0
  166. data/test/test_gem_package_tar_reader.rb +46 -0
  167. data/test/test_gem_package_tar_reader_entry.rb +109 -0
  168. data/test/test_gem_package_tar_writer.rb +144 -0
  169. data/test/test_gem_package_task.rb +59 -0
  170. data/test/test_gem_platform.rb +264 -0
  171. data/test/test_gem_remote_fetcher.rb +740 -0
  172. data/test/test_gem_requirement.rb +292 -0
  173. data/test/test_gem_server.rb +356 -0
  174. data/test/test_gem_silent_ui.rb +113 -0
  175. data/test/test_gem_source_index.rb +461 -0
  176. data/test/test_gem_spec_fetcher.rb +410 -0
  177. data/test/test_gem_specification.rb +1291 -0
  178. data/test/test_gem_stream_ui.rb +218 -0
  179. data/test/test_gem_text.rb +43 -0
  180. data/test/test_gem_uninstaller.rb +146 -0
  181. data/test/test_gem_validator.rb +63 -0
  182. data/test/test_gem_version.rb +181 -0
  183. data/test/test_gem_version_option.rb +89 -0
  184. data/test/test_kernel.rb +59 -0
  185. metadata +413 -0
@@ -0,0 +1,337 @@
1
+ require 'zlib'
2
+ require 'fileutils'
3
+
4
+ require 'rubygems/remote_fetcher'
5
+ require 'rubygems/user_interaction'
6
+ require 'rubygems/errors'
7
+ require 'rubygems/text'
8
+
9
+ ##
10
+ # SpecFetcher handles metadata updates from remote gem repositories.
11
+
12
+ class Gem::SpecFetcher
13
+
14
+ include Gem::UserInteraction
15
+ include Gem::Text
16
+
17
+ FILES = {
18
+ :all => 'specs',
19
+ :latest => 'latest_specs',
20
+ :prerelease => 'prerelease_specs',
21
+ }
22
+
23
+ ##
24
+ # The SpecFetcher cache dir.
25
+
26
+ attr_reader :dir # :nodoc:
27
+
28
+ ##
29
+ # Cache of latest specs
30
+
31
+ attr_reader :latest_specs # :nodoc:
32
+
33
+ ##
34
+ # Cache of all released specs
35
+
36
+ attr_reader :specs # :nodoc:
37
+
38
+ ##
39
+ # Cache of prerelease specs
40
+
41
+ attr_reader :prerelease_specs # :nodoc:
42
+
43
+ @fetcher = nil
44
+
45
+ def self.fetcher
46
+ @fetcher ||= new
47
+ end
48
+
49
+ def self.fetcher=(fetcher) # :nodoc:
50
+ @fetcher = fetcher
51
+ end
52
+
53
+ def initialize
54
+ @dir = File.join Gem.user_home, '.gem', 'specs'
55
+ @update_cache = File.stat(Gem.user_home).uid == Process.uid
56
+
57
+ @specs = {}
58
+ @latest_specs = {}
59
+ @prerelease_specs = {}
60
+
61
+ @caches = {
62
+ :latest => @latest_specs,
63
+ :prerelease => @prerelease_specs,
64
+ :all => @specs
65
+ }
66
+
67
+ @fetcher = Gem::RemoteFetcher.fetcher
68
+ end
69
+
70
+ ##
71
+ # Returns the local directory to write +uri+ to.
72
+
73
+ def cache_dir(uri)
74
+ File.join @dir, "#{uri.host}%#{uri.port}", File.dirname(uri.path)
75
+ end
76
+
77
+ ##
78
+ # Fetch specs matching +dependency+. If +all+ is true, all matching
79
+ # (released) versions are returned. If +matching_platform+ is
80
+ # false, all platforms are returned. If +prerelease+ is true,
81
+ # prerelease versions are included.
82
+
83
+ def fetch_with_errors(dependency, all = false, matching_platform = true, prerelease = false)
84
+ specs_and_sources, errors = find_matching_with_errors dependency, all, matching_platform, prerelease
85
+
86
+ ss = specs_and_sources.map do |spec_tuple, source_uri|
87
+ [fetch_spec(spec_tuple, URI.parse(source_uri)), source_uri]
88
+ end
89
+
90
+ return [ss, errors]
91
+
92
+ rescue Gem::RemoteFetcher::FetchError => e
93
+ raise unless warn_legacy e do
94
+ require 'rubygems/source_info_cache'
95
+
96
+ return [Gem::SourceInfoCache.search_with_source(dependency,
97
+ matching_platform, all), nil]
98
+ end
99
+ end
100
+
101
+ def fetch(*args)
102
+ fetch_with_errors(*args).first
103
+ end
104
+
105
+ def fetch_spec(spec, source_uri)
106
+ spec = spec - [nil, 'ruby', '']
107
+ spec_file_name = "#{spec.join '-'}.gemspec"
108
+
109
+ uri = source_uri + "#{Gem::MARSHAL_SPEC_DIR}#{spec_file_name}"
110
+
111
+ cache_dir = cache_dir uri
112
+
113
+ local_spec = File.join cache_dir, spec_file_name
114
+
115
+ if File.exist? local_spec then
116
+ spec = Gem.read_binary local_spec
117
+ else
118
+ uri.path << '.rz'
119
+
120
+ spec = @fetcher.fetch_path uri
121
+ spec = Gem.inflate spec
122
+
123
+ if @update_cache then
124
+ FileUtils.mkdir_p cache_dir
125
+
126
+ open local_spec, 'wb' do |io|
127
+ io.write spec
128
+ end
129
+ end
130
+ end
131
+
132
+ # TODO: Investigate setting Gem::Specification#loaded_from to a URI
133
+ Marshal.load spec
134
+ end
135
+
136
+ ##
137
+ # Find spec names that match +dependency+. If +all+ is true, all
138
+ # matching released versions are returned. If +matching_platform+
139
+ # is false, gems for all platforms are returned.
140
+
141
+ def find_matching_with_errors(dependency, all = false, matching_platform = true, prerelease = false)
142
+ found = {}
143
+
144
+ rejected_specs = {}
145
+
146
+ list(all, prerelease).each do |source_uri, specs|
147
+ found[source_uri] = specs.select do |spec_name, version, spec_platform|
148
+ if dependency.match?(spec_name, version)
149
+ if matching_platform and !Gem::Platform.match(spec_platform)
150
+ pm = (rejected_specs[dependency] ||= Gem::PlatformMismatch.new(spec_name, version))
151
+ pm.add_platform spec_platform
152
+ false
153
+ else
154
+ true
155
+ end
156
+ end
157
+ end
158
+ end
159
+
160
+ errors = rejected_specs.values
161
+
162
+ specs_and_sources = []
163
+
164
+ found.each do |source_uri, specs|
165
+ uri_str = source_uri.to_s
166
+ specs_and_sources.push(*specs.map { |spec| [spec, uri_str] })
167
+ end
168
+
169
+ [specs_and_sources, errors]
170
+ end
171
+
172
+ def find_matching(*args)
173
+ find_matching_with_errors(*args).first
174
+ end
175
+
176
+ ##
177
+ # Returns Array of gem repositories that were generated with SlimGems less
178
+ # than 1.2.
179
+
180
+ def legacy_repos
181
+ Gem.sources.reject do |source_uri|
182
+ source_uri = URI.parse source_uri
183
+ spec_path = source_uri + "specs.#{Gem.marshal_version}.gz"
184
+
185
+ begin
186
+ @fetcher.fetch_size spec_path
187
+ rescue Gem::RemoteFetcher::FetchError
188
+ begin
189
+ @fetcher.fetch_size(source_uri + 'yaml') # re-raise if non-repo
190
+ rescue Gem::RemoteFetcher::FetchError
191
+ alert_error "#{source_uri} does not appear to be a repository"
192
+ raise
193
+ end
194
+ false
195
+ end
196
+ end
197
+ end
198
+
199
+ ##
200
+ # Suggests a gem based on the supplied +gem_name+. Returns a string
201
+ # of the gem name if an approximate match can be found or nil
202
+ # otherwise. NOTE: for performance reasons only gems which exactly
203
+ # match the first character of +gem_name+ are considered.
204
+
205
+ def suggest_gems_from_name gem_name
206
+ gem_name = gem_name.downcase
207
+ gem_starts_with = gem_name[0,1]
208
+ max = gem_name.size / 2
209
+ specs = list.values.flatten(1) # flatten(1) is 1.8.7 and up
210
+
211
+ matches = specs.map { |name, version, platform|
212
+ next unless Gem::Platform.match platform
213
+
214
+ distance = levenshtein_distance gem_name, name.downcase
215
+
216
+ next if distance >= max
217
+
218
+ return [name] if distance == 0
219
+
220
+ [name, distance]
221
+ }.compact
222
+
223
+ matches = matches.uniq.sort_by { |name, dist| dist }
224
+
225
+ matches.first(5).map { |name, dist| name }
226
+ end
227
+
228
+ ##
229
+ # Returns a list of gems available for each source in Gem::sources. If
230
+ # +all+ is true, all released versions are returned instead of only latest
231
+ # versions. If +prerelease+ is true, include prerelease versions.
232
+
233
+ def list(all = false, prerelease = false)
234
+ # TODO: make type the only argument
235
+ type = if all
236
+ :all
237
+ elsif prerelease
238
+ :prerelease
239
+ else
240
+ :latest
241
+ end
242
+
243
+ list = {}
244
+ file = FILES[type]
245
+ cache = @caches[type]
246
+
247
+ Gem.sources.each do |source_uri|
248
+ source_uri = URI.parse source_uri
249
+
250
+ unless cache.include? source_uri
251
+ cache[source_uri] = load_specs source_uri, file
252
+ end
253
+
254
+ list[source_uri] = cache[source_uri]
255
+ end
256
+
257
+ if type == :all
258
+ list.values.map do |gems|
259
+ gems.reject! { |g| !g[1] || g[1].prerelease? }
260
+ end
261
+ end
262
+
263
+ list
264
+ end
265
+
266
+ ##
267
+ # Loads specs in +file+, fetching from +source_uri+ if the on-disk cache is
268
+ # out of date.
269
+
270
+ def load_specs(source_uri, file)
271
+ file_name = "#{file}.#{Gem.marshal_version}"
272
+ spec_path = source_uri + "#{file_name}.gz"
273
+ cache_dir = cache_dir spec_path
274
+ local_file = File.join(cache_dir, file_name)
275
+ loaded = false
276
+
277
+ if File.exist? local_file then
278
+ spec_dump = @fetcher.fetch_path spec_path, File.mtime(local_file)
279
+
280
+ if spec_dump.nil? then
281
+ spec_dump = Gem.read_binary local_file
282
+ else
283
+ loaded = true
284
+ end
285
+ else
286
+ spec_dump = @fetcher.fetch_path spec_path
287
+ loaded = true
288
+ end
289
+
290
+ specs = begin
291
+ Marshal.load spec_dump
292
+ rescue ArgumentError
293
+ spec_dump = @fetcher.fetch_path spec_path
294
+ loaded = true
295
+
296
+ Marshal.load spec_dump
297
+ end
298
+
299
+ if loaded and @update_cache then
300
+ begin
301
+ FileUtils.mkdir_p cache_dir
302
+
303
+ open local_file, 'wb' do |io|
304
+ io << spec_dump
305
+ end
306
+ rescue
307
+ end
308
+ end
309
+
310
+ specs
311
+ end
312
+
313
+ ##
314
+ # Warn about legacy repositories if +exception+ indicates only legacy
315
+ # repositories are available, and yield to the block. Returns false if the
316
+ # exception indicates some other FetchError.
317
+
318
+ def warn_legacy(exception)
319
+ uri = exception.uri.to_s
320
+ if uri =~ /specs\.#{Regexp.escape Gem.marshal_version}\.gz$/ then
321
+ alert_warning <<-EOF
322
+ #{Gem::NAME} 1.2+ index not found for:
323
+ \t#{legacy_repos.join "\n\t"}
324
+
325
+ #{Gem::NAME} will revert to legacy indexes degrading performance.
326
+ EOF
327
+
328
+ yield
329
+
330
+ return true
331
+ end
332
+
333
+ false
334
+ end
335
+
336
+ end
337
+
@@ -0,0 +1,1486 @@
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 'rubygems/version'
8
+ require 'rubygems/requirement'
9
+ require 'rubygems/platform'
10
+
11
+ # :stopdoc:
12
+ class Date; end # for ruby_code if date.rb wasn't required
13
+ # :startdoc:
14
+
15
+ ##
16
+ # The Specification class contains the metadata for a Gem. Typically
17
+ # defined in a .gemspec file or a Rakefile, and looks like this:
18
+ #
19
+ # spec = Gem::Specification.new do |s|
20
+ # s.name = 'example'
21
+ # s.version = '1.0'
22
+ # s.summary = 'Example gem specification'
23
+ # ...
24
+ # end
25
+ #
26
+ # For a great way to package gems, use Hoe.
27
+
28
+ class Gem::Specification
29
+
30
+ ##
31
+ # Allows deinstallation of gems with legacy platforms.
32
+
33
+ attr_accessor :original_platform # :nodoc:
34
+
35
+ ##
36
+ # The the version number of a specification that does not specify one
37
+ # (i.e. SlimGems 0.7 or earlier).
38
+
39
+ NONEXISTENT_SPECIFICATION_VERSION = -1
40
+
41
+ ##
42
+ # The specification version applied to any new Specification instances
43
+ # created. This should be bumped whenever something in the spec format
44
+ # changes.
45
+ #--
46
+ # When updating this number, be sure to also update #to_ruby.
47
+ #
48
+ # NOTE SlimGems < 1.2 cannot load specification versions > 2.
49
+
50
+ CURRENT_SPECIFICATION_VERSION = 3
51
+
52
+ ##
53
+ # An informal list of changes to the specification. The highest-valued
54
+ # key should be equal to the CURRENT_SPECIFICATION_VERSION.
55
+
56
+ SPECIFICATION_VERSION_HISTORY = {
57
+ -1 => ["(#{Gem::NAME} versions up to and including 0.7 did not have versioned specifications)"],
58
+ 1 => [
59
+ 'Deprecated "test_suite_file" in favor of the new, but equivalent, "test_files"',
60
+ '"test_file=x" is a shortcut for "test_files=[x]"'
61
+ ],
62
+ 2 => [
63
+ 'Added "required_rubygems_version"',
64
+ 'Now forward-compatible with future versions',
65
+ ],
66
+ 3 => [
67
+ 'Added Fixnum validation to the specification_version'
68
+ ]
69
+ }
70
+
71
+ # :stopdoc:
72
+ MARSHAL_FIELDS = { -1 => 16, 1 => 16, 2 => 16, 3 => 17 }
73
+
74
+ now = Time.at(Time.now.to_i)
75
+ TODAY = now - ((now.to_i + now.gmt_offset) % 86400)
76
+ # :startdoc:
77
+
78
+ ##
79
+ # Optional block used to gather newly defined instances.
80
+
81
+ @@gather = nil
82
+
83
+ ##
84
+ # List of attribute names: [:name, :version, ...]
85
+
86
+ @@required_attributes = []
87
+
88
+ ##
89
+ # List of _all_ attributes and default values:
90
+ #
91
+ # [[:name, nil],
92
+ # [:bindir, 'bin'],
93
+ # ...]
94
+
95
+ @@attributes = []
96
+
97
+ @@nil_attributes = []
98
+ @@non_nil_attributes = [:@original_platform]
99
+
100
+ ##
101
+ # List of array attributes
102
+
103
+ @@array_attributes = []
104
+
105
+ ##
106
+ # Map of attribute names to default values.
107
+
108
+ @@default_value = {}
109
+
110
+ ##
111
+ # Names of all specification attributes
112
+
113
+ def self.attribute_names
114
+ @@attributes.map { |name, default| name }
115
+ end
116
+
117
+ ##
118
+ # Default values for specification attributes
119
+
120
+ def self.attribute_defaults
121
+ @@attributes.dup
122
+ end
123
+
124
+ ##
125
+ # The default value for specification attribute +name+
126
+
127
+ def self.default_value(name)
128
+ @@default_value[name]
129
+ end
130
+
131
+ ##
132
+ # Required specification attributes
133
+
134
+ def self.required_attributes
135
+ @@required_attributes.dup
136
+ end
137
+
138
+ ##
139
+ # Is +name+ a required attribute?
140
+
141
+ def self.required_attribute?(name)
142
+ @@required_attributes.include? name.to_sym
143
+ end
144
+
145
+ ##
146
+ # Specification attributes that are arrays (appendable and so-forth)
147
+
148
+ def self.array_attributes
149
+ @@array_attributes.dup
150
+ end
151
+
152
+ ##
153
+ # Specifies the +name+ and +default+ for a specification attribute, and
154
+ # creates a reader and writer method like Module#attr_accessor.
155
+ #
156
+ # The reader method returns the default if the value hasn't been set.
157
+
158
+ def self.attribute(name, default=nil)
159
+ ivar_name = "@#{name}".intern
160
+ if default.nil? then
161
+ @@nil_attributes << ivar_name
162
+ else
163
+ @@non_nil_attributes << [ivar_name, default]
164
+ end
165
+
166
+ @@attributes << [name, default]
167
+ @@default_value[name] = default
168
+ attr_accessor(name)
169
+ end
170
+
171
+ ##
172
+ # Same as :attribute, but ensures that values assigned to the attribute
173
+ # are array values by applying :to_a to the value.
174
+
175
+ def self.array_attribute(name)
176
+ @@non_nil_attributes << ["@#{name}".intern, []]
177
+
178
+ @@array_attributes << name
179
+ @@attributes << [name, []]
180
+ @@default_value[name] = []
181
+ code = %{
182
+ def #{name}
183
+ @#{name} ||= []
184
+ end
185
+ def #{name}=(value)
186
+ @#{name} = Array(value)
187
+ end
188
+ }
189
+
190
+ module_eval code, __FILE__, __LINE__ - 9
191
+ end
192
+
193
+ ##
194
+ # Same as attribute above, but also records this attribute as mandatory.
195
+
196
+ def self.required_attribute(*args)
197
+ @@required_attributes << args.first
198
+ attribute(*args)
199
+ end
200
+
201
+ ##
202
+ # Sometimes we don't want the world to use a setter method for a
203
+ # particular attribute.
204
+ #
205
+ # +read_only+ makes it private so we can still use it internally.
206
+
207
+ def self.read_only(*names)
208
+ names.each do |name|
209
+ private "#{name}="
210
+ end
211
+ end
212
+
213
+ # Shortcut for creating several attributes at once (each with a default
214
+ # value of +nil+).
215
+
216
+ def self.attributes(*args)
217
+ args.each do |arg|
218
+ attribute(arg, nil)
219
+ end
220
+ end
221
+
222
+ ##
223
+ # Some attributes require special behaviour when they are accessed. This
224
+ # allows for that.
225
+
226
+ def self.overwrite_accessor(name, &block)
227
+ remove_method name
228
+ define_method(name, &block)
229
+ end
230
+
231
+ ##
232
+ # Defines a _singular_ version of an existing _plural_ attribute (i.e. one
233
+ # whose value is expected to be an array). This means just creating a
234
+ # helper method that takes a single value and appends it to the array.
235
+ # These are created for convenience, so that in a spec, one can write
236
+ #
237
+ # s.require_path = 'mylib'
238
+ #
239
+ # instead of:
240
+ #
241
+ # s.require_paths = ['mylib']
242
+ #
243
+ # That above convenience is available courtesy of:
244
+ #
245
+ # attribute_alias_singular :require_path, :require_paths
246
+
247
+ def self.attribute_alias_singular(singular, plural)
248
+ define_method("#{singular}=") { |val|
249
+ send("#{plural}=", [val])
250
+ }
251
+ define_method("#{singular}") {
252
+ val = send("#{plural}")
253
+ val.nil? ? nil : val.first
254
+ }
255
+ end
256
+
257
+ ##
258
+ # Dump only crucial instance variables.
259
+ #--
260
+ # MAINTAIN ORDER!
261
+
262
+ def _dump(limit)
263
+ Marshal.dump [
264
+ @rubygems_version,
265
+ @specification_version,
266
+ @name,
267
+ @version,
268
+ (Time === @date ? @date : (require 'time'; Time.parse(@date.to_s))),
269
+ @summary,
270
+ @required_ruby_version,
271
+ @required_rubygems_version,
272
+ @original_platform,
273
+ @dependencies,
274
+ @rubyforge_project,
275
+ @email,
276
+ @authors,
277
+ @description,
278
+ @homepage,
279
+ @has_rdoc,
280
+ @new_platform,
281
+ @licenses
282
+ ]
283
+ end
284
+
285
+ ##
286
+ # Load custom marshal format, re-initializing defaults as needed
287
+
288
+ def self._load(str)
289
+ array = Marshal.load str
290
+
291
+ spec = Gem::Specification.new
292
+ spec.instance_variable_set :@specification_version, array[1]
293
+
294
+ current_version = CURRENT_SPECIFICATION_VERSION
295
+
296
+ field_count = if spec.specification_version > current_version then
297
+ spec.instance_variable_set :@specification_version,
298
+ current_version
299
+ MARSHAL_FIELDS[current_version]
300
+ else
301
+ MARSHAL_FIELDS[spec.specification_version]
302
+ end
303
+
304
+ if array.size < field_count then
305
+ raise TypeError, "invalid Gem::Specification format #{array.inspect}"
306
+ end
307
+
308
+ spec.instance_variable_set :@rubygems_version, array[0]
309
+ # spec version
310
+ spec.instance_variable_set :@name, array[2]
311
+ spec.instance_variable_set :@version, array[3]
312
+ spec.instance_variable_set :@date, array[4]
313
+ spec.instance_variable_set :@summary, array[5]
314
+ spec.instance_variable_set :@required_ruby_version, array[6]
315
+ spec.instance_variable_set :@required_rubygems_version, array[7]
316
+ spec.instance_variable_set :@original_platform, array[8]
317
+ spec.instance_variable_set :@dependencies, array[9]
318
+ spec.instance_variable_set :@rubyforge_project, array[10]
319
+ spec.instance_variable_set :@email, array[11]
320
+ spec.instance_variable_set :@authors, array[12]
321
+ spec.instance_variable_set :@description, array[13]
322
+ spec.instance_variable_set :@homepage, array[14]
323
+ spec.instance_variable_set :@has_rdoc, array[15]
324
+ spec.instance_variable_set :@new_platform, array[16]
325
+ spec.instance_variable_set :@platform, array[16].to_s
326
+ spec.instance_variable_set :@license, array[17]
327
+ spec.instance_variable_set :@loaded, false
328
+
329
+ spec
330
+ end
331
+
332
+ ##
333
+ # List of depedencies that will automatically be activated at runtime.
334
+
335
+ def runtime_dependencies
336
+ dependencies.select { |d| d.type == :runtime || d.type == nil }
337
+ end
338
+
339
+ ##
340
+ # List of dependencies that are used for development
341
+
342
+ def development_dependencies
343
+ dependencies.select { |d| d.type == :development }
344
+ end
345
+
346
+ def test_suite_file # :nodoc:
347
+ warn 'test_suite_file deprecated, use test_files'
348
+ test_files.first
349
+ end
350
+
351
+ def test_suite_file=(val) # :nodoc:
352
+ warn 'test_suite_file= deprecated, use test_files='
353
+ @test_files = [] unless defined? @test_files
354
+ @test_files << val
355
+ end
356
+
357
+ ##
358
+ # true when this gemspec has been loaded from a specifications directory.
359
+ # This attribute is not persisted.
360
+
361
+ attr_accessor :loaded
362
+
363
+ ##
364
+ # Path this gemspec was loaded from. This attribute is not persisted.
365
+
366
+ attr_accessor :loaded_from
367
+
368
+ ##
369
+ # Returns an array with bindir attached to each executable in the
370
+ # executables list
371
+
372
+ def add_bindir(executables)
373
+ return nil if executables.nil?
374
+
375
+ if @bindir then
376
+ Array(executables).map { |e| File.join(@bindir, e) }
377
+ else
378
+ executables
379
+ end
380
+ rescue
381
+ return nil
382
+ end
383
+
384
+ ##
385
+ # Files in the Gem under one of the require_paths
386
+
387
+ def lib_files
388
+ @files.select do |file|
389
+ require_paths.any? do |path|
390
+ file.index(path) == 0
391
+ end
392
+ end
393
+ end
394
+
395
+ ##
396
+ # True if this gem was loaded from disk
397
+
398
+ alias :loaded? :loaded
399
+
400
+ ##
401
+ # True if this gem has files in test_files
402
+
403
+ def has_unit_tests?
404
+ not test_files.empty?
405
+ end
406
+
407
+ # :stopdoc:
408
+ alias has_test_suite? has_unit_tests?
409
+ # :startdoc:
410
+
411
+ ##
412
+ # Specification constructor. Assigns the default values to the
413
+ # attributes and yields itself for further
414
+ # initialization. Optionally takes +name+ and +version+.
415
+
416
+ def initialize name = nil, version = nil
417
+ @new_platform = nil
418
+ assign_defaults
419
+ @loaded = false
420
+ @loaded_from = nil
421
+
422
+ self.name = name if name
423
+ self.version = version if version
424
+
425
+ yield self if block_given?
426
+
427
+ @@gather.call(self) if @@gather
428
+ end
429
+
430
+ ##
431
+ # Duplicates array_attributes from +other_spec+ so state isn't shared.
432
+
433
+ def initialize_copy(other_spec)
434
+ other_ivars = other_spec.instance_variables
435
+ other_ivars = other_ivars.map { |ivar| ivar.intern } if # for 1.9
436
+ other_ivars.any? { |ivar| String === ivar }
437
+
438
+ self.class.array_attributes.each do |name|
439
+ name = :"@#{name}"
440
+ next unless other_ivars.include? name
441
+ instance_variable_set name, other_spec.instance_variable_get(name).dup
442
+ end
443
+ end
444
+
445
+ ##
446
+ # Each attribute has a default value (possibly nil). Here, we initialize
447
+ # all attributes to their default value. This is done through the
448
+ # accessor methods, so special behaviours will be honored. Furthermore,
449
+ # we take a _copy_ of the default so each specification instance has its
450
+ # own empty arrays, etc.
451
+
452
+ def assign_defaults
453
+ @@nil_attributes.each do |name|
454
+ instance_variable_set name, nil
455
+ end
456
+
457
+ @@non_nil_attributes.each do |name, default|
458
+ value = case default
459
+ when Time, Numeric, Symbol, true, false, nil then default
460
+ else default.dup
461
+ end
462
+
463
+ instance_variable_set name, value
464
+ end
465
+
466
+ # HACK
467
+ instance_variable_set :@new_platform, Gem::Platform::RUBY
468
+ end
469
+
470
+ ##
471
+ # Special loader for YAML files. When a Specification object is loaded
472
+ # from a YAML file, it bypasses the normal Ruby object initialization
473
+ # routine (#initialize). This method makes up for that and deals with
474
+ # gems of different ages.
475
+ #
476
+ # 'input' can be anything that YAML.load() accepts: String or IO.
477
+
478
+ def self.from_yaml(input)
479
+ input = normalize_yaml_input input
480
+ spec = YAML.load input
481
+
482
+ if spec && spec.class == FalseClass then
483
+ raise Gem::EndOfYAMLException
484
+ end
485
+
486
+ unless Gem::Specification === spec then
487
+ raise Gem::Exception, "YAML data doesn't evaluate to gem specification"
488
+ end
489
+
490
+ unless (spec.instance_variables.include? '@specification_version' or
491
+ spec.instance_variables.include? :@specification_version) and
492
+ spec.instance_variable_get :@specification_version
493
+ spec.instance_variable_set :@specification_version,
494
+ NONEXISTENT_SPECIFICATION_VERSION
495
+ end
496
+
497
+ spec
498
+ end
499
+
500
+ ##
501
+ # Loads ruby format gemspec from +filename+
502
+
503
+ def self.load(filename)
504
+ gemspec = nil
505
+ raise "NESTED Specification.load calls not allowed!" if @@gather
506
+ @@gather = proc { |gs| gemspec = gs }
507
+ data = File.read filename
508
+ eval data, nil, filename
509
+ gemspec
510
+ ensure
511
+ @@gather = nil
512
+ end
513
+
514
+ ##
515
+ # Make sure the YAML specification is properly formatted with dashes
516
+
517
+ def self.normalize_yaml_input(input)
518
+ result = input.respond_to?(:read) ? input.read : input
519
+ result = "--- " + result unless result =~ /\A--- /
520
+ result.gsub(/ !!null \n/, " \n")
521
+ end
522
+
523
+ ##
524
+ # Sets the rubygems_version to the current SlimGems version
525
+
526
+ def mark_version
527
+ @rubygems_version = Gem::VERSION
528
+ end
529
+
530
+ ##
531
+ # Ignore unknown attributes while loading
532
+
533
+ def method_missing(sym, *a, &b) # :nodoc:
534
+ if @specification_version > CURRENT_SPECIFICATION_VERSION and
535
+ sym.to_s =~ /=$/ then
536
+ warn "ignoring #{sym} loading #{full_name}" if $DEBUG
537
+ else
538
+ super
539
+ end
540
+ end
541
+
542
+ ##
543
+ # Adds a development dependency named +gem+ with +requirements+ to this
544
+ # Gem. For example:
545
+ #
546
+ # spec.add_development_dependency 'jabber4r', '> 0.1', '<= 0.5'
547
+ #
548
+ # Development dependencies aren't installed by default and aren't
549
+ # activated when a gem is required.
550
+
551
+ def add_development_dependency(gem, *requirements)
552
+ add_dependency_with_type(gem, :development, *requirements)
553
+ end
554
+
555
+ ##
556
+ # Adds a runtime dependency named +gem+ with +requirements+ to this Gem.
557
+ # For example:
558
+ #
559
+ # spec.add_runtime_dependency 'jabber4r', '> 0.1', '<= 0.5'
560
+
561
+ def add_runtime_dependency(gem, *requirements)
562
+ add_dependency_with_type(gem, :runtime, *requirements)
563
+ end
564
+
565
+ ##
566
+ # Adds a runtime dependency
567
+
568
+ alias add_dependency add_runtime_dependency
569
+
570
+ ##
571
+ # Returns the full name (name-version) of this Gem. Platform information
572
+ # is included (name-version-platform) if it is specified and not the
573
+ # default Ruby platform.
574
+
575
+ def full_name
576
+ if platform == Gem::Platform::RUBY or platform.nil? then
577
+ "#{@name}-#{@version}"
578
+ else
579
+ "#{@name}-#{@version}-#{platform}"
580
+ end
581
+ end
582
+
583
+ ##
584
+ # Returns the full name (name-version) of this gemspec using the original
585
+ # platform. For use with legacy gems.
586
+
587
+ def original_name # :nodoc:
588
+ if platform == Gem::Platform::RUBY or platform.nil? then
589
+ "#{@name}-#{@version}"
590
+ else
591
+ "#{@name}-#{@version}-#{@original_platform}"
592
+ end
593
+ end
594
+
595
+ ##
596
+ # The full path to the gem (install path + full name).
597
+
598
+ def full_gem_path
599
+ path = File.join installation_path, 'gems', full_name
600
+ return path if File.directory? path
601
+ File.join installation_path, 'gems', original_name
602
+ end
603
+
604
+ ##
605
+ # The default (generated) file name of the gem. See also #spec_name.
606
+ #
607
+ # spec.file_name # => "example-1.0.gem"
608
+
609
+ def file_name
610
+ full_name + '.gem'
611
+ end
612
+
613
+ ##
614
+ # The directory that this gem was installed into.
615
+
616
+ def installation_path
617
+ unless @loaded_from then
618
+ raise Gem::Exception, "spec #{full_name} is not from an installed gem"
619
+ end
620
+
621
+ File.expand_path File.dirname(File.dirname(@loaded_from))
622
+ end
623
+
624
+ ##
625
+ # Checks if this specification meets the requirement of +dependency+.
626
+
627
+ def satisfies_requirement?(dependency)
628
+ return @name == dependency.name &&
629
+ dependency.requirement.satisfied_by?(@version)
630
+ end
631
+
632
+ ##
633
+ # Returns an object you can use to sort specifications in #sort_by.
634
+
635
+ def sort_obj
636
+ [@name, @version, @new_platform == Gem::Platform::RUBY ? -1 : 1]
637
+ end
638
+
639
+ ##
640
+ # The default name of the gemspec. See also #file_name
641
+ #
642
+ # spec.spec_name # => "example-1.0.gemspec"
643
+
644
+ def spec_name
645
+ full_name + '.gemspec'
646
+ end
647
+
648
+ def <=>(other) # :nodoc:
649
+ sort_obj <=> other.sort_obj
650
+ end
651
+
652
+ ##
653
+ # Tests specs for equality (across all attributes).
654
+
655
+ def ==(other) # :nodoc:
656
+ self.class === other && same_attributes?(other)
657
+ end
658
+
659
+ alias eql? == # :nodoc:
660
+
661
+ ##
662
+ # True if this gem has the same attributes as +other+.
663
+
664
+ def same_attributes?(other)
665
+ @@attributes.each do |name, default|
666
+ return false unless self.send(name) == other.send(name)
667
+ end
668
+ true
669
+ end
670
+
671
+ private :same_attributes?
672
+
673
+ def hash # :nodoc:
674
+ @@attributes.inject(0) { |hash_code, (name, default_value)|
675
+ hash_code ^ self.send(name).hash
676
+ }
677
+ end
678
+
679
+ def encode_with coder # :nodoc:
680
+ mark_version
681
+
682
+ attributes = @@attributes.map { |name,| name.to_s }.sort
683
+ attributes = attributes - %w[name version platform]
684
+
685
+ coder.add 'name', @name
686
+ coder.add 'version', @version
687
+ platform = case @original_platform
688
+ when nil, '' then
689
+ 'ruby'
690
+ when String then
691
+ @original_platform
692
+ else
693
+ @original_platform.to_s
694
+ end
695
+ coder.add 'platform', platform
696
+
697
+ attributes.each do |name|
698
+ coder.add name, instance_variable_get("@#{name}")
699
+ end
700
+ end
701
+
702
+ def to_yaml(opts = {}) # :nodoc:
703
+ if YAML.const_defined?(:ENGINE) && !YAML::ENGINE.syck? then
704
+ super.gsub(/ !!null \n/, " \n")
705
+ else
706
+ YAML.quick_emit object_id, opts do |out|
707
+ out.map taguri, to_yaml_style do |map|
708
+ encode_with map
709
+ end
710
+ end
711
+ end
712
+ end
713
+
714
+ def init_with coder # :nodoc:
715
+ yaml_initialize coder.tag, coder.map
716
+ end
717
+
718
+ def yaml_initialize(tag, vals) # :nodoc:
719
+ vals.each do |ivar, val|
720
+ instance_variable_set "@#{ivar}", val
721
+ end
722
+
723
+ @original_platform = @platform # for backwards compatibility
724
+ self.platform = Gem::Platform.new @platform
725
+ end
726
+
727
+ ##
728
+ # Returns a Ruby code representation of this specification, such that it
729
+ # can be eval'ed and reconstruct the same specification later. Attributes
730
+ # that still have their default values are omitted.
731
+
732
+ def to_ruby
733
+ mark_version
734
+ result = []
735
+ result << "# -*- encoding: utf-8 -*-"
736
+ result << nil
737
+ result << "Gem::Specification.new do |s|"
738
+
739
+ result << " s.name = #{ruby_code name}"
740
+ result << " s.version = #{ruby_code version}"
741
+ unless platform.nil? or platform == Gem::Platform::RUBY then
742
+ result << " s.platform = #{ruby_code original_platform}"
743
+ end
744
+ result << ""
745
+ result << " s.required_rubygems_version = #{ruby_code required_rubygems_version} if s.respond_to? :required_rubygems_version="
746
+
747
+ handled = [
748
+ :dependencies,
749
+ :name,
750
+ :platform,
751
+ :required_rubygems_version,
752
+ :specification_version,
753
+ :version,
754
+ ]
755
+
756
+ attributes = @@attributes.sort_by { |attr_name,| attr_name.to_s }
757
+
758
+ attributes.each do |attr_name, default|
759
+ next if handled.include? attr_name
760
+ current_value = self.send(attr_name)
761
+ if current_value != default or
762
+ self.class.required_attribute? attr_name then
763
+ result << " s.#{attr_name} = #{ruby_code current_value}"
764
+ end
765
+ end
766
+
767
+ result << nil
768
+ result << " if s.respond_to? :specification_version then"
769
+ result << " s.specification_version = #{specification_version}"
770
+ result << nil
771
+
772
+ result << " if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then"
773
+
774
+ unless dependencies.empty? then
775
+ dependencies.each do |dep|
776
+ version_reqs_param = dep.requirements_list.inspect
777
+ dep.instance_variable_set :@type, :runtime if dep.type.nil? # HACK
778
+ result << " s.add_#{dep.type}_dependency(%q<#{dep.name}>, #{version_reqs_param})"
779
+ end
780
+ end
781
+
782
+ result << " else"
783
+
784
+ unless dependencies.empty? then
785
+ dependencies.each do |dep|
786
+ version_reqs_param = dep.requirements_list.inspect
787
+ result << " s.add_dependency(%q<#{dep.name}>, #{version_reqs_param})"
788
+ end
789
+ end
790
+
791
+ result << ' end'
792
+
793
+ result << " else"
794
+ dependencies.each do |dep|
795
+ version_reqs_param = dep.requirements_list.inspect
796
+ result << " s.add_dependency(%q<#{dep.name}>, #{version_reqs_param})"
797
+ end
798
+ result << " end"
799
+
800
+ result << "end"
801
+ result << nil
802
+
803
+ result.join "\n"
804
+ end
805
+
806
+ ##
807
+ # Checks that the specification contains all required fields, and does a
808
+ # very basic sanity check.
809
+ #
810
+ # Raises InvalidSpecificationException if the spec does not pass the
811
+ # checks..
812
+
813
+ def validate
814
+ extend Gem::UserInteraction
815
+ normalize
816
+
817
+ if rubygems_version != Gem::VERSION then
818
+ raise Gem::InvalidSpecificationException,
819
+ "expected #{Gem::NAME} version #{Gem::VERSION}, was #{rubygems_version}"
820
+ end
821
+
822
+ @@required_attributes.each do |symbol|
823
+ unless self.send symbol then
824
+ raise Gem::InvalidSpecificationException,
825
+ "missing value for attribute #{symbol}"
826
+ end
827
+ end
828
+
829
+ unless String === name then
830
+ raise Gem::InvalidSpecificationException,
831
+ "invalid value for attribute name: \"#{name.inspect}\""
832
+ end
833
+
834
+ if require_paths.empty? then
835
+ raise Gem::InvalidSpecificationException,
836
+ 'specification must have at least one require_path'
837
+ end
838
+
839
+ @files.delete_if do |file| File.directory? file end
840
+ @test_files.delete_if do |file| File.directory? file end
841
+ @executables.delete_if do |file|
842
+ File.directory? File.join(bindir, file)
843
+ end
844
+ @extra_rdoc_files.delete_if do |file| File.directory? file end
845
+ @extensions.delete_if do |file| File.directory? file end
846
+
847
+ non_files = files.select do |file|
848
+ !File.file? file
849
+ end
850
+
851
+ unless non_files.empty? then
852
+ non_files = non_files.map { |file| file.inspect }
853
+ raise Gem::InvalidSpecificationException,
854
+ "[#{non_files.join ", "}] are not files"
855
+ end
856
+
857
+ unless specification_version.is_a?(Fixnum)
858
+ raise Gem::InvalidSpecificationException,
859
+ 'specification_version must be a Fixnum (did you mean version?)'
860
+ end
861
+
862
+ case platform
863
+ when Gem::Platform, Gem::Platform::RUBY then # ok
864
+ else
865
+ raise Gem::InvalidSpecificationException,
866
+ "invalid platform #{platform.inspect}, see Gem::Platform"
867
+ end
868
+
869
+ unless Array === authors and
870
+ authors.all? { |author| String === author } then
871
+ raise Gem::InvalidSpecificationException,
872
+ 'authors must be Array of Strings'
873
+ end
874
+
875
+ licenses.each { |license|
876
+ if license.length > 64
877
+ raise Gem::InvalidSpecificationException,
878
+ "each license must be 64 characters or less"
879
+ end
880
+ }
881
+
882
+ # reject FIXME and TODO
883
+
884
+ unless authors.grep(/FIXME|TODO/).empty? then
885
+ raise Gem::InvalidSpecificationException,
886
+ '"FIXME" or "TODO" is not an author'
887
+ end
888
+
889
+ unless Array(email).grep(/FIXME|TODO/).empty? then
890
+ raise Gem::InvalidSpecificationException,
891
+ '"FIXME" or "TODO" is not an email address'
892
+ end
893
+
894
+ if description =~ /FIXME|TODO/ then
895
+ raise Gem::InvalidSpecificationException,
896
+ '"FIXME" or "TODO" is not a description'
897
+ end
898
+
899
+ if summary =~ /FIXME|TODO/ then
900
+ raise Gem::InvalidSpecificationException,
901
+ '"FIXME" or "TODO" is not a summary'
902
+ end
903
+
904
+ if homepage and not homepage.empty? and
905
+ homepage !~ /\A[a-z][a-z\d+.-]*:/i then
906
+ raise Gem::InvalidSpecificationException,
907
+ "\"#{homepage}\" is not a URI"
908
+ end
909
+
910
+ # Warnings
911
+
912
+ %w[author description email homepage summary].each do |attribute|
913
+ value = self.send attribute
914
+ alert_warning "no #{attribute} specified" if value.nil? or value.empty?
915
+ end
916
+
917
+ if summary and not summary.empty? and description == summary then
918
+ alert_warning 'description and summary are identical'
919
+ end
920
+
921
+ alert_warning "deprecated autorequire specified" if autorequire
922
+
923
+ executables.each do |executable|
924
+ executable_path = File.join bindir, executable
925
+ shebang = File.read(executable_path, 2) == '#!'
926
+
927
+ alert_warning "#{executable_path} is missing #! line" unless shebang
928
+ end
929
+
930
+ true
931
+ end
932
+
933
+ ##
934
+ # Normalize the list of files so that:
935
+ # * All file lists have redundancies removed.
936
+ # * Files referenced in the extra_rdoc_files are included in the package
937
+ # file list.
938
+
939
+ def normalize
940
+ if defined?(@extra_rdoc_files) and @extra_rdoc_files then
941
+ @extra_rdoc_files.uniq!
942
+ @files ||= []
943
+ @files.concat(@extra_rdoc_files)
944
+ end
945
+ @files.uniq! if @files
946
+ end
947
+
948
+ ##
949
+ # Return a list of all gems that have a dependency on this gemspec. The
950
+ # list is structured with entries that conform to:
951
+ #
952
+ # [depending_gem, dependency, [list_of_gems_that_satisfy_dependency]]
953
+
954
+ def dependent_gems
955
+ out = []
956
+ Gem.source_index.each do |name,gem|
957
+ gem.dependencies.each do |dep|
958
+ if self.satisfies_requirement?(dep) then
959
+ sats = []
960
+ find_all_satisfiers(dep) do |sat|
961
+ sats << sat
962
+ end
963
+ out << [gem, dep, sats]
964
+ end
965
+ end
966
+ end
967
+ out
968
+ end
969
+
970
+ def to_s # :nodoc:
971
+ "#<Gem::Specification name=#{@name} version=#{@version}>"
972
+ end
973
+
974
+ def pretty_print(q) # :nodoc:
975
+ q.group 2, 'Gem::Specification.new do |s|', 'end' do
976
+ q.breakable
977
+
978
+ attributes = @@attributes.sort_by { |attr_name,| attr_name.to_s }
979
+
980
+ attributes.each do |attr_name, default|
981
+ current_value = self.send attr_name
982
+ if current_value != default or
983
+ self.class.required_attribute? attr_name then
984
+
985
+ q.text "s.#{attr_name} = "
986
+
987
+ if attr_name == :date then
988
+ current_value = current_value.utc
989
+
990
+ q.text "Time.utc(#{current_value.year}, #{current_value.month}, #{current_value.day})"
991
+ else
992
+ q.pp current_value
993
+ end
994
+
995
+ q.breakable
996
+ end
997
+ end
998
+ end
999
+ end
1000
+
1001
+ ##
1002
+ # Adds a dependency on gem +dependency+ with type +type+ that requires
1003
+ # +requirements+. Valid types are currently <tt>:runtime</tt> and
1004
+ # <tt>:development</tt>.
1005
+
1006
+ def add_dependency_with_type(dependency, type, *requirements)
1007
+ requirements = if requirements.empty? then
1008
+ Gem::Requirement.default
1009
+ else
1010
+ requirements.flatten
1011
+ end
1012
+
1013
+ unless dependency.respond_to?(:name) &&
1014
+ dependency.respond_to?(:version_requirements)
1015
+
1016
+ dependency = Gem::Dependency.new(dependency, requirements, type)
1017
+ end
1018
+
1019
+ dependencies << dependency
1020
+ end
1021
+
1022
+ private :add_dependency_with_type
1023
+
1024
+ ##
1025
+ # Finds all gems that satisfy +dep+
1026
+
1027
+ def find_all_satisfiers(dep)
1028
+ Gem.source_index.each do |_, gem|
1029
+ yield gem if gem.satisfies_requirement? dep
1030
+ end
1031
+ end
1032
+
1033
+ private :find_all_satisfiers
1034
+
1035
+ ##
1036
+ # Return a string containing a Ruby code representation of the given
1037
+ # object.
1038
+
1039
+ def ruby_code(obj)
1040
+ case obj
1041
+ when String then '%q{' + obj + '}'
1042
+ when Array then obj.inspect
1043
+ when Gem::Version then obj.to_s.inspect
1044
+ when Date then '%q{' + obj.strftime('%Y-%m-%d') + '}'
1045
+ when Time then '%q{' + obj.strftime('%Y-%m-%d') + '}'
1046
+ when Numeric then obj.inspect
1047
+ when true, false, nil then obj.inspect
1048
+ when Gem::Platform then "Gem::Platform.new(#{obj.to_a.inspect})"
1049
+ when Gem::Requirement then "Gem::Requirement.new(#{obj.to_s.inspect})"
1050
+ else raise Gem::Exception, "ruby_code case not handled: #{obj.class}"
1051
+ end
1052
+ end
1053
+
1054
+ private :ruby_code
1055
+
1056
+ # :section: Required gemspec attributes
1057
+
1058
+ ##
1059
+ # :attr_accessor: rubygems_version
1060
+ #
1061
+ # The version of SlimGems used to create this gem.
1062
+ #
1063
+ # Do not set this, it is set automatically when the gem is packaged.
1064
+
1065
+ required_attribute :rubygems_version, Gem::VERSION
1066
+
1067
+ ##
1068
+ # :attr_accessor: specification_version
1069
+ #
1070
+ # The Gem::Specification version of this gemspec.
1071
+ #
1072
+ # Do not set this, it is set automatically when the gem is packaged.
1073
+
1074
+ required_attribute :specification_version, CURRENT_SPECIFICATION_VERSION
1075
+
1076
+ ##
1077
+ # :attr_accessor: name
1078
+ #
1079
+ # This gem's name
1080
+
1081
+ required_attribute :name
1082
+
1083
+ ##
1084
+ # :attr_accessor: version
1085
+ #
1086
+ # This gem's version
1087
+
1088
+ required_attribute :version
1089
+
1090
+ ##
1091
+ # :attr_accessor: date
1092
+ #
1093
+ # The date this gem was created
1094
+ #
1095
+ # Do not set this, it is set automatically when the gem is packaged.
1096
+
1097
+ required_attribute :date, TODAY
1098
+
1099
+ ##
1100
+ # :attr_accessor: summary
1101
+ #
1102
+ # A short summary of this gem's description. Displayed in `gem list -d`.
1103
+ #
1104
+ # The description should be more detailed than the summary. For example,
1105
+ # you might wish to copy the entire README into the description.
1106
+ #
1107
+ # As of SlimGems 1.3.2 newlines are no longer stripped.
1108
+
1109
+ required_attribute :summary
1110
+
1111
+ ##
1112
+ # :attr_accessor: require_paths
1113
+ #
1114
+ # Paths in the gem to add to $LOAD_PATH when this gem is activated.
1115
+ #
1116
+ # The default 'lib' is typically sufficient.
1117
+
1118
+ required_attribute :require_paths, ['lib']
1119
+
1120
+ # :section: Optional gemspec attributes
1121
+
1122
+ ##
1123
+ # :attr_accessor: email
1124
+ #
1125
+ # A contact email for this gem
1126
+ #
1127
+ # If you are providing multiple authors and multiple emails they should be
1128
+ # in the same order such that:
1129
+ #
1130
+ # Hash[*spec.authors.zip(spec.emails).flatten]
1131
+ #
1132
+ # Gives a hash of author name to email address.
1133
+
1134
+ attribute :email
1135
+
1136
+ ##
1137
+ # :attr_accessor: homepage
1138
+ #
1139
+ # The URL of this gem's home page
1140
+
1141
+ attribute :homepage
1142
+
1143
+ ##
1144
+ # :attr_accessor: rubyforge_project
1145
+ #
1146
+ # The rubyforge project this gem lives under. i.e. SlimGems'
1147
+ # rubyforge_project is "rubygems".
1148
+
1149
+ attribute :rubyforge_project
1150
+
1151
+ ##
1152
+ # :attr_accessor: description
1153
+ #
1154
+ # A long description of this gem
1155
+
1156
+ attribute :description
1157
+
1158
+ ##
1159
+ # :attr_accessor: autorequire
1160
+ #
1161
+ # Autorequire was used by old SlimGems to automatically require a file.
1162
+ # It no longer is supported.
1163
+
1164
+ attribute :autorequire
1165
+
1166
+ ##
1167
+ # :attr_accessor: default_executable
1168
+ #
1169
+ # The default executable for this gem.
1170
+ #
1171
+ # This is not used.
1172
+
1173
+ attribute :default_executable
1174
+
1175
+ ##
1176
+ # :attr_accessor: bindir
1177
+ #
1178
+ # The path in the gem for executable scripts
1179
+
1180
+ attribute :bindir, 'bin'
1181
+
1182
+ ##
1183
+ # :attr_accessor: has_rdoc
1184
+ #
1185
+ # Indicates that this gem is RDoc-capable.
1186
+
1187
+ attribute :has_rdoc, true
1188
+
1189
+ ##
1190
+ # True if this gem supports RDoc
1191
+
1192
+ alias :has_rdoc? :has_rdoc
1193
+
1194
+ ##
1195
+ # :attr_accessor: required_ruby_version
1196
+ #
1197
+ # The version of ruby required by this gem
1198
+
1199
+ attribute :required_ruby_version, Gem::Requirement.default
1200
+
1201
+ ##
1202
+ # :attr_accessor: required_rubygems_version
1203
+ #
1204
+ # The SlimGems version required by this gem
1205
+
1206
+ attribute :required_rubygems_version, Gem::Requirement.default
1207
+
1208
+ ##
1209
+ # :attr_accessor: platform
1210
+ #
1211
+ # The platform this gem runs on. See Gem::Platform for details.
1212
+ #
1213
+ # Setting this to any value other than Gem::Platform::RUBY or
1214
+ # Gem::Platform::CURRENT is probably wrong.
1215
+
1216
+ attribute :platform, Gem::Platform::RUBY
1217
+
1218
+ ##
1219
+ # :attr_accessor: signing_key
1220
+ #
1221
+ # The key used to sign this gem. See Gem::Security for details.
1222
+
1223
+ attribute :signing_key, nil
1224
+
1225
+ ##
1226
+ # :attr_accessor: cert_chain
1227
+ #
1228
+ # The certificate chain used to sign this gem. See Gem::Security for
1229
+ # details.
1230
+
1231
+ attribute :cert_chain, []
1232
+
1233
+ ##
1234
+ # :attr_accessor: post_install_message
1235
+ #
1236
+ # A message that gets displayed after the gem is installed
1237
+
1238
+ attribute :post_install_message, nil
1239
+
1240
+ ##
1241
+ # :attr_accessor: authors
1242
+ #
1243
+ # The list of author names who wrote this gem.
1244
+ #
1245
+ # If you are providing multiple authors and multiple emails they should be
1246
+ # in the same order such that:
1247
+ #
1248
+ # Hash[*spec.authors.zip(spec.emails).flatten]
1249
+ #
1250
+ # Gives a hash of author name to email address.
1251
+
1252
+ array_attribute :authors
1253
+
1254
+ ##
1255
+ # :attr_accessor: licenses
1256
+ #
1257
+ # The license(s) for the library. Each license must be a short name, no
1258
+ # more than 64 characters.
1259
+
1260
+ array_attribute :licenses
1261
+
1262
+ ##
1263
+ # :attr_accessor: files
1264
+ #
1265
+ # Files included in this gem. You cannot append to this accessor, you must
1266
+ # assign to it.
1267
+ #
1268
+ # Only add files you can require to this list, not directories, etc.
1269
+ #
1270
+ # Directories are automatically stripped from this list when building a gem,
1271
+ # other non-files cause an error.
1272
+
1273
+ array_attribute :files
1274
+
1275
+ ##
1276
+ # :attr_accessor: test_files
1277
+ #
1278
+ # Test files included in this gem. You cannot append to this accessor, you
1279
+ # must assign to it.
1280
+
1281
+ array_attribute :test_files
1282
+
1283
+ ##
1284
+ # :attr_accessor: rdoc_options
1285
+ #
1286
+ # An ARGV style array of options to RDoc
1287
+
1288
+ array_attribute :rdoc_options
1289
+
1290
+ ##
1291
+ # :attr_accessor: extra_rdoc_files
1292
+ #
1293
+ # Extra files to add to RDoc such as README or doc/examples.txt
1294
+
1295
+ array_attribute :extra_rdoc_files
1296
+
1297
+ ##
1298
+ # :attr_accessor: executables
1299
+ #
1300
+ # Executables included in the gem.
1301
+
1302
+ array_attribute :executables
1303
+
1304
+ ##
1305
+ # :attr_accessor: extensions
1306
+ #
1307
+ # Extensions to build when installing the gem. See
1308
+ # Gem::Installer#build_extensions for valid values.
1309
+
1310
+ array_attribute :extensions
1311
+
1312
+ ##
1313
+ # :attr_accessor: requirements
1314
+ #
1315
+ # An array or things required by this gem. Not used by anything
1316
+ # presently.
1317
+
1318
+ array_attribute :requirements
1319
+
1320
+ ##
1321
+ # :attr_reader: dependencies
1322
+ #
1323
+ # A list of Gem::Dependency objects this gem depends on.
1324
+ #
1325
+ # Use #add_dependency or #add_development_dependency to add dependencies to
1326
+ # a gem.
1327
+
1328
+ array_attribute :dependencies
1329
+
1330
+ read_only :dependencies
1331
+
1332
+ # :section: Aliased gemspec attributes
1333
+
1334
+ ##
1335
+ # Singular accessor for #executables
1336
+
1337
+ attribute_alias_singular :executable, :executables
1338
+
1339
+ ##
1340
+ # Singular accessor for #authors
1341
+
1342
+ attribute_alias_singular :author, :authors
1343
+
1344
+ ##
1345
+ # Singular accessor for #licenses
1346
+
1347
+ attribute_alias_singular :license, :licenses
1348
+
1349
+ ##
1350
+ # Singular accessor for #require_paths
1351
+
1352
+ attribute_alias_singular :require_path, :require_paths
1353
+
1354
+ ##
1355
+ # Singular accessor for #test_files
1356
+
1357
+ attribute_alias_singular :test_file, :test_files
1358
+
1359
+ overwrite_accessor :version= do |version|
1360
+ @version = Gem::Version.create(version)
1361
+ self.required_rubygems_version = '> 1.3.1' if @version.prerelease?
1362
+ return @version
1363
+ end
1364
+
1365
+ overwrite_accessor :platform do
1366
+ @new_platform
1367
+ end
1368
+
1369
+ overwrite_accessor :platform= do |platform|
1370
+ if @original_platform.nil? or
1371
+ @original_platform == Gem::Platform::RUBY then
1372
+ @original_platform = platform
1373
+ end
1374
+
1375
+ case platform
1376
+ when Gem::Platform::CURRENT then
1377
+ @new_platform = Gem::Platform.local
1378
+ @original_platform = @new_platform.to_s
1379
+
1380
+ when Gem::Platform then
1381
+ @new_platform = platform
1382
+
1383
+ # legacy constants
1384
+ when nil, Gem::Platform::RUBY then
1385
+ @new_platform = Gem::Platform::RUBY
1386
+ when 'mswin32' then # was Gem::Platform::WIN32
1387
+ @new_platform = Gem::Platform.new 'x86-mswin32'
1388
+ when 'i586-linux' then # was Gem::Platform::LINUX_586
1389
+ @new_platform = Gem::Platform.new 'x86-linux'
1390
+ when 'powerpc-darwin' then # was Gem::Platform::DARWIN
1391
+ @new_platform = Gem::Platform.new 'ppc-darwin'
1392
+ else
1393
+ @new_platform = Gem::Platform.new platform
1394
+ end
1395
+
1396
+ @platform = @new_platform.to_s
1397
+
1398
+ @new_platform
1399
+ end
1400
+
1401
+ overwrite_accessor :required_ruby_version= do |value|
1402
+ @required_ruby_version = Gem::Requirement.create(value)
1403
+ end
1404
+
1405
+ overwrite_accessor :required_rubygems_version= do |value|
1406
+ @required_rubygems_version = Gem::Requirement.create(value)
1407
+ end
1408
+
1409
+ overwrite_accessor :date= do |date|
1410
+ # We want to end up with a Time object with one-day resolution.
1411
+ # This is the cleanest, most-readable, faster-than-using-Date
1412
+ # way to do it.
1413
+ case date
1414
+ when String then
1415
+ @date = if /\A(\d{4})-(\d{2})-(\d{2})\Z/ =~ date then
1416
+ Time.local($1.to_i, $2.to_i, $3.to_i)
1417
+ else
1418
+ require 'time'
1419
+ Time.parse date
1420
+ end
1421
+ when Time then
1422
+ @date = Time.local(date.year, date.month, date.day)
1423
+ when Date then
1424
+ @date = Time.local(date.year, date.month, date.day)
1425
+ else
1426
+ @date = TODAY
1427
+ end
1428
+ end
1429
+
1430
+ overwrite_accessor :date do
1431
+ self.date = nil if @date.nil? # HACK Sets the default value for date
1432
+ @date
1433
+ end
1434
+
1435
+ overwrite_accessor :summary= do |str|
1436
+ @summary = if str then
1437
+ str.strip.
1438
+ gsub(/(\w-)\n[ \t]*(\w)/, '\1\2').
1439
+ gsub(/\n[ \t]*/, " ")
1440
+ end
1441
+ end
1442
+
1443
+ overwrite_accessor :description= do |str|
1444
+ @description = str.to_s
1445
+ end
1446
+
1447
+ overwrite_accessor :default_executable do
1448
+ begin
1449
+ if defined?(@default_executable) and @default_executable
1450
+ result = @default_executable
1451
+ elsif @executables and @executables.size == 1
1452
+ result = Array(@executables).first
1453
+ else
1454
+ result = nil
1455
+ end
1456
+ result
1457
+ rescue
1458
+ nil
1459
+ end
1460
+ end
1461
+
1462
+ overwrite_accessor :test_files do
1463
+ # Handle the possibility that we have @test_suite_file but not
1464
+ # @test_files. This will happen when an old gem is loaded via
1465
+ # YAML.
1466
+ if defined? @test_suite_file then
1467
+ @test_files = [@test_suite_file].flatten
1468
+ @test_suite_file = nil
1469
+ end
1470
+ if defined?(@test_files) and @test_files then
1471
+ @test_files
1472
+ else
1473
+ @test_files = []
1474
+ end
1475
+ end
1476
+
1477
+ overwrite_accessor :files do
1478
+ # DO NOT CHANGE TO ||= ! This is not a normal accessor. (yes, it sucks)
1479
+ @files = [@files,
1480
+ @test_files,
1481
+ add_bindir(@executables),
1482
+ @extra_rdoc_files,
1483
+ @extensions,
1484
+ ].flatten.uniq.compact
1485
+ end
1486
+ end