[rspec-devel] [ rspec-Patches-12841 ] Allow stubs to yield consecutive values

noreply at rubyforge.org noreply at rubyforge.org
Sat Sep 1 09:25:41 EDT 2007


Patches item #12841, was opened at 2007-08-06 06:13
You can respond by visiting: 
http://rubyforge.org/tracker/?func=detail&atid=3151&aid=12841&group_id=797

Category: None
Group: None
>Status: Closed
>Resolution: Rejected
Priority: 3
Submitted By: Pat Maddox (pergesu)
Assigned to: Nobody (None)
Summary: Allow stubs to yield consecutive values

Initial Comment:
Scott Taylor asked,

I have a mock of an instance of a class which descends from Array:

class ArrayDescendent < Array; end

#... in the specs...
@descendent = mock ArrayDescendent

How would I stub out ArrayDescendent#each, which is inherited from
Array, to return multiple values successively?  I could use
and_yield, but that is raising an arity error (the anonymous function/
block should expect only *one value at a time, but and_yield is
yielding both values once).  Should I be using a lambda expression here?




This patch provides the #and_yield_consecutively method for mocks and stubs.

----------------------------------------------------------------------

>Comment By: David Chelimsky (dchelimsky)
Date: 2007-09-01 13:25

Message:
Fixed with patch 13567:

http://rubyforge.org/tracker/?func=detail&atid=3151&aid=13567&group_id=797

The approach in that patch is to say and_yield multiple times rather than adding a new method.

----------------------------------------------------------------------

Comment By: David Chelimsky (dchelimsky)
Date: 2007-08-31 22:19

Message:
Our plan is to deprecate the mocking framework, but I don't think we're totally there yet. And so I leave it to you as to whether you want to submit the patch. If you do and it's fine, I'll gladly add it.

----------------------------------------------------------------------

Comment By: Rupert Voelcker (rupert)
Date: 2007-08-31 22:16

Message:
seeing as I've now worked out how to rspec rspec, I could submit (rather then knock up) a spec'd patch for this that uses the chained and_yield syntax.

....but afore I do is the mocking framework still being updated, and is this wanted as I guess there's no point if the answer to either of these is no (and I should try and get something into mocha instead).

Cheers

Rupert 

----------------------------------------------------------------------

Comment By: Rupert Voelcker (rupert)
Date: 2007-08-17 10:47

Message:
ah - cool!

----------------------------------------------------------------------

Comment By: Rupert Voelcker (rupert)
Date: 2007-08-17 10:45

Message:
Aslak - the arity issue (to me) is if you have:

obj.should_receive(:msg).and_yield([a,b],[c,d])

and you assume that this is a consecutive yield, you can use it to spec yilelder1 in the following class:

class MyClass

  def yielder1
    yield([1,2])
    yield([3,4])
  end

  def yielder2
    yield([1,2], [3,4])
  end
end

However, you couldn't then spec yielder2.

This problem is potentially there for and_return too (I think), as as far as I know you can return multiple values (although being quite new to ruby I've not used this feature yet so don't actually know).

Not sure what the differences are that Dave had in mind, but to me the difference is that and_return (as currently handled in rspec) can only return one value per message call. and_yield can return multiple values many times per message call.

P.S. still can't see where to submit a file - there's just a list of attached files and download links at the bottom and a list of changes :(  Not that this is particularly imporatant!

----------------------------------------------------------------------

Comment By: Aslak Hellesøy (aslak_hellesoy)
Date: 2007-08-17 10:23

Message:
Doh, I just understood the arity problem. I'm an idiot.

----------------------------------------------------------------------

Comment By: Aslak Hellesøy (aslak_hellesoy)
Date: 2007-08-17 10:19

Message:
Rupert: There is a form at the bottom of this page for patches. Just remember to check the checkbox.

I still haven't heard a good argument for doing this differently from #and_return. What arity problems do you mean?

I don't like the chained #and_yield form - it's overly verbose and it introduces a new idiom for something that should be similar to the cousin #and_return.

Dave, you suggested that #and_yield is a different beast than #and_return. How so?

----------------------------------------------------------------------

Comment By: Rupert Voelcker (rupert)
Date: 2007-08-16 21:56

Message:
no idea how to add the patch file, so it's at:

http://pastie.textmate.org/88416

----------------------------------------------------------------------

Comment By: Rupert Voelcker (rupert)
Date: 2007-08-16 21:51

Message:
I like the look of the chained and_yield's myself, as it's pretty clear what's happening and avoids arity probs.  An alternative is to have something along the lines of

obj.should_receive(:msg).and_yield(....stuff to yield....).with_arity(2)

then if the number of args is a multiple of the arity then yield consecutively the multiple number of times.  If the number of args isn't a multiple of the arity then raise an arity error.

Does this seem like a good idea, or would it be confusing?

Anyway, I've made up a patch for handling the chained yields (I presume it'll allow me to upload after I hit submit - not used a tracker before)

No tests as I can't work out how to run the tests on rspec and if the arity approach is better the patch is junk anyway.

----------------------------------------------------------------------

Comment By: Aslak Hellesøy (aslak_hellesoy)
Date: 2007-08-13 00:05

Message:
IMO:

obj.should_receive(:msg).and_yield([a,b],[c,d])

obj.msg # => [a,b]
obj.msg # => [c,d]
obj.msg # => [c,d] # When/if we reach the end, just yield the last one.

Wouldn't that work?

----------------------------------------------------------------------

Comment By: David Chelimsky (dchelimsky)
Date: 2007-08-12 23:27

Message:
So how would that work with arity > 1?

obj.should_receive(:msg).and_yield([a,b],[c,d])

Does that mean:
- yield two arrays on the first call
- yield one array each for the first two calls
- yield two args each for the first two calls

This problem doesn't apply to and_return.

----------------------------------------------------------------------

Comment By: Aslak Hellesøy (aslak_hellesoy)
Date: 2007-08-12 23:13

Message:
This should be done in the same way as and_return(*consecutive_values)

----------------------------------------------------------------------

Comment By: David Chelimsky (dchelimsky)
Date: 2007-08-12 22:48

Message:
Right now you can do this to get consecutive yield values:

obj.should_receive(:msg).and_yield(value1)
obj.should_receive(:msg).and_yield(value2)

The problem with putting all the values to be yielded in one statement is that rspec would have no way of knowing definitively what is a single array argument vs an array of consecutive arguments.

Conceivably, we could so something like:

obj.should_receive(:msg)
  .and_yield(value1)
  .and_yield(value2)
  .and_yield(value3)
  .and_yield(value4)

But is that really that much of an advantage?

----------------------------------------------------------------------

Comment By: Pat Maddox (pergesu)
Date: 2007-08-06 07:06

Message:
I'm not sure why I thought all? was the proper method to
use.  Just collect the return values and pass them back.

----------------------------------------------------------------------

Comment By: Pat Maddox (pergesu)
Date: 2007-08-06 06:40

Message:
I realized I should probably explain why this is necessary.

Yielded values represent the input values to an anonymous
function (block).  Since a function can take more than 1
argument, there's no way of knowing whether multiple values
are supposed to be passed in at once or returned
consecutively.  This gives us both options, without any
magic that might introduce arity errors.

----------------------------------------------------------------------

Comment By: Pat Maddox (pergesu)
Date: 2007-08-06 06:31

Message:
A little bit of multi-arity love.

it "should support yielding consecutive values to a *-arity
block" do
  yielded_values = []
 
@obj.stub!(:method_that_yields).and_yield_consecutively(["biscuits",
"and"], ["gravy", "is"], ["good", "eatin"])
  @obj.method_that_yields {|val1, val2| yielded_values <<
"#{val1} #{val2}" }
  yielded_values.should == [ "biscuits and", "gravy is",
"good eatin"]
  @obj.rspec_verify
end

Each group of yielded values is in an array.

----------------------------------------------------------------------

You can respond by visiting: 
http://rubyforge.org/tracker/?func=detail&atid=3151&aid=12841&group_id=797


More information about the rspec-devel mailing list