Ruby中的魔术方法?

时间:2010-06-19 23:28:07

标签: ruby magic-methods

Ruby爱好者!我正在尝试用红宝石编写DSL,我希望能够创建一些神奇的方法(不确定这是我想要的最准确的术语)。

我希望能够做到以下几点:

a = [1, 2, 3]
b = 2

(a contains b)

让它解决为真或假。

基本上,我如何定义函数“contains”以便它需要一个数组a和一个变量b并执行a.contains?(b),但是没有所有相关的ruby特定的语法?

3 个答案:

答案 0 :(得分:2)

如果你想要一个不使用ruby语法的DSL,你至少需要编写一个解析器来执行转换(raganwalds重写lib可能是一个起点,http://github.com/raganwald/rewrite

那就是说,你不想这样做。这是需要维护的更多代码,Ruby已经做出了许多艰难的决定,这使得编写语言语法变得困难。自然语言编程对于非程序员来说也不容易使用,因为格式的精确性是具有挑战性的方面(例如参见Applecript)。

答案 1 :(得分:1)

您可以滥用method_missing。棘手的是,您无法直接访问块局部变量。你必须在某处捕获块内部绑定(不幸的是block.binding返回块的外部绑定)。

您可以运行此代码:

DSL.new do
  a = [1, 2, 3]
  b = 2
  a contains b
end

以下内容:

class DSL
  attr_reader :last_binding

  def initialize(&block)
    set_trace_func method(:trace).to_proc
    instance_eval(&block)
    set_trace_func nil
  end

  def trace(event, file, line, id, binding, klass)
    if event.to_s == "call" and klass == self.class and id.to_s == "method_missing"
      @last_binding ||= @current_binding
      set_trace_func nil
    else
      @current_binding = binding
    end
  end

  def lvars
    eval('local_variables', last_binding).map(&:to_s)
  end

  def method_missing(name, *args)
    name = name.to_s
    if lvars.include? name
      eval(name, last_binding).send(*args.flatten)
    else
      ["#{name}?", *args]
    end
  end
end

class Array
  alias contains? include?
end

答案 2 :(得分:0)

我能想到的最接近的事情是:

def contains var, useless_symbol, arr
  arr.include? var
end

然后你可以这样称呼它:

contains b, :in, a


我认为没有办法在你自己的职能中使用中缀符号。