开发者

Check if a block includes a method without running it in Ruby

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.


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


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 :]


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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜