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.

Reply via email to