[rspec-devel] Fwd: [#11628] error mocking partial page variable only after partial has already been rendered bug

David Chelimsky dchelimsky at gmail.com
Fri Jun 22 10:43:32 EDT 2007


On 6/22/07, aslak hellesoy <aslak.hellesoy at gmail.com> wrote:
> Forwarding this to the list as it has general interest.

The RFE this refers to is:
http://rubyforge.org/tracker/index.php?func=detail&aid=11628&group_id=797&atid=3149

>
> ---------- Forwarded message ----------
> From: David Chelimsky <dchelimsky at gmail.com>
> Date: Jun 22, 2007 3:56 PM
> Subject: Re: [#11628] error mocking partial page variable only after
> partial has already been rendered bug
> To: Brian Takita <brian.takita at gmail.com>
> Cc: aslak hellesoy <aslak.hellesoy at gmail.com>
>
>
> The problem that we're trying to solve is providing stub values to
> partials. Doing that using the mocking framework does not work because
> the templates get compiled.
>
> An approach that works is this (directly in the example):
>
> render :partial => "people/person", :object => @person
>
> Here's where this starts to break down. Let's say that
> _person.html.erb is calling _address.html.erb with it's person object.
> In the example, I want to do this:
>
> template.should_receive(:render).with(:partial => "address", :person => @person)
> render :partial => "people/person", :object => @person
>
> As you might imagine, this breaks down because rails makes use render
> as an entry point for everything, so the call to render :partial =>
> "people/person" gets compared to the expectation and fails - nothing
> ever gets rendered at all because we've set a message expectation on
> render.
>
> So what if we added special high level methods to ActionView::Base
> rather than trying to intercept the deeper calls. Something like:
>
> template.should_receive_render_with(:partial => "address", :person => @person)
> render :partial => "people/person", :object => @person
>
> The should_receive_render_with method would intercept calls to render
> and compare them to the expectation. If they meet the expectation this
> gets recorded. If it's the right template with the wrong hash, it
> fails immediately. Otherwise it passes the call back to the original
> render method. We can also have it add itself as a listener to the
> runner, so when the example finishes it can raise an
> ExpectationNotMetError if it never got the message.
>
> The benefit is that you can focus on one template and essentially mock
> all of it's included partials. The downside is that if a given partial
> is invoked more than once, you'd have to be able to set that
> expectation, either with .once, .twice, .exactly(n).times appended to
> the expectation, or with repeated expectations if you want to be
> specific about different hash values.
>
> Maybe to make it more clear the method should be:
>
> template.should_render_partial(:address).with(:object => @person)
>
> That separates the partial name, which is essentially our key as to
> whether or not to pay attention to the message, from the other
> arguments we need to match against.
>
> The downside is that we essentially have to re-implement a lot of what
> mocks already do - or extract some of that stuff out to another
> library - in order to provide a "pass through" feature, which a few
> people have asked for and I'm loathe to add to the mocking framework.
>
> Thoughts?
>
> David
>
>
>
> On 6/19/07, Brian Takita <brian.takita at gmail.com> wrote:
> > Hello,
> >
> > I looked into this a little.
> >
> > It looks like in the success case, the compiled template source is evaled:
> > CompiledTemplates.module_eval(render_source, file_name, -line_offset)
> > in line 518.
> >
> > Since person is stubbed in the template object, the eval can't find
> > the local variable and falls back to the person method.
> >
> > On the failure case, @@template_args[render_symbol] is set in the first spec.
> > This causes
> > person_counter = local_assigns[:person_counter]\nperson = local_assigns[:person]
> > to be compiled, which sets person to nil.
> >
> > There are a couple of solutions:
> > Option 1:
> > ActionView::Base#compile_template? must return true for every render
> > (line 317 in base.rb) to recompile the template method.
> > @@template_args[render_symbol] must be cleared before the template is compiled
> >
> > Option 2:
> > ActionView::Base#render_template must get the locals_assigns hash somehow.
> >
> > Both seem pretty hairy.
> >
> > Brian
> >
> _______________________________________________
> rspec-devel mailing list
> rspec-devel at rubyforge.org
> http://rubyforge.org/mailman/listinfo/rspec-devel
>


More information about the rspec-devel mailing list