diff options
| author | Paul Buetow <paul@buetow.org> | 2025-02-17 00:12:20 +0200 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2025-02-17 00:12:20 +0200 |
| commit | ed8cf0c0c285abab24479b7af3bc73f2c7340822 (patch) | |
| tree | 289b123d9da62d90ff8eda8fec5ae83aa5e2363f | |
| parent | d64135e44904500d65b029fb31216a52d507a7be (diff) | |
can detect dependency loop
| -rw-r--r-- | lib/dslkeywords/resource.rb | 19 | ||||
| -rw-r--r-- | test/lib/dslkeywords/dependency_test.rb | 17 |
2 files changed, 32 insertions, 4 deletions
diff --git a/lib/dslkeywords/resource.rb b/lib/dslkeywords/resource.rb index 1634061..2320077 100644 --- a/lib/dslkeywords/resource.rb +++ b/lib/dslkeywords/resource.rb @@ -40,14 +40,25 @@ module RCM module DependencyEvaluator attr_reader :evaluated + class DependencyLoop < StandardError; end + class UnresolvedDependency < StandardError; end + def evaluate! return false if @evaluated + raise DependencyLoop, "Dependency loop detected for #{id}" if @loop_detection + + @loop_detection = true @depends_on = {} if @depends_on.nil? - @depends_on.each_key do |id| - dependency = Resource.find(id) - end + # Try to evaluate all dependencies recursively. + @depends_on.each_key.map { Resource.find(_1) }.each(&:evaluate!) + + # Raise an exception when there are still unresolved dependencies. + unresolved = @depends_on.each_key.map { Resource.find(_1) }.reject(&:evaluated) + raise UnresolvedDependency, "Unresolved dependencies: #{unresolved.map(&:id)}" if unresolved.count.positive? + + @loop_detection = false @evaluated = true end end @@ -61,7 +72,7 @@ module RCM def self.find(id) klass = Object.const_get("RCM::#{id.split('(').first.capitalize}") - resource = ObjectSpace.each_object(klass).find { |obj| obj.id == id } + resource = ObjectSpace.each_object(klass).find { _1.id == id } raise NoSuchResourceObject, "Unable to find resource #{id}" if resource.nil? resource diff --git a/test/lib/dslkeywords/dependency_test.rb b/test/lib/dslkeywords/dependency_test.rb index e9bc205..f1280eb 100644 --- a/test/lib/dslkeywords/dependency_test.rb +++ b/test/lib/dslkeywords/dependency_test.rb @@ -48,4 +48,21 @@ class RCMDependencyTest < Minitest::Test end end end + + def test_dependency_loop + assert_raises(RCM::DependencyEvaluator::DependencyLoop) do + configure_from_scratch do + notify('loop') { depends_on notify('loop') } + end + end + end + + def test_dependency_loop_indirect + assert_raises(RCM::DependencyEvaluator::DependencyLoop) do + configure_from_scratch do + notify('loop') { depends_on notify('pool') } + notify('pool') { depends_on notify('loop') } + end + end + end end |
