libgems 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (177) hide show
  1. data/ChangeLog +5811 -0
  2. data/History.txt +887 -0
  3. data/LICENSE.txt +51 -0
  4. data/README.md +87 -0
  5. data/Rakefile +113 -0
  6. data/lib/gauntlet_libgems.rb +50 -0
  7. data/lib/libgems.rb +1246 -0
  8. data/lib/libgems/builder.rb +102 -0
  9. data/lib/libgems/command.rb +534 -0
  10. data/lib/libgems/command_manager.rb +182 -0
  11. data/lib/libgems/commands/build_command.rb +53 -0
  12. data/lib/libgems/commands/cert_command.rb +86 -0
  13. data/lib/libgems/commands/check_command.rb +80 -0
  14. data/lib/libgems/commands/cleanup_command.rb +106 -0
  15. data/lib/libgems/commands/contents_command.rb +98 -0
  16. data/lib/libgems/commands/dependency_command.rb +195 -0
  17. data/lib/libgems/commands/environment_command.rb +133 -0
  18. data/lib/libgems/commands/fetch_command.rb +67 -0
  19. data/lib/libgems/commands/generate_index_command.rb +133 -0
  20. data/lib/libgems/commands/help_command.rb +172 -0
  21. data/lib/libgems/commands/install_command.rb +178 -0
  22. data/lib/libgems/commands/list_command.rb +35 -0
  23. data/lib/libgems/commands/lock_command.rb +110 -0
  24. data/lib/libgems/commands/mirror_command.rb +111 -0
  25. data/lib/libgems/commands/outdated_command.rb +33 -0
  26. data/lib/libgems/commands/owner_command.rb +75 -0
  27. data/lib/libgems/commands/pristine_command.rb +93 -0
  28. data/lib/libgems/commands/push_command.rb +56 -0
  29. data/lib/libgems/commands/query_command.rb +280 -0
  30. data/lib/libgems/commands/rdoc_command.rb +91 -0
  31. data/lib/libgems/commands/search_command.rb +31 -0
  32. data/lib/libgems/commands/server_command.rb +86 -0
  33. data/lib/libgems/commands/sources_command.rb +157 -0
  34. data/lib/libgems/commands/specification_command.rb +125 -0
  35. data/lib/libgems/commands/stale_command.rb +27 -0
  36. data/lib/libgems/commands/uninstall_command.rb +83 -0
  37. data/lib/libgems/commands/unpack_command.rb +121 -0
  38. data/lib/libgems/commands/update_command.rb +160 -0
  39. data/lib/libgems/commands/which_command.rb +86 -0
  40. data/lib/libgems/config_file.rb +345 -0
  41. data/lib/libgems/custom_require.rb +44 -0
  42. data/lib/libgems/defaults.rb +101 -0
  43. data/lib/libgems/dependency.rb +227 -0
  44. data/lib/libgems/dependency_installer.rb +286 -0
  45. data/lib/libgems/dependency_list.rb +208 -0
  46. data/lib/libgems/doc_manager.rb +242 -0
  47. data/lib/libgems/errors.rb +35 -0
  48. data/lib/libgems/exceptions.rb +91 -0
  49. data/lib/libgems/ext.rb +18 -0
  50. data/lib/libgems/ext/builder.rb +56 -0
  51. data/lib/libgems/ext/configure_builder.rb +25 -0
  52. data/lib/libgems/ext/ext_conf_builder.rb +24 -0
  53. data/lib/libgems/ext/rake_builder.rb +39 -0
  54. data/lib/libgems/format.rb +81 -0
  55. data/lib/libgems/gem_openssl.rb +92 -0
  56. data/lib/libgems/gem_path_searcher.rb +100 -0
  57. data/lib/libgems/gem_runner.rb +79 -0
  58. data/lib/libgems/gemcutter_utilities.rb +49 -0
  59. data/lib/libgems/indexer.rb +720 -0
  60. data/lib/libgems/install_update_options.rb +125 -0
  61. data/lib/libgems/installer.rb +604 -0
  62. data/lib/libgems/local_remote_options.rb +135 -0
  63. data/lib/libgems/old_format.rb +153 -0
  64. data/lib/libgems/package.rb +97 -0
  65. data/lib/libgems/package/f_sync_dir.rb +23 -0
  66. data/lib/libgems/package/tar_header.rb +266 -0
  67. data/lib/libgems/package/tar_input.rb +222 -0
  68. data/lib/libgems/package/tar_output.rb +144 -0
  69. data/lib/libgems/package/tar_reader.rb +106 -0
  70. data/lib/libgems/package/tar_reader/entry.rb +141 -0
  71. data/lib/libgems/package/tar_writer.rb +241 -0
  72. data/lib/libgems/package_task.rb +126 -0
  73. data/lib/libgems/platform.rb +183 -0
  74. data/lib/libgems/remote_fetcher.rb +414 -0
  75. data/lib/libgems/require_paths_builder.rb +18 -0
  76. data/lib/libgems/requirement.rb +153 -0
  77. data/lib/libgems/security.rb +814 -0
  78. data/lib/libgems/server.rb +872 -0
  79. data/lib/libgems/source_index.rb +597 -0
  80. data/lib/libgems/source_info_cache.rb +395 -0
  81. data/lib/libgems/source_info_cache_entry.rb +56 -0
  82. data/lib/libgems/spec_fetcher.rb +337 -0
  83. data/lib/libgems/specification.rb +1487 -0
  84. data/lib/libgems/test_utilities.rb +147 -0
  85. data/lib/libgems/text.rb +65 -0
  86. data/lib/libgems/uninstaller.rb +278 -0
  87. data/lib/libgems/user_interaction.rb +527 -0
  88. data/lib/libgems/validator.rb +240 -0
  89. data/lib/libgems/version.rb +316 -0
  90. data/lib/libgems/version_option.rb +65 -0
  91. data/lib/rbconfig/datadir.rb +20 -0
  92. data/test/bogussources.rb +8 -0
  93. data/test/data/gem-private_key.pem +27 -0
  94. data/test/data/gem-public_cert.pem +20 -0
  95. data/test/fake_certlib/openssl.rb +7 -0
  96. data/test/foo/discover.rb +0 -0
  97. data/test/gem_installer_test_case.rb +97 -0
  98. data/test/gem_package_tar_test_case.rb +132 -0
  99. data/test/gemutilities.rb +605 -0
  100. data/test/insure_session.rb +43 -0
  101. data/test/mockgemui.rb +56 -0
  102. data/test/plugin/exception/libgems_plugin.rb +2 -0
  103. data/test/plugin/load/libgems_plugin.rb +1 -0
  104. data/test/plugin/standarderror/libgems_plugin.rb +2 -0
  105. data/test/private_key.pem +27 -0
  106. data/test/public_cert.pem +20 -0
  107. data/test/rubygems_plugin.rb +21 -0
  108. data/test/simple_gem.rb +66 -0
  109. data/test/test_config.rb +12 -0
  110. data/test/test_gem.rb +780 -0
  111. data/test/test_gem_builder.rb +27 -0
  112. data/test/test_gem_command.rb +178 -0
  113. data/test/test_gem_command_manager.rb +207 -0
  114. data/test/test_gem_commands_build_command.rb +74 -0
  115. data/test/test_gem_commands_cert_command.rb +124 -0
  116. data/test/test_gem_commands_check_command.rb +18 -0
  117. data/test/test_gem_commands_contents_command.rb +156 -0
  118. data/test/test_gem_commands_dependency_command.rb +216 -0
  119. data/test/test_gem_commands_environment_command.rb +144 -0
  120. data/test/test_gem_commands_fetch_command.rb +76 -0
  121. data/test/test_gem_commands_generate_index_command.rb +135 -0
  122. data/test/test_gem_commands_install_command.rb +315 -0
  123. data/test/test_gem_commands_list_command.rb +36 -0
  124. data/test/test_gem_commands_lock_command.rb +68 -0
  125. data/test/test_gem_commands_mirror_command.rb +60 -0
  126. data/test/test_gem_commands_outdated_command.rb +40 -0
  127. data/test/test_gem_commands_owner_command.rb +105 -0
  128. data/test/test_gem_commands_pristine_command.rb +108 -0
  129. data/test/test_gem_commands_push_command.rb +81 -0
  130. data/test/test_gem_commands_query_command.rb +426 -0
  131. data/test/test_gem_commands_server_command.rb +59 -0
  132. data/test/test_gem_commands_sources_command.rb +209 -0
  133. data/test/test_gem_commands_specification_command.rb +139 -0
  134. data/test/test_gem_commands_stale_command.rb +38 -0
  135. data/test/test_gem_commands_uninstall_command.rb +83 -0
  136. data/test/test_gem_commands_unpack_command.rb +199 -0
  137. data/test/test_gem_commands_update_command.rb +207 -0
  138. data/test/test_gem_commands_which_command.rb +66 -0
  139. data/test/test_gem_config_file.rb +287 -0
  140. data/test/test_gem_dependency.rb +149 -0
  141. data/test/test_gem_dependency_installer.rb +661 -0
  142. data/test/test_gem_dependency_list.rb +230 -0
  143. data/test/test_gem_doc_manager.rb +31 -0
  144. data/test/test_gem_ext_configure_builder.rb +84 -0
  145. data/test/test_gem_ext_ext_conf_builder.rb +173 -0
  146. data/test/test_gem_ext_rake_builder.rb +81 -0
  147. data/test/test_gem_format.rb +70 -0
  148. data/test/test_gem_gem_path_searcher.rb +78 -0
  149. data/test/test_gem_gem_runner.rb +45 -0
  150. data/test/test_gem_gemcutter_utilities.rb +103 -0
  151. data/test/test_gem_indexer.rb +673 -0
  152. data/test/test_gem_install_update_options.rb +68 -0
  153. data/test/test_gem_installer.rb +857 -0
  154. data/test/test_gem_local_remote_options.rb +97 -0
  155. data/test/test_gem_package_tar_header.rb +130 -0
  156. data/test/test_gem_package_tar_input.rb +112 -0
  157. data/test/test_gem_package_tar_output.rb +97 -0
  158. data/test/test_gem_package_tar_reader.rb +46 -0
  159. data/test/test_gem_package_tar_reader_entry.rb +109 -0
  160. data/test/test_gem_package_tar_writer.rb +144 -0
  161. data/test/test_gem_package_task.rb +59 -0
  162. data/test/test_gem_platform.rb +264 -0
  163. data/test/test_gem_remote_fetcher.rb +740 -0
  164. data/test/test_gem_requirement.rb +292 -0
  165. data/test/test_gem_server.rb +356 -0
  166. data/test/test_gem_silent_ui.rb +113 -0
  167. data/test/test_gem_source_index.rb +461 -0
  168. data/test/test_gem_spec_fetcher.rb +410 -0
  169. data/test/test_gem_specification.rb +1334 -0
  170. data/test/test_gem_stream_ui.rb +218 -0
  171. data/test/test_gem_text.rb +43 -0
  172. data/test/test_gem_uninstaller.rb +146 -0
  173. data/test/test_gem_validator.rb +63 -0
  174. data/test/test_gem_version.rb +181 -0
  175. data/test/test_gem_version_option.rb +89 -0
  176. data/test/test_kernel.rb +59 -0
  177. metadata +402 -0
@@ -0,0 +1,337 @@
1
+ require 'zlib'
2
+ require 'fileutils'
3
+
4
+ require 'libgems/remote_fetcher'
5
+ require 'libgems/user_interaction'
6
+ require 'libgems/errors'
7
+ require 'libgems/text'
8
+
9
+ ##
10
+ # SpecFetcher handles metadata updates from remote gem repositories.
11
+
12
+ class LibGems::SpecFetcher
13
+
14
+ include LibGems::UserInteraction
15
+ include LibGems::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 LibGems.user_home, '.gem', 'specs'
55
+ @update_cache = File.stat(LibGems.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 = LibGems::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 LibGems::RemoteFetcher::FetchError => e
93
+ raise unless warn_legacy e do
94
+ require 'libgems/source_info_cache'
95
+
96
+ return [LibGems::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 + "#{LibGems::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 = LibGems.read_binary local_spec
117
+ else
118
+ uri.path << '.rz'
119
+
120
+ spec = @fetcher.fetch_path uri
121
+ spec = LibGems.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 LibGems::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 !LibGems::Platform.match(spec_platform)
150
+ pm = (rejected_specs[dependency] ||= LibGems::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
+ LibGems.sources.reject do |source_uri|
182
+ source_uri = URI.parse source_uri
183
+ spec_path = source_uri + "specs.#{LibGems.marshal_version}.gz"
184
+
185
+ begin
186
+ @fetcher.fetch_size spec_path
187
+ rescue LibGems::RemoteFetcher::FetchError
188
+ begin
189
+ @fetcher.fetch_size(source_uri + 'yaml') # re-raise if non-repo
190
+ rescue LibGems::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 LibGems::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 LibGems::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
+ LibGems.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}.#{LibGems.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 = LibGems.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 LibGems.marshal_version}\.gz$/ then
321
+ alert_warning <<-EOF
322
+ #{LibGems::NAME} 1.2+ index not found for:
323
+ \t#{legacy_repos.join "\n\t"}
324
+
325
+ #{LibGems::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,1487 @@
1
+ #--
2
+ # Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
3
+ # All rights reserved.
4
+ # See LICENSE.txt for permissions.
5
+ #++
6
+
7
+ require 'libgems/version'
8
+ require 'libgems/requirement'
9
+ require 'libgems/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 LibGems. Typically
17
+ # defined in a .gemspec file or a Rakefile, and looks like this:
18
+ #
19
+ # spec = LibGems::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 LibGems::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 => ["(#{LibGems::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 = LibGems::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 LibGems::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 LibGems 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, LibGems::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 = LibGems.with_rubygems_compat{ YAML.load input }
481
+
482
+ if spec && spec.class == FalseClass then
483
+ raise LibGems::EndOfYAMLException
484
+ end
485
+
486
+ unless LibGems::Specification === spec then
487
+ raise LibGems::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
+ LibGems.gemspec_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 = LibGems::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
+ # LibGems. 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 LibGems.
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 LibGems. 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 == LibGems::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 == LibGems::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 LibGems::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 == LibGems::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 = LibGems::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 << "LibGems::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 == LibGems::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 LibGems::Version.new(LibGems::GEM_VERSION) >= LibGems::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 LibGems::UserInteraction
815
+ normalize
816
+
817
+ # TODO: Fix the versioning here
818
+ if rubygems_version != LibGems::GEM_VERSION then
819
+ raise LibGems::InvalidSpecificationException,
820
+ "expected RubyGems version #{LibGems::GEM_VERSION}, was #{rubygems_version}"
821
+ end
822
+
823
+ @@required_attributes.each do |symbol|
824
+ unless self.send symbol then
825
+ raise LibGems::InvalidSpecificationException,
826
+ "missing value for attribute #{symbol}"
827
+ end
828
+ end
829
+
830
+ unless String === name then
831
+ raise LibGems::InvalidSpecificationException,
832
+ "invalid value for attribute name: \"#{name.inspect}\""
833
+ end
834
+
835
+ if require_paths.empty? then
836
+ raise LibGems::InvalidSpecificationException,
837
+ 'specification must have at least one require_path'
838
+ end
839
+
840
+ @files.delete_if do |file| File.directory? file end
841
+ @test_files.delete_if do |file| File.directory? file end
842
+ @executables.delete_if do |file|
843
+ File.directory? File.join(bindir, file)
844
+ end
845
+ @extra_rdoc_files.delete_if do |file| File.directory? file end
846
+ @extensions.delete_if do |file| File.directory? file end
847
+
848
+ non_files = files.select do |file|
849
+ !File.file? file
850
+ end
851
+
852
+ unless non_files.empty? then
853
+ non_files = non_files.map { |file| file.inspect }
854
+ raise LibGems::InvalidSpecificationException,
855
+ "[#{non_files.join ", "}] are not files"
856
+ end
857
+
858
+ unless specification_version.is_a?(Fixnum)
859
+ raise LibGems::InvalidSpecificationException,
860
+ 'specification_version must be a Fixnum (did you mean version?)'
861
+ end
862
+
863
+ case platform
864
+ when LibGems::Platform, LibGems::Platform::RUBY then # ok
865
+ else
866
+ raise LibGems::InvalidSpecificationException,
867
+ "invalid platform #{platform.inspect}, see LibGems::Platform"
868
+ end
869
+
870
+ unless Array === authors and
871
+ authors.all? { |author| String === author } then
872
+ raise LibGems::InvalidSpecificationException,
873
+ 'authors must be Array of Strings'
874
+ end
875
+
876
+ licenses.each { |license|
877
+ if license.length > 64
878
+ raise LibGems::InvalidSpecificationException,
879
+ "each license must be 64 characters or less"
880
+ end
881
+ }
882
+
883
+ # reject FIXME and TODO
884
+
885
+ unless authors.grep(/FIXME|TODO/).empty? then
886
+ raise LibGems::InvalidSpecificationException,
887
+ '"FIXME" or "TODO" is not an author'
888
+ end
889
+
890
+ unless Array(email).grep(/FIXME|TODO/).empty? then
891
+ raise LibGems::InvalidSpecificationException,
892
+ '"FIXME" or "TODO" is not an email address'
893
+ end
894
+
895
+ if description =~ /FIXME|TODO/ then
896
+ raise LibGems::InvalidSpecificationException,
897
+ '"FIXME" or "TODO" is not a description'
898
+ end
899
+
900
+ if summary =~ /FIXME|TODO/ then
901
+ raise LibGems::InvalidSpecificationException,
902
+ '"FIXME" or "TODO" is not a summary'
903
+ end
904
+
905
+ if homepage and not homepage.empty? and
906
+ homepage !~ /\A[a-z][a-z\d+.-]*:/i then
907
+ raise LibGems::InvalidSpecificationException,
908
+ "\"#{homepage}\" is not a URI"
909
+ end
910
+
911
+ # Warnings
912
+
913
+ %w[author description email homepage summary].each do |attribute|
914
+ value = self.send attribute
915
+ alert_warning "no #{attribute} specified" if value.nil? or value.empty?
916
+ end
917
+
918
+ if summary and not summary.empty? and description == summary then
919
+ alert_warning 'description and summary are identical'
920
+ end
921
+
922
+ alert_warning "deprecated autorequire specified" if autorequire
923
+
924
+ executables.each do |executable|
925
+ executable_path = File.join bindir, executable
926
+ shebang = File.read(executable_path, 2) == '#!'
927
+
928
+ alert_warning "#{executable_path} is missing #! line" unless shebang
929
+ end
930
+
931
+ true
932
+ end
933
+
934
+ ##
935
+ # Normalize the list of files so that:
936
+ # * All file lists have redundancies removed.
937
+ # * Files referenced in the extra_rdoc_files are included in the package
938
+ # file list.
939
+
940
+ def normalize
941
+ if defined?(@extra_rdoc_files) and @extra_rdoc_files then
942
+ @extra_rdoc_files.uniq!
943
+ @files ||= []
944
+ @files.concat(@extra_rdoc_files)
945
+ end
946
+ @files.uniq! if @files
947
+ end
948
+
949
+ ##
950
+ # Return a list of all gems that have a dependency on this gemspec. The
951
+ # list is structured with entries that conform to:
952
+ #
953
+ # [depending_gem, dependency, [list_of_gems_that_satisfy_dependency]]
954
+
955
+ def dependent_gems
956
+ out = []
957
+ LibGems.source_index.each do |name,gem|
958
+ gem.dependencies.each do |dep|
959
+ if self.satisfies_requirement?(dep) then
960
+ sats = []
961
+ find_all_satisfiers(dep) do |sat|
962
+ sats << sat
963
+ end
964
+ out << [gem, dep, sats]
965
+ end
966
+ end
967
+ end
968
+ out
969
+ end
970
+
971
+ def to_s # :nodoc:
972
+ "#<LibGems::Specification name=#{@name} version=#{@version}>"
973
+ end
974
+
975
+ def pretty_print(q) # :nodoc:
976
+ q.group 2, 'LibGems::Specification.new do |s|', 'end' do
977
+ q.breakable
978
+
979
+ attributes = @@attributes.sort_by { |attr_name,| attr_name.to_s }
980
+
981
+ attributes.each do |attr_name, default|
982
+ current_value = self.send attr_name
983
+ if current_value != default or
984
+ self.class.required_attribute? attr_name then
985
+
986
+ q.text "s.#{attr_name} = "
987
+
988
+ if attr_name == :date then
989
+ current_value = current_value.utc
990
+
991
+ q.text "Time.utc(#{current_value.year}, #{current_value.month}, #{current_value.day})"
992
+ else
993
+ q.pp current_value
994
+ end
995
+
996
+ q.breakable
997
+ end
998
+ end
999
+ end
1000
+ end
1001
+
1002
+ ##
1003
+ # Adds a dependency on gem +dependency+ with type +type+ that requires
1004
+ # +requirements+. Valid types are currently <tt>:runtime</tt> and
1005
+ # <tt>:development</tt>.
1006
+
1007
+ def add_dependency_with_type(dependency, type, *requirements)
1008
+ requirements = if requirements.empty? then
1009
+ LibGems::Requirement.default
1010
+ else
1011
+ requirements.flatten
1012
+ end
1013
+
1014
+ unless dependency.respond_to?(:name) &&
1015
+ dependency.respond_to?(:version_requirements)
1016
+
1017
+ dependency = LibGems::Dependency.new(dependency, requirements, type)
1018
+ end
1019
+
1020
+ dependencies << dependency
1021
+ end
1022
+
1023
+ private :add_dependency_with_type
1024
+
1025
+ ##
1026
+ # Finds all gems that satisfy +dep+
1027
+
1028
+ def find_all_satisfiers(dep)
1029
+ LibGems.source_index.each do |_, gem|
1030
+ yield gem if gem.satisfies_requirement? dep
1031
+ end
1032
+ end
1033
+
1034
+ private :find_all_satisfiers
1035
+
1036
+ ##
1037
+ # Return a string containing a Ruby code representation of the given
1038
+ # object.
1039
+
1040
+ def ruby_code(obj)
1041
+ case obj
1042
+ when String then '%q{' + obj + '}'
1043
+ when Array then obj.inspect
1044
+ when LibGems::Version then obj.to_s.inspect
1045
+ when Date then '%q{' + obj.strftime('%Y-%m-%d') + '}'
1046
+ when Time then '%q{' + obj.strftime('%Y-%m-%d') + '}'
1047
+ when Numeric then obj.inspect
1048
+ when true, false, nil then obj.inspect
1049
+ when LibGems::Platform then "LibGems::Platform.new(#{obj.to_a.inspect})"
1050
+ when LibGems::Requirement then "LibGems::Requirement.new(#{obj.to_s.inspect})"
1051
+ else raise LibGems::Exception, "ruby_code case not handled: #{obj.class}"
1052
+ end
1053
+ end
1054
+
1055
+ private :ruby_code
1056
+
1057
+ # :section: Required gemspec attributes
1058
+
1059
+ ##
1060
+ # :attr_accessor: rubygems_version
1061
+ #
1062
+ # The version of SlimGems used to create this gem.
1063
+ #
1064
+ # Do not set this, it is set automatically when the gem is packaged.
1065
+
1066
+ required_attribute :rubygems_version, LibGems::GEM_VERSION
1067
+
1068
+ ##
1069
+ # :attr_accessor: specification_version
1070
+ #
1071
+ # The LibGems::Specification version of this gemspec.
1072
+ #
1073
+ # Do not set this, it is set automatically when the gem is packaged.
1074
+
1075
+ required_attribute :specification_version, CURRENT_SPECIFICATION_VERSION
1076
+
1077
+ ##
1078
+ # :attr_accessor: name
1079
+ #
1080
+ # This gem's name
1081
+
1082
+ required_attribute :name
1083
+
1084
+ ##
1085
+ # :attr_accessor: version
1086
+ #
1087
+ # This gem's version
1088
+
1089
+ required_attribute :version
1090
+
1091
+ ##
1092
+ # :attr_accessor: date
1093
+ #
1094
+ # The date this gem was created
1095
+ #
1096
+ # Do not set this, it is set automatically when the gem is packaged.
1097
+
1098
+ required_attribute :date, TODAY
1099
+
1100
+ ##
1101
+ # :attr_accessor: summary
1102
+ #
1103
+ # A short summary of this gem's description. Displayed in `gem list -d`.
1104
+ #
1105
+ # The description should be more detailed than the summary. For example,
1106
+ # you might wish to copy the entire README into the description.
1107
+ #
1108
+ # As of SlimGems 1.3.2 newlines are no longer stripped.
1109
+
1110
+ required_attribute :summary
1111
+
1112
+ ##
1113
+ # :attr_accessor: require_paths
1114
+ #
1115
+ # Paths in the gem to add to $LOAD_PATH when this gem is activated.
1116
+ #
1117
+ # The default 'lib' is typically sufficient.
1118
+
1119
+ required_attribute :require_paths, ['lib']
1120
+
1121
+ # :section: Optional gemspec attributes
1122
+
1123
+ ##
1124
+ # :attr_accessor: email
1125
+ #
1126
+ # A contact email for this gem
1127
+ #
1128
+ # If you are providing multiple authors and multiple emails they should be
1129
+ # in the same order such that:
1130
+ #
1131
+ # Hash[*spec.authors.zip(spec.emails).flatten]
1132
+ #
1133
+ # Gives a hash of author name to email address.
1134
+
1135
+ attribute :email
1136
+
1137
+ ##
1138
+ # :attr_accessor: homepage
1139
+ #
1140
+ # The URL of this gem's home page
1141
+
1142
+ attribute :homepage
1143
+
1144
+ ##
1145
+ # :attr_accessor: rubyforge_project
1146
+ #
1147
+ # The rubyforge project this gem lives under. i.e. SlimGems'
1148
+ # rubyforge_project is "libgems".
1149
+
1150
+ attribute :rubyforge_project
1151
+
1152
+ ##
1153
+ # :attr_accessor: description
1154
+ #
1155
+ # A long description of this gem
1156
+
1157
+ attribute :description
1158
+
1159
+ ##
1160
+ # :attr_accessor: autorequire
1161
+ #
1162
+ # Autorequire was used by old SlimGems to automatically require a file.
1163
+ # It no longer is supported.
1164
+
1165
+ attribute :autorequire
1166
+
1167
+ ##
1168
+ # :attr_accessor: default_executable
1169
+ #
1170
+ # The default executable for this gem.
1171
+ #
1172
+ # This is not used.
1173
+
1174
+ attribute :default_executable
1175
+
1176
+ ##
1177
+ # :attr_accessor: bindir
1178
+ #
1179
+ # The path in the gem for executable scripts
1180
+
1181
+ attribute :bindir, 'bin'
1182
+
1183
+ ##
1184
+ # :attr_accessor: has_rdoc
1185
+ #
1186
+ # Indicates that this gem is RDoc-capable.
1187
+
1188
+ attribute :has_rdoc, true
1189
+
1190
+ ##
1191
+ # True if this gem supports RDoc
1192
+
1193
+ alias :has_rdoc? :has_rdoc
1194
+
1195
+ ##
1196
+ # :attr_accessor: required_ruby_version
1197
+ #
1198
+ # The version of ruby required by this gem
1199
+
1200
+ attribute :required_ruby_version, LibGems::Requirement.default
1201
+
1202
+ ##
1203
+ # :attr_accessor: required_rubygems_version
1204
+ #
1205
+ # The SlimGems version required by this gem
1206
+
1207
+ attribute :required_rubygems_version, LibGems::Requirement.default
1208
+
1209
+ ##
1210
+ # :attr_accessor: platform
1211
+ #
1212
+ # The platform this gem runs on. See LibGems::Platform for details.
1213
+ #
1214
+ # Setting this to any value other than LibGems::Platform::RUBY or
1215
+ # LibGems::Platform::CURRENT is probably wrong.
1216
+
1217
+ attribute :platform, LibGems::Platform::RUBY
1218
+
1219
+ ##
1220
+ # :attr_accessor: signing_key
1221
+ #
1222
+ # The key used to sign this gem. See LibGems::Security for details.
1223
+
1224
+ attribute :signing_key, nil
1225
+
1226
+ ##
1227
+ # :attr_accessor: cert_chain
1228
+ #
1229
+ # The certificate chain used to sign this gem. See LibGems::Security for
1230
+ # details.
1231
+
1232
+ attribute :cert_chain, []
1233
+
1234
+ ##
1235
+ # :attr_accessor: post_install_message
1236
+ #
1237
+ # A message that gets displayed after the gem is installed
1238
+
1239
+ attribute :post_install_message, nil
1240
+
1241
+ ##
1242
+ # :attr_accessor: authors
1243
+ #
1244
+ # The list of author names who wrote this gem.
1245
+ #
1246
+ # If you are providing multiple authors and multiple emails they should be
1247
+ # in the same order such that:
1248
+ #
1249
+ # Hash[*spec.authors.zip(spec.emails).flatten]
1250
+ #
1251
+ # Gives a hash of author name to email address.
1252
+
1253
+ array_attribute :authors
1254
+
1255
+ ##
1256
+ # :attr_accessor: licenses
1257
+ #
1258
+ # The license(s) for the library. Each license must be a short name, no
1259
+ # more than 64 characters.
1260
+
1261
+ array_attribute :licenses
1262
+
1263
+ ##
1264
+ # :attr_accessor: files
1265
+ #
1266
+ # Files included in this gem. You cannot append to this accessor, you must
1267
+ # assign to it.
1268
+ #
1269
+ # Only add files you can require to this list, not directories, etc.
1270
+ #
1271
+ # Directories are automatically stripped from this list when building a gem,
1272
+ # other non-files cause an error.
1273
+
1274
+ array_attribute :files
1275
+
1276
+ ##
1277
+ # :attr_accessor: test_files
1278
+ #
1279
+ # Test files included in this gem. You cannot append to this accessor, you
1280
+ # must assign to it.
1281
+
1282
+ array_attribute :test_files
1283
+
1284
+ ##
1285
+ # :attr_accessor: rdoc_options
1286
+ #
1287
+ # An ARGV style array of options to RDoc
1288
+
1289
+ array_attribute :rdoc_options
1290
+
1291
+ ##
1292
+ # :attr_accessor: extra_rdoc_files
1293
+ #
1294
+ # Extra files to add to RDoc such as README or doc/examples.txt
1295
+
1296
+ array_attribute :extra_rdoc_files
1297
+
1298
+ ##
1299
+ # :attr_accessor: executables
1300
+ #
1301
+ # Executables included in the gem.
1302
+
1303
+ array_attribute :executables
1304
+
1305
+ ##
1306
+ # :attr_accessor: extensions
1307
+ #
1308
+ # Extensions to build when installing the gem. See
1309
+ # LibGems::Installer#build_extensions for valid values.
1310
+
1311
+ array_attribute :extensions
1312
+
1313
+ ##
1314
+ # :attr_accessor: requirements
1315
+ #
1316
+ # An array or things required by this gem. Not used by anything
1317
+ # presently.
1318
+
1319
+ array_attribute :requirements
1320
+
1321
+ ##
1322
+ # :attr_reader: dependencies
1323
+ #
1324
+ # A list of LibGems::Dependency objects this gem depends on.
1325
+ #
1326
+ # Use #add_dependency or #add_development_dependency to add dependencies to
1327
+ # a gem.
1328
+
1329
+ array_attribute :dependencies
1330
+
1331
+ read_only :dependencies
1332
+
1333
+ # :section: Aliased gemspec attributes
1334
+
1335
+ ##
1336
+ # Singular accessor for #executables
1337
+
1338
+ attribute_alias_singular :executable, :executables
1339
+
1340
+ ##
1341
+ # Singular accessor for #authors
1342
+
1343
+ attribute_alias_singular :author, :authors
1344
+
1345
+ ##
1346
+ # Singular accessor for #licenses
1347
+
1348
+ attribute_alias_singular :license, :licenses
1349
+
1350
+ ##
1351
+ # Singular accessor for #require_paths
1352
+
1353
+ attribute_alias_singular :require_path, :require_paths
1354
+
1355
+ ##
1356
+ # Singular accessor for #test_files
1357
+
1358
+ attribute_alias_singular :test_file, :test_files
1359
+
1360
+ overwrite_accessor :version= do |version|
1361
+ @version = LibGems::Version.create(version)
1362
+ self.required_rubygems_version = '> 1.3.1' if @version.prerelease?
1363
+ return @version
1364
+ end
1365
+
1366
+ overwrite_accessor :platform do
1367
+ @new_platform
1368
+ end
1369
+
1370
+ overwrite_accessor :platform= do |platform|
1371
+ if @original_platform.nil? or
1372
+ @original_platform == LibGems::Platform::RUBY then
1373
+ @original_platform = platform
1374
+ end
1375
+
1376
+ case platform
1377
+ when LibGems::Platform::CURRENT then
1378
+ @new_platform = LibGems::Platform.local
1379
+ @original_platform = @new_platform.to_s
1380
+
1381
+ when LibGems::Platform then
1382
+ @new_platform = platform
1383
+
1384
+ # legacy constants
1385
+ when nil, LibGems::Platform::RUBY then
1386
+ @new_platform = LibGems::Platform::RUBY
1387
+ when 'mswin32' then # was LibGems::Platform::WIN32
1388
+ @new_platform = LibGems::Platform.new 'x86-mswin32'
1389
+ when 'i586-linux' then # was LibGems::Platform::LINUX_586
1390
+ @new_platform = LibGems::Platform.new 'x86-linux'
1391
+ when 'powerpc-darwin' then # was LibGems::Platform::DARWIN
1392
+ @new_platform = LibGems::Platform.new 'ppc-darwin'
1393
+ else
1394
+ @new_platform = LibGems::Platform.new platform
1395
+ end
1396
+
1397
+ @platform = @new_platform.to_s
1398
+
1399
+ @new_platform
1400
+ end
1401
+
1402
+ overwrite_accessor :required_ruby_version= do |value|
1403
+ @required_ruby_version = LibGems::Requirement.create(value)
1404
+ end
1405
+
1406
+ overwrite_accessor :required_rubygems_version= do |value|
1407
+ @required_rubygems_version = LibGems::Requirement.create(value)
1408
+ end
1409
+
1410
+ overwrite_accessor :date= do |date|
1411
+ # We want to end up with a Time object with one-day resolution.
1412
+ # This is the cleanest, most-readable, faster-than-using-Date
1413
+ # way to do it.
1414
+ case date
1415
+ when String then
1416
+ @date = if /\A(\d{4})-(\d{2})-(\d{2})\Z/ =~ date then
1417
+ Time.local($1.to_i, $2.to_i, $3.to_i)
1418
+ else
1419
+ require 'time'
1420
+ Time.parse date
1421
+ end
1422
+ when Time then
1423
+ @date = Time.local(date.year, date.month, date.day)
1424
+ when Date then
1425
+ @date = Time.local(date.year, date.month, date.day)
1426
+ else
1427
+ @date = TODAY
1428
+ end
1429
+ end
1430
+
1431
+ overwrite_accessor :date do
1432
+ self.date = nil if @date.nil? # HACK Sets the default value for date
1433
+ @date
1434
+ end
1435
+
1436
+ overwrite_accessor :summary= do |str|
1437
+ @summary = if str then
1438
+ str.strip.
1439
+ gsub(/(\w-)\n[ \t]*(\w)/, '\1\2').
1440
+ gsub(/\n[ \t]*/, " ")
1441
+ end
1442
+ end
1443
+
1444
+ overwrite_accessor :description= do |str|
1445
+ @description = str.to_s
1446
+ end
1447
+
1448
+ overwrite_accessor :default_executable do
1449
+ begin
1450
+ if defined?(@default_executable) and @default_executable
1451
+ result = @default_executable
1452
+ elsif @executables and @executables.size == 1
1453
+ result = Array(@executables).first
1454
+ else
1455
+ result = nil
1456
+ end
1457
+ result
1458
+ rescue
1459
+ nil
1460
+ end
1461
+ end
1462
+
1463
+ overwrite_accessor :test_files do
1464
+ # Handle the possibility that we have @test_suite_file but not
1465
+ # @test_files. This will happen when an old gem is loaded via
1466
+ # YAML.
1467
+ if defined? @test_suite_file then
1468
+ @test_files = [@test_suite_file].flatten
1469
+ @test_suite_file = nil
1470
+ end
1471
+ if defined?(@test_files) and @test_files then
1472
+ @test_files
1473
+ else
1474
+ @test_files = []
1475
+ end
1476
+ end
1477
+
1478
+ overwrite_accessor :files do
1479
+ # DO NOT CHANGE TO ||= ! This is not a normal accessor. (yes, it sucks)
1480
+ @files = [@files,
1481
+ @test_files,
1482
+ add_bindir(@executables),
1483
+ @extra_rdoc_files,
1484
+ @extensions,
1485
+ ].flatten.uniq.compact
1486
+ end
1487
+ end