From dchelimsky at gmail.com Tue Dec 1 07:55:26 2009 From: dchelimsky at gmail.com (David Chelimsky) Date: Tue, 1 Dec 2009 07:55:26 -0500 Subject: [rspec-devel] should_yield (is it needed?) In-Reply-To: <145359ad0911291211u29b8f374k3319aa86f43eaf9a@mail.gmail.com> References: <145359ad0911062243m66eff304oe3e3a007dd445cf0@mail.gmail.com> <57c63afe0911101700q673c5833l51ce74598381f90f@mail.gmail.com> <145359ad0911291211u29b8f374k3319aa86f43eaf9a@mail.gmail.com> Message-ID: <57c63afe0912010455j6b988f64se0ec236f34caa85e@mail.gmail.com> On Sun, Nov 29, 2009 at 3:11 PM, Martin Emde wrote: > On Tue, Nov 10, 2009 at 5:00 PM, David Chelimsky wrote: > >> >> [1,2,3].should yield_on(:each) ??? >> > > This does follow rspec syntax more closely... Let's run through a few use > cases I can imagine. > > current_user.stub!(:admin?).and_return(true) > helper.should yield_on(:admin_only) > # helper.admin_only &should_yield > > [1,2,3].should yield_on(:each).each_of(1,2,3) > # [1,2,3].each &should_yield_each(1,2,3) > > h = { :a => 1, :b => 2 } > h.should yield_on(:each).each_of(*h.to_a) > h.each &should_yield_each(*h.to_a) > > helper.should yield_on(:link_to_function, "link > text").with(an_instance_of(JavaScriptHelper::JavaScriptBuilder)) > # helper.link_to_function "link text", > &should_yield_with(an_instance_of(JavaScriptHelper::JavaScriptBuilder)) > > I think both are pretty clear until the last example when this part in my > format: *"link text", &should_yield(...)* starts to confuse what's > going on exactly. It very clearly breaks from rspec syntax in that example. > > helper.link_to_functio("link text", &assert_yielded_block) > > That actually makes a bit more sense since the intention is clear. I'm > passing a block that asserts that it will be yielded. However, this isn't > rspec at all written like that. > > The yield_on format seems to specify "When I call this method on the > receiver, I expect it to yield." This is very similar to user.should > be_an_admin in that the actual method call is not being called on the > receiver but passed into the matcher. > > Is this something we'd like to add to rspec? If so, I'll work out a patch. > > Martin > I'm a bit confused as to which syntax you're actually advocating now :) One thing I'm realizing after your suggestion that "the actual method call is not being called on the receiver" is that this is more akin to a message expectation (i.e. mock) rather than a state-based expectation (should xxx). That leads me to think this should be part of the mock framework rather than the matcher framework, which leads me to think differently about syntax. Also, from an implementation standpoint, I don't see how we can fail this example: describe "foo" do def foo; # do nothing; end it "yields" do foo &should_yield end end In this case the proc returned by should_yield is never invoked, which means we have no way of hooking into RSpec to declare that it should be. Sort of a catch 22. We might need something like this: foo should_yield The should_yield() method, in this case, can set the message expectation on some sort of hidden proxy object and then return a proc that will record the expectation as satisfied when it's called. But again, this is new syntax, which I'm not sure I want to introduce for one concept. Here's another alternative: expect { foo }.to yield_to_block expect { [1,2,3].each }.to yield_each_of(1,2,3) This relates to a family of matchers that lie somewhere between state-based matchers and mocks (raise_error, change), so it's a more familiar construct. WDYT? David -------------- next part -------------- An HTML attachment was scrubbed... URL: From peter.fitzgibbons at gmail.com Tue Dec 1 10:36:03 2009 From: peter.fitzgibbons at gmail.com (Peter Fitzgibbons) Date: Tue, 1 Dec 2009 09:36:03 -0600 Subject: [rspec-devel] should_yield (is it needed?) In-Reply-To: <57c63afe0912010455j6b988f64se0ec236f34caa85e@mail.gmail.com> References: <145359ad0911062243m66eff304oe3e3a007dd445cf0@mail.gmail.com> <57c63afe0911101700q673c5833l51ce74598381f90f@mail.gmail.com> <145359ad0911291211u29b8f374k3319aa86f43eaf9a@mail.gmail.com> <57c63afe0912010455j6b988f64se0ec236f34caa85e@mail.gmail.com> Message-ID: <670a00380912010736l3587df87k2b9f9e291b843deb@mail.gmail.com> On Tue, Dec 1, 2009 at 6:55 AM, David Chelimsky wrote: HI David and all, This seems to me to fall into the expectations category. We are setting expectation that our method taking &block will yield such and such somewhere inside. I take this to be distinguished from mocking the yield, which appears to be already captured well enough in Spec::Mocks::BaseExpectation#and_yield So, going with this belonging to Spec::Matchers, then : subject.method(args).should yield(values,to,yield) If the method is expected to yield multiple times, then match as David suggested : subject.method(args).should yield_each_of(objects, yielded, in, iterations) Unfortunately I do not have the ability yet to suggest internal implementation. Rspec already baffles my metaprogramming-fu... the stuff we're discussing cleanly surpasses it. wdyt? Peter Fitzgibbons (847) 687-7646 Email: peter.fitzgibbons at gmail.com IM GTalk: peter.fitzgibbons IM AOL: peter.fitzgibbons at gmail.com -------------- next part -------------- An HTML attachment was scrubbed... URL: From martin.emde at gmail.com Tue Dec 1 16:27:52 2009 From: martin.emde at gmail.com (Martin Emde) Date: Tue, 1 Dec 2009 13:27:52 -0800 Subject: [rspec-devel] should_yield (is it needed?) In-Reply-To: <57c63afe0912010455j6b988f64se0ec236f34caa85e@mail.gmail.com> References: <145359ad0911062243m66eff304oe3e3a007dd445cf0@mail.gmail.com> <57c63afe0911101700q673c5833l51ce74598381f90f@mail.gmail.com> <145359ad0911291211u29b8f374k3319aa86f43eaf9a@mail.gmail.com> <57c63afe0912010455j6b988f64se0ec236f34caa85e@mail.gmail.com> Message-ID: <145359ad0912011327s26abefa7m510273c4958eb54c@mail.gmail.com> I was a bit unclear about which one I'm advocating. I can see many ways to achieve this that would be clear in different circumstances and I haven't really put my weight behind one yet. David Chelimsky wrote: > I don't see how we can fail this example: > > describe "foo" do > def foo; # do nothing; end > > it "yields" do > foo &should_yield > end > end > > In this case the proc returned by should_yield is never invoked, which > means we have no way of hooking into RSpec to declare that it should be. > Sort of a catch 22. > should_yield is first invoked with "&should_yield". The should_yield method is called and returns an object responding to #to_proc. The & calls #to_proc on our object which returns a block that calls our expected method when it's executed. Here's my implementation. I actually think it is pretty cool, whether or not it's the "right" solution: http://gist.github.com/246632 Now just some thoughts on syntax... What a "should yield" expectation is really testing is an "anonymous function" call. Particularly, we're asserting that #call is called on our "block" object in some form (yield or #call). In ruby, our "anonymous functions" are blocks. The implementation and usage is then very similar to a should_receive. If I were to structure this like a should receive, I would expect something like this. # should_receive style implementation. block = mock_block.should_receive_yield.with(1,2,3) [1,2,3].method("args", &block) This actually shows that we're making a block with expectations on it, and then we're passing the block in a familiar format "&block" which most rubyists will recognize. It could even be shortened by people who understand what they're doing to something like this: [1,2,3].method("args", &mock_block.should_receive_yield.with(1,2,3)) This opens up a really comfortable feeling language where you can even have return values from the block in a way that makes sense. # this block doubles the number passed in block = mock_block.should_receive_yield.with(1,2,3).and_return(2,4,6) [1,2,3].map(&block).should == [2,4,6] My only concern for the & syntax is just how confusing it is to most people. This is really just stepping back a bit to expose my implementation a bit more. In my implementation the "mock_block.should_recieve_yield" is just "should_yield" which does the mock_block part behind the scenes. Thoughts? Martin P.S. Peter, your implementation reads well but the method(args) will call the method right away without a block. That's the catch here that makes this a tricky thing to spec. -------------- next part -------------- An HTML attachment was scrubbed... URL: