ルビー。名前付き引数を含む混合引数の場合、method_missing + send が失敗する
概要
この単純な Ruby コードがあります。 「プロキシパターン」の実装です。最後の行で失敗します
class ExecuteProxy
def initialize(controller_object)
@controller_object = controller_object
end
def method_missing(method, *args)
args.empty? ? @controller_object.send(method) : @controller_object.send(method, *args)
return
end
end
class MyClass
def no_arghuments
puts "no_arghuments"
end
def one_argument(arg1)
puts "one_argument #{arg1}"
end
def one_argument_and_named(arg1, count:1)
puts "one_argument #{arg1} and count #{count}"
end
end
obj = MyClass.new
proxy = ExecuteProxy.new(obj)
proxy.no_arghuments
proxy.one_argument("test")
proxy.one_argument_and_named("test2")
proxy.one_argument_and_named("test2", count:2)
出力
no_arghuments
one_argument test
one_argument test2 and count 1
test1.rb:20:in `one_argument_and_named': wrong number of arguments (given 2, expected 1) (ArgumentError)
from test1.rb:8:in `method_missing'
from test1.rb:31:in `<main>'
これはどうすれば解決できますか?メソッドに通常の引数 + 名前付き引数がある場合、send を使用して別のクラスに実行を渡すにはどうすればよいですか?
アップデート。機能するコードがあります。回答が役に立ちました。ありがとう。
class ExecuteProxy
def initialize(controller_object)
@controller_object = controller_object
end
def method_missing(method, *args, **kw)
@controller_object.send(method, *args, **kw)
return
end
end
class MyClass
def no_arghuments
puts "no_arghuments"
end
def one_argument(arg1)
puts "one_argument #{arg1}"
end
def one_argument_and_named(arg1, count:1)
puts "one_argument #{arg1} and count #{count}"
end
def only_named(count:, str:"")
puts "only_named count #{count}, str: #{str}"
end
def manyarguments_and_named_args(arg1, arg2, arg3 = "def", count:1, some_other:"d")
puts "manyarguments_and_named_args #{arg1}, #{arg2}, #{arg3} and count #{count}, some_other #{some_other}"
end
end
obj = MyClass.new
proxy = ExecuteProxy.new(obj)
proxy.no_arghuments
proxy.one_argument("test")
proxy.one_argument_and_named("test2")
proxy.one_argument_and_named("test2", count:2)
proxy.only_named(count:3)
proxy.only_named(count:3,str:"string")
proxy.manyarguments_and_named_args("a1","a2")
proxy.manyarguments_and_named_args("a1","a2", "s3")
proxy.manyarguments_and_named_args("a1","a2", count:4)
proxy.manyarguments_and_named_args("a1","a2", count:4, some_other:nil)
出力
no_arghuments
one_argument test
one_argument test2 and count 1
one_argument test2 and count 2
only_named count 3, str:
only_named count 3, str: string
manyarguments_and_named_args a1, a2, def and count 1, some_other d
manyarguments_and_named_args a1, a2, s3 and count 1, some_other d
manyarguments_and_named_args a1, a2, def and count 4, some_other d
manyarguments_and_named_args a1, a2, def and count 4, some_other
解決策
最後の引数として明示的なハッシュを渡す場合、それはキーワード引数としてカウントされないため、それらをダブルスプラットする必要があります。
def one_argument_and_named(arg1, count: 1)
puts "one_argument #{arg1} and count #{count}"
end
one_argument_and_named("test2", count: 2) # good
one_argument_and_named(*["test2", count: 2]) # bad
one_argument_and_named("test2", {count: 2}) # bad
one_argument_and_named("test2", **{count: 2}) # good
引数があるかどうかを確認する必要はありません。引数が空の場合は飛び散ります。
def method_missing(method, *args, **kw)
@controller_object.send(method, *args, **kw)
end
# depending on your ruby version you could do this
# only if you don't need intermidiate access to args or kw
def method_missing(method, *, **)
@controller_object.send(method, *, **)
end
# or
def method_missing(method, ...)
@controller_object.send(method, ...)
end