packages feed

dataflow 0.3.2.0 → 0.4.0.0

raw patch · 8 files changed

+325/−30 lines, 8 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- DataFlow.Core: Edge :: ID -> ID -> Operation -> Description -> Object
- DataFlow.Core: External :: ID -> Name -> Object
- DataFlow.Core: GenState :: Indent -> IndentNext -> Step -> GenState
- DataFlow.Core: Process :: ID -> Name -> Object
- DataFlow.Core: brackets :: Enclosing
- DataFlow.Core: curlyBrackets :: Enclosing
- DataFlow.Core: data GenState
- DataFlow.Core: incrStep :: Gen ()
- DataFlow.Core: type Enclosing = (Char, Char)
- DataFlow.Core: type Indent = Int
- DataFlow.Core: type IndentNext = Bool
- DataFlow.Core: type Step = Int
- DataFlow.DFD: runDfd :: Diagram -> String
+ DataFlow.Core: Brackets :: Enclosing
+ DataFlow.Core: CurlyBrackets :: Enclosing
+ DataFlow.Core: Flow :: ID -> ID -> Operation -> Description -> Object
+ DataFlow.Core: Function :: ID -> Name -> Object
+ DataFlow.Core: InputOutput :: ID -> Name -> Object
+ DataFlow.Core: data Enclosing
+ DataFlow.Core: evalDiagram :: Gen () -> String
+ DataFlow.DFD: evalDfd :: Diagram -> String

Files

+ README.md view
@@ -0,0 +1,68 @@+# DataFlow++Generate Graphviz documents from a Haskell representation.++```+cabal configure+cabal build+```++## Usage++![Legend](https://rawgit.com/owickstrom/dataflow/master/example/legend.svg)++The objects supported by DataFlow is:++* `TrustBoundary`+* `InputOutput`+* `Function`+* `Flow`++These are composed in a `Diagram` to get something printable.++For more information see the [Hackage site](https://hackage.haskell.org/package/dataflow).++## Example++```haskell+module Main where++import DataFlow.Core+import DataFlow.DFD++main :: IO ()+main = printDfd $+  Diagram "My Diagram" [+    TrustBoundary "browser" "Browser" [+      Function "webapp" "Webapp"+    ],+    TrustBoundary "aws" "Amazon AWS" [+      Function "server" "Web Server",+      Database "logs" "Logs"+    ],+    InputOutput "analytics" "Google Analytics",++    Flow "webapp" "server" "Request /" "",+    Flow "server" "logs" "Log" "User IP",+    Flow "server" "webapp" "Response" "User Profile",++    Flow "webapp" "analytics" "Log" "Page Navigation"+  ]+```++Then generate your output with dot.++```bash+runhaskell example.hs | dot -Tsvg > example.svg+```++That should generate something like the following.++![Example Output](https://rawgit.com/owickstrom/dataflow/master/example/example.svg)++## Building the Examples++```bash+cabal install+make -C example+```
dataflow.cabal view
@@ -1,16 +1,31 @@ name:                dataflow-version:             0.3.2.0+version:             0.4.0.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 format.+                     command. Currently it only supports the DFD output format+                     (http://en.wikipedia.org/wiki/Data_flow_diagram). Support+                     for a Graphviz-like input format (instead of using the+                     data structures in Haskell) is planned.+homepage:            https://github.com/owickstrom/dataflow license:             MIT license-file:        LICENSE author:              Oskar Wickström-maintainer:          oskar.wickstrom@live.com--- copyright:+maintainer:          oskar.wickstrom@gmail.com+copyright:           oskar.wickstrom@gmail.com+category:            Code Generation, Compiler, Graphs+ build-type:          Simple--- extra-source-files:+extra-source-files:  README.md,+                     LICENSE,+                     example/example.hs,+                     example/example.svg,+                     example/legend.hs,+                     example/legend.svg cabal-version:       >=1.10++source-repository head+  type: git+  location: git@github.com:owickstrom/dataflow.git  library   exposed-modules:
+ example/example.hs view
@@ -0,0 +1,23 @@+module Main where++import DataFlow.Core+import DataFlow.DFD++main :: IO ()+main = printDfd $+  Diagram "My Diagram" [+    TrustBoundary "browser" "Browser" [+      Function "webapp" "Webapp"+    ],+    TrustBoundary "aws" "Amazon AWS" [+      Function "server" "Web Server",+      Database "logs" "Logs"+    ],+    InputOutput "analytics" "Google Analytics",++    Flow "webapp" "server" "Request /" "",+    Flow "server" "logs" "Log" "User IP",+    Flow "server" "webapp" "Response" "User Profile",++    Flow "webapp" "analytics" "Log" "Page Navigation"+  ]
+ example/example.svg view
@@ -0,0 +1,70 @@+<?xml version="1.0" encoding="UTF-8" standalone="no"?>+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">+<!-- Generated by graphviz version 2.38.0 (20140413.2041)+ -->+<!-- Title: My Diagram Pages: 1 -->+<svg width="502pt" height="382pt"+ viewBox="0.00 0.00 502.14 382.14" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">+<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 378.145)">+<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>+<!-- 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"/>+<text text-anchor="start" x="32.9406" y="-157.945" font-family="sans-serif" font-weight="bold" font-size="14.00">Webapp</text>+</g>+<!-- server -->+<g id="node2" class="node"><title>server</title>+<ellipse fill="none" stroke="black" cx="285.985" cy="-255.145" rx="56.7776" ry="56.7776"/>+<text text-anchor="start" x="249.153" y="-251.945" font-family="sans-serif" font-weight="bold" font-size="14.00">Web Server</text>+</g>+<!-- webapp&#45;&gt;server -->+<g id="edge1" class="edge"><title>webapp&#45;&gt;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&#45;&gt;</text>+</g>+<!-- analytics -->+<g id="node4" class="node"><title>analytics</title>+<polygon fill="none" stroke="black" stroke-width="2" points="346.274,-120.434 225.695,-120.434 225.695,0.144879 346.274,0.144879 346.274,-120.434"/>+<text text-anchor="start" x="233.84" y="-56.9445" font-family="sans-serif" font-weight="bold" font-size="14.00">Google Analytics</text>+</g>+<!-- webapp&#45;&gt;analytics -->+<g id="edge4" class="edge"><title>webapp&#45;&gt;analytics</title>+<path fill="none" stroke="black" d="M84.2655,-126.418C94.0477,-114.842 106.228,-102.928 119.78,-95.1445 148.674,-78.5496 184.474,-69.7911 215.353,-65.1818"/>+<polygon fill="black" stroke="black" points="216.188,-68.6001 225.619,-63.7724 215.236,-61.6651 216.188,-68.6001"/>+<text text-anchor="start" x="144.799" y="-110.545" font-family="sans-serif" font-weight="bold" font-size="12.00">(4) Log</text>+<text text-anchor="start" x="119.78" y="-98.5445" font-family="sans-serif" font-size="12.00">Page Navigation</text>+</g>+<!-- server&#45;&gt;webapp -->+<g id="edge3" class="edge"><title>server&#45;&gt;webapp</title>+<path fill="none" stroke="black" d="M256.434,-206.502C240.39,-181.104 221.004,-153.919 207.84,-147.145 177.542,-131.553 139.128,-135.775 109.036,-143.516"/>+<polygon fill="black" stroke="black" points="107.874,-140.206 99.1738,-146.253 109.746,-146.952 107.874,-140.206"/>+<text text-anchor="start" x="127.792" y="-162.545" font-family="sans-serif" font-weight="bold" font-size="12.00">(3) Response</text>+<text text-anchor="start" x="132.468" y="-150.545" font-family="sans-serif" font-size="12.00">User Profile</text>+</g>+<!-- logs -->+<g id="node3" class="node"><title>logs</title>+<text text-anchor="start" x="434.958" y="-266.945" font-family="sans-serif" font-weight="bold" font-size="14.00">Logs</text>+<polyline fill="none" stroke="black" points="430.137,-258.145 470.137,-258.145 "/>+<polyline fill="none" stroke="black" points="470.137,-282.145 430.137,-282.145 "/>+</g>+<!-- server&#45;&gt;logs -->+<g id="edge2" class="edge"><title>server&#45;&gt;logs</title>+<path fill="none" stroke="black" d="M342.788,-260.307C365.568,-262.414 391.305,-264.795 411.754,-266.686"/>+<polygon fill="black" stroke="black" points="411.642,-270.191 421.922,-267.627 412.287,-263.221 411.642,-270.191"/>+<text text-anchor="start" x="365.122" y="-280.545" font-family="sans-serif" font-weight="bold" font-size="12.00">(2) Log</text>+<text text-anchor="start" x="364.129" y="-268.545" font-family="sans-serif" font-size="12.00">User IP</text>+</g>+</g>+</svg>
+ example/legend.hs view
@@ -0,0 +1,17 @@+module Main where++import DataFlow.Core+import DataFlow.DFD++main :: IO ()+main = printDfd $+  Diagram "Legend" [+    TrustBoundary "trust" "TrustBoundary" [+      Function "function" "Function",+      Database "database" "Database",+      InputOutput "io" "InputOutput"+    ],+    Flow "function" "io" "Flow" "",+    Flow "function" "database" "Flow" ""+  ]+
+ example/legend.svg view
@@ -0,0 +1,46 @@+<?xml version="1.0" encoding="UTF-8" standalone="no"?>+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">+<!-- Generated by graphviz version 2.38.0 (20140413.2041)+ -->+<!-- Title: Legend Pages: 1 -->+<svg width="297pt" height="284pt"+ viewBox="0.00 0.00 296.55 284.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">+<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 280)">+<title>Legend</title>+<polygon fill="white" stroke="none" points="-4,4 -4,-280 292.552,-280 292.552,4 -4,4"/>+<text text-anchor="start" x="110.907" y="-257" font-family="sans-serif" font-weight="bold" font-size="20.00">Legend</text>+<g id="clust1" class="cluster"><title>cluster_trust</title>+<polygon fill="none" stroke="#4d4d4d" stroke-dasharray="5,2" points="8,-8 8,-240 280.552,-240 280.552,-8 8,-8"/>+<text text-anchor="start" x="111.666" y="-228" font-family="sans-serif" font-size="10.00" fill="#4d4d4d">TrustBoundary</text>+</g>+<!-- function -->+<g id="node1" class="node"><title>function</title>+<ellipse fill="none" stroke="black" cx="60.0255" cy="-129" rx="44.051" ry="44.051"/>+<text text-anchor="start" x="33.1773" y="-125.8" font-family="sans-serif" font-weight="bold" font-size="14.00">Function</text>+</g>+<!-- database -->+<g id="node2" class="node"><title>database</title>+<text text-anchor="start" x="198.504" y="-30.8" font-family="sans-serif" font-weight="bold" font-size="14.00">Database</text>+<polyline fill="none" stroke="black" points="193.469,-22 262.469,-22 "/>+<polyline fill="none" stroke="black" points="262.469,-46 193.469,-46 "/>+</g>+<!-- function&#45;&gt;database -->+<g id="edge2" class="edge"><title>function&#45;&gt;database</title>+<path fill="none" stroke="black" d="M98.6807,-107.443C124.884,-92.4419 159.787,-72.4609 186.571,-57.1275"/>+<polygon fill="black" stroke="black" points="188.379,-60.1253 195.319,-52.1195 184.901,-54.0504 188.379,-60.1253"/>+<text text-anchor="start" x="122.051" y="-96.4" font-family="sans-serif" font-weight="bold" font-size="12.00">(2) Flow</text>+</g>+<!-- io -->+<g id="node3" class="node"><title>io</title>+<polygon fill="none" stroke="black" stroke-width="2" points="272.635,-213.665 183.304,-213.665 183.304,-124.335 272.635,-124.335 272.635,-213.665"/>+<text text-anchor="start" x="191.387" y="-165.8" font-family="sans-serif" font-weight="bold" font-size="14.00">InputOutput</text>+</g>+<!-- function&#45;&gt;io -->+<g id="edge1" class="edge"><title>function&#45;&gt;io</title>+<path fill="none" stroke="black" d="M102.9,-139.094C124.311,-144.255 150.608,-150.593 173.472,-156.105"/>+<polygon fill="black" stroke="black" points="172.828,-159.55 183.37,-158.49 174.468,-152.745 172.828,-159.55"/>+<text text-anchor="start" x="122.051" y="-157.4" font-family="sans-serif" font-weight="bold" font-size="12.00">(1) Flow</text>+</g>+</g>+</svg>
src/DataFlow/Core.hs view
@@ -1,31 +1,71 @@-module DataFlow.Core where+module DataFlow.Core (+  ID,+  Name,+  Operation,+  Description,+  Diagram(..),+  Object(..),+  Gen,+  evalDiagram,+  write,+  writeln,+  nextStep,+  indent,+  dedent,+  withIndent,+  blank,+  label,+  tag,+  bold,+  table,+  tr,+  td,+  Enclosing(..),+  objectWith,+  attrs+  ) where  import Control.Monad.State import Control.Monad.Writer +-- | An identifier corresponding to those in Graphviz. type ID = String+-- | The name of a 'Diagram' or 'Object'. type Name = String+-- | Operation heading. type Operation = String+-- | Operation description. type Description = String  -- | The top level diagram. data Diagram = Diagram Name [Object]  -- | An object in a diagram.-data Object = External ID Name+data Object =+            -- | A "Input" or "Output" in DFD.+            InputOutput ID Name+            -- | Surrounds other objects, denoting a boundary.             | TrustBoundary ID Name [Object]-            | Process ID Name+            -- | A \"Function\" in DFD.+            | Function ID Name+            -- | A \"Database\" in DFD.             | Database ID Name-            | Edge ID ID Operation Description deriving (Show, Eq)+            -- | Describes the flow of data between two objects.+            | Flow ID ID Operation Description deriving (Show, Eq)  type Indent = Int type IndentNext = Bool type Step = Int data GenState = GenState Indent IndentNext Step --- | The monad stack for generating output based on Diagram.+-- | The Gen represents some output generator that runs on a 'Diagram'.. type Gen t = WriterT [String] (State GenState) t +-- | Run the 'Gen' and get the output as a 'String'.+evalDiagram :: Gen () -> String+evalDiagram g = concat $ evalState (execWriterT g) (GenState 0 False 0)++-- | Write a string to the output (no linefeed). write :: String -> Gen () write s = do   (GenState n indentNext step) <- lift get@@ -34,6 +74,7 @@     else tell [s]   put $ GenState n False step +-- | Write a string to the output (with linefeed). writeln :: String -> Gen () writeln s = do   write s@@ -43,63 +84,80 @@ incrStep :: Gen () incrStep = modify $ \(GenState n indentNext s') -> GenState n indentNext (s' + 1) +-- | Get the next \"step\" number (the order of flow arrows in the diagram). nextStep :: Gen Int nextStep = do   incrStep   (GenState _ _ s) <- lift get   return s +-- | Increase indent with 2 spaces. indent :: Gen () indent = modify $ \(GenState n indentNext s) -> GenState (n + 2) indentNext s +-- | Decrease indent with 2 spaces. dedent :: Gen () dedent = modify $ \(GenState n indentNext s) -> GenState (n - 2) indentNext s +-- | Indent the output of gen with 2 spaces. withIndent :: Gen () -> Gen () withIndent gen = do   indent   gen   dedent +-- | Write a blank line. blank :: Gen () blank = tell [""] +-- | Write a label with the output of gen as its contents. label :: Gen () -> Gen () 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 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 () bold = tag "b" "" +-- | Write a \<table\> tag, with attributes, surrounding the output of+--   another 'Gen'. table :: String -> Gen () -> Gen () table = tag "table" +-- | Write a \<tr\> tag surrounding the output of another 'Gen'. tr :: Gen () -> Gen () tr = tag "tr" "" +-- | Write a \<td\> tag surrounding the output of another 'Gen'. td :: Gen () -> Gen () td = tag "td" "" -type Enclosing = (Char, Char)-brackets, curlyBrackets :: Enclosing-brackets = ('[', ']')-curlyBrackets = ('{', '}')+-- | The enclosing characters in a block.+data Enclosing = Brackets | CurlyBrackets +-- | Write an object with the given 'Enclosing' characters, 'ID' and 'Gen' as+--   its contents. objectWith :: Enclosing -> ID -> Gen () -> Gen ()-objectWith (before, after) id' attributes = do-  blank-  writeln $ id' ++ " " ++ [before]-  withIndent attributes-  writeln [after]+objectWith enc id' attributes =+  do+    blank+    writeln $ id' ++ " " ++ before enc+    withIndent attributes+    writeln $ after enc+  where before Brackets = "["+        before CurlyBrackets = "{"+        after Brackets = "]"+        after CurlyBrackets = "}" +-- | Write an attributes declaration for the given 'ID'. attrs :: ID -> String -> Gen ()-attrs id' = objectWith brackets id' . writeln-+attrs id' = objectWith Brackets id' . writeln
src/DataFlow/DFD.hs view
@@ -1,7 +1,5 @@ module DataFlow.DFD where -import Control.Monad.State-import Control.Monad.Writer import DataFlow.Core  -- | Type class for types that can be rendered as DFD.@@ -9,7 +7,7 @@   dfd :: t -> Gen ()  instance DFD Object where-  dfd (External id' name) = objectWith brackets id' $ do+  dfd (InputOutput id' name) = objectWith Brackets id' $ do     writeln "shape = square;"     writeln "style = bold;"     label $ bold $ write name@@ -26,11 +24,11 @@       writeln "graph[style = dashed, color=grey30];"     writeln "}" -  dfd (Process id' name) = objectWith brackets id' $ do+  dfd (Function id' name) = objectWith Brackets id' $ do     writeln "shape = circle;"     label $ bold $ write name -  dfd (Database id' name) = objectWith brackets id' $ do+  dfd (Database id' name) = objectWith Brackets id' $ do     label $       table "sides=\"TB\" cellborder=\"0\"" $         tr $@@ -38,7 +36,7 @@             bold $ write name     writeln "shape = none;" -  dfd (Edge i1 i2 operation description) = do+  dfd (Flow i1 i2 operation description) = do     step <- nextStep     blank     writeln $ i1 ++ " -> " ++ i2 ++ " ["@@ -70,9 +68,9 @@     writeln "}"  -- | Generates the DFD output as a String.-runDfd :: Diagram -> String-runDfd diagram = concat $ evalState (execWriterT (dfd diagram)) (GenState 0 False 0)+evalDfd :: Diagram -> String+evalDfd = evalDiagram . dfd  -- | Prints the DFD output to stdout. printDfd :: Diagram -> IO ()-printDfd = putStr . runDfd+printDfd = putStr . evalDfd