I'm trying to write a small tool to automate some of my database management work (primarily around versioning schemas, calculating diffs, and applying migrations). Generally Nim has been a very nice language to work in, but I am stuck on trying to determine a good way to avoid hard-coding my tool to work with a single database implementation. I would like the tool to be able to to take as an option the database driver. I've seen this thread where Araq says this:
The db_* modules all implement a common interface so that you only need to change your import statements to switch databases. And yes that's exactly what we did for this forum and yes I really like that this way you can't switch database implementations at runtime.
So my question: is there any good way to write the kind of tool I'm talking about in Nim, or is Nim just a poor choice for this kind of problem?
In a more general sense, what is the "Nim way" to handle the kinds of problems that dynamic dispatch (interfaces and concrete implementations) is designed to solve? How do I handle things where the correct action is not know until the program is running? It seems like this is a very common need in software, but I don't see a good approach in Nim. Am I missing something?
Have a look at the db nimble package. It haven't added in the db_odbc module yet, but it unifies the db_sqlite, db_mysql and db_postgres modules.
There is probably a more elegant way to do the db package than what I have done (challenge, what challenge?? :-) )
I'm a newbie myself, but it looks like each db module defines its own connection type, which (many) of the functions use.
Perhaps you can write something like:
import db_postgres
import db_mysql
...
template openDB*(dbname: string, connection, user, password, database: string) : stmt =
result = case dbname
of "postgres":
db_postgres.open(connection, user, password, database)
of "msql":
db_postgres.open(connection, user, password, database)
...
else:
raise newException(Exception, "unknown database " & dbname)
Then the resulting variable from:
var connection = openDB("postgres", ...)
will be typed and all other calls will dispatch correctly. You probably need to write a wrapper for a few other things like dbQuote which just deal with strings. With this wrapper module, other modules should be able to import the db_* modules and the wrapper module, and as long as you call the explicitly wrapped values you should be good (AFAIKT :)). There may be a way to automate this wrapper-writing with macros a little bit. (My foray into that still hasn't met with success: http://forum.nim-lang.org/t/2020 )
other modules should be able to import the db_* modules and the wrapper module
Or the wrapper module can export db_* modules, so just the wrapper module needs then to be imported.