selda 0.1.1.0 → 0.1.1.1
raw patch · 4 files changed
+123/−14 lines, 4 filesPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
+ Database.Selda.Generic: [:-] :: (a -> b) -> Attribute -> GenAttr a
+ Database.Selda.Generic: data GenAttr a
- Database.Selda.Generic: genTable :: forall a b. Relational a => TableName -> [(a -> b, Attribute)] -> GenTable a
+ Database.Selda.Generic: genTable :: forall a. Relational a => TableName -> [GenAttr a] -> GenTable a
Files
- ChangeLog.md +9/−1
- README.md +99/−2
- selda.cabal +1/−1
- src/Database/Selda/Generic.hs +14/−10
ChangeLog.md view
@@ -1,5 +1,13 @@ # Revision history for selda -## 0.1.0.0 -- 2017-04-14+## 0.1.1.1 -- 2017-04-20++* Generic tables, queries and mutation.+* Select from inline tables.+* Tutorial updates.+* Minor bugfixes.+++## 0.1.0.0 -- 2017-04-14 * Initial release.
README.md view
@@ -22,15 +22,30 @@ add-on packages. +Getting started+===============++Install the `selda` package from Hackage, as well as at least one of the+backends:++ $ cabal update+ $ cabal install selda selda-sqlite selda-postgresql++Then, read the [tutorial](#tutorial).+The [API documentation](http://hackage.haskell.org/package/selda) will probably+also come in handy.++ Requirements ============ -Selda requires SQLite 3.7.11+, or PostgreSQL 9+.+Selda requires GHC 7.10+, as well as SQLite 3.7.11+ or PostgreSQL 9+. To build the SQLite backend, you need a C compiler installed. To build the PostgreSQL backend, you need the `libpq` development libraries installed (`libpq-dev` on Debian-based Linux distributions). +<span id="tutorial"></span> A brief tutorial ================ @@ -422,7 +437,7 @@ enjoys. Using transactions in Selda is super easy: ```-transferMoney :: Text -> Text -> Double -> SeldaT s ()+transferMoney :: Text -> Text -> Double -> SeldaT IO () transferMoney from to amount = do transaction $ do update_ accounts (\(owner :*: _) -> owner .== text from)@@ -483,6 +498,88 @@ application modifying the same database as long as they're all using Selda to do it, as the cache shared between all Selda computations running in the same process.+++Generic tables and queries+==========================++Selda also supports building tables and queries from (almost) arbytrary+data types, using the `Database.Selda.Generic` module.+Re-implementing the ad hoc `people` and `addresses` tables from before in a+more disciplined manner in this way is quite easy:++```+data Person = Person+ { personName :: Text+ , age :: Int+ , pet :: Maybe Int+ } deriving Generic++data Address = Address+ { addrName :: Text+ , city :: Text+ } deriving Generic+++people :: GenTable Person+people = genTable "people" [personName :- primaryGen]++addresses :: GenTable Address+addresses = genTable "addresses" [personName :- primaryGen]+```++This will declare two tables with the same structure as their ad hoc+predecessors. Creating the tables is similarly easy:++```+create :: SeldaT IO ()+create = do+ createTable (gen people)+ createTable (gen addresses)+```++Note the use of the `gen` function here, to extract the underlying table of+columns from the generic table.++With generic tables, you can use the table's datatype's record selectors+together with the `!` operator to access its columns in queries.++```+genericGrownups :: Query s (Col s Text)+genericGrownups = do+ person <- select (gen people)+ restrict (person ! age .> 20)+ return (person ! personName)+```++However, queries over generic tables aren't magic; they still consist of the+same collections of columns as queries over non-generic tables.++```+genericGrownups2 :: Query s (Col s Text)+genericGrownups2 = do+ (name :*: age :*: _) <- select (gen people)+ restrict (age .> 20)+ return name+```++Finally, with generics it's also quite easy to re-assemble Haskell objects+from the results of a query using the `fromRel` function.++```+getPeopleOfAge :: Int -> SeldaT IO [Person]+getPeopleOfAge yrs = do+ ps <- query $ do+ p <- select (gen people)+ restrict (p ! age .== yrs)+ return p+ return (map fromRel ps)+```++And with that, we conclude this tutorial. Hopefully it has been enough to get+you comfortable started using Selda.+For a more detailed API reference, please see Selda's+[Haddock documentation](http://hackage.haskell.org/package/selda). TODOs
selda.cabal view
@@ -1,5 +1,5 @@ name: selda-version: 0.1.1.0+version: 0.1.1.1 synopsis: Type-safe, high-level EDSL for interacting with relational databases. description: This package provides an EDSL for writing portable, type-safe, high-level database code. Its feature set includes querying and modifying databases,
src/Database/Selda/Generic.hs view
@@ -1,6 +1,7 @@ {-# LANGUAGE TypeFamilies, TypeOperators, FlexibleInstances #-} {-# LANGUAGE UndecidableInstances, MultiParamTypeClasses, OverloadedStrings #-} {-# LANGUAGE FlexibleContexts, ScopedTypeVariables, ConstraintKinds #-}+{-# LANGUAGE GADTs #-} -- | Build tables and database operations from (almost) any Haskell type. -- -- While the types in this module may look somewhat intimidating, the rules@@ -14,13 +15,13 @@ -- * Performing a 'select' on a generic table returns all the table's fields -- as an inductive tuple. -- * Tuples obtained this way can be handled either as any other tuple, or--- using the '(!)' operator together with any record selector for the+-- using the '!' operator together with any record selector for the -- tuple's corresponding type. -- * Relations obtained from a query can be re-assembled into their -- corresponding data type using 'fromRel'. module Database.Selda.Generic ( Relational, Generic- , GenTable (..), Attribute, Relation+ , GenAttr (..), GenTable (..), Attribute, Relation , genTable, toRel, fromRel, (!) , insertGen, insertGen_, insertGenWithPK , primaryGen, autoPrimaryGen@@ -68,6 +69,12 @@ -- of @Foo@ has type @Int@, and the second has type @Text@. type Relation a = Rel (Rep a) +-- | A generic column attribute.+-- Essentially a pair or a record selector over the type @a@ and a column+-- attribute.+data GenAttr a where+ (:-) :: (a -> b) -> Attribute -> GenAttr a+ -- | Generate a table from the given table name and list of column attributes. -- All @Maybe@ fields in the table's type will be represented by nullable -- columns, and all non-@Maybe@ fields fill be represented by required@@ -88,9 +95,9 @@ -- This example will create a table with the column types -- @Int :*: Text :*: Int :*: Maybe Text@, where the first field is -- an auto-incrementing primary key.-genTable :: forall a b. Relational a+genTable :: forall a. Relational a => TableName- -> [(a -> b, Attribute)]+ -> [GenAttr a] -> GenTable a genTable tn attrs = GenTable $ Table tn (validate tn (map tidy cols)) where@@ -99,7 +106,7 @@ addAttrs n ci = ci { colAttrs = colAttrs ci ++ concat [ as- | (f, Attribute as) <- attrs+ | f :- Attribute as <- attrs , identify dummy f == n ] }@@ -109,7 +116,7 @@ -- of all of the type's fields. -- -- > data Person = Person--- > { id :: Auto Int+-- > { id :: Int -- > , name :: Text -- > , age :: Int -- > , pet :: Maybe Text@@ -121,9 +128,6 @@ -- -- This is mainly useful when inserting values into a table using 'insert' -- and the other functions from "Database.Selda".--- Note that since @toRel@ doesn't filter out auto-incrementing primary key--- fields, you should use 'insertGen' and friends to insert values into--- tables with auto-incrementing primary keys instead. toRel :: Relational a => a -> Relation a toRel = gToRel . from @@ -173,7 +177,7 @@ -- selector function. For instance: -- -- > data Person = Person--- > { id :: Auto Int+-- > { id :: Int -- > , name :: Text -- > , age :: Int -- > , pet :: Maybe Text