The State of Ruby ORM
solnic.eu
solnic.eu
After all the entire point of data-mapper is that your objects don't know the details about how they're stored.
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.
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)
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)
Is there some significant benefit to it returning true for Enumerable that I'm just missing?
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?
The main LINQ object is an IQueryable object that implements IEnumerable. However, IEnumerable is not the object that has all the enumerator methods on it. That is IEnumerator. The only method IEnumerable has is the GetEnumerator method which returns the IEnumerator object.
So, in actuality, the "all" method in ActiveRecord kind of acts like the IEnumerable interface in returning the actual enumerator object.
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.
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.
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.