-
Notifications
You must be signed in to change notification settings - Fork 2
Step 3 Quatro
As with our previous versions, we'll start by adding the RethinkDB driver to project.json; we'll also bring the data-config.json file from Dos/Tres into this project, changing the database name to O2F4. Follow the instructions for Tres up though the point where it says "we'll create a file Data.fs".
Parsing data.config
We'll use Data.fs in this project as well, but we'll do things a bit more functionally. We'll use Chiron to parse the JSON file, and we'll set up a discriminated union (DU) for our configuration parameters.
type ConfigParameter =
| Hostname of string
| Port of int
| AuthKey of string
| Timeout of int
| Database of string
This DU looks a bit different than the single-case DUs or enum-style DUs that we made in step 2. This is a full-fledged DU with 5 different types, 3 strings and 2 integers. The DataConfig record now becomes dead simple:
type DataConfig = { Parameters : ConfigParameter list }
We'll populate that through Chiron.
with
static member FromJson json =
match Json.parse json with
| Object config ->
let options =
config
|> Map.toList
|> List.map (fun item ->
match item with
| "Hostname", String x -> Hostname x
| "Port", Number x -> Port <| int x
| "AuthKey", String x -> AuthKey x
| "Timeout", Number x -> Timeout <| int x
| "Database", String x -> Database x
| key, value ->
raise <| InvalidOperationException
(sprintf "Unrecognized RethinkDB configuration parameter %s (value %A)" key value))
{ Parameters = options }
| _ -> { Parameters = [] }
There is a lot to learn in these lines.
- Before, if the JSON didn't parse, we raised an exception, but that was about it. In this one, if the JSON doesn't parse, we get a default connection. Maybe this is better, maybe not, but it demonstrates that there is a way to handle bad JSON other than an exception.
-
Object,String, andNumberare Chiron types (cases of a DU, actually), so ourmatchstatement uses the destructuring form to "unwrap" the DU's inner value. ForString,xis a string, and forNumber,xis a decimal (that's why we run it throughintto make our DUs. - This version will raise an exception if we attempt to set an option that we do not recognize (something like "databsae" - not that anyone I know would ever type it like that...).
Now, we'll adapt the CreateConnection () function to read this new configuration representation:
member this.CreateConnection () : IConnection =
let folder (builder : Connection.Builder) block =
match block with
| Hostname x -> builder.Hostname x
| Port x -> builder.Port x
| AuthKey x -> builder.AuthKey x
| Timeout x -> builder.Timeout x
| Database x -> builder.Db x
let bldr =
this.Parameters
|> Seq.fold folder (RethinkDB.R.Connection ())
upcast bldr.Connect()
Our folder function utilizes a match on our ConfigParameter DU. Each time through, it will return a modified version of the builder parameter, because one of them will match. We then create our builder by folding the parameter, using R.Connection () as our beginning state, then return its Connect () method.
For now, let's copy the rest of Data.fs from Tres to Quatro - this gives us the table constants and the table/index initialization code.
Dependency Injection: Functional Style
// TODO: finish writing it up