Hey, another day and another chance to write bindings
I’m a heavy node user, and because that I know some very cool libraries that deserve bindings, and I am the guy to wrote them ! But my Reason/Ocaml knowledge is not yet good enough.
This is the library I want to write bindings to: https://www.npmjs.com/package/preferences
It makes managing encrypted configs easy. You just provide a namespace, some defaults and it takes care of the rest, the rest being:
- Store the peferences on disk
- Encrypt the stored file
- Make sure you do not overwrite other preference files
- Save again to disk when you mutate any value
It is very useful and convenient for cli interfaces.
I am on the situation where I have to decide the implementation of the bindings. It can be as simple as this:
[@bs.new] [@bs.module] external preferences: (string, defaults, options) => Js.t({..}) = "";
let read = (~key=?, ~encrypt=true, ~defaults=?, namespace) =>
preferences(namespace, defaults, options(~key, ~encrypt));
And then the usage could be as simple as that:
let prefs = Preferences.preferences("com.my-namespace");
prefs##username
prefs##password
Of course I don’t like that because
- It is very JS specific, and I don’t want to write javascript with a different syntax
- I can not mutate the properties (set them)
- I can not accept a defaults object because I don’t know the shape beforehand
So my intuition tells me that I have to use a functor. That way I should be able to accept the type I should return and take as defaults (they should be almost the same) and the end user of the module should be able to define a JS record that they can mutate by deriving from abstract. Something like this:
[@bs.deriving abstract]
type options = {
key: option(string),
encrypt: bool,
};
module type T = { type t; };
module Make = (T:T) => {
type defaults = option(T.t);
[@bs.new] [@bs.module] external preferences: (string, defaults, options) => T.t = "";
let read = (~key=?, ~defaults=?, ~encrypt=true, namespace:string) =>
preferences(namespace, defaults, options(~key, ~encrypt));
}
I can imagine an usage like this:
[@bs.deriving abstract]
type prefs = {
mutable username: string,
password: string
};
module Pref = Preferences.Make({ type t = prefs ;});
let prefs = Pref.read("com.puto-pene");
Js.log( prefs -> usernameGet );
Js.log(prefs);
prefs->usernameSet("Joe");
Js.log(prefs);
What do you think about the different implementations ? The first one is simpler, but I don’t know how to avoid the described problems.
Regards