実用性はありません。
目的
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
に渡しています。
うまい書き方教えてください。