Commit d6e0fbb8 authored by Sean's avatar Sean 🎨

Refactor factor, what's my tractor?

parent 18b8e691
/// Encapsulates all db access, exposes domain types
module DbAccess
open FSharp.Data.Sql
open Domain
open Helpers
// SQL Provider / DB config
[<Literal>]
let private DbVendor = Common.DatabaseProviderTypes.POSTGRESQL
[<Literal>]
let private ConnString = "Host=localhost;Port=5432;Database=postgres;Username=test_app;Password=pass"
[<Literal>]
let private Schema = "test"
[<Literal>]
let private ResPath = __SOURCE_DIRECTORY__ + @"./lib"
[<Literal>]
let private IndivAmount = 100
[<Literal>]
let private UseOptTypes = true
type private DB =
SqlDataProvider<DatabaseVendor=DbVendor, ConnectionString=ConnString, ResolutionPath=ResPath, IndividualsAmount=IndivAmount, UseOptionTypes=UseOptTypes, Owner=Schema>
let private ctx = DB.GetDataContext(selectOperations = SelectOperations.DatabaseSide)
let private catDb = ctx.Test
// plain tables - query on these or use their functions
let private Attributes = catDb.Attributes
let private Cats = catDb.Cats
let private Breeds = catDb.Breeds
let private Owners = catDb.Owners
// join tables
let private BreedAttrs = catDb.BreedAttributes
let private OwnerCats = catDb.OwnerCats
// creating entity functions - basic building blocks for CRUD functions
let private createBreed = Breeds.``Create(name)``
let private createAttribute = Attributes.``Create(description, name)``
let private createCat = Cats.``Create(breedid, name)``
let private createOwner = Owners.``Create(age, name)``
let private createBreedAttr = BreedAttrs.``Create(attributeid, breedid)``
let private createOwnerCat = Owners.``Create(age, name)``
/// Query db for a breedId, by Breed Name
let private findBreedIdByName breedName =
query {
for b in Breeds do
where (b.Name = breedName)
take 1
select b.Id
}
/// Insert a breed into the db by name
let insertBreed breedName =
let breedEntity = createBreed (breedName)
printfn " > Makin' breed %s..." breedName
ctx.SubmitUpdates()
printfn " > ✅"
breedEntity.Id
/// Deferred insert Breed
let private defInsertBreed: string -> unit -> int = thunkOne insertBreed
/// Insert a cat into the db, will insert the breed if necessary
let insertCat cat =
let breedId =
findBreedIdByName cat.breed.name
|> Seq.tryHead
|> Option.defaultWith (defInsertBreed cat.breed.name)
let catEntity = createCat (breedId, cat.name)
printfn " > Makin' kitteh %s" cat.name
ctx.SubmitUpdates()
printfn " > ✅"
{ cat with
id = Some catEntity.Id
breed = { cat.breed with id = Some breedId } }
/// Domain modelling - normal F#!
module Domain
/// A type of cat (ignoring attributes for now)
type Breed =
{ id: int option
name: string }
/// KITTEH
type Cat =
{ id: int option
name: string
breed: Breed }
/// Make a cat with just a name and breed name, with None for ids
let mkCat catName breedName =
{ id = None
name = catName
breed =
{ id = None
name = breedName } }
/// Turn a cat to a string - my how the turn tables!
let catToStr cat = sprintf "%s(%d)" cat.name (Option.defaultValue -1 cat.id)
/// Abstract functional heplers that don't go anywhere particularly specific
module Helpers
// It's generally bad to have 'utils' bag of abstract functionality that grows forever,
// as soon as there's a logical grouping, spin off a module, maybe even a submodule
/// Take a function, and a tuple, and call the function with the destructured tuple
let withTuple f (a, b) = f a b
/// Take anything, and give back a zero - a success exit code
let returnZero _ = 0
/// Takes a function and its param and wraps it in another function,
/// useful for if you need unit -> whatever or need to defer work
let thunkOne f x = (fun _ -> f x)
/// Load data into the db the easy way!
module Loader
open Domain
open Helpers
// alias the DbAccess module with a shorter name, so it's explicit when DB
// side effects are happening, but it's still easy to read
module DB = DbAccess
/// Take a tuple and make a cat in the db, then turn it into a string
let private tupleToCatStr =
withTuple mkCat
>> DB.insertCat
>> catToStr
let private rawCatData =
[ ("Denton", "Gray Tabby")
("Mitzie", "Tuxedo")
("Saphire", "Blue Russian")
("Ailee", "Siamese")
("Oreo", "Tuxedo")
("Frisky", "Tuxedo") ]
/// Loads a set of data into the db
let load() =
rawCatData
|> List.map tupleToCatStr
|> String.concat ", "
|> printfn "Made a bunch o cats!\n***\n%s\n***\n"
|> returnZero
open FSharp.Data.Sql
[<Literal>]
let DbVendor = Common.DatabaseProviderTypes.POSTGRESQL
[<Literal>]
let ConnString = "Host=localhost;Port=5432;Database=postgres;Username=test_app;Password=pass"
[<Literal>]
let Schema = "test"
[<Literal>]
let ResPath = __SOURCE_DIRECTORY__ + @"./lib"
[<Literal>]
let IndivAmount = 100
[<Literal>]
let UseOptTypes = true
type DB =
SqlDataProvider<DatabaseVendor=DbVendor, ConnectionString=ConnString, ResolutionPath=ResPath, IndividualsAmount=IndivAmount, UseOptionTypes=UseOptTypes, Owner=Schema>
let ctx =
DB.GetDataContext(selectOperations = SelectOperations.DatabaseSide)
let catDb = ctx.Test
// Domain modelling - normal F#!
/// A type of cat (ignoring attributes for now)
type Breed = {
id : int option
name : string
}
/// KITTEH
type Cat = {
id : int option
name : string
breed : Breed
}
/// Make a cat with just a name and breed name, with None for ids
let mkCat catName breedName =
{ id = None; name = catName; breed = { id = None; name = breedName } }
/// Turn a cat to a string - my how the turn tables!
let catToStr cat =
sprintf "%s(%d)" cat.name (Option.defaultValue -1 cat.id)
// plain tables - query on these or get other functions
let Attributes = catDb.Attributes
let Cats = catDb.Cats
let Breeds = catDb.Breeds
let Owners = catDb.Owners
// join tables
let BreedAttrs = catDb.BreedAttributes
let OwnerCats = catDb.OwnerCats
// creating entity functions
let createBreed = Breeds.``Create(name)``
let createAttribute = Attributes.``Create(description, name)``
let createCat = Cats.``Create(breedid, name)``
let createOwner = Owners.``Create(age, name)``
let createBreedAttr = BreedAttrs.``Create(attributeid, breedid)``
let createOwnerCat = Owners.``Create(age, name)``
let findBreedIdByName bn =
query {
for b in Breeds do
where (b.Name = bn)
take 1
select b.Id
}
// DB functions
let insertBreed breedName () =
let breedEntity = createBreed (breedName)
ctx.SubmitUpdates()
breedEntity.Id
let insertCat cat =
let breedId =
findBreedIdByName cat.breed.name
|> Seq.tryHead
|> Option.defaultWith (insertBreed cat.breed.name)
let catEntity = createCat (breedId, cat.name)
ctx.SubmitUpdates()
{ cat with id = Some catEntity.Id; breed = { cat.breed with id = Some breedId } }
// Helpers
/// Take a function, and a tuple, and call the function with the destructured tuple
let withTuple f (a, b) =
f a b
/// Take a tuple and make a cat in the db, then turn it into a string
let tupleToCatStr =
withTuple mkCat
>> insertCat
>> catToStr
/// Take anything, and give back a zero - a success exit code
let returnZero _ = 0
let rawCatData =
[
("Denton", "Gray Tabby")
("Mitzie", "Tuxedo")
("Saphire", "Blue Russian")
("Ailee", "Siamese")
("Oreo", "Tuxedo")
("Frisky", "Tuxedo")
]
module Program
[<EntryPoint>]
let main argv =
rawCatData
|> List.map tupleToCatStr
|> String.concat ", "
|> printfn "Made a bunch o cats!\n***\n%s\n***\n"
|> returnZero
let main argv = printfn "Loading up some cats!" |> Loader.load
......@@ -11,6 +11,10 @@
</Target>
<ItemGroup>
<Compile Include="Helpers.fs" />
<Compile Include="Domain.fs" />
<Compile Include="DbAccess.fs" />
<Compile Include="Loader.fs" />
<Compile Include="Program.fs" />
</ItemGroup>
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment