0

I am writing an internal DSL in Ruby. Currently, the basic structure in this mini language is a block that must include the method during, and optionally some other methods. E.g.:

do
  something...
  during do ... end
  something else ...
end

I would like to simplify the language by treating blocks differently if they do not include the during keyword. In this case I can just treat them like any other Ruby block.

Is it possible to detect if a block includes a specific method without running it? It is ok to assume that the method is not nested within an inner scope, like an if or while statement. I assume that if possible, this will involve reflection, which is ok.

Refinement and explanations

I am not trying to solve the halting problem or overcome any trick that a Ruby hacker might think of. The solution may apply only to the most naive case.

In several languages it is possible to get some reflection on the source code at runtime. In Python method objects have fields like bindings and bytecode. In Java, it is possible, although not trivial, to read the bytecode of a method and look for invokevirtual commands.

I am not very familiar with Ruby. I don't know if Ruby objects that represent control elements, like procs and blocks, have similar fields. If my memory serves, Ruby has some framework for accessing its AST on runtime. However, I don't know if and how this framework can access specific blocks. A solution based on runtime AST parsing that works only for simple cases is perfectly acceptable.

Little Bobby Tables
  • 5,261
  • 2
  • 39
  • 49

3 Answers3

2

Spontaneously the only way that comes to mind is using the to_source method from the sourcify gem and then matching against the resulting string:

https://github.com/ngty/sourcify

Michael Kohl
  • 66,324
  • 14
  • 138
  • 158
2

Abit late to this though. Anyway, sourcify's Proc#to_source supports passing in a body matcher to help u get what u want:

x = proc do
  something...
  during do ... end
  something else ...
end

x.to_source {|body| body =~ /\bduring\b\s*(\{|do)/m }

With this, u can avoid traversing the sexp. However, whatever is returned by Proc#to_sexp & Proc#to_source, has been normalized to be ParseTree compatible, which may or may not be what u want. If u really want to grab the source (as it is originally written, eg. retaining the comment), u can try:

x.to_raw_source {|body| body =~ /\bduring\b\s*(\{|do)/m }

If u have 1.9 specific code, sourcify may break, as under the hood, it is using RubyParser to do some syntax checking, & RubyParser isn't 100% compatible w 1.9 syntax yet. However, there is on-going work to fix this issue.

Hope the above helps :]

ngty
  • 183
  • 6
0

IMO the best way for you is to do so from within the method during itself and not try anything extremely complicated unnecessarily. Although that does depend on whether you define the method or not, but you could still work out a way as to override the same if needed.

Dhruva Sagar
  • 7,089
  • 1
  • 25
  • 34
  • I thought about this, but it does not meet my needs. I need to detect this property of the block before running it. – Little Bobby Tables Jun 19 '11 at 07:11
  • Can you perhaps be a little more specific as to what you wish to achieve, I don't think it is possible to detect if the block is calling a particular function from within it. – Dhruva Sagar Jun 19 '11 at 07:17