-
-
Notifications
You must be signed in to change notification settings - Fork 158
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Show proof of concept for keyword arg matching #534
Changes from 8 commits
94c6525
73c98e4
7b823bc
f3ba1d6
d6da11e
2da7951
4667ce6
2b1067b
c1d3b2c
96e71ad
cf19d36
a89cf15
41e9cc7
44e7d41
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
require 'mocha/parameter_matchers/base' | ||
require 'mocha/ruby_version' | ||
|
||
module Mocha | ||
module ParameterMatchers | ||
# @private | ||
class LastPositionalHash < Base | ||
def initialize(value) | ||
@value = value | ||
end | ||
|
||
def matches?(available_parameters) | ||
parameter = available_parameters.shift | ||
if RUBY_V3_PLUS | ||
return false unless Hash.ruby2_keywords_hash?(@value) == Hash.ruby2_keywords_hash?(parameter) | ||
end | ||
|
||
parameter == @value | ||
end | ||
|
||
def mocha_inspect | ||
@value.mocha_inspect | ||
end | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
module Mocha | ||
RUBY_V2_PLUS = Gem::Version.new(RUBY_VERSION.dup) >= Gem::Version.new('2') | ||
RUBY_V3_PLUS = Gem::Version.new(RUBY_VERSION.dup) >= Gem::Version.new('3') | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -88,9 +88,9 @@ def should_stub_two_different_class_methods | |
found_widgets = [Widget.new] | ||
created_widget = Widget.new | ||
Widget.expects(:find).with(:all).returns(found_widgets) | ||
Widget.expects(:create).with(:model => 'wombat').returns(created_widget) | ||
Widget.expects(:create).with({ :model => 'wombat' }).returns(created_widget) | ||
assert_equal found_widgets, Widget.find(:all) | ||
assert_equal created_widget, Widget.create(:model => 'wombat') | ||
assert_equal created_widget, Widget.create({ :model => 'wombat' }) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, stub_method_owner.send(:define_method, method_name) do |*args, &block|
self_in_scope.mock.method_missing(method_name_in_scope, *args, &block)
end There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does it help if you add stub_method_owner.send(:define_method, method_name) do |*args, **kwargs, &block|
self_in_scope.mock.method_missing(method_name_in_scope, *args, **kwargs, &block)
end There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Setting Kwargs is another possibility, but I think we might need to refactor a lot more (most of the code expects an array of arguments instead of having the arguments split into positional and keyword args, plus we probably still need to have separate method definitions for other Ruby versions). Good reference: https://eregon.me/blog/2021/02/13/correct-delegation-in-ruby-2-27-3.html There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, gotcha! That blog post looks very helpful. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Was discussing with @chrisseaton (thanks!), and he suggested to try using It sounded like a great idea, so I started spiking a possible encapsulation of parameters here, but it's looking to be a pretty large and tricky change. @floehopper, how do you feel about the refactor? If agreeable, do you think we should attempt to refactor first, or should I try to finish this current approach first (setting ruby2_keywords at multiple layers) before circling back? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
@wasabigeek I'm a bit confused where "here" is, i.e. where I should be looking! Is it on the branch associated with this PR? And if so what's the best way for me to look at it - should I just look at the net diff of all the changes or each commit in turn? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah sorry, I was referencing another PR: #544, that has the proposed refactor. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Ideally I'd like to provide as simple an upgrade path as possible given that this change might mean quite pervasive changes in a lot of codebases. I think that means having a backwardly compatible version of the code (ideally with deprecation warnings highlighting the ambiguities) and a new version of the code which requires explicitness. It would be nice if the two versions of the code could co-exist in the same release of Mocha behind a configuration option, but if that makes the code ridiculously complicated, it would be fine to have the two versions of the code in separate releases of Mocha (separated by a major version bump). In the latter case, it would be good to have finalised the new API by the time of the 1st release, so the deprecation warnings could explain how to fix them as clearly as possible. I haven't had much time to think about the API, but of the two options you've proposed, I think I prefer Do you think we might need to make the positional argument matching more explicit too, i.e. If we do use any of these approaches, I'd probably prefer more verbose names, i.e. Just to check my understanding, am I right in thinking the ambiguity mentioned above will eventually go away if/when we stop supporting Ruby v2? If that's the case, would it make any sense to have a configuration option (or code that detects the Ruby version) that allows a simpler I hope that helps. Thanks again for your work on this. Let me know if you have any more questions. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry, I forgot to respond to this!
I don't have a strong opinion at the moment, though it feels like
I don't think so, unless we make a breaking change in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Fair point.
Ah, I think I see what you mean now - thanks for explaining. 👍 |
||
end | ||
|
||
def should_stub_instance_method_on_any_instance_of_a_class | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, since
expected_parameters
can be matchers as well, would that break matchers for keyword arguments? e.g. thehas_value
matcherI'll check a test case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the current implementation it still works, this passes:
However, I think it's because the keyword arg check only happens if the last expected param is a Hash, which in this case it isn't:
mocha/lib/mocha/parameters_matcher.rb
Line 32 in 44e7d41
This might become problem when we want to refactor parameters in the future (see comment).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To discuss further in #534 (comment)