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.
Well, on 1.9 it's not quite as nice as 1.8. The ruby-core team decided to change ruby so that after parsing the source the AST would be thrown away as part of some kind of optimization. That means things like ParseTree don't work anymore, and you can't inspect the AST of a Proc at runtime.
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.
Ripper can only parse static source, while ParseTree can give you the AST as it is at runtime. So with Ripper you give it the name of a file/line number and it can parse it, but it can't get any contextual information available at runtime, or anything that might've changed at runtime. With ParseTree I can give it a Proc object and it can parse it in whatever state it is at that point in time.
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.
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.