molinillo 0.3.1 → 0.4.0

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.
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: []