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

Add argument matcher for expecting a block. #1237

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

JonRowe
Copy link
Member

@JonRowe JonRowe commented Sep 17, 2018

This is @kaiwren's a_block argument matcher with fleshed out sad path specs, and a refactoring of the match logic to allow mixing with other arguments. Review please @kaiwren @ioquatix @benoittgt.

@JonRowe
Copy link
Member Author

JonRowe commented Sep 17, 2018

Some further improvements could be made here,what happens when used positionally? and does it always mean the supplied &block argument? After all object.msg(proc {}, proc {}) is valid Ruby, should we expect on this with .with(a_block, a_block) and how do we differentiated that from msg(proc{}) { }. And if we don't allow that should we reject the method expectation out of hand? We already have and_yield for block arguments too.

@ioquatix
Copy link

My only use case was to do something like expect(thing).to receive(:method).with_block, so anything that can do that I am happy with. It might even make sense to add that specific method with_block as that's what I want to use initially. I think the extended syntax is also useful.

@JonRowe
Copy link
Member Author

JonRowe commented Sep 17, 2018

@ioquatix We already have that, it's called and_yield, e.g. expect(thing).to receive(:method).and_yield

@ioquatix
Copy link

For some reason I couldn't find this initially... I did look for it!

@benoittgt
Copy link
Member

Maybe we can say this is good enough for a first step? Ready the tests I found it already a good improvement.

Good job

Copy link
Member

@benoittgt benoittgt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@kaiwren
Copy link
Contributor

kaiwren commented Sep 17, 2018

Thanks so much!

@kaiwren
Copy link
Contributor

kaiwren commented Sep 17, 2018

A quick question - IMO ArgumentListMatcher and associated code could use a little refactoring to better express intent, and to include blocks in the args verification in a more consistent way (that's one of the reasons I stopped at a_block). I'm happy to attempt this if it makes sense to do so, and of course, it goes without saying that I'll keep the changes small, and will completely understand if they are rejected. Would that be useful?

@JonRowe
Copy link
Member Author

JonRowe commented Sep 18, 2018

You are welcome to have a go at refactoring it if you think you can make it better, please make sure you understand the matcher protocol before changing method names, e.g. it's important and non optional that === is used as the match method, as it's part of our composability and is what lets argument matchers and other matchers be combined.

Thinking about this, theres now ambiguity as to what we mean when, @myronmarston I'm leaning towards adding a_block to match a block "anywhere", and then adding .with_block to mean the block argument in order to be ambiguous, meaning with(...)s arguments will remain "real" argument. Effectively .with_block would just check for the presence of &block, or could check for a specific block?

@ioquatix
Copy link

Procs/Lambdas as an argument and blocks are different things. I suggest consider a_block and a_proc/a_lambda/a_callable?

@kaiwren
Copy link
Contributor

kaiwren commented Sep 18, 2018

@JonRowe yep, well noted, I will keep that in mind.

Yep agree that bound and unbound blocks be treated differently. I was unaware of and_yield when I picked up this ticket, but +1 to @JonRowe's point IMO the contracts and use cases for with(a_block) and_yield and with(a_block, ..., a_block) should be defined before proceeding further to avoid ambiguous apis. I will defer to your judgement, and am happy to attempt an implementation once the decision is made.

@JonRowe
Copy link
Member Author

JonRowe commented Sep 18, 2018

They're not different enough, and can be passed around... Consider:

2.5.1 :001 > def a(&block)
2.5.1 :002?>   puts block.inspect
2.5.1 :003?>   block
2.5.1 :004?> end
 => :a
2.5.1 :005 > a =  a { |a| }
#<Proc:0x00007fd290a01440@(irb):4>
 => nil
2.5.1 :006 > b = a(&proc { |b| })
#<Proc:0x00007fd2909e1c58@(irb):5>
 => nil
2.5.1 :007 > c =  a(&lambda { |c| })
#<Proc:0x00007fd2909d0548@(irb):6 (lambda)>
 => nil
2.5.1 :008 > Proc === a
 => true
2.5.1 :009 > Proc === b
 => true
2.5.1 :010 > Proc === c
 => true

@kaiwren
Copy link
Contributor

kaiwren commented Sep 23, 2018

@JonRowe @benoittgt @ioquatix Working toward treating args and blocks in a more similar manner, I've done some work to try to tease out ReceivedMessage and ReceivedMessages as concrete classes from Proxy and delegate to them as a first step. Do let me know if this makes sense, or if I should move in a different direction or just drop it entirely. master...kaiwren:refactor-extract-ReceivedMessage-from-Proxy

kaiwren and others added 2 commits October 2, 2019 16:00
* cucumber feature passing
* happy path unit specs passing
* need help fixing sad path unit specs, currently marked pending
@JonRowe JonRowe changed the base branch from master to main August 2, 2020 02:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants