Hi all, Back in the Rails 3.0 days, there was thought and discussion about how the AR query API might be improved to support more advanced queries, and in general make better use of some of the capabilities of ARel. I'd had some brief discussions with Pratik at the time, during which he mentioned that the plan was to step back, allow some plugins to tackle the problem, then select a reasonable way to proceed from there. So, in April of 2010, I released MetaWhere, inspired by that discussion with Pratik and his sharing of https://gist.github.com/265308 with me.
I've maintained MetaWhere and its successor, Squeel<http://github.com/ernie/squeel>, for almost 3 years now. Combined, they have around 1600 watchers/stars/whatever-they're-called-now on GitHub (around 10% of the watchers of Rails itself). On last week's Ruby Rogues<http://rubyrogues.com/081-rr-rails-4-with-aaron-patterson/>, Avdi Grimm named Squeel his "pick", stating: [...] there is a gem called Squeel (S-Q-U-E-E-L) and it puts… it adds a lot > of niceties in doing SQL queries to Active Record. So, basically it adds > stuff where instead of passing just parameters to "where" you can pass a > block – and a block can basically just have Ruby code in it that compares > things to each other using equals or lesser than or various other operators > and Squeel takes that and turns that into SQL. And it works really, really, > well. > It’s so nice for building up queries. It goes well beyond just that little > bit of turning Ruby into SQL, but also makes it a lot easier to deal with > joins and subqueries, and basically makes some things possible that are if > not impossible, then really hard in just base Active Record and ARel. Like > doing non… doing less common types of joins, ORing together two different > result sets and stuff like that — without actually getting the results and > putting them together in memory -- actually turning it into a composite > query that does the right thing in SQL. I’ve been getting a lot of use out > of it on one project I’m working on for a client and it’s been a huge, huge > help. So, with the background and requisite testimonial out of the way, I've been hoping that perhaps Squeel, or something like it would see a merge into core at some point, based on the previously-mentioned discussion with Pratik. Seeing that AR 4.0 has accepted Akira Matsuda's PR for some enhancements to the query API, I'm hoping this means that the core team is open to considering some serious enhancements to the query API for 4.0. A response on Twitter from Santiago got me thinking that I should really do more than hope, based on previous experiences<https://github.com/rails/rails/pull/1915#commitcomment-770892> . While the most common usage of Squeel is via instance_eval, and I know core is traditionally not in favor of such dark arts, it's perfectly possible to use Squeel syntax without instance_eval, as in: Post.where { |post| post.title.like('zomg%') | post.title.like('bbq%') } # => SELECT "posts".* FROM "posts" WHERE (("posts"."title" LIKE 'zomg%' OR "posts"."title" LIKE 'bbq%')) It supports function calls: Post.where { |q| q.concat(q.title, q.description).like '%something%' } # => SELECT "posts".* FROM "posts" WHERE concat("posts"."title", "posts"."description") LIKE '%something%' ...and keypaths, allowing traversal of associations, and even polymorphic belongs_to joins: Note.joins { |join| join.notable(Person) }.where { |note| note.notable(Person).name == 'Ernie' } # => SELECT "notes".* FROM "notes" INNER JOIN "people" ON "people"."id" = "notes"."notable_id" AND "notes"."notable_type" = 'Person' WHERE "people"."name" = 'Ernie' It resolves table aliases automatically (shown with instance_eval syntax for clarity): Person.joins{children.parent.children}. where{ (children.name.like 'Ernie%') | (children.parent.name.like 'Ernie%') | (children.parent.children.name.like 'Ernie%') } => SELECT "people".* FROM "people" INNER JOIN "people" "children_people" ON "children_people"."parent_id" = "people"."id" INNER JOIN "people" "parents_people" ON "parents_people"."id" = "children_people"."parent_id" INNER JOIN "people" "children_people_2" ON "children_people_2"."parent_id" = "parents_people"."id" WHERE ((("children_people"."name" LIKE 'Ernie%' OR "parents_people"."name" LIKE 'Ernie%') OR "children_people_2"."name" LIKE 'Ernie%')) I'd go on, but to say that I (and others) think it's a useful extension to the existing AR syntax would be an understatement. Some of the examples shown above illustrate that while the instance_eval is more concise, it's also possible to create perfectly readable and expressive queries using the less magic yielded syntax. I'm not sure if it's the case now, but early in development, I ran through the AR test suite with Squeel enabled, and it didn't break any of the existing tests, either. Not making that claim at this point, but I've worked very hard to ensure solid compatibility with typical AR query syntax, the only real difference being the behavior of symbols when used as values in queries (symbol, being an identifier, maps to a field identifier, or column name, allowing "Post.where(:title => :description)" to search for posts with identical titles and descriptions). I'd welcome input as to whether there's any interest in seeing Squeel (or some subset of it) being merged to core. Thanks in advance for your consideration! -Ernie Miller -- You received this message because you are subscribed to the Google Groups "Ruby on Rails: Core" group. To view this discussion on the web visit https://groups.google.com/d/msg/rubyonrails-core/-/PGFLjX9ZeNkJ. To post to this group, send email to [email protected]. To unsubscribe from this group, send email to [email protected]. For more options, visit this group at http://groups.google.com/group/rubyonrails-core?hl=en.
