A Short "Why & How"
on using CodeGen via purescript-bridge
[ Â Â Â ] always-correct interfaces for queries to build on.
[ Â Â Â ] ensure handling all responses from the server.
Â
-- our database uses this representation
data Curator = Curator
{ email :: EmailAddress
, message :: Maybe Text
, inviter :: UserId
, token :: Text
, invited_on :: UTCTime }
Database expectation
-- our database uses this representation
data Curator = Curator
{ email :: EmailAddress
, message :: Maybe Text
, inviter :: UserId
, token :: Text
, invited_on :: UTCTime }
-- our database uses this representation
data CuratorForm = CuratorForm
{ email :: EmailAddress
, message :: Maybe Text
} deriving (Generic, Typeable, Show)
Subset of your expectation
-- our database uses this representation
data Curator = Curator
{ email :: EmailAddress
, message :: Maybe Text
, inviter :: UserId
, token :: Text
, invited_on :: UTCTime }
-- our database uses this representation
data CuratorForm = CuratorForm
{ email :: EmailAddress
, message :: Maybe Text
} deriving (Generic, Typeable, Show)
Subset of your expectation
Gen This!
Don't
initialState = { form: curatorForm }
curatorForm = CuratorForm { email: "", message: Nothing }
render state = do
-- ...
div_ $ Form.renderForm state.form NewCurator do
Form.textField "email" "Email" _email (Form.nonBlank <=< Form.emailValidator)
Form.textField "message" "A Note" _message Form.optional
A Small Example
initialState = { form: curatorForm }
curatorForm = CuratorForm { email: "", message: Nothing }
render state = do
-- ...
div_ $ Form.renderForm state.form NewCurator do
Form.textField "email" "Email" _email (Form.nonBlank <=< Form.emailValidator)
Form.textField "message" "A Note" _message Form.optional
A Small Example
initialState = { form: curatorForm }
curatorForm = CuratorForm { email: "", message: Nothing }
render state = do
-- ...
div_ $ Form.renderForm state.form NewCurator do
Form.textField "email" "Email" _email (Form.nonBlank <=< Form.emailValidator)
Form.textField "message" "A Note" _message Form.optional
can't fail on the frontend
compiler tells us what is valid
changes to backend alter frontend interfaces (as they should!)
A Small Example
[ ✓ ] always-correct interfaces for queries to build on.
[ Â Â Â ] ensure handling all responses from the server.
Serializing the Response
What could happen?
Serializing the Response
What could happen?
data CreateResponse
= CreateSuccess Int64
| CreateFailure Text
| NotUnique
deriving (Generic, Typeable, Show)
Gen This!
-- the CuratorForm is the 'form' field of our state.
response <- post (apiUrl <> "/admin/curators") (encodeJson state.form)
let Tuple typ msg = handleCreateResponse "admin.curator" response
return $ flashMessage typ msg
Curator Creation
-- the CuratorForm is the 'form' field of our state.
response <- post (apiUrl <> "/admin/curators") (encodeJson state.form)
let Tuple typ msg = handleCreateResponse "admin.curator" response
return $ flashMessage typ msg
Curator Creation
handleCreateResponse :: TranslationIndex -> Json -> Tuple Status String
handleCreateResponse idx res =
case decodeJson res.response of
Left e -> Tuple Failure e
Right cr -> do
let message = translate idx cr
case cr of
CreateSuccess _ -> Tuple Success message
NotUnique -> Tuple Warning message
CreateFailure t -> Tuple Failure message
translate :: IsTranslatable a => TranslationIndex -> a -> String
handleCreateResponse :: TranslationIndex -> Json -> Tuple Status String
handleCreateResponse idx res =
case decodeJson res.response of
Left e -> Tuple Failure e
Right cr -> do
let message = translate idx cr
case cr of
CreateSuccess _ -> Tuple Success message
NotUnique -> Tuple Warning message
CreateFailure t -> Tuple Failure message
[ ✓ ] always-correct interfaces for queries to build on.
[ ✓ ] ensure handling all responses from the server.
Â
[ ✓ ] always-correct interfaces for queries to build on.
[ ✓ ] ensure handling all responses from the server.
Â
myTypes :: [SumType 'Haskell]
myTypes = [
mkSumType (Proxy :: Proxy CuratorForm)
, mkSumType (Proxy :: Proxy CreateResponse)
-- ...
]
The Bridge
"register" those types with purescript-bridge
myTypes :: [SumType 'Haskell]
myTypes = [
mkSumType (Proxy :: Proxy CuratorForm)
, mkSumType (Proxy :: Proxy CreateResponse)
-- ...
]
The Bridge
"register" those types with purescript-bridge
Â
backend types => frontend types
not a perfect conversion
give the bridge some direction
import Language.PureScript.Bridge.PSTypes (psInt)
-- delegate to a primitive
int64Bridge :: BridgePart
int64Bridge = typeName ^== "Int64" >> return psInt
Delegating
"Purescript, You already have this and it's called something else"
psDateTime :: TypeInfo 'PureScript
psDateTime = TypeInfo {
_typePackage = ""
, _typeModule = "Types"
, _typeName = "DateStamp"
, _typeParameters = []
}
utcTimeBridge :: BridgePart
utcTimeBridge = typeName ^== "UTCTime" >> return psDateTime
Super-Delegating
"Haskell, just lemme do it"
psDateTime :: TypeInfo 'PureScript
psDateTime = TypeInfo {
_typePackage = ""
, _typeModule = "Types"
, _typeName = "DateStamp"
, _typeParameters = []
}
utcTimeBridge :: BridgePart
utcTimeBridge = typeName ^== "UTCTime" >> return psDateTime
Super-Delegating
In the module "Types.purs"
psDateTime :: TypeInfo 'PureScript
psDateTime = TypeInfo {
_typePackage = ""
, _typeModule = "Types"
, _typeName = "DateStamp"
, _typeParameters = []
}
utcTimeBridge :: BridgePart
utcTimeBridge = typeName ^== "UTCTime" >> return psDateTime
Super-Delegating
In the module "Types.purs"
you'll find a type "DateStamp"
psDateTime :: TypeInfo 'PureScript
psDateTime = TypeInfo {
_typePackage = ""
, _typeModule = "Types"
, _typeName = "DateStamp"
, _typeParameters = []
}
utcTimeBridge :: BridgePart
utcTimeBridge = typeName ^== "UTCTime" >> return psDateTime
Super-Delegating
In the module "Types.purs"
you'll find a type "DateStamp"
It requires no installed packages
Â
psDateTime :: TypeInfo 'PureScript
psDateTime = TypeInfo {
_typePackage = ""
, _typeModule = "Types"
, _typeName = "DateStamp"
, _typeParameters = []
}
utcTimeBridge :: BridgePart
utcTimeBridge = typeName ^== "UTCTime" >> return psDateTime
Super-Delegating
In the module "Types.purs"
you'll find a type "DateStamp"
It requires no installed packages
Use it if we see UTCTime
main :: IO ()
main = writePSTypes "frontend/src/" (buildBridge mainBridge) myTypes
where
mainBridge = defaultBridge <|> int64Bridge <|> utcTimeBridge
Composing a bridge
Thanks!