slimgems 1.3.8

Sign up to get free protection for your applications and to get access to all the features.
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