Merge pull request #10 from zyla/fix-enum-exception
Ross MacLeod authored
Fix enumJsonFormat not to throw an exception on invalid input
71b5a94f

composite

Composite is a group of libraries focusing on practical uses of composite records, in particular Vinyl, such as querying records from a database and converting them to JSON. These libraries are based on the excellent Frames style use of Vinyl records, though composite implements its own derived from Frames to make for a smaller dependency graph, as Frames is a full CSV parsing/printing and data manipulation library.

composite-aeson

composite-aeson provides JSON formatting facilities for records. JSON formats can be derived automatically when default formats are available, explicitly assembled, combined, or a mix. Aeson's use of FromJSON/ToJSON type classes is mostly avoided to make using JSON formats first-class while still convenient.

Example:

{-# LANGUAGE DataKinds, OverloadedStrings, PatternSynonyms, TypeOperators #-}
import qualified Data.Aeson as Aeson
import Composite.Aeson (JsonFormat, defaultJsonFormatRec, recJsonFormat, toJsonWithFormat)
import Composite.Record (Record, Rec(RNil), (:->), pattern (:*:))
import Data.Text (Text)

type FId   = "id"   :-> Int
type FName = "name" :-> Text
type User = '[FId, FName]

userFormat :: JsonFormat e (Record User)
userFormat = recJsonFormat defaultJsonFormatRec

alice :: Record User
alice = 1 :*: "Alice" :*: RNil

aliceJson :: Aeson.Value
aliceJson = toJsonWithFormat userFormat alice

composite-base

Definitions shared by the other composite libraries or generally useful when using Vinyl/Frames records.

composite-opaleye

composite-opaleye provides the necessary instances to use a Frames record with the opaleye library, letting you use records for query expressions as well as result rows.

Example:

{-# LANGUAGE Arrows, DataKinds, FlexibleContexts, OverloadedStrings, PatternSynonyms, TemplateHaskell, TypeOperators #-}
import Control.Arrow (returnA)
import Composite.Opaleye (defaultRecTable)
import Composite.Record (Record, (:->))
import Composite.TH (withLensesAndProxies)
import Control.Lens (view)
import Data.Int (Int64)
import Data.Proxy (Proxy(Proxy))
import Data.Text (Text)
import Opaleye (Column, PGInt8, PGText, Query, Table(Table), (./=), asc, constant, orderBy, queryTable, restrict)

-- For each field type defined with, withLensesAndProxies will expand to the type, a record lens for the type,
-- and a proxy for the type, so for example FId is the type, fId is a lens which accesses the "id" field of any
-- record which contains that field, and fId_ is a proxy for the field type in case it's needed.
withLensesAndProxies [d|
  type FId   = "id"   :-> Int64
  type CId   = "id"   :-> Column PGInt8
  type FName = "name" :-> Text
  type CName = "name" :-> Column PGText
  |]

type User     = '[FId, FName]
type UserCols = '[CId, CName]

userTable :: Table (Record UserCols) (Record UserCols)
userTable = Table "users" defaultRecTable

userQuery :: Query (Record UserCols)
userQuery =
  orderBy (asc $ view cName) $ proc () -> do
    user <- queryTable userTable -< ()
    let recId = view cId user
    restrict -< recId ./= constant (1 :: Int64)
    returnA -< user

example

A small servant based server which uses composite-opaleye to pull records from the database, reshape the record to an API type, and send the records out to the client as JSON via composite-aeson.

Contributing

Contributions and feedback welcome! File and issue or make a PR.

Chat

Asa (@asa) and Ross (@dridus) hang out on fpchat.