class ComputedModel::DepGraph
A dependency graph representation used within ComputedModel::Model
. Usually you don't need to use this class directly.
@api private
@example
graph = ComputedModel::DepGraph.new graph << ComputedModel::DepGraph::Node.new(:computed, :foo, { bar: [] }) graph << ComputedModel::DepGraph::Node.new(:loaded, :bar, {}) plan = graph.tsort.plan([:foo])
Public Class Methods
merge(graphs)
click to toggle source
@param graphs [Array<ComputedModel::DepGraph>] @return [ComputedModel::DepGraph]
# File lib/computed_model/dep_graph.rb, line 104 def self.merge(graphs) new_graph = ComputedModel::DepGraph.new nodes_by_name = graphs.flat_map(&:nodes).group_by(&:name) nodes_by_name.each do |name, nodes| type = nodes.first.type raise ArgumentError, "Field #{name} has multiple different types" unless nodes.all? { |node| node.type == type } new_graph << ComputedModel::DepGraph::Node.new(type, name, nodes.map { |n| n.edges.transform_values { |e| e.spec } }) end new_graph end
new()
click to toggle source
# File lib/computed_model/dep_graph.rb, line 17 def initialize @nodes = {} end
Public Instance Methods
<<(node)
click to toggle source
Adds the new node.
@param node [Node] @return [void] @raise [ArgumentError] when the node already exists
@example
graph = ComputedModel::DepGraph.new graph << ComputedModel::DepGraph::Node.new(:computed, :foo, {})
# File lib/computed_model/dep_graph.rb, line 42 def <<(node) raise ArgumentError, "Field already declared: #{node.name}" if @nodes.key?(node.name) @nodes[node.name] = node end
[](name)
click to toggle source
Returns the node with the specified name.
@param name [Symbol] the name of the node @return [Node, nil]
@example
graph = ComputedModel::DepGraph.new graph[:foo]
# File lib/computed_model/dep_graph.rb, line 29 def [](name) @nodes[name] end
nodes()
click to toggle source
@return [Array<ComputedModel::DepGraph::Node>]
# File lib/computed_model/dep_graph.rb, line 49 def nodes @nodes.values end
tsort()
click to toggle source
Preprocess the graph by topological sorting. This is a necessary step for loader planning.
@return [ComputedModel::DepGraph::Sorted]
@example
graph = ComputedModel::DepGraph.new graph << ComputedModel::DepGraph::Node.new(:computed, :foo, { bar: [] }) graph << ComputedModel::DepGraph::Node.new(:loaded, :bar, {}) sorted = graph.tsort
# File lib/computed_model/dep_graph.rb, line 62 def tsort load_order = [] visiting = Set[] visited = Set[] @nodes.each_value do |node| next unless node.type == :primary load_order << node.name visiting.add node.name visited.add node.name end raise ArgumentError, 'No primary loader defined' if load_order.empty? raise "Multiple primary fields: #{load_order.inspect}" if load_order.size > 1 @nodes.each_value do |node| tsort_dfs(node.name, load_order, visiting, visited) end nodes_in_order = load_order.reverse.map { |name| @nodes[name] } ComputedModel::DepGraph::Sorted.new(self, nodes_in_order) end
Private Instance Methods
tsort_dfs(name, load_order, visiting, visited)
click to toggle source
# File lib/computed_model/dep_graph.rb, line 86 def tsort_dfs(name, load_order, visiting, visited) return if visited.include?(name) raise ComputedModel::CyclicDependency, "Cyclic dependency for ##{name}" if visiting.include?(name) raise "No dependency info for ##{name}" unless @nodes.key?(name) visiting.add(name) @nodes[name].edges.each_value do |edge| tsort_dfs(edge.name, load_order, visiting, visited) end load_order << name visiting.delete(name) visited.add(name) end