packages feed

elm-export-persistent (empty) → 0.1.1

raw patch · 7 files changed

+346/−0 lines, 7 filesdep +aesondep +basedep +elm-exportsetup-changed

Dependencies added: aeson, base, elm-export, persistent, scientific, text, unordered-containers

Files

+ ChangeLog.md view
@@ -0,0 +1,5 @@+# Revision history for elm-export-persistent++## 0.1.0.0  -- 2016-12-12++* First version. Released on an unsuspecting world.
+ LICENSE view
@@ -0,0 +1,22 @@+MIT++Copyright (c) 2016 William Casarin++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.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ elm-export-persistent.cabal view
@@ -0,0 +1,45 @@+-- Initial elm-export-persistent.cabal generated by cabal init.  For+-- further documentation, see http://haskell.org/cabal/users-guide/++name:                elm-export-persistent+version:             0.1.1+synopsis:            elm-export persistent entities+description:+  Ent is a newtype that wraps Persistent Entity's, allowing you to export them+  to Elm types. Specifically, it adds a To/From JSON instance which adds an id+  field, as well as an ElmType instance that adds an id field constructor.+homepage:            https://github.com/jb55/elm-export-persistent+license:             MIT+license-file:        LICENSE+author:              William Casarin+maintainer:          bill@casarin.me+-- copyright:+category:            Web, Database, Data+build-type:          Simple+extra-source-files:  ChangeLog.md+cabal-version:       >=1.10+tested-with:         GHC == 7.10.1+                   , GHC == 7.10.2+                   , GHC == 7.10.3+                   , GHC == 8.0.1+                   , GHC == 8.0.2++source-repository head+  type:     git+  location: https://github.com/jb55/elm-export-persistent++library+  exposed-modules: Elm.Export.Persist+                 , Elm.Export.Persist.Ent+                 , Elm.Export.Persist.BackendKey+  -- other-modules:+  -- other-extensions:+  build-depends: base >=4.8 && <5+               , elm-export+               , persistent+               , aeson+               , text+               , unordered-containers+               , scientific+  hs-source-dirs:      src+  default-language:    Haskell2010
+ src/Elm/Export/Persist.hs view
@@ -0,0 +1,141 @@+-- |+-- Module      :  Elm.Export.Persist+-- Copyright   :  (C) 2016-17 William Casarin+-- License     :  MIT+-- Maintainer  :  William Casarin <bill@casarin.me>+-- Stability   :  experimental+-- Portability :  non-portable+--+-- == Usage+-- #usage#+--+-- @+-- newtype 'Ent' (field :: 'Symbol') a = 'Ent' ('Entity' a)+--   deriving ('Generic')+--+-- type 'EntId' a = 'Ent' "id" a+-- @+--+-- 'Ent' is a newtype that wraps Persistent 'Entity's, allowing you to export+-- them to Elm types. Specifically, it adds a {To,From}JSON instance which+-- adds an @id@ field, as well as an @'ElmType'@ instance that adds an @id@+-- field constructor.+--+-- == Example+-- #example#+--+-- Let\'s define a Persistent model:+--+-- @+-- {-\# LANGUAGE FlexibleInstances #-}+-- {-\# LANGUAGE GADTs #-}+-- {-\# LANGUAGE GeneralizedNewtypeDeriving #-}+-- {-\# LANGUAGE MultiParamTypeClasses #-}+-- {-\# LANGUAGE OverloadedStrings #-}+-- {-\# LANGUAGE QuasiQuotes #-}+-- {-\# LANGUAGE TemplateHaskell #-}+-- {-\# LANGUAGE TypeFamilies #-}+-- {-\# LANGUAGE DeriveGeneric #-}+-- {-\# LANGUAGE StandaloneDeriving #-}+--+-- module Main where+--+-- import Data.Aeson+-- import Data.'Text'+-- import Database.Persist.TH+-- import Elm+-- import GHC.Generics+--+-- import Elm.Export.Persist+-- import Elm.Export.Persist.BackendKey ()+--+-- share [mkPersist sqlSettings, mkMigrate "migrateAccount"] [persistLowerCase|+-- Account+--   email 'Text'+--   password 'Text'+--   deriving Show 'Generic'+--   UniqueEmail email+-- |]+--+-- instance 'ToJSON' Account+-- instance 'FromJSON' Account+-- instance 'ElmType' Account+--+-- -- use GeneralizedNewtypeDeriving for ids+-- -- this picks a simpler int-encoding+-- deriving instance 'ElmType' AccountId+-- @+--+-- Now let\'s export it with an id field:+--+-- @+-- module Main where+--+-- import Db+-- import Elm+-- import Data.Proxy+--+-- mkSpecBody :: 'ElmType' a => a -> ['Text']+-- mkSpecBody a =+--   [ toElmTypeSource    a+--   , toElmDecoderSource a+--   , toElmEncoderSource a+--   ]+--+-- defImports :: ['Text']+-- defImports =+--   [ "import Json.Decode exposing (..)"+--   , "import Json.Decode.Pipeline exposing (..)"+--   , "import Json.Encode"+--   , "import Http"+--   , "import String"+--   ]+--+-- accountSpec :: Spec+-- accountSpec =+--   Spec [\"Generated\", \"Account\"] $+--        defImports ++ mkSpecBody ('Proxy' :: 'Proxy' ('EntId' Account))+--+-- main :: IO ()+-- main = specsToDir [accountSpec] \"some\/where\/output\"+-- @+--+-- This generates:+--+-- @+-- module Generated.Account exposing (..)+--+-- import Json.Decode exposing (..)+-- import Json.Decode.Pipeline exposing (..)+-- import Json.Encode+-- import Http+-- import String+--+-- type alias Account =+--     { accountEmail : String+--     , accountPassword : String+--     , id : Int+--     }+--+-- decodeAccount : Decoder Account+-- decodeAccount =+--     decode Account+--         |> required "accountEmail" string+--         |> required "accountPassword" string+--         |> required "id" int+--+-- encodeAccount : Account -> Json.Encode.Value+-- encodeAccount x =+--     Json.Encode.object+--         [ ( "accountEmail", Json.Encode.string x.accountEmail )+--         , ( "accountPassword", Json.Encode.string x.accountPassword )+--         , ( "id", Json.Encode.int x.id )+--         ]+-- @++module Elm.Export.Persist+  ( module Elm.Export.Persist.Ent+  )+  where++import Elm.Export.Persist.Ent
+ src/Elm/Export/Persist/BackendKey.hs view
@@ -0,0 +1,27 @@+-- |+-- Module      :  Elm.Export.Persist.Ent+-- Copyright   :  (C) 2016-17 William Casarin+-- License     :  MIT+-- Maintainer  :  William Casarin <bill@casarin.me>+-- Stability   :  experimental+-- Portability :  non-portable+--+-- Orphan instances needed for SQL keys+--+-- This is usually required, but optionally exported in case you have your own+-- already++{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE StandaloneDeriving #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}++module Elm.Export.Persist.BackendKey () where++import GHC.Generics+import Elm+import Database.Persist+import Database.Persist.Sql++deriving instance Generic (BackendKey SqlBackend)+deriving instance ElmType (BackendKey SqlBackend)
+ src/Elm/Export/Persist/Ent.hs view
@@ -0,0 +1,104 @@+-- |+-- Module      :  Elm.Export.Persist.Ent+-- Copyright   :  (C) 2016-17 William Casarin+-- License     :  MIT+-- Maintainer  :  William Casarin <bill@casarin.me>+-- Stability   :  experimental+-- Portability :  non-portable++{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DataKinds #-}++module Elm.Export.Persist.Ent+    ( Ent(..)+    , EntId+    ) where++import Database.Persist+import Database.Persist.Sql+import Data.Aeson+import Elm+import Data.Proxy+import Data.Text+import Data.Scientific+import GHC.TypeLits+import GHC.Generics++import qualified Data.HashMap.Strict as Map+import qualified Data.Text as T++-- | 'Entity' wrapper that adds `ToJSON`, `FromJSON`, and `ElmType` instances+--+-- The first type parameter 'field' is a symbol used for the key name+--+-- >>> toElmTypeSource (Proxy :: Proxy (Ent "userId" User))+-- "type alias User = { userName : String, userId : Int }"+newtype Ent (field :: Symbol) a = Ent (Entity a)+  deriving (Generic)++-- | 'Ent' alias, using "id" as the key+--+-- >>> toElmTypeSource (Proxy :: Proxy (EntId User))+-- "type alias User = { userName : String, id : Int }"+type EntId a = Ent "id" a++elmIdField :: Text -> ElmValue+elmIdField keyfield =+  ElmField keyfield (ElmPrimitiveRef EInt)++addIdToVals :: String -> ElmValue -> ElmValue+addIdToVals keyname ev =+  case ev of+    ef@(ElmField{}) ->+      Values ef (elmIdField (T.pack keyname))+    Values v1 rest -> Values v1 (addIdToVals keyname rest)+    _ -> ev+++instance (KnownSymbol field, ElmType a) => ElmType (Ent field a) where+  toElmType _ =+    case toElmType (Proxy :: Proxy a) of+      ElmDatatype name (RecordConstructor x (Values v vals)) ->+        ElmDatatype name (RecordConstructor x+                            (Values v (addIdToVals keyname vals)))+      x -> x+    where+      keyname :: String+      keyname = symbolVal (Proxy :: Proxy field)++instance (KnownSymbol field, ToJSON a) => ToJSON (Ent field a) where+  toJSON (Ent (Entity k val)) =+    case toJSON val of+      Object hmap -> Object (Map.insert keyname (toJSON k) hmap)+      x           -> x+    where+      keyname :: Text+      keyname = T.pack $ symbolVal (Proxy :: Proxy field)++valToKey :: ToBackendKey SqlBackend record => Value -> Maybe (Key record)+valToKey (Number n) = toSqlKey <$> toBoundedInteger n+valToKey _          = Nothing++instance ( ToBackendKey SqlBackend a+         , PersistEntity a+         , KnownSymbol field+         , FromJSON a) => FromJSON (Ent field a) where+  parseJSON obj@(Object o) =+    let+      keyname :: String+      keyname = symbolVal (Proxy :: Proxy field)+      mkey = Map.lookup (T.pack keyname) o+      keyParser = do key <- maybe (fail $ "Ent: no key found for field " ++ keyname)+                            pure mkey+                     maybe (fail "Ent: could not parse key as Int64")+                           pure (valToKey key)+    in+      Ent <$>+        (Entity <$> keyParser+                <*> parseJSON obj)+  parseJSON _ = fail "Ent: should be an Object"+