libgems 0.0.1

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