rubygems-update 1.8.30 → 2.0.0.preview2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of rubygems-update might be problematic. Click here for more details.

Files changed (241) hide show
  1. checksums.yaml +6 -6
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +3 -0
  4. data/.autotest +6 -3
  5. data/History.txt +137 -63
  6. data/LICENSE.txt +1 -5
  7. data/Manifest.txt +69 -32
  8. data/README.rdoc +11 -9
  9. data/Rakefile +24 -38
  10. data/bin/gem +0 -9
  11. data/bin/update_rubygems +1 -0
  12. data/lib/rubygems.rb +193 -405
  13. data/lib/rubygems/available_set.rb +95 -0
  14. data/lib/rubygems/command.rb +88 -45
  15. data/lib/rubygems/command_manager.rb +67 -40
  16. data/lib/rubygems/commands/build_command.rb +5 -23
  17. data/lib/rubygems/commands/cert_command.rb +199 -57
  18. data/lib/rubygems/commands/check_command.rb +14 -39
  19. data/lib/rubygems/commands/cleanup_command.rb +9 -1
  20. data/lib/rubygems/commands/contents_command.rb +30 -12
  21. data/lib/rubygems/commands/dependency_command.rb +3 -8
  22. data/lib/rubygems/commands/environment_command.rb +13 -8
  23. data/lib/rubygems/commands/fetch_command.rb +3 -16
  24. data/lib/rubygems/commands/generate_index_command.rb +7 -47
  25. data/lib/rubygems/commands/help_command.rb +1 -1
  26. data/lib/rubygems/commands/install_command.rb +69 -36
  27. data/lib/rubygems/commands/list_command.rb +6 -4
  28. data/lib/rubygems/commands/lock_command.rb +1 -1
  29. data/lib/rubygems/commands/mirror_command.rb +17 -0
  30. data/lib/rubygems/commands/outdated_command.rb +6 -3
  31. data/lib/rubygems/commands/owner_command.rb +13 -5
  32. data/lib/rubygems/commands/pristine_command.rb +19 -4
  33. data/lib/rubygems/commands/push_command.rb +12 -1
  34. data/lib/rubygems/commands/query_command.rb +43 -27
  35. data/lib/rubygems/commands/rdoc_command.rb +23 -28
  36. data/lib/rubygems/commands/search_command.rb +4 -18
  37. data/lib/rubygems/commands/server_command.rb +1 -1
  38. data/lib/rubygems/commands/setup_command.rb +124 -38
  39. data/lib/rubygems/commands/sources_command.rb +16 -16
  40. data/lib/rubygems/commands/specification_command.rb +11 -13
  41. data/lib/rubygems/commands/uninstall_command.rb +24 -7
  42. data/lib/rubygems/commands/unpack_command.rb +7 -3
  43. data/lib/rubygems/commands/update_command.rb +22 -36
  44. data/lib/rubygems/commands/yank_command.rb +98 -0
  45. data/lib/rubygems/compatibility.rb +51 -0
  46. data/lib/rubygems/config_file.rb +82 -54
  47. data/lib/rubygems/core_ext/kernel_gem.rb +53 -0
  48. data/lib/rubygems/core_ext/kernel_require.rb +119 -0
  49. data/lib/rubygems/defaults.rb +10 -21
  50. data/lib/rubygems/dependency.rb +61 -10
  51. data/lib/rubygems/dependency_installer.rb +157 -69
  52. data/lib/rubygems/dependency_list.rb +11 -19
  53. data/lib/rubygems/dependency_resolver.rb +562 -0
  54. data/lib/rubygems/deprecate.rb +40 -40
  55. data/lib/rubygems/errors.rb +77 -24
  56. data/lib/rubygems/exceptions.rb +25 -7
  57. data/lib/rubygems/ext/builder.rb +20 -23
  58. data/lib/rubygems/ext/configure_builder.rb +2 -2
  59. data/lib/rubygems/ext/ext_conf_builder.rb +5 -45
  60. data/lib/rubygems/ext/rake_builder.rb +2 -2
  61. data/lib/rubygems/gem_runner.rb +3 -16
  62. data/lib/rubygems/gemcutter_utilities.rb +22 -7
  63. data/lib/rubygems/indexer.rb +6 -159
  64. data/lib/rubygems/install_message.rb +12 -0
  65. data/lib/rubygems/install_update_options.rb +56 -18
  66. data/lib/rubygems/installer.rb +244 -134
  67. data/lib/rubygems/installer_test_case.rb +71 -19
  68. data/lib/rubygems/mock_gem_ui.rb +17 -0
  69. data/lib/rubygems/name_tuple.rb +110 -0
  70. data/lib/rubygems/package.rb +514 -43
  71. data/lib/rubygems/package/digest_io.rb +64 -0
  72. data/lib/rubygems/package/old.rb +147 -0
  73. data/lib/rubygems/package/tar_header.rb +18 -55
  74. data/lib/rubygems/package/tar_reader.rb +20 -3
  75. data/lib/rubygems/package/tar_writer.rb +63 -7
  76. data/lib/rubygems/package_task.rb +3 -4
  77. data/lib/rubygems/path_support.rb +14 -7
  78. data/lib/rubygems/platform.rb +19 -26
  79. data/lib/rubygems/rdoc.rb +316 -0
  80. data/lib/rubygems/remote_fetcher.rb +117 -54
  81. data/lib/rubygems/request_set.rb +182 -0
  82. data/lib/rubygems/requirement.rb +63 -26
  83. data/lib/rubygems/security.rb +295 -555
  84. data/lib/rubygems/security/policies.rb +115 -0
  85. data/lib/rubygems/security/policy.rb +227 -0
  86. data/lib/rubygems/security/signer.rb +136 -0
  87. data/lib/rubygems/security/trust_dir.rb +104 -0
  88. data/lib/rubygems/server.rb +45 -55
  89. data/lib/rubygems/source.rb +144 -0
  90. data/lib/rubygems/source_list.rb +87 -0
  91. data/lib/rubygems/source_local.rb +92 -0
  92. data/lib/rubygems/source_specific_file.rb +28 -0
  93. data/lib/rubygems/spec_fetcher.rb +116 -184
  94. data/lib/rubygems/specification.rb +731 -335
  95. data/lib/rubygems/ssl_certs/AddTrustExternalCARoot.pem +88 -30
  96. data/lib/rubygems/ssl_certs/Entrust_net-Secure-Server-Certification-Authority.pem +90 -0
  97. data/lib/rubygems/ssl_certs/VerisignClass3PublicPrimaryCertificationAuthority-G2.pem +57 -0
  98. data/lib/rubygems/syck_hack.rb +2 -0
  99. data/lib/rubygems/test_case.rb +199 -109
  100. data/lib/rubygems/test_utilities.rb +25 -5
  101. data/lib/rubygems/uninstaller.rb +62 -20
  102. data/lib/rubygems/user_interaction.rb +10 -0
  103. data/lib/rubygems/validator.rb +33 -40
  104. data/lib/rubygems/version.rb +19 -8
  105. data/setup.rb +8 -1
  106. data/test/rubygems/alternate_cert.pem +9 -0
  107. data/test/rubygems/alternate_cert_32.pem +9 -0
  108. data/test/rubygems/alternate_key.pem +9 -0
  109. data/test/rubygems/bad_rake.rb +1 -0
  110. data/test/rubygems/child_cert.pem +9 -0
  111. data/test/rubygems/child_cert_32.pem +9 -0
  112. data/test/rubygems/child_key.pem +9 -0
  113. data/test/rubygems/data/null-type.gemspec.rz +0 -0
  114. data/test/rubygems/expired_cert.pem +9 -0
  115. data/test/rubygems/future_cert.pem +9 -0
  116. data/test/rubygems/future_cert_32.pem +9 -0
  117. data/test/rubygems/good_rake.rb +1 -0
  118. data/test/rubygems/grandchild_cert.pem +9 -0
  119. data/test/rubygems/grandchild_cert_32.pem +9 -0
  120. data/test/rubygems/grandchild_key.pem +9 -0
  121. data/test/rubygems/invalid_issuer_cert.pem +9 -0
  122. data/test/rubygems/invalid_issuer_cert_32.pem +9 -0
  123. data/test/rubygems/invalid_key.pem +9 -0
  124. data/test/rubygems/invalid_signer_cert.pem +9 -0
  125. data/test/rubygems/invalid_signer_cert_32.pem +9 -0
  126. data/test/rubygems/invalidchild_cert.pem +9 -0
  127. data/test/rubygems/invalidchild_cert_32.pem +9 -0
  128. data/test/rubygems/invalidchild_key.pem +9 -0
  129. data/test/rubygems/plugin/exception/rubygems_plugin.rb +1 -1
  130. data/test/rubygems/plugin/standarderror/rubygems_plugin.rb +1 -1
  131. data/test/rubygems/private_key.pem +7 -25
  132. data/test/rubygems/public_cert.pem +8 -18
  133. data/test/rubygems/public_cert_32.pem +10 -0
  134. data/test/rubygems/public_key.pem +4 -0
  135. data/test/rubygems/rubygems/commands/crash_command.rb +1 -1
  136. data/test/rubygems/test_config.rb +4 -6
  137. data/test/rubygems/test_deprecate.rb +76 -0
  138. data/test/rubygems/test_gem.rb +318 -83
  139. data/test/rubygems/test_gem_available_set.rb +106 -0
  140. data/test/rubygems/test_gem_command.rb +10 -0
  141. data/test/rubygems/test_gem_command_manager.rb +55 -9
  142. data/test/rubygems/test_gem_commands_build_command.rb +11 -19
  143. data/test/rubygems/test_gem_commands_cert_command.rb +441 -42
  144. data/test/rubygems/test_gem_commands_cleanup_command.rb +29 -1
  145. data/test/rubygems/test_gem_commands_contents_command.rb +23 -0
  146. data/test/rubygems/test_gem_commands_dependency_command.rb +5 -0
  147. data/test/rubygems/test_gem_commands_fetch_command.rb +19 -20
  148. data/test/rubygems/test_gem_commands_generate_index_command.rb +2 -83
  149. data/test/rubygems/test_gem_commands_help_command.rb +2 -1
  150. data/test/rubygems/test_gem_commands_install_command.rb +647 -48
  151. data/test/rubygems/test_gem_commands_mirror.rb +32 -0
  152. data/test/rubygems/test_gem_commands_owner_command.rb +4 -8
  153. data/test/rubygems/test_gem_commands_pristine_command.rb +99 -4
  154. data/test/rubygems/test_gem_commands_push_command.rb +62 -8
  155. data/test/rubygems/test_gem_commands_query_command.rb +51 -0
  156. data/test/rubygems/test_gem_commands_search_command.rb +25 -0
  157. data/test/rubygems/test_gem_commands_setup_command.rb +45 -0
  158. data/test/rubygems/test_gem_commands_sources_command.rb +21 -6
  159. data/test/rubygems/test_gem_commands_specification_command.rb +33 -1
  160. data/test/rubygems/test_gem_commands_uninstall_command.rb +91 -31
  161. data/test/rubygems/test_gem_commands_unpack_command.rb +3 -3
  162. data/test/rubygems/test_gem_commands_update_command.rb +56 -38
  163. data/test/rubygems/test_gem_commands_which_command.rb +4 -4
  164. data/test/rubygems/test_gem_commands_yank_command.rb +97 -0
  165. data/test/rubygems/test_gem_config_file.rb +66 -21
  166. data/test/rubygems/test_gem_dependency.rb +46 -0
  167. data/test/rubygems/test_gem_dependency_installer.rb +228 -18
  168. data/test/rubygems/test_gem_dependency_list.rb +0 -9
  169. data/test/rubygems/test_gem_dependency_resolver.rb +327 -0
  170. data/test/rubygems/test_gem_ext_configure_builder.rb +4 -4
  171. data/test/rubygems/test_gem_ext_ext_conf_builder.rb +21 -49
  172. data/test/rubygems/test_gem_ext_rake_builder.rb +13 -13
  173. data/test/rubygems/test_gem_gem_runner.rb +27 -5
  174. data/test/rubygems/test_gem_gemcutter_utilities.rb +19 -0
  175. data/test/rubygems/test_gem_indexer.rb +14 -227
  176. data/test/rubygems/test_gem_install_update_options.rb +83 -3
  177. data/test/rubygems/test_gem_installer.rb +211 -236
  178. data/test/rubygems/test_gem_local_remote_options.rb +8 -2
  179. data/test/rubygems/test_gem_name_tuple.rb +15 -0
  180. data/test/rubygems/test_gem_package.rb +547 -0
  181. data/test/rubygems/test_gem_package_old.rb +37 -0
  182. data/test/rubygems/test_gem_package_tar_reader.rb +32 -0
  183. data/test/rubygems/test_gem_package_tar_writer.rb +84 -1
  184. data/test/rubygems/test_gem_path_support.rb +4 -30
  185. data/test/rubygems/test_gem_platform.rb +3 -6
  186. data/test/rubygems/test_gem_rdoc.rb +245 -0
  187. data/test/rubygems/test_gem_remote_fetcher.rb +51 -5
  188. data/test/rubygems/test_gem_request_set.rb +70 -0
  189. data/test/rubygems/test_gem_requirement.rb +53 -24
  190. data/test/rubygems/test_gem_security.rb +189 -43
  191. data/test/rubygems/test_gem_security_policy.rb +376 -0
  192. data/test/rubygems/test_gem_security_signer.rb +184 -0
  193. data/test/rubygems/test_gem_security_trust_dir.rb +94 -0
  194. data/test/rubygems/test_gem_server.rb +31 -36
  195. data/test/rubygems/test_gem_silent_ui.rb +2 -2
  196. data/test/rubygems/test_gem_source.rb +188 -0
  197. data/test/rubygems/test_gem_source_list.rb +87 -0
  198. data/test/rubygems/test_gem_source_local.rb +83 -0
  199. data/test/rubygems/test_gem_source_specific_file.rb +33 -0
  200. data/test/rubygems/test_gem_spec_fetcher.rb +91 -255
  201. data/test/rubygems/test_gem_specification.rb +293 -39
  202. data/test/rubygems/test_gem_uninstaller.rb +136 -13
  203. data/test/rubygems/test_gem_validator.rb +14 -41
  204. data/test/rubygems/test_gem_version.rb +15 -21
  205. data/test/rubygems/test_require.rb +193 -0
  206. data/test/rubygems/wrong_key_cert.pem +9 -0
  207. data/test/rubygems/wrong_key_cert_32.pem +9 -0
  208. metadata +171 -83
  209. metadata.gz.sig +1 -0
  210. data/CVE-2013-4287.txt +0 -36
  211. data/CVE-2013-4363.txt +0 -45
  212. data/ci_build.sh +0 -27
  213. data/cruise_config.rb +0 -32
  214. data/lib/rbconfig/datadir.rb +0 -13
  215. data/lib/rubygems/builder.rb +0 -99
  216. data/lib/rubygems/custom_require.rb +0 -69
  217. data/lib/rubygems/doc_manager.rb +0 -243
  218. data/lib/rubygems/format.rb +0 -82
  219. data/lib/rubygems/gem_openssl.rb +0 -90
  220. data/lib/rubygems/gem_path_searcher.rb +0 -172
  221. data/lib/rubygems/old_format.rb +0 -153
  222. data/lib/rubygems/package/f_sync_dir.rb +0 -23
  223. data/lib/rubygems/package/tar_input.rb +0 -234
  224. data/lib/rubygems/package/tar_output.rb +0 -146
  225. data/lib/rubygems/require_paths_builder.rb +0 -18
  226. data/lib/rubygems/source_index.rb +0 -406
  227. data/lib/rubygems/ssl_certs/AddTrustExternalCARoot-2048.pem +0 -25
  228. data/lib/rubygems/ssl_certs/Class3PublicPrimaryCertificationAuthority.pem +0 -14
  229. data/lib/rubygems/ssl_certs/DigiCertHighAssuranceEVRootCA.pem +0 -23
  230. data/lib/rubygems/ssl_certs/EntrustnetSecureServerCertificationAuthority.pem +0 -28
  231. data/lib/rubygems/ssl_certs/GeoTrustGlobalCA.pem +0 -20
  232. data/test/rubygems/test_bundled_ca.rb +0 -59
  233. data/test/rubygems/test_gem_builder.rb +0 -44
  234. data/test/rubygems/test_gem_doc_manager.rb +0 -32
  235. data/test/rubygems/test_gem_ext_builder.rb +0 -58
  236. data/test/rubygems/test_gem_format.rb +0 -88
  237. data/test/rubygems/test_gem_gem_path_searcher.rb +0 -94
  238. data/test/rubygems/test_gem_package_tar_input.rb +0 -129
  239. data/test/rubygems/test_gem_package_tar_output.rb +0 -101
  240. data/test/rubygems/test_gem_source_index.rb +0 -250
  241. data/util/update_bundled_ca_certificates.rb +0 -103
@@ -10,6 +10,10 @@ require 'rubygems/deprecate'
10
10
  ##
11
11
  # Gem::DependencyList is used for installing and uninstalling gems in the
12
12
  # correct order to avoid conflicts.
13
+ #--
14
+ # TODO: It appears that all but topo-sort functionality is being duplicated
15
+ # (or is planned to be duplicated) elsewhere in rubygems. Is the majority of
16
+ # this class necessary anymore? Especially #ok?, #why_not_ok?
13
17
 
14
18
  class Gem::DependencyList
15
19
  attr_reader :specs
@@ -27,19 +31,10 @@ class Gem::DependencyList
27
31
 
28
32
  def self.from_specs
29
33
  list = new
30
- list.add(*Gem::Specification.map)
34
+ list.add(*Gem::Specification.to_a)
31
35
  list
32
36
  end
33
37
 
34
- ##
35
- # Creates a DependencyList from a Gem::SourceIndex +source_index+
36
-
37
- def self.from_source_index(ignored=nil)
38
- warn "NOTE: DependencyList.from_source_index ignores it's arg" if ignored
39
-
40
- from_specs
41
- end
42
-
43
38
  ##
44
39
  # Creates a new DependencyList. If +development+ is true, development
45
40
  # dependencies will be included.
@@ -143,7 +138,7 @@ class Gem::DependencyList
143
138
  # If removing the gemspec creates breaks a currently ok dependency, then it
144
139
  # is NOT ok to remove the gemspec.
145
140
 
146
- def ok_to_remove?(full_name)
141
+ def ok_to_remove?(full_name, check_dev=true)
147
142
  gem_to_remove = find_name full_name
148
143
 
149
144
  siblings = @specs.find_all { |s|
@@ -154,7 +149,9 @@ class Gem::DependencyList
154
149
  deps = []
155
150
 
156
151
  @specs.each do |spec|
157
- spec.dependencies.each do |dep|
152
+ check = check_dev ? spec.dependencies : spec.runtime_dependencies
153
+
154
+ check.each do |dep|
158
155
  deps << dep if gem_to_remove.satisfies_requirement?(dep)
159
156
  end
160
157
  end
@@ -213,7 +210,7 @@ class Gem::DependencyList
213
210
  @specs.each(&block)
214
211
  end
215
212
 
216
- def tsort_each_child(node, &block)
213
+ def tsort_each_child(node)
217
214
  specs = @specs.sort.reverse
218
215
 
219
216
  dependencies = node.runtime_dependencies
@@ -242,11 +239,6 @@ class Gem::DependencyList
242
239
  def active_count(specs, ignored)
243
240
  specs.count { |spec| ignored[spec.full_name].nil? }
244
241
  end
245
- end
246
242
 
247
- class Gem::DependencyList
248
- class << self
249
- extend Gem::Deprecate
250
- deprecate :from_source_index, "from_specs", 2011, 11
251
- end
252
243
  end
244
+
@@ -0,0 +1,562 @@
1
+ require 'rubygems'
2
+ require 'rubygems/dependency'
3
+ require 'rubygems/exceptions'
4
+
5
+ require 'uri'
6
+ require 'net/http'
7
+
8
+ module Gem
9
+
10
+ # Raised when a DependencyConflict reaches the toplevel.
11
+ # Indicates which dependencies were incompatible.
12
+ #
13
+ class DependencyResolutionError < Gem::Exception
14
+ def initialize(conflict)
15
+ @conflict = conflict
16
+ a, b = conflicting_dependencies
17
+
18
+ super "unable to resolve conflicting dependencies '#{a}' and '#{b}'"
19
+ end
20
+
21
+ attr_reader :conflict
22
+
23
+ def conflicting_dependencies
24
+ @conflict.conflicting_dependencies
25
+ end
26
+ end
27
+
28
+ # Raised when a dependency requests a gem for which there is
29
+ # no spec.
30
+ #
31
+ class UnsatisfiableDepedencyError < Gem::Exception
32
+ def initialize(dep)
33
+ super "unable to find any gem matching dependency '#{dep}'"
34
+
35
+ @dependency = dep
36
+ end
37
+
38
+ attr_reader :dependency
39
+ end
40
+
41
+ # Raised when dependencies conflict and create the inability to
42
+ # find a valid possible spec for a request.
43
+ #
44
+ class ImpossibleDependenciesError < Gem::Exception
45
+ def initialize(request, conflicts)
46
+ s = conflicts.size == 1 ? "" : "s"
47
+ super "detected #{conflicts.size} conflict#{s} with dependency '#{request.dependency}'"
48
+ @request = request
49
+ @conflicts = conflicts
50
+ end
51
+
52
+ def dependency
53
+ @request.dependency
54
+ end
55
+
56
+ attr_reader :conflicts
57
+ end
58
+
59
+ # Given a set of Gem::Dependency objects as +needed+ and a way
60
+ # to query the set of available specs via +set+, calculates
61
+ # a set of ActivationRequest objects which indicate all the specs
62
+ # that should be activated to meet the all the requirements.
63
+ #
64
+ class DependencyResolver
65
+
66
+ # Represents a specification retrieved via the rubygems.org
67
+ # API. This is used to avoid having to load the full
68
+ # Specification object when all we need is the name, version,
69
+ # and dependencies.
70
+ #
71
+ class APISpecification
72
+ def initialize(set, api_data)
73
+ @set = set
74
+ @name = api_data[:name]
75
+ @version = Gem::Version.new api_data[:number]
76
+ @dependencies = api_data[:dependencies].map do |name, ver|
77
+ Gem::Dependency.new name, ver.split(/\s*,\s*/)
78
+ end
79
+ end
80
+
81
+ attr_reader :name, :version, :dependencies
82
+
83
+ def full_name
84
+ "#{@name}-#{@version}"
85
+ end
86
+ end
87
+
88
+ # The global rubygems pool, available via the rubygems.org API.
89
+ # Returns instances of APISpecification.
90
+ #
91
+ class APISet
92
+ def initialize
93
+ @data = Hash.new { |h,k| h[k] = [] }
94
+ end
95
+
96
+ # Return data for all versions of the gem +name+.
97
+ #
98
+ def versions(name)
99
+ if @data.key?(name)
100
+ return @data[name]
101
+ end
102
+
103
+ u = URI.parse "http://rubygems.org/api/v1/dependencies?gems=#{name}"
104
+ str = Net::HTTP.get(u)
105
+
106
+ Marshal.load(str).each do |ver|
107
+ @data[ver[:name]] << ver
108
+ end
109
+
110
+ @data[name]
111
+ end
112
+
113
+ # Return an array of APISpecification objects matching
114
+ # DependencyRequest +req+.
115
+ #
116
+ def find_all(req)
117
+ res = []
118
+
119
+ versions(req.name).each do |ver|
120
+ if req.dependency.match? req.name, ver[:number]
121
+ res << APISpecification.new(self, ver)
122
+ end
123
+ end
124
+
125
+ res
126
+ end
127
+
128
+ # A hint run by the resolver to allow the Set to fetch
129
+ # data for DependencyRequests +reqs+.
130
+ #
131
+ def prefetch(reqs)
132
+ names = reqs.map { |r| r.dependency.name }
133
+ needed = names.find_all { |d| !@data.key?(d) }
134
+
135
+ return if needed.empty?
136
+
137
+ u = URI.parse "http://rubygems.org/api/v1/dependencies?gems=#{needed.join ','}"
138
+ str = Net::HTTP.get(u)
139
+
140
+ Marshal.load(str).each do |ver|
141
+ @data[ver[:name]] << ver
142
+ end
143
+ end
144
+ end
145
+
146
+ # Represents a possible Specification object returned
147
+ # from IndexSet. Used to delay needed to download full
148
+ # Specification objects when only the +name+ and +version+
149
+ # are needed.
150
+ #
151
+ class IndexSpecification
152
+ def initialize(set, name, version, source, plat)
153
+ @set = set
154
+ @name = name
155
+ @version = version
156
+ @source = source
157
+ @platform = plat
158
+
159
+ @spec = nil
160
+ end
161
+
162
+ attr_reader :name, :version, :source
163
+
164
+ def full_name
165
+ "#{@name}-#{@version}"
166
+ end
167
+
168
+ def spec
169
+ @spec ||= @set.load_spec(@name, @version, @source)
170
+ end
171
+
172
+ def dependencies
173
+ spec.dependencies
174
+ end
175
+ end
176
+
177
+ # The global rubygems pool represented via the traditional
178
+ # source index.
179
+ #
180
+ class IndexSet
181
+ def initialize
182
+ @f = Gem::SpecFetcher.fetcher
183
+
184
+ @all = Hash.new { |h,k| h[k] = [] }
185
+
186
+ list, _ = @f.available_specs(:released)
187
+ list.each do |uri, specs|
188
+ specs.each do |n|
189
+ @all[n.name] << [uri, n]
190
+ end
191
+ end
192
+
193
+ @specs = {}
194
+ end
195
+
196
+ # Return an array of IndexSpecification objects matching
197
+ # DependencyRequest +req+.
198
+ #
199
+ def find_all(req)
200
+ res = []
201
+
202
+ name = req.dependency.name
203
+
204
+ @all[name].each do |uri, n|
205
+ if req.dependency.match? n
206
+ res << IndexSpecification.new(self, n.name, n.version,
207
+ uri, n.platform)
208
+ end
209
+ end
210
+
211
+ res
212
+ end
213
+
214
+ # No prefetching needed since we load the whole index in
215
+ # initially.
216
+ #
217
+ def prefetch(gems)
218
+ end
219
+
220
+ # Called from IndexSpecification to get a true Specification
221
+ # object.
222
+ #
223
+ def load_spec(name, ver, source)
224
+ key = "#{name}-#{ver}"
225
+ @specs[key] ||= source.fetch_spec(Gem::NameTuple.new(name, ver))
226
+ end
227
+ end
228
+
229
+ # A set which represents the installed gems. Respects
230
+ # all the normal settings that control where to look
231
+ # for installed gems.
232
+ #
233
+ class CurrentSet
234
+ def find_all(req)
235
+ req.dependency.matching_specs
236
+ end
237
+
238
+ def prefetch(gems)
239
+ end
240
+ end
241
+
242
+ # Create DependencyResolver object which will resolve
243
+ # the tree starting with +needed+ Depedency objects.
244
+ #
245
+ # +set+ is an object that provides where to look for
246
+ # specifications to satisify the Dependencies. This
247
+ # defaults to IndexSet, which will query rubygems.org.
248
+ #
249
+ def initialize(needed, set=IndexSet.new)
250
+ @set = set || IndexSet.new # Allow nil to mean IndexSet
251
+ @needed = needed
252
+
253
+ @conflicts = nil
254
+ end
255
+
256
+ # Provide a DependencyResolver that queries only against
257
+ # the already installed gems.
258
+ #
259
+ def self.for_current_gems(needed)
260
+ new needed, CurrentSet.new
261
+ end
262
+
263
+ # Contains all the conflicts encountered while doing resolution
264
+ #
265
+ attr_reader :conflicts
266
+
267
+ # Proceed with resolution! Returns an array of ActivationRequest
268
+ # objects.
269
+ #
270
+ def resolve
271
+ @conflicts = []
272
+
273
+ needed = @needed.map { |n| DependencyRequest.new(n, nil) }
274
+
275
+ res = resolve_for needed, []
276
+
277
+ if res.kind_of? DependencyConflict
278
+ raise DependencyResolutionError.new(res)
279
+ end
280
+
281
+ res
282
+ end
283
+
284
+ # Used internally to indicate that a dependency conflicted
285
+ # with a spec that would be activated.
286
+ #
287
+ class DependencyConflict
288
+ def initialize(dependency, activated, failed_dep=dependency)
289
+ @dependency = dependency
290
+ @activated = activated
291
+ @failed_dep = failed_dep
292
+ end
293
+
294
+ attr_reader :dependency, :activated
295
+
296
+ # Return the Specification that listed the dependency
297
+ #
298
+ def requester
299
+ @failed_dep.requester
300
+ end
301
+
302
+ def for_spec?(spec)
303
+ @dependency.name == spec.name
304
+ end
305
+
306
+ # Return the 2 dependency objects that conflicted
307
+ #
308
+ def conflicting_dependencies
309
+ [@failed_dep.dependency, @activated.request.dependency]
310
+ end
311
+ end
312
+
313
+ # Used Internally. Wraps a Depedency object to also track
314
+ # which spec contained the Dependency.
315
+ #
316
+ class DependencyRequest
317
+ def initialize(dep, act)
318
+ @dependency = dep
319
+ @requester = act
320
+ end
321
+
322
+ attr_reader :dependency, :requester
323
+
324
+ def name
325
+ @dependency.name
326
+ end
327
+
328
+ def matches_spec?(spec)
329
+ @dependency.matches_spec? spec
330
+ end
331
+
332
+ def to_s
333
+ @dependency.to_s
334
+ end
335
+
336
+ def ==(other)
337
+ case other
338
+ when Dependency
339
+ @dependency == other
340
+ when DependencyRequest
341
+ @dependency == other.dep && @requester == other.requester
342
+ else
343
+ false
344
+ end
345
+ end
346
+ end
347
+
348
+ # Specifies a Specification object that should be activated.
349
+ # Also contains a dependency that was used to introduce this
350
+ # activation.
351
+ #
352
+ class ActivationRequest
353
+ def initialize(spec, req, others_possible=true)
354
+ @spec = spec
355
+ @request = req
356
+ @others_possible = others_possible
357
+ end
358
+
359
+ attr_reader :spec, :request
360
+
361
+ # Indicate if this activation is one of a set of possible
362
+ # requests for the same Dependency request.
363
+ #
364
+ def others_possible?
365
+ @others_possible
366
+ end
367
+
368
+ # Return the ActivationRequest that contained the dependency
369
+ # that we were activated for.
370
+ #
371
+ def parent
372
+ @request.requester
373
+ end
374
+
375
+ def name
376
+ @spec.name
377
+ end
378
+
379
+ def full_name
380
+ @spec.full_name
381
+ end
382
+
383
+ def version
384
+ @spec.version
385
+ end
386
+
387
+ def full_spec
388
+ Gem::Specification === @spec ? @spec : @spec.spec
389
+ end
390
+
391
+ def download(path)
392
+ if @spec.respond_to? :source
393
+ source = @spec.source
394
+ else
395
+ source = Gem.sources.first
396
+ end
397
+
398
+ source.download full_spec, path
399
+ end
400
+
401
+ def ==(other)
402
+ case other
403
+ when Gem::Specification
404
+ @spec == other
405
+ when ActivationRequest
406
+ @spec == other.spec && @request == other.request
407
+ else
408
+ false
409
+ end
410
+ end
411
+
412
+ ##
413
+ # Indicates if the requested gem has already been installed.
414
+
415
+ def installed?
416
+ this_spec = full_spec
417
+
418
+ Gem::Specification.any? do |s|
419
+ s == this_spec
420
+ end
421
+ end
422
+ end
423
+
424
+ def requests(s, act)
425
+ reqs = []
426
+ s.dependencies.each do |d|
427
+ next unless d.type == :runtime
428
+ reqs << DependencyRequest.new(d, act)
429
+ end
430
+
431
+ @set.prefetch(reqs)
432
+
433
+ reqs
434
+ end
435
+
436
+ # The meat of the algorithm. Given +needed+ DependencyRequest objects
437
+ # and +specs+ being a list to ActivationRequest, calculate a new list
438
+ # of ActivationRequest objects.
439
+ #
440
+ def resolve_for(needed, specs)
441
+ until needed.empty?
442
+ dep = needed.shift
443
+
444
+ # If there is already a spec activated for the requested name...
445
+ if existing = specs.find { |s| dep.name == s.name }
446
+
447
+ # then we're done since this new dep matches the
448
+ # existing spec.
449
+ next if dep.matches_spec? existing
450
+
451
+ # There is a conflict! We return the conflict
452
+ # object which will be seen by the caller and be
453
+ # handled at the right level.
454
+
455
+ # If the existing activation indicates that there
456
+ # are other possibles for it, then issue the conflict
457
+ # on the dep for the activation itself. Otherwise, issue
458
+ # it on the requester's request itself.
459
+ #
460
+ if existing.others_possible?
461
+ conflict = DependencyConflict.new(dep, existing)
462
+ else
463
+ depreq = existing.request.requester.request
464
+ conflict = DependencyConflict.new(depreq, existing, dep)
465
+ end
466
+ @conflicts << conflict
467
+
468
+ return conflict
469
+ end
470
+
471
+ # Get a list of all specs that satisfy dep
472
+ possible = @set.find_all(dep)
473
+
474
+ case possible.size
475
+ when 0
476
+ # If there are none, then our work here is done.
477
+ raise UnsatisfiableDepedencyError.new(dep)
478
+ when 1
479
+ # If there is one, then we just add it to specs
480
+ # and process the specs dependencies by adding
481
+ # them to needed.
482
+
483
+ spec = possible.first
484
+ act = ActivationRequest.new(spec, dep, false)
485
+
486
+ specs << act
487
+
488
+ # Put the deps for at the beginning of needed
489
+ # rather than the end to match the depth first
490
+ # searching done by the multiple case code below.
491
+ #
492
+ # This keeps the error messages consistent.
493
+ needed = requests(spec, act) + needed
494
+ else
495
+ # There are multiple specs for this dep. This is
496
+ # the case that this class is built to handle.
497
+
498
+ # Sort them so that we try the highest versions
499
+ # first.
500
+ possible = possible.sort_by { |s| s.version }
501
+
502
+ # We track the conflicts seen so that we can report them
503
+ # to help the user figure out how to fix the situation.
504
+ conflicts = []
505
+
506
+ # To figure out which to pick, we keep resolving
507
+ # given each one being activated and if there isn't
508
+ # a conflict, we know we've found a full set.
509
+ #
510
+ # We use an until loop rather than #reverse_each
511
+ # to keep the stack short since we're using a recursive
512
+ # algorithm.
513
+ #
514
+ until possible.empty?
515
+ s = possible.pop
516
+
517
+ # Recursively call #resolve_for with this spec
518
+ # and add it's dependencies into the picture...
519
+
520
+ act = ActivationRequest.new(s, dep)
521
+
522
+ try = requests(s, act) + needed
523
+
524
+ res = resolve_for(try, specs + [act])
525
+
526
+ # While trying to resolve these dependencies, there may
527
+ # be a conflict!
528
+
529
+ if res.kind_of? DependencyConflict
530
+ # The conflict might be created not by this invocation
531
+ # but rather one up the stack, so if we can't attempt
532
+ # to resolve this conflict (conflict isn't with the spec +s+)
533
+ # then just return it so the caller can try to sort it out.
534
+ return res unless res.for_spec? s
535
+
536
+ # Otherwise, this is a conflict that we can attempt to fix
537
+ conflicts << [s, res]
538
+
539
+ # Optimization:
540
+ #
541
+ # Because the conflict indicates the dependency that trigger
542
+ # it, we can prune possible based on this new information.
543
+ #
544
+ # This cuts down on the number of iterations needed.
545
+ possible.delete_if { |x| !res.dependency.matches_spec? x }
546
+ else
547
+ # No conflict, return the specs
548
+ return res
549
+ end
550
+ end
551
+
552
+ # We tried all possibles and nothing worked, so we let the user
553
+ # know and include as much information about the problem since
554
+ # the user is going to have to take action to fix this.
555
+ raise ImpossibleDependenciesError.new(dep, conflicts)
556
+ end
557
+ end
558
+
559
+ specs
560
+ end
561
+ end
562
+ end