project-m36-0.4: examples/CustomTupleable.hs
{-# LANGUAGE DeriveGeneric, OverloadedStrings #-}
--this example shows how to implement a non-generics-defined Tupleable instance which is required in scenarios where Haskell-side types are not supported server-side or where one wishes to represent a nested relation
import ProjectM36.Client
import ProjectM36.Tuple
import ProjectM36.Tupleable
import ProjectM36.Relation
import ProjectM36.Atom
import ProjectM36.Attribute as A
import qualified Data.Set as S
import qualified Data.Map as M
import Data.Text (Text)
import GHC.Generics
import Data.Proxy
--in this contrived example, we wish to represent one relation with a nested relation of comments
data Blog = Blog {
title :: Text,
comments :: S.Set Comment
} deriving (Show)
data Comment = Comment {
authorName :: Text,
comment :: Text
} deriving (Eq, Show, Generic, Ord)
instance Tupleable Comment
instance Tupleable Blog where
toTuple blogentry =
mkRelationTupleFromMap (M.fromList [("title", TextAtom (title blogentry)),
("comments", RelationAtom relFromComments)])
where
commentTuples = map toTuple (S.toList (comments blogentry))
relFromComments = case mkRelationFromTuples (toAttributes (Proxy :: Proxy Comment)) commentTuples of
Left err -> error (show err)
Right rel -> rel
fromTuple tupIn = do
titleAtom <- atomForAttributeName "title" tupIn
commentsAtom <- atomForAttributeName "comments" tupIn
commentsRel <- relationForAtom commentsAtom
comments' <- mapM fromTuple (tuplesList commentsRel)
pure Blog {
title = atomToText titleAtom,
comments = S.fromList comments'}
toAttributes _ = A.attributesFromList [Attribute "title" TextAtomType,
Attribute "comments" $ RelationAtomType (toAttributes (Proxy :: Proxy Comment))]
main :: IO ()
main = do
let exampleBlog = Blog { title = "Cat Pics",
comments = S.fromList [Comment {authorName = "Steve",
comment = "great"},
Comment {authorName = "Bob",
comment = "enough"}
]}
print (toTuple exampleBlog)
print (fromTuple (toTuple exampleBlog) :: Either RelationalError Blog)