From a05133981b9b7532cca230749120355efe6462a5 Mon Sep 17 00:00:00 2001 From: Andre Arko Date: Sun, 25 Apr 2021 22:21:39 -0700 Subject: [PATCH 1/4] create failing spec for keyword args proxy when the proxy fails to forward keywoard arguments, the error for methods that only accept keyword arguments looks like this: ArgumentError: wrong number of arguments (given 1, expected 0) --- .../spec/elasticsearch/model/proxy_spec.rb | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/elasticsearch-model/spec/elasticsearch/model/proxy_spec.rb b/elasticsearch-model/spec/elasticsearch/model/proxy_spec.rb index e9d9712c8..f21baad3d 100644 --- a/elasticsearch-model/spec/elasticsearch/model/proxy_spec.rb +++ b/elasticsearch-model/spec/elasticsearch/model/proxy_spec.rb @@ -31,6 +31,10 @@ def bar 'insta barr' end + def keyword_method(foo: 'default value') + foo + end + def as_json(options) {foo: 'bar'} end @@ -98,7 +102,6 @@ def changes_to_save end context 'when instances are cloned' do - let!(:model) do DummyProxyModel.new end @@ -121,4 +124,9 @@ def changes_to_save expect(duplicate).to eq(duplicate_target) end end + + it 'forwards keyword arguments to target methods' do + expect(DummyProxyModel.new.__elasticsearch__.keyword_method(foo: 'bar')).to eq('bar') + end + end From 689385c9e757a96972fa32fc986ff9de38e456bf Mon Sep 17 00:00:00 2001 From: Andre Arko Date: Sun, 25 Apr 2021 22:26:44 -0700 Subject: [PATCH 2/4] fix proxying keyword arguments this resolves a bug with Rails 6.1 on Ruby 3, where keyword arguments are not forwarded and this causes an ArgumentError --- elasticsearch-model/lib/elasticsearch/model/proxy.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/elasticsearch-model/lib/elasticsearch/model/proxy.rb b/elasticsearch-model/lib/elasticsearch/model/proxy.rb index 955280fba..969593e46 100644 --- a/elasticsearch-model/lib/elasticsearch/model/proxy.rb +++ b/elasticsearch-model/lib/elasticsearch/model/proxy.rb @@ -117,8 +117,8 @@ def initialize(target) # Delegate methods to `@target` # - def method_missing(method_name, *arguments, &block) - target.respond_to?(method_name) ? target.__send__(method_name, *arguments, &block) : super + def method_missing(method_name, *args, **kwargs, &block) + target.respond_to?(method_name) ? target.__send__(method_name, *args, **kwargs, &block) : super end # Respond to methods from `@target` From b4b4c6f27432edb1a0fa0a31515e20a54fdd78e9 Mon Sep 17 00:00:00 2001 From: Andre Arko Date: Sun, 25 Apr 2021 22:28:06 -0700 Subject: [PATCH 3/4] use respond_to_missing? not respond_to? since Ruby 1.9.2, about 12 years ago, you're supposed to define respond_to_missing? if you also define method_missing. there's more explanation in this blog post by a ruby committer in 2010: http://blog.marc-andre.ca/2010/11/15/methodmissing-politely/ --- elasticsearch-model/lib/elasticsearch/model/proxy.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/elasticsearch-model/lib/elasticsearch/model/proxy.rb b/elasticsearch-model/lib/elasticsearch/model/proxy.rb index 969593e46..f26cda899 100644 --- a/elasticsearch-model/lib/elasticsearch/model/proxy.rb +++ b/elasticsearch-model/lib/elasticsearch/model/proxy.rb @@ -123,7 +123,7 @@ def method_missing(method_name, *args, **kwargs, &block) # Respond to methods from `@target` # - def respond_to?(method_name, include_private = false) + def respond_to_missing?(method_name, include_private = false) target.respond_to?(method_name) || super end From d9c062c6626646dec43de8218002fa401c989d12 Mon Sep 17 00:00:00 2001 From: Andre Arko Date: Mon, 26 Apr 2021 15:33:14 -0700 Subject: [PATCH 4/4] update method missing for Ruby 2.x-3.0 all at once --- elasticsearch-model/lib/elasticsearch/model/proxy.rb | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/elasticsearch-model/lib/elasticsearch/model/proxy.rb b/elasticsearch-model/lib/elasticsearch/model/proxy.rb index f26cda899..6fdf66322 100644 --- a/elasticsearch-model/lib/elasticsearch/model/proxy.rb +++ b/elasticsearch-model/lib/elasticsearch/model/proxy.rb @@ -115,10 +115,13 @@ def initialize(target) @target = target end - # Delegate methods to `@target` + def ruby2_keywords(*) # :nodoc: + end if RUBY_VERSION < "2.7" + + # Delegate methods to `@target`. As per [the Ruby 3.0 explanation for keyword arguments](https://www.ruby-lang.org/en/news/2019/12/12/separation-of-positional-and-keyword-arguments-in-ruby-3-0/), the only way to work on Ruby <2.7, and 2.7, and 3.0+ is to use `ruby2_keywords`. # - def method_missing(method_name, *args, **kwargs, &block) - target.respond_to?(method_name) ? target.__send__(method_name, *args, **kwargs, &block) : super + ruby2_keywords def method_missing(method_name, *arguments, &block) + target.respond_to?(method_name) ? target.__send__(method_name, *arguments, &block) : super end # Respond to methods from `@target`