packages feed

pptable (empty) → 0.1.0.0

raw patch · 7 files changed

+445/−0 lines, 7 filesdep +HUnitdep +QuickCheckdep +basesetup-changed

Dependencies added: HUnit, QuickCheck, base, boxes, containers, generic-deriving, pptable, pretty, syb, tasty, tasty-hunit, tasty-quickcheck, vector

Files

+ LICENSE view
@@ -0,0 +1,9 @@+The MIT License (MIT)++Copyright (c) 2016 Guru Devanla++Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:++The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ README.md view
@@ -0,0 +1,41 @@+# README #++PrettyTable : Print any list, vector or map as a well-formatted readable table.++### Text.PrettyPrint.PrettyTable ###++* This module provides simple functions used to print values in tabular format+* Version 0.1.0+* Contributes and Bug Reports Welcome++### Examples ###++``` haskell+ -- Printing a list of records+ :set -XDeriveGeneric+ :set -XDeriveDataTypeable++ import qualified GHC.Generics as G+ import Data.Data++ -- A record structure that will in a list+ data Stock = Stock {ticker::String, price::Double, marketCap::Double} deriving (Data, G.Generic)++ -- Create an instance of Tabilize+ instance Tabilize Stock++ let yahoo =  Stock {ticker="YHOO", price=42.29101010, marketCap=40e9}+ let google = Stock {ticker="GOOG", price=774.210101, marketCap=532.09e9}+ let amazon = Stock {ticker="AMZN", price=799.161717, marketCap=378.86e9}++ -- List of records+ let tickers = [yahoo, google, amazon]++ printList tickers++ ticker     price          marketCap+ "YHOO"     42.2910101         4.0e10+ "GOOG"     774.210101      5.3209e11+ "AMZN"     799.161717      3.7886e11++```
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ pptable.cabal view
@@ -0,0 +1,47 @@+name:                pptable+version:             0.1.0.0+synopsis:            Pretty Print containers in a tabular format+description:         Please see README.md+homepage:            https://github.com/gdevanla/pptable#readme+license:             MIT+license-file:        LICENSE+author:              Guru Devanla+maintainer:          grdvnl@gmail.com+copyright:           2016 Guru Devanla+category:            Text+build-type:          Simple+extra-source-files:  README.md+cabal-version:       >=1.10++library+  hs-source-dirs:      src+  exposed-modules:     Text.PrettyPrint.Tabilize, Text.PrettyPrint.PrettyTable+  build-depends:       base >= 4.7 && < 5+                     , syb+                     , containers+                     , pretty+                     , boxes+                     , vector+                     , generic-deriving+  default-language:    Haskell2010++test-suite pptable-test+  type:                exitcode-stdio-1.0+  hs-source-dirs:      test+  main-is:             Spec.hs+  build-depends:       base+                     , pptable+                     , tasty+                     , HUnit+                     , QuickCheck+                     , tasty-hunit+                     , tasty-quickcheck+                     , containers+                     , vector+                     , boxes+  ghc-options:         -threaded -rtsopts -with-rtsopts=-N+  default-language:    Haskell2010++source-repository head+  type:     git+  location: https://github.com/githubuser/pptable
+ src/Text/PrettyPrint/PrettyTable.hs view
@@ -0,0 +1,85 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE Rank2Types #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE DefaultSignatures #-}+{-# LANGUAGE TypeOperators #-}++-- | This module features methods that can be used to print values contained+-- inside a list \/ a map \/ or a vector+-- in a tabular format.++module Text.PrettyPrint.PrettyTable+    (+      -- ** Example of output+      -- $outputexample++      -- ** Basic Steps+      -- $steps+      +      -- ** Usage Example+      -- $examplelist+      +      -- ** Tabilize methods+      Tabilize(..)++      -- ** Custom Rendering+      -- $return_box+      +    ) where++import Text.PrettyPrint.Tabilize+++-- $outputexample+-- > ticker     price          marketCap+-- > "YHOO"     42.2910101         4.0e10+-- > "GOOG"     774.210101      5.3209e11+-- > "AMZN"     799.161717      3.7886e11++-- $steps+--+-- * Declare the new data type that will be the element of a list, map or a vector.+-- * The type could be a basic data constructor or a record type+-- * Derive 'Generic' and 'Data' for the type+-- * Make the type an instance of 'Text.PrettyPrint.Tabilize.Tabilize'+-- * Create values of the type and add them to a list, map or vector+-- * Use one of the 'printList', 'printMap' and 'printVector' methods to print the values in a tabular format++-- $notes+-- Note that any nested algebraic instances will be displayed by calling their respective 'Show' methods++-- $examplelist+--+-- > -- Printing a list of records+-- > :set -XDeriveGeneric+-- > :set -XDeriveDataTypeable+-- >+-- > import qualified GHC.Generics as G+-- > import Data.Data+-- >+-- > -- A record structure that will in a list+-- > data Stock = Stock {ticker::String, price::Double, marketCap::Double} deriving (Data, G.Generic)+-- >+-- > -- Create an instance of Tabilize+-- > instance Tabilize Stock+-- > +-- > let yahoo =  Stock {ticker="YHOO", price=42.29101010, marketCap=40e9}+-- > let google = Stock {ticker="GOOG", price=774.210101, marketCap=532.09e9}+-- > let amazon = Stock {ticker="AMZN", price=799.161717, marketCap=378.86e9}+-- >+-- > -- List of records+-- > let tickers = [yahoo, google, amazon]+-- > +-- > printList tickers+-- > +-- > ticker     price          marketCap+-- > "YHOO"     42.2910101         4.0e10+-- > "GOOG"     774.210101      5.3209e11+-- > "AMZN"     799.161717      3.7886e11+++-- $return_box+-- In cases, where the 'printList', 'printMap' or 'printVector' statements are not adequate, the 'listToBox', 'vectorToBox' and 'mapToBox' methods+-- return the B.Box value. The user can then use some of the methods provided by+-- "Text.PrettyPrint.Box" module.
+ src/Text/PrettyPrint/Tabilize.hs view
@@ -0,0 +1,186 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE Rank2Types #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE DefaultSignatures #-}+{-# LANGUAGE TypeOperators #-}++-- | Module implements the default methods for Tabilize++module Text.PrettyPrint.Tabilize+    (+      +      Tabilize+    , printList+    , printMap+    , printVector+    +    , listToBox+    , mapToBox+    , vectorToBox++    )where++import Data.Data+import Data.Typeable+import Data.Generics.Aliases+import GHC.Generics as G+import GHC.Show+import qualified Data.Map as Map+import qualified Text.PrettyPrint.Boxes as B+import qualified Data.List as List+import Text.Printf+import qualified Data.Vector as V++++-- | The Generalized class that provides the print+-- | functionality for any type that derives Generics and Data+class GTabilize f where+  gprintTable :: f a -> [B.Box]++--- | The instance class for Unit type+instance GTabilize U1 where+  gprintTable _ = []++-- | Any records or product types+-- | Nested algebraic types are printed using their respective Show methods+instance (GTabilize a, GTabilize b) => GTabilize (a :*: b) where+    gprintTable (a :*: b) = gprintTable a ++ gprintTable b++-- | Sum types+instance (GTabilize a, GTabilize b) => GTabilize (a :+: b) where+    gprintTable (L1 x) = gprintTable x+    gprintTable (R1 x) = gprintTable x++-- | +instance (GTabilize a) => GTabilize (M1 i c a) where+  gprintTable (M1 x) = gprintTable x+++-- | The leaf method that actually creates the Boxes that will+-- | be used to render the boxes as a table.+instance (Data a, Show a) => GTabilize (K1 i a) where+  gprintTable (K1 x) = createBox x++-- | Create the B.Box around the provided value+-- | If the value is an algebraic type the algebraic types+-- | Show method is used. All values have to be an instance of+-- | Data. This dependency is there to support identify Numeric+-- | values for right-aligning those values+createBox :: (Data x, Show x) => x -> [B.Box]+createBox x+  | isBool = [B.text $ show x]+  | isNumeric = [B.text $ printf "%10s" . show $ x]+  | otherwise = [B.text $ show x]+  where+    isBool = get_type == "Bool"+    isNumeric = get_type == "Integer" || get_type == "Double" || get_type == "Float"+    get_type = tyConName . typeRepTyCon . typeOf $ x++-- | Perform the final alignment of all+-- | generated [[B.Box]]+alignBox :: [[B.Box]] -> B.Box+alignBox b = B.hsep 5 B.left cols+  where+    cols = List.map (B.vcat B.left) $ List.transpose b++-- | Helper method that detects an algebraic data type+isAlgRepConstr t = case dataTypeRep . dataTypeOf $ t of+  AlgRep [_] -> True+  _ -> False++-- | Specialized class that provides default methods+-- methods to print List, Map or Vector values as a+-- pretty table+class Tabilize a where+  -- | Return a list of values wrapped in a Box. Each entry in input is assumed to be a+  -- list of records keyed by any data type. The first entry in the+  -- list of values will be used to infer the names of fields+  listToBox :: [a] -> [[B.Box]]++  -- | Return a list of values wrapped in Box. Each entry in input is assumed to be a+  -- list of records keyed by any data type. The first entry in the+  -- list of values will be used to infer the names of fields+  mapToBox ::(Show b) => Map.Map b a -> [[B.Box]]++  +  -- | Return a list of values wrapped in Box. Each entry in input is assumed to be a+  -- list of records keyed by any data type. The first entry in the+  -- list of values will be used to infer the names of fields+  vectorToBox :: V.Vector a -> [[B.Box]]++  default listToBox :: (G.Generic a, GTabilize (Rep a)) => [a] -> [[B.Box]]+  listToBox a = case a of+    [] -> [[B.nullBox]]+    x:xs -> gprintTable (from x):listToBox xs++  +  default mapToBox :: (G.Generic a, GTabilize (Rep a), Show b) => Map.Map b a -> [[B.Box]]+  mapToBox m =  Map.elems+                (Map.mapWithKey+                 (\k v -> B.text (show k):gprintTable (from v))+                 m)++  default vectorToBox :: (G.Generic a, GTabilize (Rep a)) => V.Vector a -> [[B.Box]]+  vectorToBox v = V.toList $ fmap (\x -> (gprintTable (from x))) v++  -- |+  -- > import qualified Data.Map as M+  -- > -- declare a Map+  -- > data Portfolio = M.Map String Stock+  -- > Add the Stock values we create+  -- > let p = M.fromList [("YHOO", yahoo), ("GOOG", google), ("AMZN" amazon)]+  -- >+  -- > printMap p+  -- >+  -- > Key        ticker     price          marketCap+  -- > "amzn"     "AMZN"     799.161717      3.7886e11+  -- > "goog"     "GOOG"     774.210101      5.3209e11+  -- > "yhoo"     "YHOO"     42.2910101         4.0e10+  printMap :: (Show b, Data a) => Map.Map b a -> IO ()+  printMap m = do+    let r = head . Map.elems $ m+    let header = constrFields . toConstr $ r+    let header_box = "Key":List.map (B.text) header+    B.printBox $ alignBox $ header_box:mapToBox m++  -- |+  -- > -- List of records+  -- > let tickers = [yahoo, google, amazon]+  -- > +  -- > printList tickers+  -- > +  -- > ticker     price          marketCap+  -- > "YHOO"     42.2910101         4.0e10+  -- > "GOOG"     774.210101      5.3209e11+  -- > "AMZN"     799.161717      3.7886e11+  printList :: (Data a) => [a] -> IO ()+  printList m = do+    let r = head $ m+    let header = constrFields . toConstr $ r+    let header_box = List.map (B.text) header+    B.printBox $ alignBox $ header_box:listToBox m++  -- |+  -- > import qualified Data.Vector as V+  -- > -- Vector of records+  -- > let tickers = V.fromList [yahoo, google, amazon]+  -- > +  -- > printVector tickers+  -- > +  -- > ticker     price          marketCap+  -- > "YHOO"     42.2910101         4.0e10+  -- > "GOOG"     774.210101      5.3209e11+  -- > "AMZN"     799.161717      3.7886e11+  printVector :: (Data a) => V.Vector a -> IO ()+  printVector m = do+    let r = m V.! 0+    let header = constrFields . toConstr $ r+    let header_box = List.map (B.text) header+    B.printBox $ alignBox $ header_box:vectorToBox m++  -- print :: (Functor f) => f a -> f [B.Box]+  -- default ppTabFL :: (G.Generic a, Tabilize (Rep a), Functor f) => (f a) -> f [B.Box]+  -- ppTabFL f = fmap (\x -> (gprintTable (from x))) f+
+ test/Spec.hs view
@@ -0,0 +1,75 @@+{-# OPTIONS_GHC -fno-warn-missing-signatures #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DeriveDataTypeable #-}++import Test.Tasty+import Data.Map as M+import Data.List as L+import Data.Vector as V+import Text.PrettyPrint.PrettyTable+import GHC.Generics as G+import Data.Data+import Text.PrettyPrint.Boxes as B++import Test.Tasty+import Test.Tasty.QuickCheck as QC+import Test.Tasty.HUnit++-- R0 has to derive from Data, since+-- it will be nested +data R0 =  R0 {test_string::String,+               test_integer::Integer,+               test_float::Float,+               test_double::Double}+  deriving (Data, Show, G.Generic)+data R01 =  R01 {r1_id::Int, nested_r::R0}+  deriving (Show, G.Generic)+data R3 =  R3 {r3_id::Int, nested_rlist::[R0]}+  deriving (Show, G.Generic)+data R4 = R4 {r4_id::Int, nested_rtuple::(R0,R0)}+  deriving (Show, G.Generic)++instance Tabilize R0+instance Tabilize R01+instance Tabilize R3+instance Tabilize R4+++getR0 = R0 {test_string="Jack-somone"+           , test_integer=10+           , test_double=10.101+           , test_float=0.101021}++-- nested record+getR1 = R01 {r1_id=1001, nested_r=getR0}++-- nested record in list+getR3 = R3 {r3_id=1001, nested_rlist=[getR0, getR0]}+-- tested nested record in tuple +getR4 = R4 {r4_id=1001, nested_rtuple=(getR0, getR0)}++--testVector = testCase "testVector" (+    -- do+      -- create Vector+      -- getBox+      -- inspect Box (rows and columns)+--    )++testList = testCase "testList"+  (+    do+      let b = (L.head . L.head) $ listToBox [getR0]+      assertEqual "check rows" (B.rows b) 1+      assertEqual "check cols" (B.cols b) 13+  )++testLists = testGroup "test printing lists" [+  testList+  ]++tests :: TestTree+tests = testGroup "Tests" [+  testLists+  ]++main = defaultMain tests