Skip to content
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

Give users an error when they use a value matcher in a block expectation expression #530

Merged
merged 5 commits into from Apr 20, 2014

Conversation

@myronmarston
Copy link
Member

myronmarston commented Apr 17, 2014

Start of fix for #526.

  • Fix the block matchers to give a clear error when given a value rather than a block arg.
  • Add a deprecation warning to 2.99.
@myronmarston
Copy link
Member Author

myronmarston commented Apr 17, 2014

class ExpectationTarget
# @private
# Used as a sentinel value to be able to tell when the user

This comment has been minimized.

Copy link
@JonRowe

JonRowe Apr 18, 2014

Member

I've never heard the term "sentinel value" before, I like it.

else
@block_expectation = false
@target = value
end

This comment has been minimized.

Copy link
@JonRowe

JonRowe Apr 18, 2014

Member

So I'm thinking if we did this as a factory method, that returned either a ExpectationTarget or a BlockExpectationTarget rather than in #initialize, we could skip the conditional checks later on.

This comment has been minimized.

Copy link
@cupakromer

cupakromer Apr 18, 2014

Member

Any reason this couldn't be part of expect syntax definition?

This comment has been minimized.

Copy link
@myronmarston

myronmarston Apr 18, 2014

Author Member

So I'm thinking if we did this as a factory method, that returned either a ExpectationTarget or a BlockExpectationTarget rather than in #initialize, we could skip the conditional checks later on.

Great idea, I'll push a fix for this soon.

Any reason this couldn't be part of expect syntax definition?

Well, this version of the logic (that has to infer if it's a block expectation or not) really fits better here, as the @block_expectation variable is really internal state. I'm moving in the direction of a factory method. I don't want it in the syntax file, though; those inline method defs are intended to be short.

@@ -25,7 +46,8 @@ def initialize(target)
# @return [Boolean] true if the expectation succeeds (else raises)
# @see RSpec::Matchers
def to(matcher=nil, message=nil, &block)
prevent_operator_matchers(:to, matcher)
enforce_block_matcher(matcher) if @block_expectation
prevent_operator_matchers(:to) unless matcher

This comment has been minimized.

Copy link
@JonRowe

JonRowe Apr 18, 2014

Member

As per my previous comment, these could easily be:

def to(...)
  enforce_block_matcher(matcher)
  super
end

in a hypothetical BlockExpectationTarget

# Used as a sentinel value to be able to tell when the user
# did not pass an argument. We can't use `nil` for that because
# `nil` is a valid value to pass.
UndefinedValue = Module.new

This comment has been minimized.

Copy link
@cupakromer

cupakromer Apr 18, 2014

Member

Any particular reason for the choice of Module?

This comment has been minimized.

Copy link
@myronmarston

myronmarston Apr 18, 2014

Author Member

For singleton objects that are assigned to a constant I prefer to use a module or a class over just Object.new because the constant assignment takes care of giving it a good to_s/inspect rather than just Object#inspect.

For Module vs Class I tend to use a module if it's not intended to be instantiated. It probably doesn't matter but a module feels more limited/lightweight to me.

This comment has been minimized.

Copy link
@cupakromer

cupakromer Apr 18, 2014

Member

Thanks for that insight. That helps me a bunch.

raise ArgumentError.new("You must pass an argument or a block to #expect but not both.") unless target.size == 1
::RSpec::Expectations::ExpectationTarget.new(target.first)
def expect(value=::RSpec::Expectations::ExpectationTarget::UndefinedValue, &block)
::RSpec::Expectations::ExpectationTarget.new(value, block)

This comment has been minimized.

Copy link
@cupakromer

cupakromer Apr 18, 2014

Member

Per the comments above. Could this be the factory method which creates instances of the different target types?

This comment has been minimized.

Copy link
@JonRowe

JonRowe Apr 18, 2014

Member

We'd want the factory method somewhere else, in general we don't like the monkey patched methods to do much.

@myronmarston
Copy link
Member Author

myronmarston commented Apr 18, 2014

OK, I refactored based on @JonRowe's suggestion. Still have a couple more TODOs (see above).

@myronmarston
Copy link
Member Author

myronmarston commented Apr 18, 2014

Hmm, I'm actually wondering if supports_block_expectations? is better than block_matcher?. I think in the custom matcher DSL it reads better:

RSpec::Matchers.define :match_a_block do
  match { ... }
  supports_block_expectations
end

Also, block_matcher? makes it sound like a matcher that returns true from that can't also work with values, but @cupakromer had a case where it could support either.

Any preference?

@JonRowe
Copy link
Member

JonRowe commented Apr 18, 2014

I'm ok with supports_block_expectations?

@cupakromer
Copy link
Member

cupakromer commented Apr 18, 2014

Agree, 👍 for supports_block_expectations

This is used to give users a clear error when they
wrongly use a value matcher against a block, expecting
it to match against the return value of the block:

expect { 3 }.to eq(3)
@myronmarston
Copy link
Member Author

myronmarston commented Apr 18, 2014

OK, I think this is ready to go (except for the 2.99 deprecation)....care to rereview?

@JonRowe
Copy link
Member

JonRowe commented Apr 20, 2014

LGTM

myronmarston added a commit that referenced this pull request Apr 20, 2014
Give users an error when they use a value matcher in a block expectation expression
@myronmarston myronmarston merged commit 1603a39 into master Apr 20, 2014
1 check passed
1 check passed
continuous-integration/travis-ci The Travis CI build passed
Details
@myronmarston myronmarston deleted the block-matcher-clarification branch Apr 20, 2014
HoneyryderChuck pushed a commit to HoneyryderChuck/rspec-html-matchers that referenced this pull request May 24, 2014
@myronmarston myronmarston mentioned this pull request Jun 19, 2014
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked issues

Successfully merging this pull request may close these issues.

None yet

3 participants
You can’t perform that action at this time.