hyperstack-org / hyperstack

Hyperstack ALPHA https://hyperstack.org
https://docs.hyperstack.org
MIT License
540 stars 42 forks source link

WhileLoading edge cases #256

Open catmando opened 4 years ago

catmando commented 4 years ago

In more complicated scenarios WhileLoading is failing:

1) If the same component class is mounted in various places on the display. 2) Sometimes the while loading state can be left as waiting for loading, when the loading has completed. (this is hard to reproduce)

Patches look like this:

module Hyperstack
  module Internal
    module Component
      class WhileLoadingWrapper < RescueWrapper
        render do
          if @waiting_on_resources && !quiet?
            RenderingContext.raise_if_not_quiet = false
          else
            @waiting_on_resources = false
            @Child.instance_eval do
              mutate if @__hyperstack_while_loading_waiting_on_resources
              @__hyperstack_while_loading_waiting_on_resources = false
            end
            RenderingContext.raise_if_not_quiet = self # <---- save self as part of the context
          end
          RescueMetaWrapper(children_elements: @ChildrenElements)
        end

        # if you want to apply this patch you will need to clear the existing before mount callback
        send("_before_mount_callbacks").clear

        before_mount do
          @Child.class.rescues RenderingContext::NotQuiet do |e|
           # pass the rescue wrapper back in as part of the exception
            e.while_loading_rescue_wrapper.instance_variable_set(:@waiting_on_resources, true)
            @__hyperstack_while_loading_waiting_on_resources = true
          end
        end

        after_render do
          # after each render clear the raise if not quiet
          RenderingContext.raise_if_not_quiet = false
        end
      end
    end
  end
end

module Hyperstack
  module Internal
    module Component
      class RenderingContext
        class NotQuiet < Exception
          # add the while loading instance to the error so we can use it in the rescue
          attr_reader :while_loading_rescue_wrapper
          def initialize(component, wrapper)
            @while_loading_rescue_wrapper = wrapper
            super("#{component} is waiting on resources - this should never happen")
          end
        end
        class << self
          attr_accessor :waiting_on_resources

          def raise_if_not_quiet?
            @while_loading_rescue_wrapper
          end

          def raise_if_not_quiet=(wrapper)
            @while_loading_rescue_wrapper = wrapper
          end

          def quiet_test(component)
            return unless component.waiting_on_resources && raise_if_not_quiet? #&& component.class != RescueMetaWrapper <- WHY  can't create a spec that this fails without this, but several fail with it.
            # raise the error with the current rescue wrapper context
            raise NotQuiet.new(component, @while_loading_rescue_wrapper)
          end
        end
      end
    end
  end
end
catmando commented 4 years ago

the diff:

--- a/ruby/hyper-component/lib/hyperstack/internal/component/rendering_context.rb
+++ b/ruby/hyper-component/lib/hyperstack/internal/component/rendering_context.rb
@@ -2,21 +2,27 @@ module Hyperstack
   module Internal
     module Component
       class RenderingContext
-        class NotQuiet < Exception; end
+        class NotQuiet < Exception
+          attr_reader :while_loading_rescue_wrapper
+          def initialize(component, wrapper)
+            @while_loading_rescue_wrapper = wrapper
+            super("#{component} is waiting on resources - this should never happen")
+          end
+        end
         class << self
           attr_accessor :waiting_on_resources

           def raise_if_not_quiet?
-            @raise_if_not_quiet
+            @while_loading_rescue_wrapper
           end

-          def raise_if_not_quiet=(x)
-            @raise_if_not_quiet = x
+          def raise_if_not_quiet=(wrapper)
+            @while_loading_rescue_wrapper = wrapper
           end

           def quiet_test(component)
             return unless component.waiting_on_resources && raise_if_not_quiet? #&& component.class != RescueMetaWrapper <- WHY  can't create a spec that this fails without this, but several fail with it.
-            raise NotQuiet.new("#{component} is waiting on resources")
+            raise NotQuiet.new(component, @while_loading_rescue_wrapper)
           end

           def render(name, *args, &block)
diff --git a/ruby/hyper-component/lib/hyperstack/internal/component/while_loading_wrapper.rb b/ruby/hyper-component/lib/hyperstack/internal/component/while_loading_wrapper.rb
index 63e1e305..c44a97b5 100644
--- a/ruby/hyper-component/lib/hyperstack/internal/component/while_loading_wrapper.rb
+++ b/ruby/hyper-component/lib/hyperstack/internal/component/while_loading_wrapper.rb
@@ -11,18 +11,21 @@ module Hyperstack
               mutate if @__hyperstack_while_loading_waiting_on_resources
               @__hyperstack_while_loading_waiting_on_resources = false
             end
-            RenderingContext.raise_if_not_quiet = true
+            RenderingContext.raise_if_not_quiet = self
           end
           RescueMetaWrapper(children_elements: @ChildrenElements)
         end

         before_mount do
-          wrapper = self
-          @Child.class.rescues RenderingContext::NotQuiet do
-            wrapper.instance_variable_set(:@waiting_on_resources, true)
+          @Child.class.rescues RenderingContext::NotQuiet do |e|
+            e.while_loading_rescue_wrapper.instance_variable_set(:@waiting_on_resources, true)
             @__hyperstack_while_loading_waiting_on_resources = true
           end
         end
+
+        after_render do
+          RenderingContext.raise_if_not_quiet = false
+        end
       end
     end
   end