実用性はありません。
目的
RubyでもSwiftのように外部引数名を使いたい
// Swift
func range(from start: Int, to end: Int) {
print(start)
print(end)
}
range(from: 0, to: 5)
Syntax
新しいメソッドの書き方を導入します。とは言え既存のシンタックスに沿って解釈できる形です。
func range(from: :start, to: :end) do |args|
puts args.start
puts args.end
end
funcメソッドにrange(from: :start, to: :end)とblockを渡している、という解釈になります。
あとは、
-
method_missingでrange(from: :start, to: :end)の実行をフック -
funcの中で引数をセット - メソッドを定義
するだけです。
実装
class BasicObject
class << self
def func(args, &block)
method = args[:method]
args_map = args[:args_map]
define_method(method) do |args|
keys = args_map.values.map { |v| ":#{v}" }.join(',')
values = args_map.keys.map { |k| "'#{args[k]}'"}.join(',')
s = eval("Struct.new(#{keys}).new(#{values})")
self.instance_exec(s, &block)
end
end
def method_missing(method, *args)
super unless args.first.is_a?(Hash)
return {:args_map => args.first, :method => method}
end
end
end
テスト
class A
func range(from: :start, to: :end) do |args|
puts args.start
puts args.end
end
end
a = A.new
a.range(from: 0, to: 5)
出来ましたね。
問題点
一応やりたいことはできましたが、method_missingでフックされる範囲が広すぎるため、失うものが多すぎます。
全く実用性はありません。
やりたかったこと
Binding#local_variable_setで引数をコンテキストに渡してやるつもりでしたが、Binding#execでProcを評価出来ないため、諦めてStructをargsに渡しています。
うまい書き方教えてください。