dataflow 0.4.1.0 → 0.4.2.0
raw patch · 5 files changed
+138/−88 lines, 5 filesdep +containersPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependencies added: containers
API changes (from Hackage documentation)
- DataFlow.Core: type Gen t = WriterT [String] (State GenState) t
- DataFlow.DFD: class DFD t
- DataFlow.DFD: instance DFD Diagram
- DataFlow.DFD: instance DFD Object
+ DataFlow.Core: type Renderer t = WriterT [String] (State RendererState) t
+ DataFlow.DFD: class RenderDFD t
+ DataFlow.DFD: exists :: (ID, ID) -> DFDState Bool
+ DataFlow.DFD: instance RenderDFD Diagram
+ DataFlow.DFD: instance RenderDFD Object
+ DataFlow.DFD: register :: (ID, ID) -> DFDState ()
+ DataFlow.DFD: return' :: t -> DFDRenderer t
+ DataFlow.DFD: shouldInvert :: (ID, ID) -> DFDState Bool
+ DataFlow.DFD: type DFDRenderer t = DFDState (Renderer t)
+ DataFlow.DFD: type DFDState v = State (Map (ID, ID) Bool) v
- DataFlow.Core: attrs :: ID -> String -> Gen ()
+ DataFlow.Core: attrs :: ID -> String -> Renderer ()
- DataFlow.Core: blank :: Gen ()
+ DataFlow.Core: blank :: Renderer ()
- DataFlow.Core: bold :: Gen () -> Gen ()
+ DataFlow.Core: bold :: Renderer () -> Renderer ()
- DataFlow.Core: dedent :: Gen ()
+ DataFlow.Core: dedent :: Renderer ()
- DataFlow.Core: evalDiagram :: Gen () -> String
+ DataFlow.Core: evalDiagram :: Renderer () -> String
- DataFlow.Core: indent :: Gen ()
+ DataFlow.Core: indent :: Renderer ()
- DataFlow.Core: label :: Gen () -> Gen ()
+ DataFlow.Core: label :: Renderer () -> Renderer ()
- DataFlow.Core: nextStep :: Gen Int
+ DataFlow.Core: nextStep :: Renderer Int
- DataFlow.Core: objectWith :: Enclosing -> ID -> Gen () -> Gen ()
+ DataFlow.Core: objectWith :: Enclosing -> ID -> Renderer () -> Renderer ()
- DataFlow.Core: table :: String -> Gen () -> Gen ()
+ DataFlow.Core: table :: String -> Renderer () -> Renderer ()
- DataFlow.Core: tag :: String -> String -> Gen () -> Gen ()
+ DataFlow.Core: tag :: String -> String -> Renderer () -> Renderer ()
- DataFlow.Core: td :: Gen () -> Gen ()
+ DataFlow.Core: td :: Renderer () -> Renderer ()
- DataFlow.Core: tr :: Gen () -> Gen ()
+ DataFlow.Core: tr :: Renderer () -> Renderer ()
- DataFlow.Core: withIndent :: Gen () -> Gen ()
+ DataFlow.Core: withIndent :: Renderer () -> Renderer ()
- DataFlow.Core: write :: String -> Gen ()
+ DataFlow.Core: write :: String -> Renderer ()
- DataFlow.Core: writeln :: String -> Gen ()
+ DataFlow.Core: writeln :: String -> Renderer ()
- DataFlow.DFD: dfd :: DFD t => t -> Gen ()
+ DataFlow.DFD: dfd :: RenderDFD t => t -> DFDRenderer ()
Files
- README.md +3/−2
- dataflow.cabal +3/−2
- example/example.svg +5/−5
- src/DataFlow/Core.hs +35/−35
- src/DataFlow/DFD.hs +92/−44
README.md view
@@ -16,11 +16,12 @@ * `TrustBoundary` * `InputOutput` * `Function`+* `Database` * `Flow` These are composed in a `Diagram` to get something printable. -For more information see the [Hackage site](https://hackage.haskell.org/package/dataflow).+For more on Haskell data types, see [the Hackage site](https://hackage.haskell.org/package/dataflow). ## Example @@ -70,5 +71,5 @@ ## Release ```bash-cabal clean && cabal build && cabal haddock && cabal sdist && cabal upload dist/dataflow-*.tar.gz+cabal clean && cabal build && cabal sdist && cabal upload dist/dataflow-*.tar.gz ```
dataflow.cabal view
@@ -1,5 +1,5 @@ name: dataflow-version: 0.4.1.0+version: 0.4.2.0 synopsis: Generate Graphviz documents from a Haskell representation. description: Outputs .dot files that can be processed by the dot command. Currently it only supports the DFD output format@@ -33,6 +33,7 @@ DataFlow.DFD build-depends: base >=4 && <4.8,- mtl >=2.2+ mtl >=2.2,+ containers >= 0.4 hs-source-dirs: src default-language: Haskell2010
example/example.svg view
@@ -10,14 +10,14 @@ <title>My Diagram</title> <polygon fill="white" stroke="none" points="-4,4 -4,-378.145 498.137,-378.145 498.137,4 -4,4"/> <text text-anchor="start" x="193.172" y="-355.145" font-family="sans-serif" font-weight="bold" font-size="20.00">My Diagram</text>-<g id="clust1" class="cluster"><title>cluster_browser</title>-<polygon fill="none" stroke="#4d4d4d" stroke-dasharray="5,2" points="8,-110.145 8,-230.145 109.78,-230.145 109.78,-110.145 8,-110.145"/>-<text text-anchor="start" x="40.5524" y="-218.145" font-family="sans-serif" font-size="10.00" fill="#4d4d4d">Browser</text>-</g> <g id="clust2" class="cluster"><title>cluster_aws</title> <polygon fill="none" stroke="#4d4d4d" stroke-dasharray="5,2" points="221.346,-190.145 221.346,-338.145 486.137,-338.145 486.137,-190.145 221.346,-190.145"/> <text text-anchor="start" x="323.078" y="-326.145" font-family="sans-serif" font-size="10.00" fill="#4d4d4d">Amazon AWS</text> </g>+<g id="clust1" class="cluster"><title>cluster_browser</title>+<polygon fill="none" stroke="#4d4d4d" stroke-dasharray="5,2" points="8,-110.145 8,-230.145 109.78,-230.145 109.78,-110.145 8,-110.145"/>+<text text-anchor="start" x="40.5524" y="-218.145" font-family="sans-serif" font-size="10.00" fill="#4d4d4d">Browser</text>+</g> <!-- webapp --> <g id="node1" class="node"><title>webapp</title> <ellipse fill="none" stroke="black" cx="58.8898" cy="-161.145" rx="42.7799" ry="42.7799"/>@@ -32,7 +32,7 @@ <g id="edge1" class="edge"><title>webapp->server</title> <path fill="none" stroke="black" d="M98.8414,-177.411C133.323,-191.81 184.216,-213.063 224.076,-229.709"/> <polygon fill="black" stroke="black" points="222.86,-232.994 233.436,-233.618 225.557,-226.534 222.86,-232.994"/>-<text text-anchor="middle" x="163.81" y="-224.545" font-family="sans-serif" font-size="12.00">webappserver-></text>+<text text-anchor="start" x="129.128" y="-225.545" font-family="sans-serif" font-weight="bold" font-size="12.00">(1) Request /</text> </g> <!-- analytics --> <g id="node4" class="node"><title>analytics</title>
src/DataFlow/Core.hs view
@@ -5,7 +5,7 @@ Description, Diagram(..), Object(..),- Gen,+ Renderer, evalDiagram, write, writeln,@@ -56,97 +56,97 @@ type Indent = Int type IndentNext = Bool type Step = Int-data GenState = GenState Indent IndentNext Step+data RendererState = RendererState Indent IndentNext Step --- | The Gen represents some output generator that runs on a 'Diagram'..-type Gen t = WriterT [String] (State GenState) t+-- | The Renderer represents some output generator that runs on a 'Diagram'.+type Renderer t = WriterT [String] (State RendererState) t --- | Run the 'Gen' and get the output as a 'String'.-evalDiagram :: Gen () -> String-evalDiagram g = concat $ evalState (execWriterT g) (GenState 0 False 0)+-- | Run the 'Renderer' and get the output as a 'String'.+evalDiagram :: Renderer () -> String+evalDiagram g = concat $ evalState (execWriterT g) (RendererState 0 False 0) -- | Write a string to the output (no linefeed).-write :: String -> Gen ()+write :: String -> Renderer () write s = do- (GenState n indentNext step) <- lift get+ (RendererState n indentNext step) <- lift get if indentNext then tell [replicate n ' ' ++ s] else tell [s]- put $ GenState n False step+ put $ RendererState n False step -- | Write a string to the output (with linefeed).-writeln :: String -> Gen ()+writeln :: String -> Renderer () writeln s = do write s write "\n"- modify $ \(GenState n _ s') -> GenState n True s'+ modify $ \(RendererState n _ s') -> RendererState n True s' -incrStep :: Gen ()-incrStep = modify $ \(GenState n indentNext s') -> GenState n indentNext (s' + 1)+incrStep :: Renderer ()+incrStep = modify $ \(RendererState n indentNext s') -> RendererState n indentNext (s' + 1) -- | Get the next \"step\" number (the order of flow arrows in the diagram).-nextStep :: Gen Int+nextStep :: Renderer Int nextStep = do incrStep- (GenState _ _ s) <- lift get+ (RendererState _ _ s) <- lift get return s -- | Increase indent with 2 spaces.-indent :: Gen ()-indent = modify $ \(GenState n indentNext s) -> GenState (n + 2) indentNext s+indent :: Renderer ()+indent = modify $ \(RendererState n indentNext s) -> RendererState (n + 2) indentNext s -- | Decrease indent with 2 spaces.-dedent :: Gen ()-dedent = modify $ \(GenState n indentNext s) -> GenState (n - 2) indentNext s+dedent :: Renderer ()+dedent = modify $ \(RendererState n indentNext s) -> RendererState (n - 2) indentNext s -- | Indent the output of gen with 2 spaces.-withIndent :: Gen () -> Gen ()+withIndent :: Renderer () -> Renderer () withIndent gen = do indent gen dedent -- | Write a blank line.-blank :: Gen ()+blank :: Renderer () blank = tell [""] -- | Write a label with the output of gen as its contents.-label :: Gen () -> Gen ()+label :: Renderer () -> Renderer () label contents = do write "label = <" contents writeln ">;" -- | Write an HTML tag t with the output of gen as its contents.-tag :: String -> String -> Gen () -> Gen ()+tag :: String -> String -> Renderer () -> Renderer () tag t a contents = do write $ "<" ++ t ++ (if null a then "" else " " ++ a) ++ ">" contents write $ "</" ++ t ++ ">" --- | Write a \<b\> tag surrounding the output of another 'Gen'.-bold :: Gen () -> Gen ()+-- | Write a \<b\> tag surrounding the output of another 'Renderer'.+bold :: Renderer () -> Renderer () bold = tag "b" "" -- | Write a \<table\> tag, with attributes, surrounding the output of--- another 'Gen'.-table :: String -> Gen () -> Gen ()+-- another 'Renderer'.+table :: String -> Renderer () -> Renderer () table = tag "table" --- | Write a \<tr\> tag surrounding the output of another 'Gen'.-tr :: Gen () -> Gen ()+-- | Write a \<tr\> tag surrounding the output of another 'Renderer'.+tr :: Renderer () -> Renderer () tr = tag "tr" "" --- | Write a \<td\> tag surrounding the output of another 'Gen'.-td :: Gen () -> Gen ()+-- | Write a \<td\> tag surrounding the output of another 'Renderer'.+td :: Renderer () -> Renderer () td = tag "td" "" -- | The enclosing characters in a block. data Enclosing = Brackets | CurlyBrackets --- | Write an object with the given 'Enclosing' characters, 'ID' and 'Gen' as+-- | Write an object with the given 'Enclosing' characters, 'ID' and 'Renderer' as -- its contents.-objectWith :: Enclosing -> ID -> Gen () -> Gen ()+objectWith :: Enclosing -> ID -> Renderer () -> Renderer () objectWith enc id' attributes = do blank@@ -159,5 +159,5 @@ after CurlyBrackets = "}" -- | Write an attributes declaration for the given 'ID'.-attrs :: ID -> String -> Gen ()+attrs :: ID -> String -> Renderer () attrs id' = objectWith Brackets id' . writeln
src/DataFlow/DFD.hs view
@@ -1,34 +1,73 @@ module DataFlow.DFD where +import Control.Monad.Identity+import Control.Monad.State+import Data.Map (Map)+import qualified Data.Map as Map import DataFlow.Core +type DFDState v = State (Map (ID, ID) Bool) v+type DFDRenderer t = DFDState (Renderer t)+ -- | Type class for types that can be rendered as DFD.-class DFD t where- dfd :: t -> Gen ()+class RenderDFD t where+ dfd :: t -> DFDRenderer () -instance DFD Object where- dfd (InputOutput id' name) = objectWith Brackets id' $ do- writeln "shape = square;"- writeln "style = bold;"- label $ bold $ write name+return' :: t -> DFDRenderer t+return' v = return . lift . return $ v +exists :: (ID, ID) -> DFDState Bool+exists k = do+ m <- get+ return $ case Map.lookup k m of+ (Just _) -> True+ _ -> False++register :: (ID, ID) -> DFDState ()+register k = do+ m <- get+ put $ Map.insert k True m+ return ()++shouldInvert :: (ID, ID) -> DFDState Bool+shouldInvert k@(i1, i2) = do+ e <- exists k+ if e+ then return False+ else do+ ie <- exists (i2, i1)+ if ie+ then return True+ else do+ register k+ return False++instance RenderDFD Object where+ dfd (InputOutput id' name) =+ return $ objectWith Brackets id' $ do+ writeln "shape = square;"+ writeln "style = bold;"+ label $ bold $ write name+ dfd (TrustBoundary id' name objects) = do- blank- writeln $ "subgraph cluster_" ++ id' ++ " {"- withIndent $ do- mapM_ dfd objects+ renderObjects <- mapM dfd objects+ return $ do blank- writeln "fontsize = 10;"- writeln "fontcolor = grey30;"- label $ write name- writeln "graph[style = dashed, color=grey30];"- writeln "}"+ writeln $ "subgraph cluster_" ++ id' ++ " {"+ withIndent $ do+ blank+ sequence_ renderObjects+ writeln "fontsize = 10;"+ writeln "fontcolor = grey30;"+ label $ write name+ writeln "graph[style = dashed, color=grey30];"+ writeln "}" - dfd (Function id' name) = objectWith Brackets id' $ do+ dfd (Function id' name) = return $ objectWith Brackets id' $ do writeln "shape = circle;" label $ bold $ write name - dfd (Database id' name) = objectWith Brackets id' $ do+ dfd (Database id' name) = return $ objectWith Brackets id' $ do label $ table "sides=\"TB\" cellborder=\"0\"" $ tr $@@ -36,40 +75,49 @@ bold $ write name writeln "shape = none;" - dfd (Flow i1 i2 operation description) = do- step <- nextStep- blank- writeln $ i1 ++ " -> " ++ i2 ++ " ["- withIndent $- label $ do- bold $ write $ "(" ++ show step ++ ") " ++ operation- write "<br/>"- write description- writeln "]"--instance DFD Diagram where- dfd (Diagram title objects) = do- writeln $ "digraph \"" ++ title ++ "\" {"- withIndent $ do- attrs "graph" "fontname=\"sans-serif\""- attrs "node" "fontname=\"sans-serif\""- attrs "edge" "fontname=\"sans-serif\", fontsize=12"+ dfd (Flow i1 i2 operation description)= do+ back <- shouldInvert (i1, i2)+ return $ do+ step <- nextStep blank+ if back+ then writeln $ i2 ++ " -> " ++ i1 ++ " ["+ else writeln $ i1 ++ " -> " ++ i2 ++ " ["+ withIndent $ do+ when back $+ writeln "dir = back;"+ label $ do+ bold $ write $ "(" ++ show step ++ ") " ++ operation+ write "<br/>"+ write description+ writeln "]" - writeln "labelloc = \"t\";"- label $ bold $ write title- writeln "fontsize = 20;"+instance RenderDFD Diagram where+ dfd (Diagram title objects) =+ do+ renderObjects <- mapM dfd objects+ return $ do+ writeln $ "digraph \"" ++ title ++ "\" {"+ withIndent $ do+ attrs "graph" "fontname=\"sans-serif\""+ attrs "node" "fontname=\"sans-serif\""+ attrs "edge" "fontname=\"sans-serif\", fontsize=12"+ blank - writeln "nodesep = 1;"- writeln "rankdir = LR;"+ writeln "labelloc = \"t\";"+ label $ bold $ write title+ writeln "fontsize = 20;" - mapM_ dfd objects+ writeln "nodesep = 1;"+ writeln "rankdir = LR;" - writeln "}"+ sequence_ renderObjects + writeln "}"+ -- | Generates the DFD output as a String. evalDfd :: Diagram -> String-evalDfd = evalDiagram . dfd+evalDfd d = evalDiagram (evalState (dfd d) Map.empty) -- | Prints the DFD output to stdout. printDfd :: Diagram -> IO ()