@@ -19,7 +19,7 @@ import Data.Variant (match)
1919import Yoga.Om as Om
2020import Data.Time.Duration (Milliseconds(..))
2121import Effect.Aff.Retry (RetryStatus(..), limitRetries, constantDelay)
22−import Yoga.Om.Layer (OmLayer, Scope, makeLayer, makeScopedLayer, bracketLayer, fresh, combineRequirements, runLayer, runScoped, runScopedWith, withScoped, provide, recovering, repeating)
22+import Yoga.Om.Layer (OmLayer, Scope, makeLayer, makeScopedLayer, bracketLayer, fresh, combineRequirements, runLayer, runScoped, runScopedWith, withScoped, provide, recovering, repeating, wireLayers)
2323
2424-- Example types
2525type Config = { port :: Int, host :: String }
@@ -666,3 +666,99 @@ main = launchAff_ $ runSpec [ consoleReporter ] do
666666 { exception: \_ -> pure { count: 0 } }
667667 (runLayer {} repeated)
668668 result.count `shouldEqual` 4
669+
670+ describe "wireLayers" do
671+
672+ it "wires a simple linear chain (A -> B -> C)" do
673+ let
674+ a_base :: OmLayer (scope :: Scope) () { base :: String }
675+ a_base = makeLayer (pure { base: "foundation" })
676+
677+ b_mid :: OmLayer (scope :: Scope, base :: String) () { mid :: String }
678+ b_mid = makeLayer do
679+ { base } <- Om.ask
680+ pure { mid: base <> "-middle" }
681+
682+ c_top :: OmLayer (scope :: Scope, mid :: String) () { top :: String }
683+ c_top = makeLayer do
684+ { mid } <- Om.ask
685+ pure { top: mid <> "-top" }
686+
687+ app = wireLayers { a_base, b_mid, c_top }
688+
689+ result <- liftAff $ runScoped app
690+ result.base `shouldEqual` "foundation"
691+ result.mid `shouldEqual` "foundation-middle"
692+ result.top `shouldEqual` "foundation-middle-top"
693+
694+ it "wires independent layers with no inter-dependencies" do
695+ let
696+ a_x :: OmLayer (scope :: Scope) () { x :: Int }
697+ a_x = makeLayer (pure { x: 1 })
698+
699+ b_y :: OmLayer (scope :: Scope) () { y :: Int }
700+ b_y = makeLayer (pure { y: 2 })
701+
702+ c_z :: OmLayer (scope :: Scope) () { z :: Int }
703+ c_z = makeLayer (pure { z: 3 })
704+
705+ app = wireLayers { a_x, b_y, c_z }
706+
707+ result <- liftAff $ runScoped app
708+ result.x `shouldEqual` 1
709+ result.y `shouldEqual` 2
710+ result.z `shouldEqual` 3
711+
712+ it "wires a diamond dependency" do
713+ let
714+ a_db :: OmLayer (scope :: Scope) () { db :: String }
715+ a_db = makeLayer (pure { db: "postgres" })
716+
717+ b_cache :: OmLayer (scope :: Scope, db :: String) () { cache :: String }
718+ b_cache = makeLayer do
719+ { db } <- Om.ask
720+ pure { cache: db <> "-cache" }
721+
722+ b_repo :: OmLayer (scope :: Scope, db :: String) () { repo :: String }
723+ b_repo = makeLayer do
724+ { db } <- Om.ask
725+ pure { repo: db <> "-repo" }
726+
727+ d_app :: OmLayer (scope :: Scope, cache :: String, repo :: String) () { app :: String }
728+ d_app = makeLayer do
729+ { cache, repo } <- Om.ask
730+ pure { app: cache <> "+" <> repo }
731+
732+ app = wireLayers { a_db, b_cache, b_repo, d_app }
733+
734+ result <- liftAff $ runScoped app
735+ result.db `shouldEqual` "postgres"
736+ result.cache `shouldEqual` "postgres-cache"
737+ result.repo `shouldEqual` "postgres-repo"
738+ result.app `shouldEqual` "postgres-cache+postgres-repo"
739+
740+ it "memoizes shared dependencies within wireLayers" do
741+ counter <- liftEffect $ Ref.new 0
742+ let
743+ a_db :: OmLayer (scope :: Scope) () { db :: String }
744+ a_db = makeLayer do
745+ liftEffect $ Ref.modify_ (_ + 1) counter
746+ pure { db: "connected" }
747+
748+ b_cache :: OmLayer (scope :: Scope, db :: String) () { cache :: String }
749+ b_cache = makeLayer do
750+ { db } <- Om.ask
751+ pure { cache: db <> "-cache" }
752+
753+ b_repo :: OmLayer (scope :: Scope, db :: String) () { repo :: String }
754+ b_repo = makeLayer do
755+ { db } <- Om.ask
756+ pure { repo: db <> "-repo" }
757+
758+ app = wireLayers { a_db, b_cache, b_repo }
759+
760+ result <- liftAff $ runScoped app
761+ result.cache `shouldEqual` "connected-cache"
762+ result.repo `shouldEqual` "connected-repo"
763+ count <- liftEffect $ Ref.read counter
764+ count `shouldEqual` 1