Safe Haskell | None |
---|---|
Language | Haskell2010 |
Graphula is a compact interface for generating data and linking its dependencies. You can use this interface to generate fixtures for automated testing.
The interface is extensible and supports pluggable front-ends.
runGraphIdentity . runGraphulaT $ do
-- Compose dependencies at the value level
Identity vet <- node Veterinarian () mempty
Identity owner <- node
Owner (only vet) mempty
-- TypeApplications is not necessary, but recommended for clarity.
Identity dog node @Dog (owner, vet) $ edit $ d - d { name = "fido" }
Synopsis
- node :: forall a m. (GraphulaContext m '[a], GenerateKey a) => Dependencies a -> NodeOptions a -> m (Entity a)
- nodeKeyed :: forall a m. GraphulaContext m '[a] => Key a -> Dependencies a -> NodeOptions a -> m (Entity a)
- type GraphulaNode m a = (HasDependencies a, Logging m a, PersistEntityBackend a ~ SqlBackend, PersistEntity a, Typeable a, Arbitrary a)
- type family GraphulaContext (m :: Type -> Type) (ts :: [Type]) :: Constraint where ...
- data NodeOptions a
- edit :: (a -> a) -> NodeOptions a
- ensure :: (a -> Bool) -> NodeOptions a
- class HasDependencies a where
- type Dependencies a
- type KeySource a :: KeySourceType
- dependsOn :: a -> Dependencies a -> a
- data KeySourceType
- class (GenerateKeyInternal (KeySource a) a, KeyConstraint (KeySource a) a) => GenerateKey a
- newtype Only a = Only {
- fromOnly :: a
- only :: a -> Only a
- type MonadGraphula m = (Monad m, MonadGraphulaBackend m, MonadGraphulaFrontend m, MonadIO m)
- class MonadGraphulaBackend m where
- class MonadGraphulaFrontend m where
- insert :: (PersistEntityBackend a ~ SqlBackend, PersistEntity a, Monad m) => Maybe (Key a) -> a -> m (Maybe (Entity a))
- remove :: (PersistEntityBackend a ~ SqlBackend, PersistEntity a, Monad m) => Key a -> m ()
- runGraphulaT :: MonadUnliftIO m => Maybe Int -> (forall b. ReaderT SqlBackend n b -> m b) -> GraphulaT n m a -> m a
- data GraphulaT n m a
- runGraphulaLoggedT :: MonadUnliftIO m => GraphulaLoggedT m a -> m a
- runGraphulaLoggedWithFileT :: MonadUnliftIO m => FilePath -> GraphulaLoggedT m a -> m a
- data GraphulaLoggedT m a
- runGraphulaIdempotentT :: MonadUnliftIO m => GraphulaIdempotentT m a -> m a
- data GraphulaIdempotentT m a
- class NoConstraint a
- data GenerationFailure
Graph Declaration
node :: forall a m. (GraphulaContext m '[a], GenerateKey a) => Dependencies a -> NodeOptions a -> m (Entity a) Source #
Generate a value with data dependencies. This leverages
HasDependencies
to insert the specified data in the generated value. All
dependency data is inserted after any editing operations.
node @Dog (ownerId, veterinarianId) mempty node @Dog (ownerId, veterinarianId) $ edit $ \dog -> dog {name = "fido"}
A value that has an externally managed key must use
instead.nodeKeyed
nodeKeyed :: forall a m. GraphulaContext m '[a] => Key a -> Dependencies a -> NodeOptions a -> m (Entity a) Source #
Generate a value with data dependencies given an externally managed
key. This leverages HasDependencies
to insert the specified data
in the generated value. All dependency data is inserted after any
editing operations.
someKey <- generateKey node @Cat someKey (ownerId, veterinarianId) mempty anotherKey <- generateKey node @Cat anotherKey (ownerId, veterinarianId) $ edit $ \cat -> cat {name = "milo"}
A value that has an automatically managed key may use
instead.node
type GraphulaNode m a = (HasDependencies a, Logging m a, PersistEntityBackend a ~ SqlBackend, PersistEntity a, Typeable a, Arbitrary a) Source #
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" }
GraphulaContext m '[] = MonadGraphula m | |
GraphulaContext m (t ': ts) = (GraphulaNode m t, GraphulaContext m ts) |
Node options
data NodeOptions a Source #
Options for generating an individual node
can be created and combined with the Monoidal
operations NodeOptions
and (<>)
.mempty
a1 <- node A () mempty
a2 <- node
A () $ edit $ a -> a { someField = True }
a3 <- node @A () $ ensure $ (== True) . someField
Instances
Generic (NodeOptions a) Source # | |
Defined in Graphula type Rep (NodeOptions a) :: Type -> Type # from :: NodeOptions a -> Rep (NodeOptions a) x # to :: Rep (NodeOptions a) x -> NodeOptions a # | |
Semigroup (NodeOptions a) Source # | |
Defined in Graphula (<>) :: NodeOptions a -> NodeOptions a -> NodeOptions a # sconcat :: NonEmpty (NodeOptions a) -> NodeOptions a # stimes :: Integral b => b -> NodeOptions a -> NodeOptions a # | |
Monoid (NodeOptions a) Source # | |
Defined in Graphula mempty :: NodeOptions a # mappend :: NodeOptions a -> NodeOptions a -> NodeOptions a # mconcat :: [NodeOptions a] -> NodeOptions a # | |
type Rep (NodeOptions a) Source # | |
Defined in Graphula |
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
Declaring Dependencies and key source
class HasDependencies a where Source #
Nothing
type Dependencies a Source #
A data type that contains values to be injected into a
via
dependsOn
. The default generic implementation of dependsOn
supports
tuples as Dependencies
. Data types with a single dependency should use
Only
as a 1-tuple.
note: The contents of a tuple must be ordered as they appear in the
definition of a
.
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'SourceExternal -- explicitly pass a key using
Arbitrary
nodeKeyed
Most types will use
or SourceDefault
. Only
use SourceArbitrary
if the key for a value is always defined
externally.SourceExternal
type KeySource _a = 'SourceDefault
dependsOn :: a -> Dependencies a -> a Source #
Assign values from the Dependencies
collection to a value.
dependsOn
must be an idempotent operation.
Law:
(\x d -> x `dependsOn` d `dependsOn` d) = dependsOn
default dependsOn :: (HasEot a, HasEot (Dependencies a), GHasDependencies (Proxy a) (Proxy (Dependencies a)) (Eot a) (Eot (Dependencies a))) => a -> Dependencies a -> a Source #
data KeySourceType Source #
SourceDefault | Generate keys using the database's |
SourceArbitrary | |
SourceExternal | Always explicitly pass an external key |
Abstract over how keys are generated using SourceDefault
or
class (GenerateKeyInternal (KeySource a) a, KeyConstraint (KeySource a) a) => GenerateKey a Source #
Abstract over how keys are generated using
or SourceDefault
SourceArbitrary
Instances
(GenerateKeyInternal (KeySource a) a, KeyConstraint (KeySource a) a) => GenerateKey a Source # | |
Defined in Graphula |
Singular Dependencies
For entities that only have singular Dependencies
Instances
Functor Only Source # | |
Foldable Only Source # | |
Defined in Graphula 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 # elem :: Eq a => a -> Only a -> Bool # maximum :: Ord a => Only a -> a # | |
Traversable Only Source # | |
Eq a => Eq (Only a) Source # | |
Ord a => Ord (Only a) Source # | |
Show a => Show (Only a) Source # | |
Generic (Only a) Source # | |
type Rep (Only a) Source # | |
type Keys (Only (Entity a)) Source # | |
The Graph Monad
Type Classes
type MonadGraphula m = (Monad m, MonadGraphulaBackend m, MonadGraphulaFrontend m, MonadIO m) Source #
class MonadGraphulaBackend m where Source #
type Logging m :: Type -> Constraint Source #
Instances
(MonadGraphulaBackend m, MonadIO m) => MonadGraphulaBackend (GraphulaLoggedT m) Source # | |
Defined in Graphula type Logging (GraphulaLoggedT m) :: Type -> Constraint Source # askGen :: GraphulaLoggedT m (IORef QCGen) Source # logNode :: Logging (GraphulaLoggedT m) a => a -> GraphulaLoggedT m () Source # | |
MonadIO m => MonadGraphulaBackend (GraphulaT n m) Source # | |
class MonadGraphulaFrontend m where Source #
insert :: (PersistEntityBackend a ~ SqlBackend, PersistEntity a, Monad m) => Maybe (Key a) -> a -> m (Maybe (Entity a)) Source #
remove :: (PersistEntityBackend a ~ SqlBackend, PersistEntity a, Monad m) => Key a -> m () Source #
Instances
Backends
:: MonadUnliftIO m | |
=> Maybe Int | Optional seed |
-> (forall b. ReaderT SqlBackend n b -> m b) | Database runner |
-> GraphulaT n m a | |
-> m a |
Instances
runGraphulaLoggedT :: MonadUnliftIO m => GraphulaLoggedT m a -> m a Source #
An extension of runGraphulaT
that logs all nodes to a temporary file on
Exception
and re-throws the Exception
.
runGraphulaLoggedWithFileT :: MonadUnliftIO m => FilePath -> GraphulaLoggedT m a -> m a Source #
A variant of runGraphulaLoggedT
that accepts a file path to logged to
instead of utilizing a temp file.
data GraphulaLoggedT m a Source #
Instances
Frontends
runGraphulaIdempotentT :: MonadUnliftIO m => GraphulaIdempotentT m a -> m a Source #
A wrapper around a graphula frontend that produces finalizers to remove graph nodes on error or completion. An idempotent graph produces no data outside of its own closure.
runGraphIdentity . runGraphulaIdempotentT . runGraphulaT $ do node @PancakeBreakfast () mempty
data GraphulaIdempotentT m a Source #
Instances
Extras
class NoConstraint a Source #
Graphula accepts constraints for various uses. Frontends do not always
utilize these constraints. NoConstraint
is a universal class that all
types inhabit. It has no behavior and no additional constraints.
Instances
NoConstraint a Source # | |
Defined in Graphula.Internal |
Exceptions
data GenerationFailure Source #
GenerationFailureMaxAttemptsToConstrain TypeRep | Could not satisfy constraints defined using |
GenerationFailureMaxAttemptsToInsert TypeRep | Could not satisfy database constraints on insert |
Instances
Eq GenerationFailure Source # | |
Defined in Graphula (==) :: GenerationFailure -> GenerationFailure -> Bool # (/=) :: GenerationFailure -> GenerationFailure -> Bool # | |
Show GenerationFailure Source # | |
Defined in Graphula showsPrec :: Int -> GenerationFailure -> ShowS # show :: GenerationFailure -> String # showList :: [GenerationFailure] -> ShowS # | |
Exception GenerationFailure Source # | |
Defined in Graphula |