Hi!
I'm new to this language. I saw it mentioned in the D newsgroup and got interested. I read the tutorial and I really liked it.
I was wondering how would one implement ActiveRecord as in Ruby. I mostly program in Ruby. What I envision is to generate efficient code that represent a table in a database along with a row of that table, with typed fields representing the columns. In Ruby this is done at runtime: a connection to the database is made to retrieve the metadata and all methods are defined. But I see this can't be done in Nimrod since compile-time execution is allowed only for literals, you could never open a connection.
Another approach would be to have the db schema in a file, load it at compile time and then generate the code. But you always have to remember to keep the schema file and the db synchronized.
Anyway... what would be the best way to do it in Nimrod?
Just write the frigging SQL queries!
Been there, done that, is not typesafe, sucks too. However, that's nice is that you can tweak the query interactively before you throw it into your program.
> Just write the frigging SQL queries! > Been there, done that, is not typesafe, sucks too
I agree, both approaches suck.
ActiveRecord and hand-written queries are the two opposite extremes - there is a middle road, which is data mappers, and this can be implemented in a type-safe and much more pure, elegant and "correct" (in DDD terms) manner with Nim.
The primary reason you would opt for this instead of AR or DAO, is simplicity - things like lazy-loading collections in AR implementations is complex, and leads to implicit queries, which means keeping track of performance becomes a real chore and something you need to constantly think about.
The argument that tipped the scales for me, personally, is transaction boundaries. Let's say your Customer object has a Country relation. If you can have expressions like "customer.country.name", this seems to imply that the country name is in fact part of the Customer aggregate, which is of course a lie - the point is, you can't tell from the shape and boundaries of your models where the actual transaction boundaries exist; an embedded object (or list) could be either just a relation with another entity, or it could be part of the aggregate, you can't tell, and it's not obvious how deep or far the effects reach when you issue destructive commands like save or delete.
According to DDD, the correct approach is to model Costumer with a country_id, rather than a Country object, because this reflects the true boundaries of the Customer entity: the country_id is part of the Customer aggregate, but the Country object is not. So the shape of your model reflects the true transaction boundaries of your model.
Instead of "customer.country.name", you would need to write e.g. "countries.get(customer.country_id).name", which is a bit more verbose, but describes what's really happening - you are making a round-trip to the database for the Country object (or at the very least to a level 1 cache) but the point is, the code actually imparts what you're doing and enables you to understand the performance aspects of what you're doing; transactions become explicit, not implicit, and this enables you to create optimizations. For example, a customer list page that fetches a page of 20 Customers has very different performance aspects from a customer profile page that fetches just one Customer - on the list page, you probably don't want 20 round-trips for each related Country object.
And I know that AR implementations have answers to these problems - you can configure your finder to eagerly fetch the country relation on the list page, etc... but you pay for this with (lots of) complexity in the AR library - and it hides performance implications, which you need to then learn about e.g. by monitoring the queries that get performed and configuring the finder accordingly.
The bottom line is, if you like simplicity, AR isn't the way to go, and raw queries probably isn't either - the data mapper lands somewhere in between the two, in terms of simplicity and easy of use.
Just my two cents :-)