molinillo 0.3.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a110136ef30be826a9e0ccbe5bf217e207db85a4
4
- data.tar.gz: 34d167f2673c8bdf03bda56073a224dc81ffe72e
3
+ metadata.gz: f860bde0bd4aed39aadb78bce1dd854bedf9d34e
4
+ data.tar.gz: eaebd8e7e9070425f2c4c80b65fc562ff8fe210a
5
5
  SHA512:
6
- metadata.gz: ec56826cd3020c0ae87f8f657e0209d446296119c32bb8ce9337b8672bd8a3420048d50b3579e8c1888c08346f75d03549160aa7b24bf61c3c58cee986e01d8b
7
- data.tar.gz: 4e941b7a924b71742108cec967d39702d16739ce4fec2e2b83d892eb7ed83575bf1a2e7a31b208dfabcd8bed9708c5ec1c1af70891622fdac1d18dffa0a133d7
6
+ metadata.gz: 2ede3f0f7aecfdf3aab7be57f3bf23f2c23e2fe440aa8c470bcfd5bdd212941aa4ad52dd5a958886448c64fd515af63ce9342dd343912d8f8213cfbc5ff6ddbf
7
+ data.tar.gz: 82b5e19bb50aa3db372c838fca16a8b61628ca7bd9cfd9aa525d9cd7af4218768bf48e21b9801232959993cd258207e4deeb4356fd6b575d781af9ba2ebd7065
@@ -34,40 +34,34 @@ module Molinillo
34
34
  # A directed edge of a {DependencyGraph}
35
35
  # @attr [Vertex] origin The origin of the directed edge
36
36
  # @attr [Vertex] destination The destination of the directed edge
37
- # @attr [Array] requirements The requirements the directed edge represents
38
- Edge = Struct.new(:origin, :destination, :requirements)
37
+ # @attr [Object] requirement The requirement the directed edge represents
38
+ Edge = Struct.new(:origin, :destination, :requirement)
39
39
 
40
- # @return [{String => Vertex}] vertices that have no {Vertex#predecessors},
41
- # keyed by by {Vertex#name}
42
- attr_reader :root_vertices
43
40
  # @return [{String => Vertex}] the vertices of the dependency graph, keyed
44
41
  # by {Vertex#name}
45
42
  attr_reader :vertices
46
- # @return [Set<Edge>] the edges of the dependency graph
47
- attr_reader :edges
48
43
 
49
44
  def initialize
50
45
  @vertices = {}
51
- @edges = Set.new
52
- @root_vertices = {}
53
46
  end
54
47
 
55
48
  # Initializes a copy of a {DependencyGraph}, ensuring that all {#vertices}
56
- # have the correct {Vertex#graph} set
49
+ # are properly copied.
57
50
  def initialize_copy(other)
58
51
  super
59
- @vertices = other.vertices.reduce({}) do |vertices, (name, vertex)|
60
- vertices.tap do |hash|
61
- hash[name] = vertex.dup.tap { |v| v.graph = self }
52
+ @vertices = {}
53
+ traverse = lambda do |new_v, old_v|
54
+ return if new_v.outgoing_edges.size == old_v.outgoing_edges.size
55
+ old_v.outgoing_edges.each do |edge|
56
+ destination = add_vertex(edge.destination.name, edge.destination.payload)
57
+ add_edge_no_circular(new_v, destination, edge.requirement)
58
+ traverse.call(destination, edge.destination)
62
59
  end
63
60
  end
64
- @root_vertices = Hash[@vertices.select { |n, _v| other.root_vertices[n] }]
65
- @edges = other.edges.map do |edge|
66
- Edge.new(
67
- vertex_named(edge.origin.name),
68
- vertex_named(edge.destination.name),
69
- edge.requirements.dup
70
- )
61
+ other.vertices.each do |name, vertex|
62
+ new_vertex = add_vertex(name, vertex.payload, vertex.root?)
63
+ new_vertex.explicit_requirements.replace(vertex.explicit_requirements)
64
+ traverse.call(new_vertex, vertex)
71
65
  end
72
66
  end
73
67
 
@@ -80,7 +74,12 @@ module Molinillo
80
74
  # by a recursive traversal of each {#root_vertices} and its
81
75
  # {Vertex#successors}
82
76
  def ==(other)
83
- root_vertices == other.root_vertices
77
+ return false unless other
78
+ vertices.each do |name, vertex|
79
+ other_vertex = other.vertex_named(name)
80
+ return false unless other_vertex
81
+ return false unless other_vertex.successors.map(&:name).to_set == vertex.successors.map(&:name).to_set
82
+ end
84
83
  end
85
84
 
86
85
  # @param [String] name
@@ -89,15 +88,13 @@ module Molinillo
89
88
  # @param [Object] requirement the requirement that is requiring the child
90
89
  # @return [void]
91
90
  def add_child_vertex(name, payload, parent_names, requirement)
92
- is_root = parent_names.include?(nil)
93
- parent_nodes = parent_names.compact.map { |n| vertex_named(n) }
94
- vertex = vertex_named(name) || if is_root
95
- add_root_vertex(name, payload)
96
- else
97
- add_vertex(name, payload)
98
- end
99
- vertex.payload ||= payload
100
- parent_nodes.each do |parent_node|
91
+ vertex = add_vertex(name, payload)
92
+ parent_names.each do |parent_name|
93
+ unless parent_name
94
+ vertex.root = true
95
+ next
96
+ end
97
+ parent_node = vertex_named(parent_name)
101
98
  add_edge(parent_node, vertex, requirement)
102
99
  end
103
100
  vertex
@@ -106,16 +103,11 @@ module Molinillo
106
103
  # @param [String] name
107
104
  # @param [Object] payload
108
105
  # @return [Vertex] the vertex that was added to `self`
109
- def add_vertex(name, payload)
110
- vertex = vertices[name] ||= Vertex.new(self, name, payload)
111
- vertex.tap { |v| v.payload = payload }
112
- end
113
-
114
- # @param [String] name
115
- # @param [Object] payload
116
- # @return [Vertex] the vertex that was added to `self`
117
- def add_root_vertex(name, payload)
118
- add_vertex(name, payload).tap { |v| root_vertices[name] = v }
106
+ def add_vertex(name, payload, root = false)
107
+ vertex = vertices[name] ||= Vertex.new(name, payload)
108
+ vertex.payload ||= payload
109
+ vertex.root ||= root
110
+ vertex
119
111
  end
120
112
 
121
113
  # Detaches the {#vertex_named} `name` {Vertex} from the graph, recursively
@@ -123,12 +115,12 @@ module Molinillo
123
115
  # @param [String] name
124
116
  # @return [void]
125
117
  def detach_vertex_named(name)
126
- vertex = vertex_named(name)
127
- return unless vertex
128
- successors = vertex.successors
129
- vertices.delete(name)
130
- edges.reject! { |e| e.origin == vertex || e.destination == vertex }
131
- successors.each { |v| detach_vertex_named(v.name) unless root_vertices[v.name] || v.predecessors.any? }
118
+ return unless vertex = vertices.delete(name)
119
+ vertex.outgoing_edges.each do |e|
120
+ v = e.destination
121
+ v.incoming_edges.delete(e)
122
+ detach_vertex_named(v.name) unless v.root? || v.predecessors.any?
123
+ end
132
124
  end
133
125
 
134
126
  # @param [String] name
@@ -140,7 +132,8 @@ module Molinillo
140
132
  # @param [String] name
141
133
  # @return [Vertex,nil] the root vertex with the given name
142
134
  def root_vertex_named(name)
143
- root_vertices[name]
135
+ vertex = vertex_named(name)
136
+ vertex if vertex && vertex.root?
144
137
  end
145
138
 
146
139
  # Adds a new {Edge} to the dependency graph
@@ -149,18 +142,24 @@ module Molinillo
149
142
  # @param [Object] requirement the requirement that this edge represents
150
143
  # @return [Edge] the added edge
151
144
  def add_edge(origin, destination, requirement)
152
- if origin == destination || destination.path_to?(origin)
145
+ if destination.path_to?(origin)
153
146
  raise CircularDependencyError.new([origin, destination])
154
147
  end
155
- Edge.new(origin, destination, [requirement]).tap { |e| edges << e }
148
+ add_edge_no_circular(origin, destination, requirement)
149
+ end
150
+
151
+ private
152
+
153
+ def add_edge_no_circular(origin, destination, requirement)
154
+ edge = Edge.new(origin, destination, requirement)
155
+ origin.outgoing_edges << edge
156
+ destination.incoming_edges << edge
157
+ edge
156
158
  end
157
159
 
158
160
  # A vertex in a {DependencyGraph} that encapsulates a {#name} and a
159
161
  # {#payload}
160
162
  class Vertex
161
- # @return [DependencyGraph] the graph this vertex is a node of
162
- attr_accessor :graph
163
-
164
163
  # @return [String] the name of the vertex
165
164
  attr_accessor :name
166
165
 
@@ -171,50 +170,62 @@ module Molinillo
171
170
  # this vertex
172
171
  attr_reader :explicit_requirements
173
172
 
174
- # @param [DependencyGraph] graph see {#graph}
173
+ # @return [Boolean] whether the vertex is considered a root vertex
174
+ attr_accessor :root
175
+ alias_method :root?, :root
176
+
175
177
  # @param [String] name see {#name}
176
178
  # @param [Object] payload see {#payload}
177
- def initialize(graph, name, payload)
178
- @graph = graph
179
+ def initialize(name, payload)
179
180
  @name = name
180
181
  @payload = payload
181
182
  @explicit_requirements = []
183
+ @outgoing_edges = []
184
+ @incoming_edges = []
182
185
  end
183
186
 
184
187
  # @return [Array<Object>] all of the requirements that required
185
188
  # this vertex
186
189
  def requirements
187
- incoming_edges.map(&:requirements).flatten + explicit_requirements
190
+ incoming_edges.map(&:requirement) + explicit_requirements
188
191
  end
189
192
 
190
193
  # @return [Array<Edge>] the edges of {#graph} that have `self` as their
191
194
  # {Edge#origin}
192
- def outgoing_edges
193
- graph.edges.select { |e| e.origin.shallow_eql?(self) }
194
- end
195
+ attr_accessor :outgoing_edges
195
196
 
196
197
  # @return [Array<Edge>] the edges of {#graph} that have `self` as their
197
198
  # {Edge#destination}
198
- def incoming_edges
199
- graph.edges.select { |e| e.destination.shallow_eql?(self) }
200
- end
199
+ attr_accessor :incoming_edges
201
200
 
202
- # @return [Set<Vertex>] the vertices of {#graph} that have an edge with
201
+ # @return [Array<Vertex>] the vertices of {#graph} that have an edge with
203
202
  # `self` as their {Edge#destination}
204
203
  def predecessors
205
- incoming_edges.map(&:origin).to_set
204
+ incoming_edges.map(&:origin)
205
+ end
206
+
207
+ # @return [Array<Vertex>] the vertices of {#graph} where `self` is a
208
+ # {#descendent?}
209
+ def recursive_predecessors
210
+ vertices = predecessors
211
+ vertices += vertices.map(&:recursive_predecessors).flatten(1)
212
+ vertices.uniq!
213
+ vertices
206
214
  end
207
215
 
208
- # @return [Set<Vertex>] the vertices of {#graph} that have an edge with
216
+ # @return [Array<Vertex>] the vertices of {#graph} that have an edge with
209
217
  # `self` as their {Edge#origin}
210
218
  def successors
211
- outgoing_edges.map(&:destination).to_set
219
+ outgoing_edges.map(&:destination)
212
220
  end
213
221
 
214
- # @return [Set<Vertex>] the vertices of {#graph} where `self` is an
222
+ # @return [Array<Vertex>] the vertices of {#graph} where `self` is an
215
223
  # {#ancestor?}
216
224
  def recursive_successors
217
- successors + successors.map(&:recursive_successors).reduce(Set.new, &:+)
225
+ vertices = successors
226
+ vertices += vertices.map(&:recursive_successors).flatten(1)
227
+ vertices.uniq!
228
+ vertices
218
229
  end
219
230
 
220
231
  # @return [String] a string suitable for debugging
@@ -226,7 +237,7 @@ module Molinillo
226
237
  # by a recursive traversal of each {Vertex#successors}
227
238
  def ==(other)
228
239
  shallow_eql?(other) &&
229
- successors == other.successors
240
+ successors.to_set == other.successors.to_set
230
241
  end
231
242
 
232
243
  # @return [Boolean] whether the two vertices are equal, determined
@@ -248,7 +259,7 @@ module Molinillo
248
259
  # dependency graph?
249
260
  # @return true iff there is a path following edges within this {#graph}
250
261
  def path_to?(other)
251
- successors.include?(other) || successors.any? { |v| v.path_to?(other) }
262
+ equal?(other) || successors.any? { |v| v.path_to?(other) }
252
263
  end
253
264
 
254
265
  alias_method :descendent?, :path_to?
@@ -257,7 +268,7 @@ module Molinillo
257
268
  # dependency graph?
258
269
  # @return true iff there is a path following edges within this {#graph}
259
270
  def ancestor?(other)
260
- predecessors.include?(other) || predecessors.any? { |v| v.ancestor?(other) }
271
+ other.path_to?(self)
261
272
  end
262
273
 
263
274
  alias_method :is_reachable_from?, :ancestor?
@@ -1,3 +1,3 @@
1
1
  module Molinillo
2
- VERSION = '0.3.1'
2
+ VERSION = '0.4.0'
3
3
  end
@@ -166,7 +166,7 @@ module Molinillo
166
166
  # @return [DependencyState] the initial state for the resolution
167
167
  def initial_state
168
168
  graph = DependencyGraph.new.tap do |dg|
169
- original_requested.each { |r| dg.add_root_vertex(name_for(r), nil).tap { |v| v.explicit_requirements << r } }
169
+ original_requested.each { |r| dg.add_vertex(name_for(r), nil, true).tap { |v| v.explicit_requirements << r } }
170
170
  end
171
171
 
172
172
  requirements = sort_dependencies(original_requested, graph, {})
@@ -252,7 +252,7 @@ module Molinillo
252
252
  name_for_explicit_dependency_source => vertex.explicit_requirements,
253
253
  name_for_locking_dependency_source => Array(locked_requirement_named(name)),
254
254
  }
255
- vertex.incoming_edges.each { |edge| (requirements[edge.origin.payload] ||= []).unshift(*edge.requirements) }
255
+ vertex.incoming_edges.each { |edge| (requirements[edge.origin.payload] ||= []).unshift(edge.requirement) }
256
256
  conflicts[name] = Conflict.new(
257
257
  requirement,
258
258
  Hash[requirements.select { |_, r| !r.empty? }],
@@ -326,7 +326,7 @@ module Molinillo
326
326
  existing_spec = existing_node.payload
327
327
  if requirement_satisfied_by?(requirement, activated, existing_spec)
328
328
  new_requirements = requirements.dup
329
- push_state_for_requirements(new_requirements)
329
+ push_state_for_requirements(new_requirements, false)
330
330
  else
331
331
  return if attempt_to_swap_possibility
332
332
  create_conflict
@@ -393,17 +393,17 @@ module Molinillo
393
393
  def require_nested_dependencies_for(activated_spec)
394
394
  nested_dependencies = dependencies_for(activated_spec)
395
395
  debug(depth) { "Requiring nested dependencies (#{nested_dependencies.map(&:to_s).join(', ')})" }
396
- nested_dependencies.each { |d| activated.add_child_vertex name_for(d), nil, [name_for(activated_spec)], d }
396
+ nested_dependencies.each { |d| activated.add_child_vertex(name_for(d), nil, [name_for(activated_spec)], d) }
397
397
 
398
- push_state_for_requirements(requirements + nested_dependencies)
398
+ push_state_for_requirements(requirements + nested_dependencies, nested_dependencies.size > 0)
399
399
  end
400
400
 
401
401
  # Pushes a new {DependencyState} that encapsulates both existing and new
402
402
  # requirements
403
403
  # @param [Array] new_requirements
404
404
  # @return [void]
405
- def push_state_for_requirements(new_requirements, new_activated = activated.dup)
406
- new_requirements = sort_dependencies(new_requirements.uniq, new_activated, conflicts)
405
+ def push_state_for_requirements(new_requirements, requires_sort = true, new_activated = activated.dup)
406
+ new_requirements = sort_dependencies(new_requirements.uniq, new_activated, conflicts) if requires_sort
407
407
  new_requirement = new_requirements.shift
408
408
  new_name = new_requirement ? name_for(new_requirement) : ''
409
409
  possibilities = new_requirement ? search_for(new_requirement) : []
@@ -424,7 +424,7 @@ module Molinillo
424
424
  def handle_missing_or_push_dependency_state(state)
425
425
  if state.requirement && state.possibilities.empty? && allow_missing?(state.requirement)
426
426
  state.activated.detach_vertex_named(state.name)
427
- push_state_for_requirements(state.requirements, state.activated)
427
+ push_state_for_requirements(state.requirements.dup, false, state.activated)
428
428
  else
429
429
  states.push state
430
430
  end
@@ -1,13 +1,12 @@
1
1
  require File.expand_path('../spec_helper', __FILE__)
2
- require 'set'
3
2
 
4
3
  module Molinillo
5
4
  describe DependencyGraph do
6
5
  describe 'in general' do
7
6
  before do
8
7
  @graph = DependencyGraph.new
9
- @root = @graph.add_root_vertex('Root', 'Root')
10
- @root2 = @graph.add_root_vertex('Root2', 'Root2')
8
+ @root = @graph.add_vertex('Root', 'Root', true)
9
+ @root2 = @graph.add_vertex('Root2', 'Root2', true)
11
10
  @child = @graph.add_child_vertex('Child', 'Child', %w(Root), 'Child')
12
11
  end
13
12
 
@@ -40,7 +39,7 @@ module Molinillo
40
39
  end
41
40
 
42
41
  it 'detaches a root vertex without successors' do
43
- root = @graph.add_root_vertex('root', 'root')
42
+ root = @graph.add_vertex('root', 'root', true)
44
43
  @graph.detach_vertex_named(root.name)
45
44
  @graph.vertex_named(root.name).
46
45
  should.equal nil
@@ -49,7 +48,7 @@ module Molinillo
49
48
  end
50
49
 
51
50
  it 'detaches a root vertex with successors' do
52
- root = @graph.add_root_vertex('root', 'root')
51
+ root = @graph.add_vertex('root', 'root', true)
53
52
  child = @graph.add_child_vertex('child', 'child', %w(root), 'child')
54
53
  @graph.detach_vertex_named(root.name)
55
54
  @graph.vertex_named(root.name).
@@ -61,8 +60,8 @@ module Molinillo
61
60
  end
62
61
 
63
62
  it 'detaches a root vertex with successors with other parents' do
64
- root = @graph.add_root_vertex('root', 'root')
65
- root2 = @graph.add_root_vertex('root2', 'root2')
63
+ root = @graph.add_vertex('root', 'root', true)
64
+ root2 = @graph.add_vertex('root2', 'root2', true)
66
65
  child = @graph.add_child_vertex('child', 'child', %w(root root2), 'child')
67
66
  @graph.detach_vertex_named(root.name)
68
67
  @graph.vertex_named(root.name).
@@ -70,7 +69,7 @@ module Molinillo
70
69
  @graph.vertex_named(child.name).
71
70
  should.equal child
72
71
  child.predecessors.
73
- should.equal Set[root2]
72
+ should.equal [root2]
74
73
  @graph.vertices.count.
75
74
  should.equal 2
76
75
  end
@@ -32,7 +32,7 @@ module Molinillo
32
32
  graph.add_edge(parent, v, dependency)
33
33
  end
34
34
  else
35
- graph.add_root_vertex(name, dependency)
35
+ graph.add_vertex(name, dependency, true)
36
36
  end
37
37
  hash['dependencies'].each do |dep|
38
38
  add_dependencies_to_graph.call(graph, node, dep)
metadata CHANGED
@@ -1,41 +1,41 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: molinillo
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel E. Giddins
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-07-24 00:00:00.000000000 Z
11
+ date: 2015-07-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ~>
18
18
  - !ruby/object:Gem::Version
19
19
  version: '1.5'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ~>
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.5'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ">="
31
+ - - '>='
32
32
  - !ruby/object:Gem::Version
33
33
  version: '0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ">="
38
+ - - '>='
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  description:
@@ -74,12 +74,12 @@ require_paths:
74
74
  - lib
75
75
  required_ruby_version: !ruby/object:Gem::Requirement
76
76
  requirements:
77
- - - ">="
77
+ - - '>='
78
78
  - !ruby/object:Gem::Version
79
79
  version: '0'
80
80
  required_rubygems_version: !ruby/object:Gem::Requirement
81
81
  requirements:
82
- - - ">="
82
+ - - '>='
83
83
  - !ruby/object:Gem::Version
84
84
  version: '0'
85
85
  requirements: []