<table>
<tr><th align="left">methodfinder</th><td>2.1.0</td></tr>
<tr><th align="left">ruby</th><td>2.4.2p198 (2017-09-14 revision 59899) [x86_64-linux]</td></tr>
<tr><th align="left">OS</th><td>Linux (Arch)</td></tr>
</table>
Since 82f9c09dfdb9536d9c382f747ac8459d2b204caa, it's no longer possible to pass a hash as the last argument to `Object#find_method` or `MethodFinder.find`.
```
>> { foo: "bar" }.find_method({ foo: "bar", baz: "quux" }, { baz: "quux" })
ArgumentError: unknown keyword: baz
from ~/.gem/ruby/2.4.0/gems/methodfinder-2.1.0/lib/methodfinder.rb:23:in `find_method'
from (irb):43
from /usr/bin/irb:11:in `<main>'
```
```
>> MethodFinder.find({ foo: "bar" }, { foo: "bar", baz: "quux" }, { baz: "quux" })
ArgumentError: unknown keyword: baz
from ~/.gem/ruby/2.4.0/gems/methodfinder-2.1.0/lib/methodfinder.rb:70:in `find'
from (irb):44
from /usr/bin/irb:11:in `<main>'
```
It's not syntactical so the obvious workarounds don't work e.g.:
```
>> h = { "baz".to_sym => "quux" }
>> { foo: "bar" }.find_method({ foo: "bar", baz: "quux" }, h)
ArgumentError: unknown keyword: baz
from ~/.gem/ruby/2.4.0/gems/methodfinder-2.1.0/lib/methodfinder.rb:23:in `find_method'
from (irb):43
from /usr/bin/irb:11:in `<main>'
```
One workaround is to append the keyword argument e.g.:
```
mf.find({ foo: "bar" }, { foo: "bar", baz: "quux" }, { "baz": "quux" }, { debug: false })
#=> ["Hash#merge", "Hash#merge!", "Hash#update"]
```
Though it doesn't currently work with `find_method`:
```
{ foo: "bar" }.find_method({ foo: "bar", baz: "quux" }, { baz: "quux" }, { debug: false })
ArgumentError: unknown keyword: baz
from ~/.gem/ruby/2.4.0/gems/methodfinder-2.1.0/lib/methodfinder.rb:70:in `find'
from ~/.gem/ruby/2.4.0/gems/methodfinder-2.1.0/lib/methodfinder.rb:33:in `find_method'
from (irb):55
from /usr/bin/irb:11:in `<main>'
```
Not sure how to solve this (without removing the `debug` option or adding e.g. `mf.debug`) as it appears to be a fundamental (mis)feature of keyword args:
> When a method has keyword arguments, Ruby offers [implicit conversion](https://github.com/ruby/ruby/blob/c8b3f1b470e343e7408ab5883f046b1056d94ccc/test/ruby/test_keyword.rb#L490) of a `Hash` argument into keyword arguments. This conversion is performed by [calling `to_hash` on the last argument to that method](https://github.com/ruby/ruby/blob/ed8b452e37322e66eb94dafa36f2b5da8f5ca44a/vm.c#L2431), **before assigning optional arguments**. If `to_hash` returns an instance of `Hash`, the hash is taken as keyword arguments to that method.
-- [Heads up: Ruby implicitly converts a hash to keyword arguments](https://makandracards.com/makandra/36013-heads-up-ruby-implicitly-converts-a-hash-to-keyword-arguments)
> Ruby checks that:
> - after all mandatory unnamed arguments are filled
> - if the last remaining argument is hash-like
> - and all its keys are symbols
> - and the method called uses keyword arguments
> => then that parameter is used for keyword arguments.
-- [Bug #11967: Mixing kwargs with optional parameters changes way method parameters are parsed](https://bugs.ruby-lang.org/issues/11967)
I know this took forever, but this is now working. Since I was on it already I cleaned up/refactored quite a bit of code and completely redesigned the debugging.
@chocolateboy Could you do me a favor and play around with this for a bit before I do the 2.2.0 release on Rubygems?
And, a nit/suggestion: I expected to find a version defined in the module somewhere (to confirm that I was using the latest version) e.g. `MethodFinder::VERSION`, but can't find any references to it in `lib`.
Thanks for the feedback!
```
→ METHOD_FINDER_DEBUG=1 irb
>> "foo".find_method("FOO")
!
+@
-@
[]
[]=
__id__
__send__
ai
ascii_only?
...
=> ["String#swapcase", "String#swapcase!", "String#upcase", "String#upcase!"]
>> MethodFinder.toggle_debug!
=> false
>> "foo".find_method("FOO")
=> ["String#swapcase", "String#swapcase!", "String#upcase", "String#upcase!"]
```
and
```
MethodFinder::VERSION
=> "2.2.1"
```
I had pushed 2.2.0 a bit too early and yanked it after I saw your comments 😢 The code is pushed in case you want to play around with it a bit more.
I'd like to release the gem today, will try to get it included in Ruby Weekly.
Heads up for my features plans:
1. Changing the gems structure to be like `bundler gem` so this becomes a bit more standard.
2. Changing the blacklisting API so people don't have to modify constants directly, which always struck me as a bit odd.
BTW: Due to your continuous help, I added you as a collaborator to this repository. It's ok to fix smaller typos etc. directly (though I may later rebase and force push, I'm a bit anal about commit history), but for code changes etc. we can do pull requests as usual, deal?
Deal, thanks! But please don't ever force push to master! That just causes a lot of [pain](https://github.com/citizen428/methodfinder/pull/12)/[work](https://github.com/citizen428/methodfinder/pull/13) for anyone/everyone downstream.
Haha, no worries, once I add a collaborator I switch to a more proper work
flow, but as long it’s just me I do pretty much whatever.
On Wed, 2 May 2018 at 09:39 chocolateboy <[email protected]> wrote:
> (If you anticipate doing this regularly, you could do it on e.g. a develop
> branch and just use master for releases.)
>
> —
> You are receiving this because you modified the open/close state.
> Reply to this email directly, view it on GitHub
> <https://github.com/citizen428/methodfinder/issues/10#issuecomment-385848381>,
> or mute the thread
> <https://github.com/notifications/unsubscribe-auth/AAC7cYGVAQpvN2tsKifYJxwDyi06fAf0ks5tuRx3gaJpZM4QJnQi>
> .
>