on master help sign in
Configs become ByteStrings, unify ConfigsT monads, further bump reflex-platform to fix hydration.
commitf7b3c430ed8becd2b833c6a620c1d3cfc9fb3423 authorCale Gibbard <cgibbard@gmail.com> dateThu 13 Jun 2019, 22:20 (7 years ago) parent1764169
27 files changed+106−346
ChangeLog.md+4−1 dep/reflex-platform/github.json+2−2 lib/backend/obelisk-backend.cabal+1−1 lib/backend/src/Obelisk/Backend.hs+15−15 lib/executable-config/backend/obelisk-executable-config-backend.cabal−20 lib/executable-config/backend/src/Obelisk/ExecutableConfig/Backend.hs−77 lib/executable-config/default.nix−2 lib/executable-config/frontend/obelisk-executable-config-frontend.cabal−21 lib/executable-config/frontend/src/Obelisk/ExecutableConfig/Frontend.hs−115 lib/executable-config/inject/obelisk-executable-config-inject.cabal+2 lib/executable-config/inject/src/Obelisk/ExecutableConfig/Inject.hs+8−5 lib/executable-config/lookup/default.nix+3−1 lib/executable-config/lookup/obelisk-executable-config-lookup.cabal+13−7 lib/executable-config/lookup/src-android/Obelisk/ExecutableConfig/Lookup.hs+4−4 lib/executable-config/lookup/src-directory/Obelisk/ExecutableConfig/Internal/ConfigDirectory.hs−28 lib/executable-config/lookup/src-ghcjs/Obelisk/ExecutableConfig/Lookup.hs+8−2 lib/executable-config/lookup/src-ios/Obelisk/ExecutableConfig/Lookup.hs+1−1 lib/executable-config/lookup/src/Obelisk/ExecutableConfig/Lookup.hs−9 lib/frontend/obelisk-frontend.cabal−1 lib/frontend/src/Obelisk/Frontend.hs+12−12 lib/frontend/src/Obelisk/Frontend/Cookie.hs+3−3 lib/route/obelisk-route.cabal+1−1 lib/route/src/Obelisk/Route/Frontend.hs+5−4 lib/run/obelisk-run.cabal+1−1 lib/run/src/Obelisk/Run.hs+14−11 skeleton/common/src/Common/Api.hs+1−1 skeleton/frontend/src/Frontend.hs+8−1
ChangeLog.md+4−1
@@ -3,7 +3,10 @@
33This project's release branch is `master`. This log is written from the perspective of the release branch: when changes hit `master`, they are considered released, and the date should reflect that release.
44
55## Unreleased
6−
6+* Configs become ByteStrings.
7+* FrontendConfigsT and BackendConfigsT have been unified into ConfigsT and changes are made available via getConfig/getConfigs
8+ * The frontend will still only have access to configs that are placed in config/frontend and config/common, while the backend has access to the entire contents of the config directory.
9+ * You will need to use paths like "backend/clientSessionKey" to access a backend key for example, as there are no longer separate functions like getBackendConfig/getFrontendConfig.
710
811## v0.1.1.0 - 2019-05-17
912
dep/reflex-platform/github.json+2−2
@@ -2,6 +2,6 @@
22 "owner": "reflex-frp",
33 "repo": "reflex-platform",
44 "branch": "cg-bump-reflex-dom",
5− "rev": "8f73981efe443807f081adeed6101de47474fd65",
6− "sha256": "1qjj0afmgbgwichya8fxkp8rb37i8smxa5j2dy9kypbyncavcm6w"
5+ "rev": "7d03c728784db8cc1e39b08a81e49ed39fc96063",
6+ "sha256": "08s3x6nqqc0y75wmpgp8zdfpk2qf3lrcpg01qzz41sj2217hmd45"
77}
lib/backend/obelisk-backend.cabal+1−1
@@ -16,7 +16,7 @@ library
1616 lens,
1717 mtl,
1818 obelisk-asset-serve-snap,
19− obelisk-executable-config-backend,
19+ obelisk-executable-config-inject,
2020 obelisk-executable-config-lookup,
2121 obelisk-frontend,
2222 obelisk-route,
lib/backend/src/Obelisk/Backend.hs+15−15
@@ -41,8 +41,8 @@ import Data.Text (Text)
4141import qualified Data.Text as T
4242import Data.Text.Encoding
4343import Obelisk.Asset.Serve.Snap (serveAsset)
44−import Obelisk.ExecutableConfig.Backend
45−import Obelisk.ExecutableConfig.Lookup
44+import Obelisk.Configs
45+import qualified Obelisk.ExecutableConfig.Lookup as Lookup
4646import Obelisk.Frontend
4747import Obelisk.Route
4848import Obelisk.Snap.Extras (doNotCache, serveFileIfExistsAs)
@@ -55,14 +55,14 @@ import System.IO (BufferMode (..), hSetBuffering, stderr, stdout)
5555
5656data Backend backendRoute frontendRoute = Backend
5757 { _backend_routeEncoder :: Encoder (Either Text) Identity (R (Sum backendRoute (ObeliskRoute frontendRoute))) PageName
58− , _backend_run :: ((R backendRoute -> BackendConfigsT Snap ()) -> BackendConfigsT IO ()) -> BackendConfigsT IO ()
58+ , _backend_run :: ((R backendRoute -> ConfigsT Snap ()) -> ConfigsT IO ()) -> ConfigsT IO ()
5959 }
6060
61−-- | All the configs separated by which context they are accessible in. (Common
62−-- configs are present in both.)
61+-- | All the configs separated by which context they are accessible in. Common
62+-- and frontend configs are present in both.
6363data ComponentConfigs = ComponentConfigs
64− { _componentConfigs_backend :: Map Text Text
65− , _componentConfigs_frontend :: Map Text Text
64+ { _componentConfigs_backend :: Map Text ByteString
65+ , _componentConfigs_frontend :: Map Text ByteString
6666 }
6767
6868-- | The static assets provided must contain a compiled GHCJS app that corresponds exactly to the Frontend provided
@@ -80,7 +80,7 @@ serveDefaultObeliskApp
8080 -> ([Text]
8181 -> m ())
8282 -> Frontend (R appRoute)
83− -> Map Text Text
83+ -> Map Text ByteString
8484 -> R (ObeliskRoute appRoute)
8585 -> m ()
8686serveDefaultObeliskApp urlEnc serveStaticAsset frontend configs = serveObeliskApp urlEnc serveStaticAsset frontendApp configs
@@ -139,7 +139,7 @@ serveObeliskApp
139139 => (R appRoute -> Text)
140140 -> ([Text] -> m ())
141141 -> GhcjsApp (R appRoute)
142− -> Map Text Text
142+ -> Map Text ByteString
143143 -> R (ObeliskRoute appRoute)
144144 -> m ()
145145serveObeliskApp urlEnc serveStaticAsset frontendApp config = \case
@@ -174,7 +174,7 @@ serveGhcjsApp
174174 :: (MonadSnap m, HasCookies m)
175175 => (R appRouteComponent -> Text)
176176 -> GhcjsApp (R appRouteComponent)
177− -> Map Text Text
177+ -> Map Text ByteString
178178 -> R (GhcjsAppRoute appRouteComponent)
179179 -> m ()
180180serveGhcjsApp urlEnc app config = \case
@@ -188,13 +188,13 @@ runBackend backend frontend = case checkEncoder $ _backend_routeEncoder backend
188188 Left e -> fail $ "backend error:\n" <> T.unpack e
189189 Right validFullEncoder -> do
190190 configs <- getComponentConfigs
191− runBackendConfigsT (_componentConfigs_backend configs) $
191+ runConfigsT (_componentConfigs_backend configs) $
192192 _backend_run backend $ \serveRoute -> do
193193 runSnapWithCommandLineArgs $ do
194194 getRouteWith validFullEncoder >>= \case
195195 Identity r -> case r of
196196 InL backendRoute :=> Identity a ->
197− runBackendConfigsT (_componentConfigs_backend configs) $
197+ runConfigsT (_componentConfigs_backend configs) $
198198 serveRoute $ backendRoute :/ a
199199 InR obeliskRoute :=> Identity a ->
200200 serveDefaultObeliskApp (mkRouteToUrl validFullEncoder) (serveStaticAssets defaultStaticAssets) frontend (_componentConfigs_frontend configs) $ obeliskRoute :/ a
@@ -208,7 +208,7 @@ renderGhcjsFrontend
208208 :: (MonadSnap m, HasCookies m)
209209 => (route -> Text)
210210 -> route
211− -> Map Text Text
211+ -> Map Text ByteString
212212 -> Frontend route
213213 -> m ByteString
214214renderGhcjsFrontend urlEnc route configs f = do
@@ -222,10 +222,10 @@ instance HasCookies Snap where
222222
223223getComponentConfigs :: IO ComponentConfigs
224224getComponentConfigs = do
225− allConfigs <- getConfigs
225+ allConfigs <- Lookup.getConfigs
226226 return $ ComponentConfigs
227227 { _componentConfigs_backend =
228− Map.filterWithKey (\k _ -> isMemberOf k ["common", "backend"]) allConfigs
228+ allConfigs
229229 , _componentConfigs_frontend =
230230 Map.filterWithKey (\k _ -> isMemberOf k ["common", "frontend"]) allConfigs
231231 }
lib/executable-config/backend/obelisk-executable-config-backend.cabaldeleted−20
@@ -1,20 +0,0 @@
1−name: obelisk-executable-config-backend
2−version: 0.1
3−cabal-version: >= 1.2
4−build-type: Simple
5−
6−library
7− hs-source-dirs: src
8− build-depends: base,
9− containers,
10− monad-control,
11− mtl,
12− primitive,
13− ref-tf,
14− snap-core,
15− text,
16− transformers,
17− transformers-base
18− exposed-modules:
19− Obelisk.ExecutableConfig.Backend
20− ghc-options: -Wall
lib/executable-config/backend/src/Obelisk/ExecutableConfig/Backend.hsdeleted−77
@@ -1,77 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE DataKinds #-}
3−{-# LANGUAGE DefaultSignatures #-}
4−{-# LANGUAGE DeriveFunctor #-}
5−{-# LANGUAGE FlexibleContexts #-}
6−{-# LANGUAGE FlexibleInstances #-}
7−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
8−{-# LANGUAGE MultiParamTypeClasses #-}
9−{-# LANGUAGE OverloadedStrings #-}
10−{-# LANGUAGE RankNTypes #-}
11−{-# LANGUAGE TypeFamilies #-}
12−{-# LANGUAGE UndecidableInstances #-}
13−module Obelisk.ExecutableConfig.Backend
14− ( HasBackendConfigs(..)
15− , BackendConfigsT
16− , runBackendConfigsT
17− , mapBackendConfigsT
18− ) where
19−
20−import Control.Applicative
21−import Control.Monad
22−import Control.Monad.Base
23−import Control.Monad.Fix
24−import Control.Monad.Primitive
25−import Control.Monad.Ref
26−import Control.Monad.IO.Class
27−import Control.Monad.Trans.Class
28−import Control.Monad.Trans.Control
29−import Control.Monad.Trans.Reader
30−import Data.Map (Map)
31−import qualified Data.Map as Map
32−import Data.Text (Text)
33−import Snap.Core (MonadSnap)
34−
35−class Monad m => HasBackendConfigs m where
36− getBackendConfig :: Text -> m (Maybe Text)
37− default getBackendConfig :: (HasBackendConfigs m', m ~ t m', MonadTrans t) => Text -> m (Maybe Text)
38− getBackendConfig = lift . getBackendConfig
39− getCommonConfig :: Text -> m (Maybe Text)
40− default getCommonConfig :: (HasBackendConfigs m', m ~ t m', MonadTrans t) => Text -> m (Maybe Text)
41− getCommonConfig = lift . getCommonConfig
42−
43−newtype BackendConfigsT m a = BackendConfigsT { unBackendConfigsT :: ReaderT (Map Text Text) m a }
44− deriving
45− ( Functor
46− , Applicative
47− , Monad
48− , MonadPlus
49− , Alternative
50− , MonadFix
51− , MonadIO
52− , MonadBase m'
53− , MonadBaseControl m'
54− , MonadRef
55− , MonadTrans
56− , MonadSnap
57− )
58−
59−instance PrimMonad m => PrimMonad (BackendConfigsT m) where
60− type PrimState (BackendConfigsT m) = PrimState m
61− primitive = lift . primitive
62−
63−runBackendConfigsT
64− :: Map Text Text
65− -> BackendConfigsT m a
66− -> m a
67−runBackendConfigsT cs child = runReaderT (unBackendConfigsT child) cs
68−
69−instance Monad m => HasBackendConfigs (BackendConfigsT m) where
70− getBackendConfig k = BackendConfigsT $ Map.lookup ("backend/" <> k) <$> ask
71− getCommonConfig k = BackendConfigsT $ Map.lookup ("common/" <> k) <$> ask
72−
73−mapBackendConfigsT
74− :: (forall x. m x -> n x)
75− -> BackendConfigsT m a
76− -> BackendConfigsT n a
77−mapBackendConfigsT f (BackendConfigsT x) = BackendConfigsT $ mapReaderT f x
lib/executable-config/default.nix−2
@@ -20,8 +20,6 @@ let injectConfig = config: assets: nixpkgs.runCommand "inject-config" {} (''
2020 '');
2121in with nixpkgs.haskell.lib; {
2222 haskellOverlay = self: super: {
23− obelisk-executable-config-backend = self.callCabal2nix "obelisk-executable-config-backend" (filterGitSource ./backend) {};
24− obelisk-executable-config-frontend = self.callCabal2nix "obelisk-executable-config-frontend" (filterGitSource ./frontend) {};
2523 obelisk-executable-config-lookup = self.callPackage (filterGitSource ./lookup) {};
2624 };
2725 platforms = {
lib/executable-config/frontend/obelisk-executable-config-frontend.cabaldeleted−21
@@ -1,21 +0,0 @@
1−name: obelisk-executable-config-frontend
2−version: 0.1
3−cabal-version: >= 1.2
4−build-type: Simple
5−
6−library
7− hs-source-dirs: src
8− build-depends: base,
9− containers,
10− ghcjs-dom,
11− jsaddle,
12− mtl,
13− primitive,
14− ref-tf,
15− reflex,
16− reflex-dom-core,
17− text,
18− transformers
19− exposed-modules:
20− Obelisk.ExecutableConfig.Frontend
21− ghc-options: -Wall
lib/executable-config/frontend/src/Obelisk/ExecutableConfig/Frontend.hsdeleted−115
@@ -1,115 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE DataKinds #-}
3−{-# LANGUAGE DefaultSignatures #-}
4−{-# LANGUAGE DeriveFunctor #-}
5−{-# LANGUAGE FlexibleContexts #-}
6−{-# LANGUAGE FlexibleInstances #-}
7−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
8−{-# LANGUAGE MultiParamTypeClasses #-}
9−{-# LANGUAGE OverloadedStrings #-}
10−{-# LANGUAGE RankNTypes #-}
11−{-# LANGUAGE TypeFamilies #-}
12−{-# LANGUAGE UndecidableInstances #-}
13−module Obelisk.ExecutableConfig.Frontend
14− ( HasFrontendConfigs(..)
15− , FrontendConfigsT
16− , runFrontendConfigsT
17− , mapFrontendConfigsT
18− ) where
19−
20−import Control.Monad.Fix
21−import Control.Monad.Primitive
22−import Control.Monad.Ref
23−import Control.Monad.IO.Class
24−import Control.Monad.Trans.Class
25−import Control.Monad.Trans.Reader
26−import Data.Map (Map)
27−import qualified Data.Map as Map
28−import Data.Text (Text)
29−import Reflex
30−import Reflex.Host.Class
31−import Reflex.Dom.Core
32−#ifndef ghcjs_HOST_OS
33−import GHCJS.DOM.Types (MonadJSM)
34−#endif
35−
36−class Monad m => HasFrontendConfigs m where
37− getFrontendConfig :: Text -> m (Maybe Text)
38− default getFrontendConfig :: (HasFrontendConfigs m', m ~ t m', MonadTrans t) => Text -> m (Maybe Text)
39− getFrontendConfig = lift . getFrontendConfig
40− getCommonConfig :: Text -> m (Maybe Text)
41− default getCommonConfig :: (HasFrontendConfigs m', m ~ t m', MonadTrans t) => Text -> m (Maybe Text)
42− getCommonConfig = lift . getCommonConfig
43−
44−instance HasFrontendConfigs m => HasFrontendConfigs (BehaviorWriterT t w m)
45−instance HasFrontendConfigs m => HasFrontendConfigs (DynamicWriterT t w m)
46−instance HasFrontendConfigs m => HasFrontendConfigs (EventWriterT t w m)
47−instance HasFrontendConfigs m => HasFrontendConfigs (PostBuildT t m)
48−instance HasFrontendConfigs m => HasFrontendConfigs (QueryT t q m)
49−instance HasFrontendConfigs m => HasFrontendConfigs (ReaderT r m)
50−instance HasFrontendConfigs m => HasFrontendConfigs (RequesterT t request response m)
51−instance HasFrontendConfigs m => HasFrontendConfigs (StaticDomBuilderT t m)
52−instance HasFrontendConfigs m => HasFrontendConfigs (TriggerEventT t m)
53−
54−newtype FrontendConfigsT m a = FrontendConfigsT { unFrontendConfigsT :: ReaderT (Map Text Text) m a }
55− deriving
56− ( Functor
57− , Applicative
58− , DomBuilder t
59− , Monad
60− , MonadFix
61− , MonadHold t
62− , MonadIO
63−#ifndef ghcjs_HOST_OS
64− , MonadJSM
65−#endif
66− , MonadRef
67− , MonadReflexCreateTrigger t
68− , MonadSample t
69− , MonadTrans
70− , NotReady t
71− , PostBuild t
72− , TriggerEvent t
73− , HasDocument
74− , DomRenderHook t
75− , HasJSContext
76− , HasJS js
77− )
78−
79−instance PerformEvent t m => PerformEvent t (FrontendConfigsT m) where
80− type Performable (FrontendConfigsT m) = FrontendConfigsT (Performable m)
81− performEvent e = FrontendConfigsT $ ReaderT $ \configs ->
82− performEvent $ runFrontendConfigsT configs <$> e
83− performEvent_ e = FrontendConfigsT $ ReaderT $ \configs ->
84− performEvent_ $ runFrontendConfigsT configs <$> e
85−
86−instance Adjustable t m => Adjustable t (FrontendConfigsT m) where
87− runWithReplace a e = FrontendConfigsT $ runWithReplace (unFrontendConfigsT a) (unFrontendConfigsT <$> e)
88− traverseDMapWithKeyWithAdjust f m e = FrontendConfigsT $ traverseDMapWithKeyWithAdjust (\k v -> unFrontendConfigsT $ f k v) m e
89− traverseIntMapWithKeyWithAdjust f m e = FrontendConfigsT $ traverseIntMapWithKeyWithAdjust (\k v -> unFrontendConfigsT $ f k v) m e
90− traverseDMapWithKeyWithAdjustWithMove f m e = FrontendConfigsT $ traverseDMapWithKeyWithAdjustWithMove (\k v -> unFrontendConfigsT $ f k v) m e
91−
92−instance Prerender js t m => Prerender js t (FrontendConfigsT m) where
93− type Client (FrontendConfigsT m) = FrontendConfigsT (Client m)
94− prerender server client = FrontendConfigsT $ ReaderT $ \configs ->
95− prerender (runFrontendConfigsT configs server) (runFrontendConfigsT configs client)
96−
97−instance PrimMonad m => PrimMonad (FrontendConfigsT m) where
98− type PrimState (FrontendConfigsT m) = PrimState m
99− primitive = lift . primitive
100−
101−runFrontendConfigsT
102− :: Map Text Text
103− -> FrontendConfigsT m a
104− -> m a
105−runFrontendConfigsT cs child = runReaderT (unFrontendConfigsT child) cs
106−
107−instance Monad m => HasFrontendConfigs (FrontendConfigsT m) where
108− getFrontendConfig k = FrontendConfigsT $ Map.lookup ("frontend/" <> k) <$> ask
109− getCommonConfig k = FrontendConfigsT $ Map.lookup ("common/" <> k) <$> ask
110−
111−mapFrontendConfigsT
112− :: (forall x. m x -> n x)
113− -> FrontendConfigsT m a
114− -> FrontendConfigsT n a
115−mapFrontendConfigsT f (FrontendConfigsT x) = FrontendConfigsT $ mapReaderT f x
lib/executable-config/inject/obelisk-executable-config-inject.cabal+2
@@ -8,6 +8,8 @@ library
88
99 build-depends:
1010 base,
11+ bytestring,
12+ base64-bytestring,
1113 containers,
1214 reflex-dom-core,
1315 text
lib/executable-config/inject/src/Obelisk/ExecutableConfig/Inject.hs+8−5
@@ -3,20 +3,23 @@ module Obelisk.ExecutableConfig.Inject where
33
44import Control.Monad (mapM_)
55import Control.Monad.IO.Class (MonadIO)
6+import Data.ByteString (ByteString)
7+import qualified Data.ByteString.Base64 as B64
68import Data.Map (Map)
79import qualified Data.Map as Map
810import Data.Semigroup ((<>))
911import Data.Text (Text)
12+import qualified Data.Text.Encoding as T
1013import Reflex.Dom.Core hiding (value)
1114
1215-- | Produces a @<script>@ tag with an @id@ attribute containing the key and
13−-- whose value is the provided configuration
14−injectPure :: DomBuilder t m => Text -> Text -> m ()
16+-- whose value is the provided configuration, encoded in base64.
17+injectPure :: DomBuilder t m => Text -> ByteString -> m ()
1518injectPure key value =
16− let attrs = ("type" =: "text/plain" <> "data-obelisk-executable-config-inject-key" =: key)
17− in elAttr "script" attrs $ text value
19+ let attrs = ("type" =: "text/plain" <> "data-obelisk-executable-config-inject-key" =: key <> "data-hydration-skip" =: "")
20+ in elAttr "script" attrs $ text (T.decodeUtf8 (B64.encode value))
1821
1922-- | Produces injectable @<script>@ tags containing the configuration keys
2023-- (filepaths) and values.
21−injectExecutableConfigs :: (MonadIO m, DomBuilder t m) => Map Text Text -> m ()
24+injectExecutableConfigs :: (MonadIO m, DomBuilder t m) => Map Text ByteString -> m ()
2225injectExecutableConfigs = mapM_ (uncurry injectPure) . Map.toList
lib/executable-config/lookup/default.nix+3−1
@@ -1,5 +1,7 @@
11{ hostPlatform, ghc
22, mkDerivation, base, bytestring, filepath, stdenv, text, transformers
3+, transformers-base, ref-tf, primitive, monad-control, reflex, reflex-dom
4+, jsaddle
35, android-activity ? null, jsaddle-wkwebview ? null, ghcjs-dom ? null
46}:
57let isAndroid = hostPlatform.libc == "bionic";
@@ -10,7 +12,7 @@ in mkDerivation {
1012 version = "0.1";
1113 src = ./.;
1214 libraryHaskellDepends = [
13− base bytestring filepath text transformers
15+ base bytestring filepath text transformers transformers-base ref-tf primitive monad-control reflex reflex-dom jsaddle
1416 ] ++ (if isAndroid then [
1517 android-activity
1618 ] else if isIOS then [
lib/executable-config/lookup/obelisk-executable-config-lookup.cabal+13−7
@@ -4,26 +4,25 @@ cabal-version: >= 1.2
44build-type: Simple
55
66library
7− exposed-modules: Obelisk.ExecutableConfig.Lookup
7+ exposed-modules: Obelisk.ExecutableConfig.Lookup, Obelisk.Configs
88 if os(ios)
9− hs-source-dirs: src-ios, src-directory
9+ hs-source-dirs: src-ios, src
1010 other-modules: Obelisk.ExecutableConfig.Internal.ConfigDirectory
1111 build-depends: jsaddle-wkwebview
1212 build-depends: directory
1313 else
1414 if os(linux-android)
15− hs-source-dirs: src-android
15+ hs-source-dirs: src-android, src
1616 c-sources: cbits/ExecutableConfig.c
1717 other-modules: Obelisk.ExecutableConfig.Internal.AssetManager
1818 build-depends: android-activity
1919 ld-options: -landroid
2020 else
2121 if impl(ghcjs)
22− hs-source-dirs: src-ghcjs
22+ hs-source-dirs: src-ghcjs, src
2323 build-depends: ghcjs-dom
2424 else
25− hs-source-dirs: src, src-directory
26− other-modules: Obelisk.ExecutableConfig.Internal.ConfigDirectory
25+ hs-source-dirs: src-other, src
2726 build-depends: directory
2827
2928 build-depends:
@@ -32,6 +31,13 @@ library
3231 containers,
3332 filepath,
3433 text,
35− transformers
34+ transformers,
35+ transformers-base,
36+ ref-tf,
37+ primitive,
38+ monad-control,
39+ reflex,
40+ reflex-dom,
41+ jsaddle
3642
3743 ghc-options: -Wall
lib/executable-config/lookup/src-android/Obelisk/ExecutableConfig/Lookup.hs+4−4
@@ -14,7 +14,7 @@ import Foreign.Ptr (nullPtr)
1414
1515import Obelisk.ExecutableConfig.Internal.AssetManager
1616
17−getFromMgr :: AAssetManager -> BS.ByteString -> IO (Maybe Text)
17+getFromMgr :: AAssetManager -> BS.ByteString -> IO (Maybe ByteString)
1818getFromMgr mgr name = do
1919 let open = do
2020 a <- BS.useAsCString name $ \fn ->
@@ -26,9 +26,9 @@ getFromMgr mgr name = do
2626 bracket open close $ mapM $ \asset -> do
2727 b <- asset_getBuffer asset
2828 l <- asset_getLength asset
29− fmap T.decodeUtf8 $ BS.packCStringLen (b, fromIntegral l)
29+ BS.packCStringLen (b, fromIntegral l)
3030
31−getConfigs :: IO (Map Text Text)
31+getConfigs :: IO (Map Text ByteString)
3232getConfigs = bracket getAssets freeAssetManager $ \mgrObj -> do
3333 mgr <- assetManagerFromJava mgrObj
3434 let openDir = do
@@ -46,7 +46,7 @@ getConfigs = bracket getAssets freeAssetManager $ \mgrObj -> do
4646 Nothing -> error "could not open configuration manifest 'config.files'"
4747 result <- fmap Map.fromList $ forM configPaths $ \fp ->
4848 getFromMgr mgr ("config/" <> fp) >>= \case
49− Just v -> return (T.decodeUtf8 fp, v)
49+ Just v -> return (fp, v)
5050 Nothing -> error $ "Config present in config.files but not in assets: " <> show fp
5151 putStrLn $ "getConfigs: found " <> show result
5252 pure result
lib/executable-config/lookup/src-directory/Obelisk/ExecutableConfig/Internal/ConfigDirectory.hsdeleted−28
@@ -1,28 +0,0 @@
1−{-# LANGUAGE LambdaCase #-}
2−{-# LANGUAGE OverloadedStrings #-}
3−module Obelisk.ExecutableConfig.Internal.ConfigDirectory where
4−
5−import Control.Monad
6−import Data.Map (Map)
7−import qualified Data.Map as Map
8−import Data.Text (Text)
9−import Data.Text as T
10−import Data.Text.IO as T
11−import System.Directory
12−import System.FilePath.Posix ((</>))
13−
14−getConfigsFromDirectory :: FilePath -> IO (Map Text Text)
15−getConfigsFromDirectory base = do
16− doesDirectoryExist base >>= \case
17− True -> do
18− ps <- listDirectory base
19− fmap mconcat $ forM ps $ \p -> do
20− subdirConfigs <- getConfigsFromDirectory $ base </> p
21− pure $ Map.mapKeys (T.pack . (p </>) . T.unpack) subdirConfigs
22− False -> do
23− doesFileExist base >>= \case
24− True -> do
25− f <- T.readFile base
26− pure $ Map.singleton "" f
27− False -> do
28− pure mempty
lib/executable-config/lookup/src-ghcjs/Obelisk/ExecutableConfig/Lookup.hs+8−2
@@ -2,10 +2,13 @@
22{-# LANGUAGE OverloadedStrings #-}
33module Obelisk.ExecutableConfig.Lookup where
44
5+import Data.ByteString (ByteString)
6+import qualified Data.ByteString.Base64 as B64
57import Data.Map (Map)
68import qualified Data.Map as Map
79import Data.Maybe (catMaybes)
810import Data.Text (Text)
11+import qualified Data.Text as T
912import Data.Traversable (for)
1013import GHCJS.DOM
1114import GHCJS.DOM.Document (getHead)
@@ -16,7 +19,7 @@ import GHCJS.DOM.NodeList (item, getLength)
1619import GHCJS.DOM.ParentNode (querySelectorAll)
1720import GHCJS.DOM.Types (Node(Node), castTo)
1821
19−getConfigs :: IO (Map Text Text)
22+getConfigs :: IO (Map Text ByteString)
2023getConfigs = do
2124 Just doc <- currentDocument
2225 Just hd <- getHead doc
@@ -28,9 +31,12 @@ getConfigs = do
2831 dataset <- getDataset e
2932 (,)
3033 <$> get dataset ("obelisk-executable-config-inject-key" :: Text)
31− <*> getInnerHTML e
34+ <*> (fmap decodeOrFail (getInnerHTML e))
3235 where
3336 collToList es = do
3437 len <- getLength es
3538 list <- traverse (item es) [0..len-1]
3639 pure $ catMaybes list
40+ decodeOrFail x = case B64.decode (T.decodeUtf8 x) of
41+ Left e -> error ("Obelisk.ExecutableConfig.Lookup.getConfigs: error when decoding base64: " ++ e)
42+ Right x -> x
lib/executable-config/lookup/src-ios/Obelisk/ExecutableConfig/Lookup.hs+1−1
@@ -10,7 +10,7 @@ import System.FilePath.Posix
1010
1111import Obelisk.ExecutableConfig.Internal.ConfigDirectory
1212
13−getConfigs :: IO (Map Text Text)
13+getConfigs :: IO (Map Text ByteString)
1414getConfigs = mainBundleResourcePath >>= \case
1515 Nothing -> error "Could not get bundle resource path."
1616 Just p -> getConfigsFromDirectory $ T.unpack (T.decodeUtf8 p) </> "config"
lib/executable-config/lookup/src/Obelisk/ExecutableConfig/Lookup.hsdeleted−9
@@ -1,9 +0,0 @@
1−module Obelisk.ExecutableConfig.Lookup where
2−
3−import Data.Map (Map)
4−import Data.Text (Text)
5−
6−import Obelisk.ExecutableConfig.Internal.ConfigDirectory
7−
8−getConfigs :: IO (Map Text Text)
9−getConfigs = getConfigsFromDirectory "config"
lib/frontend/obelisk-frontend.cabal−1
@@ -14,7 +14,6 @@ library
1414 jsaddle,
1515 lens,
1616 mtl,
17− obelisk-executable-config-frontend,
1817 obelisk-executable-config-inject,
1918 obelisk-executable-config-lookup,
2019 obelisk-route,
lib/frontend/src/Obelisk/Frontend.hs+12−12
@@ -53,9 +53,9 @@ import Obelisk.Frontend.Cookie
5353import Obelisk.Route.Frontend
5454import Reflex.Dom.Core
5555import Reflex.Host.Class
56−import Obelisk.ExecutableConfig.Frontend
56+import Obelisk.Configs
5757import Obelisk.ExecutableConfig.Inject (injectExecutableConfigs)
58−import Obelisk.ExecutableConfig.Lookup (getConfigs)
58+import qualified Obelisk.ExecutableConfig.Lookup as Lookup
5959import Web.Cookie
6060
6161import Debug.Trace
@@ -81,7 +81,7 @@ type ObeliskWidget js t route m =
8181 , Prerender js t m
8282 , PrebuildAgnostic t route m
8383 , PrebuildAgnostic t route (Client m)
84− , HasFrontendConfigs m
84+ , HasConfigs m
8585 , HasCookies m
8686 )
8787
@@ -89,8 +89,8 @@ type PrebuildAgnostic t route m =
8989 ( SetRoute t route m
9090 , RouteToUrl route m
9191 , MonadFix m
92− , HasFrontendConfigs m
93− , HasFrontendConfigs (Performable m)
92+ , HasConfigs m
93+ , HasConfigs (Performable m)
9494 )
9595
9696data Frontend route = Frontend
@@ -155,7 +155,7 @@ runFrontend validFullEncoder frontend = do
155155 True
156156#endif
157157 }
158− configs <- liftIO getConfigs
158+ configs <- liftIO Lookup.getConfigs
159159 when (_frontendMode_hydrate mode) removeHTMLConfigs
160160 -- There's no fundamental reason that adjustRoute needs to control setting the
161161 -- initial route and *also* the useHash parameter; that's why these are
@@ -167,7 +167,7 @@ runFrontend validFullEncoder frontend = do
167167runFrontendWithConfigsAndCurrentRoute
168168 :: forall backendRoute route
169169 . FrontendMode
170− -> Map Text Text
170+ -> Map Text ByteString
171171 -> Encoder Identity Identity (R (Sum backendRoute (ObeliskRoute route))) PageName
172172 -> Frontend (R route)
173173 -> JSM ()
@@ -202,11 +202,11 @@ runFrontendWithConfigsAndCurrentRoute mode configs validFullEncoder frontend = d
202202 w appendHead appendBody = do
203203 rec switchover <- runRouteViewT ve switchover (_frontendMode_adjustRoute mode) $ do
204204 (switchover'', fire) <- newTriggerEvent
205− mapRoutedT (mapSetRouteT (mapRouteToUrlT (appendHead . runFrontendConfigsT configs))) $ do
205+ mapRoutedT (mapSetRouteT (mapRouteToUrlT (appendHead . runConfigsT configs))) $ do
206206 -- The order here is important - baseTag has to be before headWidget!
207207 baseTag
208208 _frontend_head frontend
209− mapRoutedT (mapSetRouteT (mapRouteToUrlT (appendBody . runFrontendConfigsT configs))) $ do
209+ mapRoutedT (mapSetRouteT (mapRouteToUrlT (appendBody . runConfigsT configs))) $ do
210210 _frontend_body frontend
211211 switchover' <- case _frontendMode_hydrate mode of
212212 True -> lift $ lift $ lift $ lift $ HydrationDomBuilderT $ asks _hydrationDomBuilderEnv_switchover
@@ -221,9 +221,9 @@ runFrontendWithConfigsAndCurrentRoute mode configs validFullEncoder frontend = d
221221renderFrontendHtml
222222 :: ( t ~ DomTimeline
223223 , MonadIO m
224− , widget ~ RoutedT t r (SetRouteT t r (RouteToUrlT r (FrontendConfigsT (CookiesT (PostBuildT t (StaticDomBuilderT t (PerformEventT t DomHost)))))))
224+ , widget ~ RoutedT t r (SetRouteT t r (RouteToUrlT r (ConfigsT (CookiesT (PostBuildT t (StaticDomBuilderT t (PerformEventT t DomHost)))))))
225225 )
226− => Map Text Text
226+ => Map Text ByteString
227227 -> Cookies
228228 -> (r -> Text)
229229 -> r
@@ -233,7 +233,7 @@ renderFrontendHtml
233233 -> m ByteString
234234renderFrontendHtml configs cookies urlEnc route frontend headExtra bodyExtra = do
235235 --TODO: We should probably have a "NullEventWriterT" or a frozen reflex timeline
236− html <- fmap snd $ liftIO $ renderStatic $ fmap fst $ runCookiesT cookies $ runFrontendConfigsT configs $ flip runRouteToUrlT urlEnc $ runSetRouteT $ flip runRoutedT (pure route) $
236+ html <- fmap snd $ liftIO $ renderStatic $ fmap fst $ runCookiesT cookies $ runConfigsT configs $ flip runRouteToUrlT urlEnc $ runSetRouteT $ flip runRoutedT (pure route) $
237237 el "html" $ do
238238 el "head" $ do
239239 baseTag
lib/frontend/src/Obelisk/Frontend/Cookie.hs+3−3
@@ -26,7 +26,7 @@ import GHCJS.DOM.Document (getCookie, Document)
2626import GHCJS.DOM.Types (MonadJSM)
2727import Web.Cookie
2828
29−import Obelisk.ExecutableConfig.Frontend
29+import Obelisk.Configs
3030import Obelisk.Route.Frontend
3131
3232class Monad m => HasCookies m where
@@ -46,8 +46,8 @@ instance HasCookies m => HasCookies (SetRouteT t r m)
4646instance HasCookies m => HasCookies (StaticDomBuilderT t m)
4747instance HasCookies m => HasCookies (TriggerEventT t m)
4848instance HasCookies m => HasCookies (RoutedT t r m)
49−instance HasCookies m => HasCookies (FrontendConfigsT m)
50−instance HasFrontendConfigs m => HasFrontendConfigs (CookiesT m)
49+instance HasCookies m => HasCookies (ConfigsT m)
50+instance HasConfigs m => HasConfigs (CookiesT m)
5151
5252newtype CookiesT m a = CookiesT { unCookiesT :: ReaderT Cookies m a }
5353 deriving
lib/route/obelisk-route.cabal+1−1
@@ -21,7 +21,7 @@ library
2121 monad-control,
2222 mtl,
2323 network-uri,
24− obelisk-executable-config-frontend,
24+ obelisk-executable-config-lookup,
2525 primitive,
2626 ref-tf,
2727 reflex,
lib/route/src/Obelisk/Route/Frontend.hs+5−4
@@ -58,7 +58,6 @@ module Obelisk.Route.Frontend
5858
5959import Prelude hiding ((.), id)
6060
61−import Obelisk.ExecutableConfig.Frontend
6261import Obelisk.Route
6362
6463import Control.Category (Category (..), (.))
@@ -97,6 +96,8 @@ import qualified Data.List as L
9796
9897import Unsafe.Coerce
9998
99+import Obelisk.Configs
100+
100101infixr 5 :~
101102pattern (:~) :: Reflex t => f a -> Dynamic t a -> DSum f (Compose (Dynamic t) Identity)
102103pattern a :~ b <- a :=> (coerceDynamic . getCompose -> b)
@@ -178,7 +179,7 @@ instance (Monad m, MonadQuery t vs m) => MonadQuery t vs (RoutedT t r m) where
178179 askQueryResult = lift askQueryResult
179180 queryIncremental = lift . queryIncremental
180181
181−instance HasFrontendConfigs m => HasFrontendConfigs (RoutedT t r m)
182+instance HasConfigs m => HasConfigs (RoutedT t r m)
182183
183184instance (Monad m, RouteToUrl r m) => RouteToUrl r (QueryT t q m)
184185
@@ -342,7 +343,7 @@ instance PrimMonad m => PrimMonad (SetRouteT t r m ) where
342343 type PrimState (SetRouteT t r m) = PrimState m
343344 primitive = lift . primitive
344345
345−instance HasFrontendConfigs m => HasFrontendConfigs (SetRouteT t r m)
346+instance HasConfigs m => HasConfigs (SetRouteT t r m)
346347
347348instance (MonadHold t m, Adjustable t m) => Adjustable t (SetRouteT t r m) where
348349 runWithReplace a0 a' = SetRouteT $ runWithReplace (coerce a0) $ coerceEvent a'
@@ -441,7 +442,7 @@ instance (Monad m, MonadQuery t vs m) => MonadQuery t vs (RouteToUrlT r m) where
441442 askQueryResult = lift askQueryResult
442443 queryIncremental = lift . queryIncremental
443444
444−instance HasFrontendConfigs m => HasFrontendConfigs (RouteToUrlT t m)
445+instance HasConfigs m => HasConfigs (RouteToUrlT t m)
445446
446447runRouteViewT
447448 :: forall t m r a.
lib/run/obelisk-run.cabal+1−1
@@ -26,7 +26,7 @@ library
2626 , network
2727 , obelisk-asset-serve-snap
2828 , obelisk-backend
29− , obelisk-executable-config-backend
29+ , obelisk-executable-config-lookup
3030 , obelisk-frontend
3131 , obelisk-route
3232 , process
lib/run/src/Obelisk/Run.hs+14−11
@@ -34,6 +34,7 @@ import Data.Streaming.Network (bindPortTCP)
3434import Data.String (fromString)
3535import Data.Text (Text)
3636import qualified Data.Text as T
37+import qualified Data.Text.Encoding as T
3738import Language.Javascript.JSaddle.Run (syncPoint)
3839import Language.Javascript.JSaddle.WebSockets
3940import Network.HTTP.Client (Manager, defaultManagerSettings, newManager)
@@ -48,7 +49,7 @@ import Network.WebSockets (ConnectionOptions)
4849import Network.WebSockets.Connection (defaultConnectionOptions)
4950import qualified Obelisk.Asset.Serve.Snap as Snap
5051import Obelisk.Backend
51−import Obelisk.ExecutableConfig.Backend
52+import Obelisk.Configs
5253import Obelisk.Frontend
5354import Obelisk.Route.Frontend
5455import Reflex.Dom.Core
@@ -77,13 +78,13 @@ run port serveStaticAsset backend frontend = do
7778 Right validFullEncoder -> do
7879 configs <- getComponentConfigs
7980 backendTid <- forkIO $ handle handleBackendErr $ withArgs ["--quiet", "--port", show port] $ do
80− runBackendConfigsT (_componentConfigs_backend configs) $
81+ runConfigsT (_componentConfigs_backend configs) $
8182 _backend_run backend $ \serveRoute -> do
8283 runSnapWithCommandLineArgs $ do
8384 getRouteWith validFullEncoder >>= \case
8485 Identity r -> case r of
8586 InL backendRoute :=> Identity a ->
86− runBackendConfigsT (_componentConfigs_backend configs) $
87+ runConfigsT (_componentConfigs_backend configs) $
8788 serveRoute $ backendRoute :/ a
8889 InR obeliskRoute :=> Identity a ->
8990 serveDefaultObeliskApp (mkRouteToUrl validFullEncoder) serveStaticAsset frontend (_componentConfigs_frontend configs) $ obeliskRoute :/ a
@@ -94,16 +95,18 @@ run port serveStaticAsset backend frontend = do
9495runServeAsset :: FilePath -> [Text] -> Snap ()
9596runServeAsset rootPath = Snap.serveAsset "" rootPath . T.unpack . T.intercalate "/"
9697
97−getConfigRoute :: Map Text Text -> Either Text URI
98+getConfigRoute :: Map Text ByteString -> Either Text URI
9899getConfigRoute configs = case Map.lookup "common/route" configs of
99− Just r -> case URI.mkURI $ T.strip r of
100− Just route -> Right route
101− Nothing -> Left $ "Couldn't parse route as URI; value read was: " <> T.pack (show r)
102− Nothing -> Left $ "Couldn't find config file common/route; it should contain the site's canonical root URI" <> T.pack (show $ Map.keys configs)
100+ Just r ->
101+ let stripped = T.strip (T.decodeUtf8 r)
102+ in case URI.mkURI stripped of
103+ Just route -> Right route
104+ Nothing -> Left $ "Couldn't parse route as URI; value read was: " <> T.pack (show stripped)
105+ Nothing -> Left $ "Couldn't find config file common/route; it should contain the site's canonical root URI" <> T.pack (show $ Map.keys configs)
103106
104107runWidget
105108 :: RunConfig
106− -> Map Text Text
109+ -> Map Text ByteString
107110 -> Frontend (R route)
108111 -> Encoder Identity Identity (R (Sum backendRoute (ObeliskRoute route))) PageName
109112 -> IO ()
@@ -125,7 +128,7 @@ runWidget conf configs frontend validFullEncoder = do
125128
126129obeliskApp
127130 :: forall route backendRoute
128− . Map Text Text
131+ . Map Text ByteString
129132 -> ConnectionOptions
130133 -> Frontend (R route)
131134 -> Encoder Identity Identity (R (Sum backendRoute (ObeliskRoute route))) PageName
@@ -158,7 +161,7 @@ obeliskApp configs opts frontend validFullEncoder uri backend = do
158161 _ -> backend req sendResponse
159162
160163renderJsaddleFrontend
161− :: Map Text Text
164+ :: Map Text ByteString
162165 -> Cookies
163166 -> (route -> Text)
164167 -> route
skeleton/common/src/Common/Api.hs+1−1
@@ -1,4 +1,4 @@
11module Common.Api where
22
33commonStuff :: String
4−commonStuff = "Here is a string defined in code common to the frontend and backend."
4+commonStuff = "Here is a string defined in Common.Api"
skeleton/frontend/src/Frontend.hs+8−1
@@ -4,7 +4,9 @@
44module Frontend where
55
66import qualified Data.Text as T
7+import qualified Data.Text.Encoding as T
78import Obelisk.Frontend
9+import Obelisk.Configs
810import Obelisk.Route
911import Reflex.Dom.Core
1012
@@ -12,7 +14,6 @@ import Common.Api
1214import Common.Route
1315import Obelisk.Generated.Static
1416
15−
1617frontend :: Frontend (R FrontendRoute)
1718frontend = Frontend
1819 { _frontend_head = el "title" $ text "Obelisk Minimal Example"
@@ -20,4 +21,10 @@ frontend = Frontend
2021 text "Welcome to Obelisk!"
2122 el "p" $ text $ T.pack commonStuff
2223 elAttr "img" ("src" =: static @"obelisk.jpg") blank
24+ el "div" $ do
25+ exampleConfig <- getConfig "common/example"
26+ case exampleConfig of
27+ Nothing -> text "No config file found in config/common/example"
28+ Just s -> text (T.decodeUtf8 s)
29+ return ()
2330 }