红宝石爱好者!我正在尝试用 ruby 编写 DSL,我希望能够创建一些魔术方法(不确定这是我想要的最准确的术语)。
我希望能够执行以下操作:
a = [1, 2, 3]
b = 2
(a contains b)
并让它解析为真或假。
本质上,我如何定义函数“包含”,以便它接受一个数组a
和一个变量b
并执行a.contains?(b)
,但没有所有相关的 ruby 特定语法?
红宝石爱好者!我正在尝试用 ruby 编写 DSL,我希望能够创建一些魔术方法(不确定这是我想要的最准确的术语)。
我希望能够执行以下操作:
a = [1, 2, 3]
b = 2
(a contains b)
并让它解析为真或假。
本质上,我如何定义函数“包含”,以便它接受一个数组a
和一个变量b
并执行a.contains?(b)
,但没有所有相关的 ruby 特定语法?
如果你想要一个不使用 ruby 语法的 DSL,你至少需要编写一个解析器来执行转换(raganwalds rewrite lib 可能是一个起点,http ://github.com/raganwald/rewrite )
也就是说,你不想这样做。这是需要维护的更多代码,Ruby 已经做出了很多艰难的决定,使得编写语言语法变得困难。自然语言编程对于非程序员来说也不是很容易使用,因为格式的准确性是具有挑战性的方面(例如,参见 applescript)。
你可以滥用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
我能想到的最接近的事情是:
def contains var, useless_symbol, arr
arr.include? var
end
然后你可以这样称呼它:
contains b, :in, a
我认为没有任何方法可以在您自己的函数中使用中缀表示法。