carat 1.9.9.pre1

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 (184) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +16 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +24 -0
  5. data/CHANGELOG.md +2006 -0
  6. data/CODE_OF_CONDUCT.md +40 -0
  7. data/CONTRIBUTING.md +23 -0
  8. data/DEVELOPMENT.md +119 -0
  9. data/ISSUES.md +96 -0
  10. data/LICENSE.md +23 -0
  11. data/README.md +32 -0
  12. data/Rakefile +308 -0
  13. data/bin/carat +21 -0
  14. data/bin/carat_ruby +56 -0
  15. data/carat.gemspec +32 -0
  16. data/lib/carat.rb +446 -0
  17. data/lib/carat/anonymizable_uri.rb +32 -0
  18. data/lib/carat/capistrano.rb +16 -0
  19. data/lib/carat/cli.rb +407 -0
  20. data/lib/carat/cli/binstubs.rb +38 -0
  21. data/lib/carat/cli/cache.rb +35 -0
  22. data/lib/carat/cli/check.rb +35 -0
  23. data/lib/carat/cli/clean.rb +26 -0
  24. data/lib/carat/cli/common.rb +56 -0
  25. data/lib/carat/cli/config.rb +84 -0
  26. data/lib/carat/cli/console.rb +38 -0
  27. data/lib/carat/cli/exec.rb +44 -0
  28. data/lib/carat/cli/gem.rb +195 -0
  29. data/lib/carat/cli/init.rb +33 -0
  30. data/lib/carat/cli/inject.rb +33 -0
  31. data/lib/carat/cli/install.rb +156 -0
  32. data/lib/carat/cli/open.rb +23 -0
  33. data/lib/carat/cli/outdated.rb +80 -0
  34. data/lib/carat/cli/package.rb +45 -0
  35. data/lib/carat/cli/platform.rb +43 -0
  36. data/lib/carat/cli/show.rb +74 -0
  37. data/lib/carat/cli/update.rb +73 -0
  38. data/lib/carat/cli/viz.rb +27 -0
  39. data/lib/carat/constants.rb +5 -0
  40. data/lib/carat/current_ruby.rb +183 -0
  41. data/lib/carat/definition.rb +628 -0
  42. data/lib/carat/dep_proxy.rb +43 -0
  43. data/lib/carat/dependency.rb +110 -0
  44. data/lib/carat/deployment.rb +59 -0
  45. data/lib/carat/deprecate.rb +15 -0
  46. data/lib/carat/dsl.rb +331 -0
  47. data/lib/carat/endpoint_specification.rb +76 -0
  48. data/lib/carat/env.rb +75 -0
  49. data/lib/carat/environment.rb +42 -0
  50. data/lib/carat/fetcher.rb +423 -0
  51. data/lib/carat/friendly_errors.rb +85 -0
  52. data/lib/carat/gem_helper.rb +180 -0
  53. data/lib/carat/gem_helpers.rb +26 -0
  54. data/lib/carat/gem_installer.rb +9 -0
  55. data/lib/carat/gem_path_manipulation.rb +8 -0
  56. data/lib/carat/gem_tasks.rb +2 -0
  57. data/lib/carat/graph.rb +169 -0
  58. data/lib/carat/index.rb +197 -0
  59. data/lib/carat/injector.rb +64 -0
  60. data/lib/carat/installer.rb +339 -0
  61. data/lib/carat/lazy_specification.rb +83 -0
  62. data/lib/carat/lockfile_parser.rb +167 -0
  63. data/lib/carat/match_platform.rb +13 -0
  64. data/lib/carat/psyched_yaml.rb +26 -0
  65. data/lib/carat/remote_specification.rb +57 -0
  66. data/lib/carat/resolver.rb +334 -0
  67. data/lib/carat/retry.rb +60 -0
  68. data/lib/carat/ruby_dsl.rb +11 -0
  69. data/lib/carat/ruby_version.rb +117 -0
  70. data/lib/carat/rubygems_ext.rb +170 -0
  71. data/lib/carat/rubygems_integration.rb +619 -0
  72. data/lib/carat/runtime.rb +289 -0
  73. data/lib/carat/settings.rb +208 -0
  74. data/lib/carat/setup.rb +24 -0
  75. data/lib/carat/shared_helpers.rb +149 -0
  76. data/lib/carat/similarity_detector.rb +63 -0
  77. data/lib/carat/source.rb +46 -0
  78. data/lib/carat/source/git.rb +294 -0
  79. data/lib/carat/source/git/git_proxy.rb +162 -0
  80. data/lib/carat/source/path.rb +226 -0
  81. data/lib/carat/source/path/installer.rb +43 -0
  82. data/lib/carat/source/rubygems.rb +381 -0
  83. data/lib/carat/source_list.rb +101 -0
  84. data/lib/carat/spec_set.rb +154 -0
  85. data/lib/carat/ssl_certs/.document +1 -0
  86. data/lib/carat/ssl_certs/AddTrustExternalCARoot-2048.pem +25 -0
  87. data/lib/carat/ssl_certs/AddTrustExternalCARoot.pem +32 -0
  88. data/lib/carat/ssl_certs/Class3PublicPrimaryCertificationAuthority.pem +14 -0
  89. data/lib/carat/ssl_certs/DigiCertHighAssuranceEVRootCA.pem +23 -0
  90. data/lib/carat/ssl_certs/EntrustnetSecureServerCertificationAuthority.pem +28 -0
  91. data/lib/carat/ssl_certs/GeoTrustGlobalCA.pem +20 -0
  92. data/lib/carat/ssl_certs/certificate_manager.rb +66 -0
  93. data/lib/carat/ssl_certs/index.rubygems.org/GlobalSignRootCA.pem +21 -0
  94. data/lib/carat/ssl_certs/rubygems.global.ssl.fastly.net/DigiCertHighAssuranceEVRootCA.pem +23 -0
  95. data/lib/carat/ssl_certs/rubygems.org/AddTrustExternalCARoot.pem +25 -0
  96. data/lib/carat/templates/Executable +16 -0
  97. data/lib/carat/templates/Executable.standalone +12 -0
  98. data/lib/carat/templates/Gemfile +4 -0
  99. data/lib/carat/templates/newgem/.travis.yml.tt +3 -0
  100. data/lib/carat/templates/newgem/CODE_OF_CONDUCT.md.tt +13 -0
  101. data/lib/carat/templates/newgem/Gemfile.tt +4 -0
  102. data/lib/carat/templates/newgem/LICENSE.txt.tt +21 -0
  103. data/lib/carat/templates/newgem/README.md.tt +39 -0
  104. data/lib/carat/templates/newgem/Rakefile.tt +25 -0
  105. data/lib/carat/templates/newgem/bin/console.tt +14 -0
  106. data/lib/carat/templates/newgem/bin/setup.tt +7 -0
  107. data/lib/carat/templates/newgem/exe/newgem.tt +3 -0
  108. data/lib/carat/templates/newgem/ext/newgem/extconf.rb.tt +3 -0
  109. data/lib/carat/templates/newgem/ext/newgem/newgem.c.tt +9 -0
  110. data/lib/carat/templates/newgem/ext/newgem/newgem.h.tt +6 -0
  111. data/lib/carat/templates/newgem/gitignore.tt +16 -0
  112. data/lib/carat/templates/newgem/lib/newgem.rb.tt +12 -0
  113. data/lib/carat/templates/newgem/lib/newgem/version.rb.tt +7 -0
  114. data/lib/carat/templates/newgem/newgem.gemspec.tt +43 -0
  115. data/lib/carat/templates/newgem/rspec.tt +2 -0
  116. data/lib/carat/templates/newgem/spec/newgem_spec.rb.tt +11 -0
  117. data/lib/carat/templates/newgem/spec/spec_helper.rb.tt +2 -0
  118. data/lib/carat/templates/newgem/test/minitest_helper.rb.tt +4 -0
  119. data/lib/carat/templates/newgem/test/test_newgem.rb.tt +11 -0
  120. data/lib/carat/ui.rb +7 -0
  121. data/lib/carat/ui/rg_proxy.rb +21 -0
  122. data/lib/carat/ui/shell.rb +103 -0
  123. data/lib/carat/ui/silent.rb +44 -0
  124. data/lib/carat/vendor/molinillo/lib/molinillo.rb +5 -0
  125. data/lib/carat/vendor/molinillo/lib/molinillo/dependency_graph.rb +266 -0
  126. data/lib/carat/vendor/molinillo/lib/molinillo/errors.rb +69 -0
  127. data/lib/carat/vendor/molinillo/lib/molinillo/gem_metadata.rb +3 -0
  128. data/lib/carat/vendor/molinillo/lib/molinillo/modules/specification_provider.rb +90 -0
  129. data/lib/carat/vendor/molinillo/lib/molinillo/modules/ui.rb +63 -0
  130. data/lib/carat/vendor/molinillo/lib/molinillo/resolution.rb +415 -0
  131. data/lib/carat/vendor/molinillo/lib/molinillo/resolver.rb +43 -0
  132. data/lib/carat/vendor/molinillo/lib/molinillo/state.rb +43 -0
  133. data/lib/carat/vendor/net/http/faster.rb +26 -0
  134. data/lib/carat/vendor/net/http/persistent.rb +1230 -0
  135. data/lib/carat/vendor/net/http/persistent/ssl_reuse.rb +128 -0
  136. data/lib/carat/vendor/thor/lib/thor.rb +484 -0
  137. data/lib/carat/vendor/thor/lib/thor/actions.rb +319 -0
  138. data/lib/carat/vendor/thor/lib/thor/actions/create_file.rb +103 -0
  139. data/lib/carat/vendor/thor/lib/thor/actions/create_link.rb +59 -0
  140. data/lib/carat/vendor/thor/lib/thor/actions/directory.rb +118 -0
  141. data/lib/carat/vendor/thor/lib/thor/actions/empty_directory.rb +135 -0
  142. data/lib/carat/vendor/thor/lib/thor/actions/file_manipulation.rb +316 -0
  143. data/lib/carat/vendor/thor/lib/thor/actions/inject_into_file.rb +107 -0
  144. data/lib/carat/vendor/thor/lib/thor/base.rb +656 -0
  145. data/lib/carat/vendor/thor/lib/thor/command.rb +133 -0
  146. data/lib/carat/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb +77 -0
  147. data/lib/carat/vendor/thor/lib/thor/core_ext/io_binary_read.rb +10 -0
  148. data/lib/carat/vendor/thor/lib/thor/core_ext/ordered_hash.rb +98 -0
  149. data/lib/carat/vendor/thor/lib/thor/error.rb +32 -0
  150. data/lib/carat/vendor/thor/lib/thor/group.rb +281 -0
  151. data/lib/carat/vendor/thor/lib/thor/invocation.rb +178 -0
  152. data/lib/carat/vendor/thor/lib/thor/line_editor.rb +17 -0
  153. data/lib/carat/vendor/thor/lib/thor/line_editor/basic.rb +35 -0
  154. data/lib/carat/vendor/thor/lib/thor/line_editor/readline.rb +88 -0
  155. data/lib/carat/vendor/thor/lib/thor/parser.rb +4 -0
  156. data/lib/carat/vendor/thor/lib/thor/parser/argument.rb +73 -0
  157. data/lib/carat/vendor/thor/lib/thor/parser/arguments.rb +175 -0
  158. data/lib/carat/vendor/thor/lib/thor/parser/option.rb +125 -0
  159. data/lib/carat/vendor/thor/lib/thor/parser/options.rb +218 -0
  160. data/lib/carat/vendor/thor/lib/thor/rake_compat.rb +71 -0
  161. data/lib/carat/vendor/thor/lib/thor/runner.rb +322 -0
  162. data/lib/carat/vendor/thor/lib/thor/shell.rb +81 -0
  163. data/lib/carat/vendor/thor/lib/thor/shell/basic.rb +421 -0
  164. data/lib/carat/vendor/thor/lib/thor/shell/color.rb +149 -0
  165. data/lib/carat/vendor/thor/lib/thor/shell/html.rb +126 -0
  166. data/lib/carat/vendor/thor/lib/thor/util.rb +267 -0
  167. data/lib/carat/vendor/thor/lib/thor/version.rb +3 -0
  168. data/lib/carat/vendored_fileutils.rb +9 -0
  169. data/lib/carat/vendored_molinillo.rb +2 -0
  170. data/lib/carat/vendored_persistent.rb +11 -0
  171. data/lib/carat/vendored_thor.rb +3 -0
  172. data/lib/carat/version.rb +6 -0
  173. data/lib/carat/vlad.rb +11 -0
  174. data/lib/carat/worker.rb +73 -0
  175. data/man/carat-config.ronn +178 -0
  176. data/man/carat-exec.ronn +136 -0
  177. data/man/carat-install.ronn +383 -0
  178. data/man/carat-package.ronn +66 -0
  179. data/man/carat-platform.ronn +42 -0
  180. data/man/carat-update.ronn +188 -0
  181. data/man/carat.ronn +98 -0
  182. data/man/gemfile.5.ronn +473 -0
  183. data/man/index.txt +7 -0
  184. metadata +321 -0
@@ -0,0 +1,63 @@
1
+ module Carat::Molinillo
2
+ # Conveys information about the resolution process to a user.
3
+ module UI
4
+ # The {IO} object that should be used to print output. `STDOUT`, by default.
5
+ #
6
+ # @return [IO]
7
+ def output
8
+ STDOUT
9
+ end
10
+
11
+ # Called roughly every {#progress_rate}, this method should convey progress
12
+ # to the user.
13
+ #
14
+ # @return [void]
15
+ def indicate_progress
16
+ output.print '.' unless debug?
17
+ end
18
+
19
+ # How often progress should be conveyed to the user via
20
+ # {#indicate_progress}, in seconds. A third of a second, by default.
21
+ #
22
+ # @return [Float]
23
+ def progress_rate
24
+ 0.33
25
+ end
26
+
27
+ # Called before resolution begins.
28
+ #
29
+ # @return [void]
30
+ def before_resolution
31
+ output.print 'Resolving dependencies...'
32
+ end
33
+
34
+ # Called after resolution ends (either successfully or with an error).
35
+ # By default, prints a newline.
36
+ #
37
+ # @return [void]
38
+ def after_resolution
39
+ output.puts
40
+ end
41
+
42
+ # Conveys debug information to the user.
43
+ #
44
+ # @param [Integer] depth the current depth of the resolution process.
45
+ # @return [void]
46
+ def debug(depth = 0)
47
+ if debug?
48
+ debug_info = yield
49
+ debug_info = debug_info.inspect unless debug_info.is_a?(String)
50
+ output.puts debug_info.split("\n").map { |s| ' ' * depth + s }
51
+ end
52
+ end
53
+
54
+ # Whether or not debug messages should be printed.
55
+ # By default, whether or not the `MOLINILLO_DEBUG` environment variable is
56
+ # set.
57
+ #
58
+ # @return [Boolean]
59
+ def debug?
60
+ @debug_mode ||= ENV['MOLINILLO_DEBUG']
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,415 @@
1
+ module Carat::Molinillo
2
+ class Resolver
3
+ # A specific resolution from a given {Resolver}
4
+ class Resolution
5
+ # A conflict that the resolution process encountered
6
+ # @attr [Object] requirement the requirement that immediately led to the conflict
7
+ # @attr [{String,Nil=>[Object]}] requirements the requirements that caused the conflict
8
+ # @attr [Object, nil] existing the existing spec that was in conflict with
9
+ # the {#possibility}
10
+ # @attr [Object] possibility the spec that was unable to be activated due
11
+ # to a conflict
12
+ # @attr [Object] locked_requirement the relevant locking requirement.
13
+ # @attr [Array<Array<Object>>] requirement_trees the different requirement
14
+ # trees that led to every requirement for the conflicting name.
15
+ Conflict = Struct.new(
16
+ :requirement,
17
+ :requirements,
18
+ :existing,
19
+ :possibility,
20
+ :locked_requirement,
21
+ :requirement_trees
22
+ )
23
+
24
+ # @return [SpecificationProvider] the provider that knows about
25
+ # dependencies, requirements, specifications, versions, etc.
26
+ attr_reader :specification_provider
27
+
28
+ # @return [UI] the UI that knows how to communicate feedback about the
29
+ # resolution process back to the user
30
+ attr_reader :resolver_ui
31
+
32
+ # @return [DependencyGraph] the base dependency graph to which
33
+ # dependencies should be 'locked'
34
+ attr_reader :base
35
+
36
+ # @return [Array] the dependencies that were explicitly required
37
+ attr_reader :original_requested
38
+
39
+ # @param [SpecificationProvider] specification_provider
40
+ # see {#specification_provider}
41
+ # @param [UI] resolver_ui see {#resolver_ui}
42
+ # @param [Array] requested see {#original_requested}
43
+ # @param [DependencyGraph] base see {#base}
44
+ def initialize(specification_provider, resolver_ui, requested, base)
45
+ @specification_provider = specification_provider
46
+ @resolver_ui = resolver_ui
47
+ @original_requested = requested
48
+ @base = base
49
+ @states = []
50
+ @iteration_counter = 0
51
+ end
52
+
53
+ # Resolves the {#original_requested} dependencies into a full dependency
54
+ # graph
55
+ # @raise [ResolverError] if successful resolution is impossible
56
+ # @return [DependencyGraph] the dependency graph of successfully resolved
57
+ # dependencies
58
+ def resolve
59
+ start_resolution
60
+
61
+ while state
62
+ break unless state.requirements.any? || state.requirement
63
+ indicate_progress
64
+ if state.respond_to?(:pop_possibility_state) # DependencyState
65
+ debug(depth) { "Creating possibility state for #{requirement} (#{possibilities.count} remaining)" }
66
+ state.pop_possibility_state.tap { |s| states.push(s) if s }
67
+ end
68
+ process_topmost_state
69
+ end
70
+
71
+ activated.freeze
72
+ ensure
73
+ end_resolution
74
+ end
75
+
76
+ # @return [Integer] the number of resolver iterations in between calls to
77
+ # {#resolver_ui}'s {UI#indicate_progress} method
78
+ attr_accessor :iteration_rate
79
+ private :iteration_rate
80
+
81
+ # @return [Time] the time at which resolution began
82
+ attr_accessor :started_at
83
+ private :started_at
84
+
85
+ # @return [Array<ResolutionState>] the stack of states for the resolution
86
+ attr_accessor :states
87
+ private :states
88
+
89
+ private
90
+
91
+ # Sets up the resolution process
92
+ # @return [void]
93
+ def start_resolution
94
+ @started_at = Time.now
95
+
96
+ states.push(initial_state)
97
+
98
+ debug { "Starting resolution (#{@started_at})" }
99
+ resolver_ui.before_resolution
100
+ end
101
+
102
+ # Ends the resolution process
103
+ # @return [void]
104
+ def end_resolution
105
+ resolver_ui.after_resolution
106
+ debug do
107
+ "Finished resolution (#{@iteration_counter} steps) " \
108
+ "(Took #{(ended_at = Time.now) - @started_at} seconds) (#{ended_at})"
109
+ end
110
+ debug { 'Unactivated: ' + Hash[activated.vertices.reject { |_n, v| v.payload }].keys.join(', ') } if state
111
+ debug { 'Activated: ' + Hash[activated.vertices.select { |_n, v| v.payload }].keys.join(', ') } if state
112
+ end
113
+
114
+ require 'carat/vendor/molinillo/lib/molinillo/state'
115
+ require 'carat/vendor/molinillo/lib/molinillo/modules/specification_provider'
116
+
117
+ ResolutionState.new.members.each do |member|
118
+ define_method member do |*args, &block|
119
+ state.send(member, *args, &block)
120
+ end
121
+ end
122
+
123
+ SpecificationProvider.instance_methods(false).each do |instance_method|
124
+ define_method instance_method do |*args, &block|
125
+ begin
126
+ specification_provider.send(instance_method, *args, &block)
127
+ rescue NoSuchDependencyError => error
128
+ if state
129
+ vertex = activated.vertex_named(name_for error.dependency)
130
+ error.required_by += vertex.incoming_edges.map { |e| e.origin.name }
131
+ error.required_by << name_for_explicit_dependency_source unless vertex.explicit_requirements.empty?
132
+ end
133
+ raise
134
+ end
135
+ end
136
+ end
137
+
138
+ # Processes the topmost available {RequirementState} on the stack
139
+ # @return [void]
140
+ def process_topmost_state
141
+ if possibility
142
+ attempt_to_activate
143
+ else
144
+ create_conflict if state.is_a? PossibilityState
145
+ unwind_for_conflict until possibility && state.is_a?(DependencyState)
146
+ end
147
+ end
148
+
149
+ # @return [Object] the current possibility that the resolution is trying
150
+ # to activate
151
+ def possibility
152
+ possibilities.last
153
+ end
154
+
155
+ # @return [RequirementState] the current state the resolution is
156
+ # operating upon
157
+ def state
158
+ states.last
159
+ end
160
+
161
+ # Creates the initial state for the resolution, based upon the
162
+ # {#requested} dependencies
163
+ # @return [DependencyState] the initial state for the resolution
164
+ def initial_state
165
+ graph = DependencyGraph.new.tap do |dg|
166
+ original_requested.each { |r| dg.add_root_vertex(name_for(r), nil).tap { |v| v.explicit_requirements << r } }
167
+ end
168
+
169
+ requirements = sort_dependencies(original_requested, graph, {})
170
+ initial_requirement = requirements.shift
171
+ DependencyState.new(
172
+ initial_requirement && name_for(initial_requirement),
173
+ requirements,
174
+ graph,
175
+ initial_requirement,
176
+ initial_requirement && search_for(initial_requirement),
177
+ 0,
178
+ {}
179
+ )
180
+ end
181
+
182
+ # Unwinds the states stack because a conflict has been encountered
183
+ # @return [void]
184
+ def unwind_for_conflict
185
+ debug(depth) { "Unwinding for conflict: #{requirement}" }
186
+ conflicts.tap do |c|
187
+ states.slice!((state_index_for_unwind + 1)..-1)
188
+ raise VersionConflict.new(c) unless state
189
+ state.conflicts = c
190
+ end
191
+ end
192
+
193
+ # @return [Integer] The index to which the resolution should unwind in the
194
+ # case of conflict.
195
+ def state_index_for_unwind
196
+ current_requirement = requirement
197
+ existing_requirement = requirement_for_existing_name(name)
198
+ until current_requirement.nil?
199
+ current_state = find_state_for(current_requirement)
200
+ return states.index(current_state) if state_any?(current_state)
201
+ current_requirement = parent_of(current_requirement)
202
+ end
203
+
204
+ until existing_requirement.nil?
205
+ existing_state = find_state_for(existing_requirement)
206
+ return states.index(existing_state) if state_any?(existing_state)
207
+ existing_requirement = parent_of(existing_requirement)
208
+ end
209
+ -1
210
+ end
211
+
212
+ # @return [Object] the requirement that led to `requirement` being added
213
+ # to the list of requirements.
214
+ def parent_of(requirement)
215
+ return nil unless requirement
216
+ seen = false
217
+ state = states.reverse_each.find do |s|
218
+ seen ||= s.requirement == requirement
219
+ seen && s.requirement != requirement && !s.requirements.include?(requirement)
220
+ end
221
+ state && state.requirement
222
+ end
223
+
224
+ # @return [Object] the requirement that led to a version of a possibility
225
+ # with the given name being activated.
226
+ def requirement_for_existing_name(name)
227
+ return nil unless activated.vertex_named(name).payload
228
+ states.reverse_each.find { |s| !s.activated.vertex_named(name).payload }.requirement
229
+ end
230
+
231
+ # @return [ResolutionState] the state whose `requirement` is the given
232
+ # `requirement`.
233
+ def find_state_for(requirement)
234
+ return nil unless requirement
235
+ states.reverse_each.find { |i| requirement == i.requirement && i.is_a?(DependencyState) }
236
+ end
237
+
238
+ # @return [Boolean] whether or not the given state has any possibilities
239
+ # left.
240
+ def state_any?(state)
241
+ state && state.possibilities.any?
242
+ end
243
+
244
+ # @return [Conflict] a {Conflict} that reflects the failure to activate
245
+ # the {#possibility} in conjunction with the current {#state}
246
+ def create_conflict
247
+ vertex = activated.vertex_named(name)
248
+ requirements = {
249
+ name_for_explicit_dependency_source => vertex.explicit_requirements,
250
+ name_for_locking_dependency_source => Array(locked_requirement_named(name)),
251
+ }
252
+ vertex.incoming_edges.each { |edge| (requirements[edge.origin.payload] ||= []).unshift(*edge.requirements) }
253
+ conflicts[name] = Conflict.new(
254
+ requirement,
255
+ Hash[requirements.select { |_, r| !r.empty? }],
256
+ vertex.payload,
257
+ possibility,
258
+ locked_requirement_named(name),
259
+ requirement_trees
260
+ )
261
+ end
262
+
263
+ # @return [Array<Array<Object>>] The different requirement
264
+ # trees that led to every requirement for the current spec.
265
+ def requirement_trees
266
+ activated.vertex_named(name).requirements.map { |r| requirement_tree_for(r) }
267
+ end
268
+
269
+ # @return [Array<Object>] the list of requirements that led to
270
+ # `requirement` being required.
271
+ def requirement_tree_for(requirement)
272
+ tree = []
273
+ while requirement
274
+ tree.unshift(requirement)
275
+ requirement = parent_of(requirement)
276
+ end
277
+ tree
278
+ end
279
+
280
+ # Indicates progress roughly once every second
281
+ # @return [void]
282
+ def indicate_progress
283
+ @iteration_counter += 1
284
+ @progress_rate ||= resolver_ui.progress_rate
285
+ if iteration_rate.nil?
286
+ if Time.now - started_at >= @progress_rate
287
+ self.iteration_rate = @iteration_counter
288
+ end
289
+ end
290
+
291
+ if iteration_rate && (@iteration_counter % iteration_rate) == 0
292
+ resolver_ui.indicate_progress
293
+ end
294
+ end
295
+
296
+ # Calls the {#resolver_ui}'s {UI#debug} method
297
+ # @param [Integer] depth the depth of the {#states} stack
298
+ # @param [Proc] block a block that yields a {#to_s}
299
+ # @return [void]
300
+ def debug(depth = 0, &block)
301
+ resolver_ui.debug(depth, &block)
302
+ end
303
+
304
+ # Attempts to activate the current {#possibility}
305
+ # @return [void]
306
+ def attempt_to_activate
307
+ debug(depth) { 'Attempting to activate ' + possibility.to_s }
308
+ existing_node = activated.vertex_named(name)
309
+ if existing_node.payload
310
+ debug(depth) { "Found existing spec (#{existing_node.payload})" }
311
+ attempt_to_activate_existing_spec(existing_node)
312
+ else
313
+ attempt_to_activate_new_spec
314
+ end
315
+ end
316
+
317
+ # Attempts to activate the current {#possibility} (given that it has
318
+ # already been activated)
319
+ # @return [void]
320
+ def attempt_to_activate_existing_spec(existing_node)
321
+ existing_spec = existing_node.payload
322
+ if requirement_satisfied_by?(requirement, activated, existing_spec)
323
+ new_requirements = requirements.dup
324
+ push_state_for_requirements(new_requirements)
325
+ else
326
+ return if attempt_to_swap_possibility
327
+ create_conflict
328
+ debug(depth) { "Unsatisfied by existing spec (#{existing_node.payload})" }
329
+ unwind_for_conflict
330
+ end
331
+ end
332
+
333
+ # Attempts to swp the current {#possibility} with the already-activated
334
+ # spec with the given name
335
+ # @return [Boolean] Whether the possibility was swapped into {#activated}
336
+ def attempt_to_swap_possibility
337
+ swapped = activated.dup
338
+ swapped.vertex_named(name).payload = possibility
339
+ return unless swapped.vertex_named(name).requirements.
340
+ all? { |r| requirement_satisfied_by?(r, swapped, possibility) }
341
+ attempt_to_activate_new_spec
342
+ end
343
+
344
+ # Attempts to activate the current {#possibility} (given that it hasn't
345
+ # already been activated)
346
+ # @return [void]
347
+ def attempt_to_activate_new_spec
348
+ satisfied = begin
349
+ locked_requirement = locked_requirement_named(name)
350
+ requested_spec_satisfied = requirement_satisfied_by?(requirement, activated, possibility)
351
+ locked_spec_satisfied = !locked_requirement ||
352
+ requirement_satisfied_by?(locked_requirement, activated, possibility)
353
+ debug(depth) { 'Unsatisfied by requested spec' } unless requested_spec_satisfied
354
+ debug(depth) { 'Unsatisfied by locked spec' } unless locked_spec_satisfied
355
+ requested_spec_satisfied && locked_spec_satisfied
356
+ end
357
+ if satisfied
358
+ activate_spec
359
+ else
360
+ create_conflict
361
+ unwind_for_conflict
362
+ end
363
+ end
364
+
365
+ # @param [String] requirement_name the spec name to search for
366
+ # @return [Object] the locked spec named `requirement_name`, if one
367
+ # is found on {#base}
368
+ def locked_requirement_named(requirement_name)
369
+ vertex = base.vertex_named(requirement_name)
370
+ vertex && vertex.payload
371
+ end
372
+
373
+ # Add the current {#possibility} to the dependency graph of the current
374
+ # {#state}
375
+ # @return [void]
376
+ def activate_spec
377
+ conflicts.delete(name)
378
+ debug(depth) { 'Activated ' + name + ' at ' + possibility.to_s }
379
+ vertex = activated.vertex_named(name)
380
+ vertex.payload = possibility
381
+ require_nested_dependencies_for(possibility)
382
+ end
383
+
384
+ # Requires the dependencies that the recently activated spec has
385
+ # @param [Object] activated_spec the specification that has just been
386
+ # activated
387
+ # @return [void]
388
+ def require_nested_dependencies_for(activated_spec)
389
+ nested_dependencies = dependencies_for(activated_spec)
390
+ debug(depth) { "Requiring nested dependencies (#{nested_dependencies.map(&:to_s).join(', ')})" }
391
+ nested_dependencies.each { |d| activated.add_child_vertex name_for(d), nil, [name_for(activated_spec)], d }
392
+
393
+ push_state_for_requirements(requirements + nested_dependencies)
394
+ end
395
+
396
+ # Pushes a new {DependencyState} that encapsulates both existing and new
397
+ # requirements
398
+ # @param [Array] new_requirements
399
+ # @return [void]
400
+ def push_state_for_requirements(new_requirements)
401
+ new_requirements = sort_dependencies(new_requirements.uniq, activated, conflicts)
402
+ new_requirement = new_requirements.shift
403
+ states.push DependencyState.new(
404
+ new_requirement ? name_for(new_requirement) : '',
405
+ new_requirements,
406
+ activated.dup,
407
+ new_requirement,
408
+ new_requirement ? search_for(new_requirement) : [],
409
+ depth,
410
+ conflicts.dup
411
+ )
412
+ end
413
+ end
414
+ end
415
+ end