On Sep 2, 11:47 am, "Daniel Parker" <
dcpar...@gmail.com> wrote:
> I poked around in DataObjects, and I've seen a version where it injects
> another method or two when you're using sql-specific repositories. Not hard
> to do it that way. Of course nobody would want to compose raw SQL when
> they're not using a sql engine.
Daniel,
I think you mean DataMapper. Anyways. Yeah, so what's your use-case
for this?
No offense, just going to lay out my reasoning for why DM is the way
it is.
First, lemme clarify something: The order of the properties is not the
order that an object should be materialized in. That depends on
Query#fields. If you try to materialize just by "lining up the fields"
you'll quickly run into issues with composite fields or lazy-loaded
attributes and eventually prefetching.
I ask because AR::find_by_sql is wrong. It's a bleeding of
responsibilities. DM is for CRUD. It's an O/R Mapper, not a reporting
tool. The objects returned from a find_by_sql are often not
persistable objects and it completely subverts the majority of the
code in DM.
As Adam suggested, if all you want is a convenience to translate query
results into object instances a simple helper in your application
could manage that with very little work. Execute a DO query, map the
results to objects. Something like:
: # This is probably close, but untested, caveat emptor.
: module ApplicationHelper
: def query(model, sql, *args)
: connection = DataObjects::Connection.new(Merb.config[:databases]
[:default])
: reader = connection.create_command(sql).execute_reader(*args)
: results = []
: while(reader.next!) do
: results <<
model.new(Hash[*reader.fields.zip(reader.values).flatten])
: end
: return results
: ensure
: connection.close
: end
: end
You could even pass the fields explicitly to ensure everything's type-
cast properly with Command#set_types.
Reporting tools are valuable for sure. But when you don't have one
handy, the solution is not a half-baked extension to your O/RM. Just
use the database-drivers directly. It only adds a line or two of code
to use the DO connections directly, and the performance pay-off is
well worth it.
So. Why do the objects that come back need to be model instances?
I'll share an example of how we've done it wrong on an app or two...
We have a Photo model with a #preview(max_width, max_height) method.
It's awesome. It takes a look at itself to determine wether it's
published or not, and if so, generates a preview image or thumbnail
at the appropriate size (retaining aspect-ratio) in the public folder
so Apache can serve it. If it's unpublished, it generates a file in a
private folder and returns the path so the app can X-SENDFILE it.
So it depends on knowing the source-path of the original image, and
wether it's published.
Problem is, when we want to do a custom, fast query on our /photos/
index page to bring back the photos for a specific user, we get these
raw Arrays, not Photos.
Ok, easy enough, we move it to a class-method that takes the id,
created_at, and published_at so we know where on the file-system to
find the source file, whether to watermark it, etc.
The problem is, we're making it the responsibility of the Photo model
to know how to generate preview images, and that just doesn't make
sense.
What we need here is a separate class that lives outside of our domain-
model. A Preview class that can take these attributes. It's easier to
test. It's cleaner with a clear separation of responsibilities. It's
easier to re-use between projects as long as our Photo implements a
fairly simple interface.
Most importantly, it just works with a few pieces of data, and it
doesn't matter where it comes from. It's cake to use it with models,
Arrays, Structs, whatever. And it lives in the right Layer of our
application. Not the Business Logic Layer. It lives much higher in the
stack, in the Application Layer.
And so... it's a problem you can work around, and I'd wager that in
many if not most cases, your application code will be the better for
it. So, with that said, it's difficult for me to anticipate a "I can't
write this page I need because DM doesn't have this feature" sort of
use-case, but if you can come up with one I'll do my best to give it
some genuine consideration.
Thanks, -Sam