Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
92 lines (77 sloc) 3.31 KB
The easiest way to explain this package is to walk through a case study of using
it. This is a literate Haskell file in the test suite, so let's get some imports
out of the way first.
> {-# LANGUAGE DeriveGeneric #-}
>
> module AppA where
>
> ------------------------------------------------------------------------------
> import Armor
> import Control.Lens
> import Data.Aeson
> import Data.ByteString (ByteString)
> import Data.ByteString.Lazy (fromStrict, toStrict)
> import qualified Data.Map as M
> import qualified Data.Text as T
> import Data.Text.Encoding
> import Data.Typeable
> import GHC.Generics
> import Text.Read
> ------------------------------------------------------------------------------
Imagine you have the following data types:
> data Employee = Employee
> { employeeFirstName :: String
> , employeeLastName :: String
> , employeeTenure :: Int
> } deriving (Eq, Ord, Show, Read, Typeable, Generic)
>
> data EmployeeLevel = Executive | Manager | Worker
> deriving (Eq, Ord, Show, Read, Typeable, Generic)
Ignore this for now. It's just here for testing.
You want to store this data in your database as a serialized JSON blob. (That
might not very plausible for this example, but it's definitely a fairly common
thing, so suspend disbelief for a moment.)
> instance FromJSON Employee
> instance ToJSON Employee
>
> instance FromJSON EmployeeLevel
> instance ToJSON EmployeeLevel
Now, to use the armor package you need to define an `Armored` instance for your
data type. To do that you need to define two things. A version number and a list
of serializations you want armored. We'll discuss the serializations in more
detail below.
One notable point about the serializations is that we need to be able to create
a unique identifier for them later. So armor requires a `Map String (APrism'
ByteString a)` where the `String` is a unique and hopefully meaningful
identifier for this serialization.
> instance Armored Employee where
> version = Version 0
> serializations = M.fromList
> [ ("show", showPrism)
> , ("aeson", aesonPrism)
> ]
>
> instance Armored EmployeeLevel where
> version = Version 0
> serializations = M.fromList
> [ ("show", showPrism)
> , ("aeson", aesonPrism)
> ]
This tutorial is a part of the armor test suite, and since we don't want armor
to depend on any specific serialization packages we're using Show as an example
of how armor supports any number of serializations.
A serialization is simply a pair of a serialization function that converts your
data type to ByteString and a deserialization function that converts a
ByteString to a Maybe of your data type. If you're familiar with the lens
package, this is a prism, so that's what we use here.
> showPrism :: (Read a, Show a) => Prism' ByteString a
> showPrism =
> prism' (encodeUtf8 . T.pack . show) (readMaybe . T.unpack . decodeUtf8)
>
> aesonPrism :: (FromJSON a, ToJSON a) => Prism' ByteString a
> aesonPrism =
> prism' (toStrict . encode) (decode . fromStrict)
>
Once you have defined your `Armored` instances, the next step is to define your
tests. To see an example of that go here:
https://github.com/mightybyte/armor/blob/master/test/TestAppA.lhs