lib/data-import/dependency_resolver.rb
module DataImport
class DependencyResolver
def initialize(plan, strategy = LoopStrategy)
@plan = plan
@strategy = strategy
end
def resolve(options = {})
resolved_dependencies = @strategy.new(dependency_graph).call(options)
resolved_dependencies = resolved_dependencies.map {|definition| @plan.definition(definition) }
ExecutionPlan.new(resolved_dependencies)
end
def dependency_graph
Hash[@plan.definitions.map do |definition|
[definition.name, definition.dependencies]
end]
end
private :dependency_graph
class LoopStrategy
def initialize(graph)
@graph = graph
end
def call(options = {})
definitions_to_execute = definitions_for_execution(options[:run_only])
resolved_dependencies = []
while resolved_dependencies.size < definitions_to_execute.size
definitions_to_execute.each do |name|
next if resolved_dependencies.include?(name)
if (direct_dependencies(name) - resolved_dependencies).empty?
resolved_dependencies << name
end
end
end
resolved_dependencies
end
def definitions_for_execution(run_only = nil)
(run_only || @graph.keys).map do |name|
[name] + recursive_dependencies(name)
end.flatten.uniq
end
private :definitions_for_execution
def direct_dependencies(name)
if @graph.has_key?(name)
@graph[name]
else
raise MissingDefinitionError.new(name)
end
end
private :direct_dependencies
def recursive_dependencies(name, visited_definitions = [])
direct_dependencies(name).map do |dep|
raise CircularDependencyError.new(name, dep) if visited_definitions.include?(dep)
recursive_dependencies(dep, visited_definitions + [name])
end.flatten + direct_dependencies(name)
end
private :recursive_dependencies
end
end
end