graphula-2.0.2.2: A simple interface for generating persistent data and linking its dependencies
Safe HaskellSafe-Inferred
LanguageHaskell2010

Graphula

Description

Graphula is a compact interface for generating data and linking its dependencies. You can use this interface to generate fixtures for automated testing.

{- config/models

School
  name Text
  deriving Generic

Teacher
  schoolId SchoolId
  name Text
  deriving Generic

Course
  schoolId SchoolId
  teacherId TeacherId
  name Text
  deriving Generic

-}

instance Arbitrary School where
  -- ...

instance Arbitrary Teacher where
  -- ...

instance Arbitrary Course where
  -- ...

instance HasDependencies School

instance HasDependencies Teacher where
  type Dependencies Teacher = Only SchoolId

instance HasDependencies Course where
  type Dependencies Course = (SchoolId, CourseId)

runGraphulaT runDB $ do
  school <- node @School () mempty

  teacher <- node @Teacher (onlyKey school)
     $ edit
     $ t -> t { teacherName = "Alice" }

  course <- node @Course (keys (school, teacher))
     $ ensure
     $ not . courseIsArchived
Synopsis

Basic usage

Model requirements

class HasDependencies a where Source #

Minimal complete definition

Nothing

Associated Types

type Dependencies a Source #

A data type declaring the model's dependencies

Models with no dependencies can declare an empty instance,

instance HasDependencies School

Models with one dependency must use the Only 1-tuple constructor,

instance HasDependencies Teacher where
  type Dependencies Teacher = Only SchoolId

Models with multiple dependencies use tuple syntax,

instance HasDependencies Course where
  type Dependencies Course = (SchoolId, TeacherId)

type Dependencies _a = ()

type KeySource a :: KeySourceType Source #

Specify the method for resolving a node's key

This can be

'SourceDefault   -- automatically generate keys from the database
'SourceArbitrary -- automatically generate keys using Arbitrary
'SourceExternal  -- explicitly pass a key using nodeKeyed

Most types will use SourceDefault or SourceArbitrary. Only use SourceExternal if the key for a value is always defined externally.

Methods

dependsOn :: a -> Dependencies a -> a Source #

Assign values from the Dependencies collection to a value

This must be an idempotent operation. Law:

(\x d -> x `dependsOn` d `dependsOn` d) = dependsOn

The default, Generic-based implementation will assign values by the order of the fields in the model's type.

newtype Only a Source #

For entities that only have singular Dependencies

Constructors

Only 

Fields

Instances

Instances details
Foldable Only Source # 
Instance details

Defined in Graphula.Dependencies

Methods

fold :: Monoid m => Only m -> m #

foldMap :: Monoid m => (a -> m) -> Only a -> m #

foldMap' :: Monoid m => (a -> m) -> Only a -> m #

foldr :: (a -> b -> b) -> b -> Only a -> b #

foldr' :: (a -> b -> b) -> b -> Only a -> b #

foldl :: (b -> a -> b) -> b -> Only a -> b #

foldl' :: (b -> a -> b) -> b -> Only a -> b #

foldr1 :: (a -> a -> a) -> Only a -> a #

foldl1 :: (a -> a -> a) -> Only a -> a #

toList :: Only a -> [a] #

null :: Only a -> Bool #

length :: Only a -> Int #

elem :: Eq a => a -> Only a -> Bool #

maximum :: Ord a => Only a -> a #

minimum :: Ord a => Only a -> a #

sum :: Num a => Only a -> a #

product :: Num a => Only a -> a #

Traversable Only Source # 
Instance details

Defined in Graphula.Dependencies

Methods

traverse :: Applicative f => (a -> f b) -> Only a -> f (Only b) #

sequenceA :: Applicative f => Only (f a) -> f (Only a) #

mapM :: Monad m => (a -> m b) -> Only a -> m (Only b) #

sequence :: Monad m => Only (m a) -> m (Only a) #

Functor Only Source # 
Instance details

Defined in Graphula.Dependencies

Methods

fmap :: (a -> b) -> Only a -> Only b #

(<$) :: a -> Only b -> Only a #

Generic (Only a) Source # 
Instance details

Defined in Graphula.Dependencies

Associated Types

type Rep (Only a) :: Type -> Type #

Methods

from :: Only a -> Rep (Only a) x #

to :: Rep (Only a) x -> Only a #

Show a => Show (Only a) Source # 
Instance details

Defined in Graphula.Dependencies

Methods

showsPrec :: Int -> Only a -> ShowS #

show :: Only a -> String #

showList :: [Only a] -> ShowS #

Eq a => Eq (Only a) Source # 
Instance details

Defined in Graphula.Dependencies

Methods

(==) :: Only a -> Only a -> Bool #

(/=) :: Only a -> Only a -> Bool #

Ord a => Ord (Only a) Source # 
Instance details

Defined in Graphula.Dependencies

Methods

compare :: Only a -> Only a -> Ordering #

(<) :: Only a -> Only a -> Bool #

(<=) :: Only a -> Only a -> Bool #

(>) :: Only a -> Only a -> Bool #

(>=) :: Only a -> Only a -> Bool #

max :: Only a -> Only a -> Only a #

min :: Only a -> Only a -> Only a #

type Rep (Only a) Source # 
Instance details

Defined in Graphula.Dependencies

type Rep (Only a) = D1 ('MetaData "Only" "Graphula.Dependencies" "graphula-2.0.2.2-KaQe6t09vpIEjDjoM12z1N" 'True) (C1 ('MetaCons "Only" 'PrefixI 'True) (S1 ('MetaSel ('Just "fromOnly") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 a)))
type Keys (Only (Entity a)) Source # 
Instance details

Defined in Graphula.Key

type Keys (Only (Entity a)) = Only (Key a)

only :: a -> Only a Source #

Defining the graph

node :: forall a m. (MonadGraphula m, Logging m a, Arbitrary a, HasDependencies a, GenerateKey a, PersistEntityBackend a ~ SqlBackend, PersistEntity a, Typeable a, GraphulaSafeToInsert a) => Dependencies a -> NodeOptions a -> m (Entity a) Source #

Generate a node with a default (Database-provided) key

a <- node @A () mempty

edit :: (a -> a) -> NodeOptions a Source #

Modify the node after it's been generated

a <- node @A () $ edit $ \a -> a { someField = True }

ensure :: (a -> Bool) -> NodeOptions a Source #

Require a node to satisfy the specified predicate

a <- node @A () $ ensure $ (== True) . someField

N.B. ensuring a condition that is infrequently met can be innefficient.

Running the graph

data GraphulaT n m a Source #

Instances

Instances details
MonadTrans (GraphulaT n) Source # 
Instance details

Defined in Graphula

Methods

lift :: Monad m => m a -> GraphulaT n m a #

MonadIO m => MonadIO (GraphulaT n m) Source # 
Instance details

Defined in Graphula

Methods

liftIO :: IO a -> GraphulaT n m a #

Applicative m => Applicative (GraphulaT n m) Source # 
Instance details

Defined in Graphula

Methods

pure :: a -> GraphulaT n m a #

(<*>) :: GraphulaT n m (a -> b) -> GraphulaT n m a -> GraphulaT n m b #

liftA2 :: (a -> b -> c) -> GraphulaT n m a -> GraphulaT n m b -> GraphulaT n m c #

(*>) :: GraphulaT n m a -> GraphulaT n m b -> GraphulaT n m b #

(<*) :: GraphulaT n m a -> GraphulaT n m b -> GraphulaT n m a #

Functor m => Functor (GraphulaT n m) Source # 
Instance details

Defined in Graphula

Methods

fmap :: (a -> b) -> GraphulaT n m a -> GraphulaT n m b #

(<$) :: a -> GraphulaT n m b -> GraphulaT n m a #

Monad m => Monad (GraphulaT n m) Source # 
Instance details

Defined in Graphula

Methods

(>>=) :: GraphulaT n m a -> (a -> GraphulaT n m b) -> GraphulaT n m b #

(>>) :: GraphulaT n m a -> GraphulaT n m b -> GraphulaT n m b #

return :: a -> GraphulaT n m a #

MonadIO m => MonadGraphulaBackend (GraphulaT n m) Source # 
Instance details

Defined in Graphula

Associated Types

type Logging (GraphulaT n m) :: Type -> Constraint Source #

Methods

askGen :: GraphulaT n m (IORef QCGen) Source #

logNode :: Logging (GraphulaT n m) a => a -> GraphulaT n m () Source #

(MonadIO m, MonadIO n) => MonadGraphulaFrontend (GraphulaT n m) Source # 
Instance details

Defined in Graphula

MonadUnliftIO m => MonadUnliftIO (GraphulaT n m) Source # 
Instance details

Defined in Graphula

Methods

withRunInIO :: ((forall a. GraphulaT n m a -> IO a) -> IO b) -> GraphulaT n m b #

type Logging (GraphulaT n m) Source # 
Instance details

Defined in Graphula

runGraphulaT Source #

Arguments

:: MonadUnliftIO m 
=> Maybe Int

Optional seed

-> (forall b. ReaderT SqlBackend n b -> m b)

Database runner

-> GraphulaT n m a 
-> m a 

Advanced usage

Non-serial keys

data KeySourceType Source #

Constructors

SourceDefault

Generate keys using the database's DEFAULT strategy

SourceArbitrary

Generate keys using the Arbitrary instance for the Key

SourceExternal

Always explicitly pass an external key

See nodeKeyed.

nodeKeyed :: forall a m. (MonadGraphula m, Logging m a, Arbitrary a, HasDependencies a, PersistEntityBackend a ~ SqlBackend, PersistEntity a, Typeable a, GraphulaSafeToInsert a) => Key a -> Dependencies a -> NodeOptions a -> m (Entity a) Source #

Generate a node with an explictly-given key

let someKey = UUID.fromString "..."
a <- nodeKeyed @A someKey () mempty

Running with logging

data GraphulaLoggedT m a Source #

Instances

Instances details
MonadTrans GraphulaLoggedT Source # 
Instance details

Defined in Graphula.Logged

Methods

lift :: Monad m => m a -> GraphulaLoggedT m a #

MonadIO m => MonadIO (GraphulaLoggedT m) Source # 
Instance details

Defined in Graphula.Logged

Methods

liftIO :: IO a -> GraphulaLoggedT m a #

Applicative m => Applicative (GraphulaLoggedT m) Source # 
Instance details

Defined in Graphula.Logged

Functor m => Functor (GraphulaLoggedT m) Source # 
Instance details

Defined in Graphula.Logged

Methods

fmap :: (a -> b) -> GraphulaLoggedT m a -> GraphulaLoggedT m b #

(<$) :: a -> GraphulaLoggedT m b -> GraphulaLoggedT m a #

Monad m => Monad (GraphulaLoggedT m) Source # 
Instance details

Defined in Graphula.Logged

(MonadGraphulaBackend m, MonadIO m) => MonadGraphulaBackend (GraphulaLoggedT m) Source # 
Instance details

Defined in Graphula.Logged

Associated Types

type Logging (GraphulaLoggedT m) :: Type -> Constraint Source #

(Monad m, MonadGraphulaFrontend m) => MonadGraphulaFrontend (GraphulaLoggedT m) Source # 
Instance details

Defined in Graphula.Logged

MonadUnliftIO m => MonadUnliftIO (GraphulaLoggedT m) Source # 
Instance details

Defined in Graphula.Logged

Methods

withRunInIO :: ((forall a. GraphulaLoggedT m a -> IO a) -> IO b) -> GraphulaLoggedT m b #

Monad m => MonadReader (IORef (Seq Text)) (GraphulaLoggedT m) Source # 
Instance details

Defined in Graphula.Logged

type Logging (GraphulaLoggedT m) Source # 
Instance details

Defined in Graphula.Logged

runGraphulaLoggedT :: MonadUnliftIO m => GraphulaLoggedT m a -> m a Source #

Run the graph while logging to a temporary file

Running idempotently

data GraphulaIdempotentT m a Source #

Instances

Instances details
MonadTrans GraphulaIdempotentT Source # 
Instance details

Defined in Graphula.Idempotent

Methods

lift :: Monad m => m a -> GraphulaIdempotentT m a #

MonadIO m => MonadIO (GraphulaIdempotentT m) Source # 
Instance details

Defined in Graphula.Idempotent

Methods

liftIO :: IO a -> GraphulaIdempotentT m a #

Applicative m => Applicative (GraphulaIdempotentT m) Source # 
Instance details

Defined in Graphula.Idempotent

Functor m => Functor (GraphulaIdempotentT m) Source # 
Instance details

Defined in Graphula.Idempotent

Methods

fmap :: (a -> b) -> GraphulaIdempotentT m a -> GraphulaIdempotentT m b #

(<$) :: a -> GraphulaIdempotentT m b -> GraphulaIdempotentT m a #

Monad m => Monad (GraphulaIdempotentT m) Source # 
Instance details

Defined in Graphula.Idempotent

(MonadIO m, MonadGraphulaFrontend m) => MonadGraphulaFrontend (GraphulaIdempotentT m) Source # 
Instance details

Defined in Graphula.Idempotent

MonadUnliftIO m => MonadUnliftIO (GraphulaIdempotentT m) Source # 
Instance details

Defined in Graphula.Idempotent

Methods

withRunInIO :: ((forall a. GraphulaIdempotentT m a -> IO a) -> IO b) -> GraphulaIdempotentT m b #

Monad m => MonadReader (IORef (m ())) (GraphulaIdempotentT m) Source # 
Instance details

Defined in Graphula.Idempotent

Methods

ask :: GraphulaIdempotentT m (IORef (m ())) #

local :: (IORef (m ()) -> IORef (m ())) -> GraphulaIdempotentT m a -> GraphulaIdempotentT m a #

reader :: (IORef (m ()) -> a) -> GraphulaIdempotentT m a #

Useful synonymns

When declaring your own functions that call node, these synonyms can help with the constraint soup.

genSchoolWithTeacher
  :: GraphulaContext m '[School, Teacher]
  -> m (Entity Teacher)
genSchoolWithTeacher = do
  school <- node @School () mempty
  node @Teacher (onlyKey school) mempty

type family GraphulaContext (m :: Type -> Type) (ts :: [Type]) :: Constraint where ... Source #

A constraint over lists of nodes for MonadGraphula, and GraphulaNode.

Helpful for defining utility functions over many nodes.

mkABC :: (GraphulaContext m '[A, B, C]) => m (Node m C)
mkABC = do
  a <- node A () mempty
  b <- node B (only a) mempty
  node @C (a, b) $ edit $ n ->
    n { cc = "spanish" }

Equations

GraphulaContext m '[] = MonadGraphula m 
GraphulaContext m (t ': ts) = (GraphulaNode m t, GraphulaContext m ts) 

Lower-level details

These exports are likely to be removed from this module in a future version. If you are using them, consider importing from their own modules.

class MonadGraphulaBackend m where Source #

Associated Types

type Logging m :: Type -> Constraint Source #

Methods

askGen :: m (IORef QCGen) Source #

logNode :: Logging m a => a -> m () Source #

Instances

Instances details
(MonadGraphulaBackend m, MonadIO m) => MonadGraphulaBackend (GraphulaLoggedT m) Source # 
Instance details

Defined in Graphula.Logged

Associated Types

type Logging (GraphulaLoggedT m) :: Type -> Constraint Source #

MonadIO m => MonadGraphulaBackend (GraphulaT n m) Source # 
Instance details

Defined in Graphula

Associated Types

type Logging (GraphulaT n m) :: Type -> Constraint Source #

Methods

askGen :: GraphulaT n m (IORef QCGen) Source #

logNode :: Logging (GraphulaT n m) a => a -> GraphulaT n m () Source #

class MonadGraphulaFrontend m where Source #

data NodeOptions a Source #

Options for generating an individual node

NodeOptions can be created and combined with the Monoidal operations (<>) and mempty.

a1 <- node @A () mempty
a2 <- node @A () $ edit $ \a -> a { someField = True }
a3 <- node @A () $ ensure $ (== True) . someField

Instances

Instances details
Monoid (NodeOptions a) Source # 
Instance details

Defined in Graphula.Node

Semigroup (NodeOptions a) Source # 
Instance details

Defined in Graphula.Node

Generic (NodeOptions a) Source # 
Instance details

Defined in Graphula.Node

Associated Types

type Rep (NodeOptions a) :: Type -> Type #

Methods

from :: NodeOptions a -> Rep (NodeOptions a) x #

to :: Rep (NodeOptions a) x -> NodeOptions a #

type Rep (NodeOptions a) Source # 
Instance details

Defined in Graphula.Node

type Rep (NodeOptions a)

class (GenerateKeyInternal (KeySource a) a, KeyConstraint (KeySource a) a) => GenerateKey a Source #

Abstract constraint that some a can generate a key

This is part of ensuring better error messages.

Instances

Instances details
(GenerateKeyInternal (KeySource a) a, KeyConstraint (KeySource a) a) => GenerateKey a Source # 
Instance details

Defined in Graphula.Dependencies

class NoConstraint a Source #

Instances

Instances details
NoConstraint a Source # 
Instance details

Defined in Graphula.NoConstraint