LoginSignup
3
1

More than 5 years have passed since last update.

Rubyのキーワード引数に外部引数名を付けて遊ぶ

Last updated at Posted at 2017-09-04

実用性はありません。

目的

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_missingrange(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#execProcを評価出来ないため、諦めてStructargsに渡しています。
うまい書き方教えてください。

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1