It used ParseTree to parse idiomatic Ruby code against Enumerable APIs into SQL.
It uses sourcify (which uses ParseTree on 1.8 and Ripper on 1.9). The semantics are identical to standard Enumerable API. In fact, it's not recommended, but you can turn off dm-ambition and the expressions will still work as-is -- only instead of generating a query from the block it iterates over the results and filter in-memory.
However, with sourcify you can sort of emulate the 1.8 behaviour, but it's not perfect. It uses ripper to parse the file where the Proc is defined, and then provides the same callbacks that ParseTree does. It mostly seems to work, but it does run into problems if two Procs are defined on the same line, eg: users.select { ... }.select { ... }.
I'm actually a little disappointed that ruby-core decided to go in this direction. I think that decision will end up costing the community because it makes it harder for runtime introspection of code. It means less tools will be written on top of the runtime, and things like Ambition become harder if not impossible to do reliably. I'm glad to see Rubinius is going in the opposite direction though.
In a nutshell, how sourcify works is it gets the source_location for the Proc, which gives you the file name and line number. It uses Ripper to parse the file, and the Proc starting at that line number. It provides an object that can be fed into SexpProcessor to extract information about the block of code within the Proc.
Since it's bound to Proc#source_location one of the limitations is it can't distinguish between two or more Proc objects defined on one line (eg users.select { |u| u.id == 1 }.select{ |u| u.name == 'John Doe' }). However, that doesn't seem to be too much of a limitation.