persistent-documentation-0.1.0.4: Documentation DSL for persistent entities
Safe HaskellNone
LanguageHaskell2010

Database.Persist.Documentation

Description

This module contains code for documenting a set of persistent entity definitions. All the library provides is a means to render a Markdown document with table and column documentation and comments. A further expansion could use the information here to generate PostgreSQL COMMENTs on the fields and tables too.

Getting Started

You probably already have a persistent entity definitions somewhere, and they probably look like this:

share [mkPersist sqlSettings] [persistUpperCase|
  User
    firstName Text.Text
    active    Bool
    deriving Show Eq Read Ord
|]

The persistUpperCase QuasiQuoter parses the block of text and returns a value of type [EntityDef]. We need to get our hands on that definition so we can document it. We'll use the mkEntityDefList function to expose it:

share
  [ mkPersist sqlSettings
  , mkEntityDefList "entityDefs"
  ] [persistUpperCase|
  User
    firstName Text.Text
    active    Bool
    deriving Show Eq Read Ord
|]

You may want to factor out the quasiquoter into a term and import from another module. This has an important downside: the ID fields from the QuasiQuoter are given as Int64 regardless of what they actually are. It's not possible for the persistent quasiquoter to properly know the types of the IDs.

Documentating The Schema

Now, we're going to use the document function to link up the entityDefs with a documentation expression (type EntityDoc).

docs :: [EntityDef]
docs = document entityDefs $ do
  pure ()

The EntityDoc type is a monad, and we'll use do notation to sequence multiple entity definitions.

docs :: [EntityDef]
docs = document entityDefs $ do
  User --^ do
    pure ()

The --^ operator mimics the Haddock comment syntax. We use the constructor of the entity (in this case, User). On the right, we provide documentation for the entity. The right hand expression will have the type FieldDoc, and we can use do notation to construct it.

We can use string literals to document the entity itself, with the OverloadedStrings extension enabled. The string literals are concatenated, and used to provide entity-level comments. You'll need to manage whitespace yourself, though.

docs :: [EntityDef]
docs = document entityDefs $ do
  User --^ do
    "This is user documentation. "
    "You can have multiple lines, but you need to watch out for spaces. "
    "The lines will be combined."

We can also document the entity fields. We do this using the # operator.

docs :: [EntityDef]
docs = document entityDefs $ do
  User --^ do
    "This is user documentation. "
    "You can have multiple lines, but you need to watch out for spaces. "
    "The lines will be combined."

    UserFirstName # "The user's first name."
    UserActive    # "Whether or not the user is able to log in."

This attaches the comment to the entity field.

Rendering the Documentation

Finally, we'll use render and provide a Renderer to generate documentation. For an example of what this looks like, check out the file test/example.md in the repository (linked from the README).

renderedDocs :: Text
renderedDocs = render markdownTableRenderer docs
Synopsis

The Documentation DSL

document :: [EntityDef] -> EntityDoc -> [EntityDef] Source #

This function accepts a list of EntityDef and an EntityDoc block, and substitutes the entityComments and fieldComments from the EntityDoc.

Since: 0.1.0.0

(--^) Source #

Arguments

:: forall a r. (KnowResult a ~ r, Typeable r, RC r) 
=> a

A constructor for the Entity r you want to document.

-> FieldDoc r

A block that contains documentation for the Entity r.

-> EntityDoc 

Define documentation for an entity. The left-hand side takes the Entity constructor, and the right hand side takes a FieldDoc expression that documents the entity and it's fields.

Example

Expand
x :: EntityDoc
x = do
  User --^ do
    "This comment is for the entity User."
    UserName # "This comment is for a field.""

Since: 0.1.0.0

(#) :: FC rec typ => EntityField rec typ -> Text -> FieldDoc rec Source #

Write documentation for the given EntityField.

Example

Expand
x :: EntityDoc
x = do
  User --^ do
    "This comment is for the entity User."
    UserName # "This comment is for a field.""

Since: 0.1.0.0

type EntityDoc = EntityDoc' () Source #

An expression of EntityDoc is used to document the persistent schema. To construct an EntityDoc, you'll use the Entity constructor and the (--^) operator. Everything to the right of the (--^) operator is a 'FieldDoc rec' for the given entity.

This type is a monad, and you can use do notation to sequence the documentation.

doc :: EntityDoc
doc =  do
  User --^ "Documentation for a User"
  Dog --^ "Documentation for a Dog"

Since: 0.1.0.0

type FieldDoc s = FieldDoc' s () Source #

A FieldDoc expression provides documentation for the given Entity. This type is a Monad and you will want to use do notation to create this.

There are two ways to create FieldDoc lines:

  • String literals. These are collected and appended as documentation for the entity itself.
  • The (#) operator, which accepts an EntityField and the text documentation for that entity.

Since: 0.1.0.0

deriveShowFields :: [UnboundEntityDef] -> Q [Dec] Source #

Given a list of entity definitions, derives Show for all their fields. This is necessary for using this library for internal reasons, unfortunately.

Since: 0.1.0.0

Rendering Documentation

data Renderer rendered where Source #

A renderer for documented entities, abstract in the intermediate representations of entities and fields.

Since: 0.1.0.0

Constructors

Renderer 

Fields

  • :: { renderField :: FieldDef -> Maybe Text -> renderedField

    Render a field definition as some intermediate structure

  •    , renderFields :: [renderedField] -> renderedFields

    Fold a collection of rendered fields

  •    , renderEntity :: EntityDef -> Maybe Text -> renderedFields -> renderedEntity

    Attach some entity-level metadata to a rendered collection of fields

  •    , renderEntities :: [renderedEntity] -> rendered

    Finally, fold a collection of rendered entities

  •    } -> Renderer rendered
     

render :: Renderer rendered -> [EntityDef] -> rendered Source #

Given a Renderer for a list of entity defintiions, render it.

Since: 0.1.0.0

markdownTableRenderer :: Renderer Text Source #

A Renderer that generates Markdown tables for an entity.

Example

Expand

Given entityDefs like:

entityDefs :: [EntityDef]
entityDefs = [persistUpperCase|
  User
    firstName Text.Text
    active    Bool
    deriving Show Eq Read Ord
|]

and a doc block like:

docs :: [EntityDef]
docs = document entityDefs $ do
  User --^ do
    "you can use string literals to write documentation for the entity itself. "
    "The strings will be mappended together, so you'll need to handle "
    "whitespace yourself."
    UserFirstName # "The user's first name."
    UserActive # "Whether or not the user is able to log in."
    UserId # "You can document the user's ID field."

This will rende the given Markdown output:

# User

you can use string literals to write documentation for the entity itself. The strings will be
mappended together, so you'll need to handle whitespace yourself.

* Primary ID: id

| Column name | Type | Description |
|-|-|-|
| id | integer (64) | You can document the user's ID field. |
| firstName | string | The user's first name. |
| active | boolean | Whether or not the user is able to log in. |

Since: 0.1.0.0