
The State of Ruby ORM - chrismealy
http://solnic.eu/2011/11/29/the-state-of-ruby-orm.html
======
vidarh
The thing that really grates me is that Datamapper (the ORM) is that all their
examples tightly couples the models to the database in direct contravention of
the data mapper pattern that gives it its name.

After all the entire point of data-mapper is that your objects don't know the
details about how they're stored.

~~~
grandalf
look at it as hints. It's not actually tightly coupled, as m of that stuff is
optional sugar that allows for better optimization.

~~~
vidarh
That "sugar" is in the wrong place for a project that names itself after a
pattern intended to keep the models and storage separate. If Datamapper
actually tried to implement the datamapper pattern, you would've put it in
separate mapper classes, not littered around the models.

~~~
grandalf
Fair point. Why don't you create a gist with an example of what the model +
hint declarations would look like in separate classes. I'm not convinced that
this distinction matters, but I'm sure the DM developers would respectfully
take a close look at your insights. They are a really great group, very
helpful on IRC, etc.

~~~
vidarh
I would if I had time. Or they could just read the chapter of Patterns of
Enterprise Application Architecture that they took the name from - it's the
chapter right after Active Record :) Page 165. Martin Fowler went into a
decent amount of depth in describing the pattern. His site also has a decent
summary available for free.

I don't think this is exactly is lack of awareness on their part, though - as
someone else has pointed out, Datamapper actually implements the Active Record
pattern. Structurally the two are very different.

------
scottschulthess
His example

User.where(:active => true).kind_of?(Enumerable)

What's the issue here? If you change it to

User.where(:active => true).all.kind_of?(Enumerable)

~~~
akmiller
Yeah, I'm not sure I understand that either. The whole point is that with the
first example it is NOT an Enumerable yet. You are building, what is
essentially, a query object and until you access something that query will not
be executed at which point it will be an Enumerable as you point to with the
"all" method.

~~~
masklinn
I guess TFAA would like the query under construction to be Enumerable, and
Enumerating it to serialize it without needing the `all` indirection.
Something similar to LINQ.

In fact, some of Enumerable's messages could even be used to further build the
query (e.g. `Enumerable#drop(Integer)` simply bumps the query's offset)

~~~
akmiller
That makes some sense to me, but technically that object at that point is not
an "Enumerable" so you are essentially faking it knowing that it would be once
executed.

Is there some significant benefit to it returning true for Enumerable that I'm
just missing?

~~~
masklinn
> That makes some sense to me, but technically that object at that point is
> not an "Enumerable"

Why not? An enumerable is anything that can be enumerated, the query object
holds the potential of being enumerated, it's just an actual underlying query
away but that's of no relevance to the interface. The only reason why it's not
"technically Enumerable" at that point is... that it does not implement
Enumerable's interface.

> so you are essentially faking it knowing that it would be once executed.

Why would you be faking anything? Is LINQ faking anything when it produces
IEnumerable<T> after each "operation"?

> Is there some significant benefit to it returning true for Enumerable that
> I'm just missing?

Interface simplicity? Workflow simplicity? I see no reason for it not to? Get
rid of the useless `all` method?

~~~
burke
Strictly speaking, you're not actually enumerating for a lot of the potential
use-cases of enumerable methods.

For example, say #reject were implemented:

    
    
        User.where(admin: true).reject(office: current_user.office)
    

There are two ways to implement this: One by literally iterating over the
result set and filtering it; and Two by adding a projection to the query
builder.

In the second case, it is small-e enumerable, but not strictly Enumerable,
since Enumerable very explicitly defines each method in terms of #each. That
doesn't make sense for a query builder.

It could implement Enumerable and override every single method, but the point
a few people are making here and in the post comments is that checking
kind_of?(Enumerable) is non-idiomatic. Including modules for semantic meaning
is appealing to programmers from certain backgrounds for completely
understandable reasons, but simply checking for the presence of the method
needed is more clear -- ie. respond_to?(:each) is the intended solution.

TL;DR: Modules aren't Interfaces.

------
lattepiu
Three years ago the state was much worse, so I ended up writing my own ORM,
which tries to address some of the issues pointed out here.

My primary need was to be able to map the same models to different schemas,
including very badly designed legacy ones, so the mapper layer had to be
clearly separated from the model and very hackable. Also, the query syntax had
to be powerful enough to avoid SQL whenever possible, so that the same queries
could be applied to different environments.

Right now it implements Units Of Work and Identity Mappers; it has deep
querying and multilevel strategic loading (not limited to one level as with
DataMapper); functions and aggregates; and many other features, wrapped in a
familiar easy syntax.

If someone wants to try it, I'd love to hear some feedback: it's at
<https://github.com/me/spider>.

------
DanielRibeiro
I find Datamapper's codebase quite easy to follow. It uses a modular
architecture, separating different concerns in different modules, which can be
hard to follow if you are new to Ruby.

About the raw SQL: Sequel excels at this. And you can combine easily both.

I found the post interesting, but would have loved to see more examples to the
arguments. Specially in the frameworks the author gives so much praise.

------
apotheon
interesting topic, lame explanation

