Edge Rails (2.2) breakage

4 views
Skip to first unread message

Mislav Marohnić

unread,
Sep 21, 2008, 6:46:08 PM9/21/08
to ha...@googlegroups.com
In order to fix a bug, I've noticed that Haml template_test breaks on edge because "evaluate_assigns" method has been renamed.

I've pushed commits to my fork on GitHub to fix this: ce5ea1, f7d701

Next, I fixed some Sass options so that template_test can be run standalone (previously it depended on Sass tests being run first): bcc94e

And lastly, I've made a failing test for edge Rails breakage: render(:layout) in views with a block: bdfe0f

This is broken:

  - render :layout => 'boxed' do
    %p Some content inside a partial layout.

This test passes in Rails 2.1.1 and fails on edge. I need help fixing this, Nathan.

Nathan Weizenbaum

unread,
Sep 21, 2008, 7:36:25 PM9/21/08
to ha...@googlegroups.com
I'll pull these and look into the render-with-block issue as soon as I'm
done with what I'm working on now.

Nathan Weizenbaum

unread,
Sep 21, 2008, 10:21:30 PM9/21/08
to ha...@googlegroups.com
I just realized: these changes need to be rebased onto stable.

Mislav Marohnić

unread,
Sep 22, 2008, 3:30:55 AM9/22/08
to ha...@googlegroups.com
On Mon, Sep 22, 2008 at 04:21, Nathan Weizenbaum <nex...@gmail.com> wrote:

I just realized: these changes need to be rebased onto stable.

They rebase cleanly. I did that for you and pushed to my stable. 

Mislav Marohnić

unread,
Sep 24, 2008, 10:51:30 AM9/24/08
to ha...@googlegroups.com
On Mon, Sep 22, 2008 at 01:36, Nathan Weizenbaum <nex...@gmail.com> wrote:

I'll pull these and look into the render-with-block issue as soon as I'm
done with what I'm working on now.

I've tried to fix this today without success. I barely understand how Haml buffer works, much less the innerworkings of Rails rendering/compiling mechanism.

I bisected the breakage to Rails 933697a5fc5f4c56c4fd7fbbd31b8973df9c1054 (replacing _erbout with @output_buffer).

The exception says that buffer was nil. When I noticed that it comes from `concat` used in `_render_with_layout`, I tried a hack to unconditionally overwrite that method:

  ActionView::Base.class_eval do
    def concat(string)
      haml_buffer.buffer.concat(string)
    end
  end

Now I got rid of the exception, but the template doesn't render properly. I expected (pseudo-code):

  LAYOUT {
    layout_content
  }

But this was rendered:

  layout_content
  LAYOUT without content

Naturally, this is an issue with concat-ing to the buffer. After seeing what important role `@_proc_for_layout` plays, I tried to track down how it works but got lost in depths of ActionView::Renderable#render.

Nathan Weizenbaum

unread,
Sep 24, 2008, 12:34:18 PM9/24/08
to ha...@googlegroups.com
Don't worry about it, I've figured it out. It looks like there are two
separate problems: one is a Rails issue and one is a Haml issue. I'm
working on a fix for both.

Nathan Weizenbaum

unread,
Sep 24, 2008, 9:54:54 PM9/24/08
to ha...@googlegroups.com
Alright, I've pushed your changes and fixes for the render :layout stuff.

Mislav Marohnić

unread,
Sep 24, 2008, 11:38:54 PM9/24/08
to ha...@googlegroups.com
On Thu, Sep 25, 2008 at 03:54, Nathan Weizenbaum <nex...@gmail.com> wrote:

Alright, I've pushed your changes and fixes for the render :layout stuff.

Thank you very much! :)

I'd like to say I understand what you did there just now, but I can't seem to figure it out. Like my friend said after seeing how many hours I lost on this: I'm getting old ...

Nathan Weizenbaum

unread,
Sep 25, 2008, 3:14:55 AM9/25/08
to ha...@googlegroups.com
Well, there were two separate issues that were both complex and both breaking render :layout.

The first was fixed in r2871122. It was more or less a Rails bug, although I'm not sure what a fix on the Rails side would look like. The issue stems from the fact that Haml needs to keep track of whether the view object is currently in a Haml context or not. If it is, Haml overrides all sorts of ActionView methods to make them Haml-compatible, and if it's not, it delegates the methods to the standard handlers (see all the calls to #is_haml?). Whenever a Haml template is entered, is_haml? becomes true. However, since it's possible that any sub-template could use another system, whenever #render is called is_haml? becomes false until it returns. If it ends up rendering a Haml template, it bec omes true again within the template.

For the most part, this system works well. However, there's a "dead zone" in there between when #render is called and when the new template gets rendered where is_haml? is false but it's actually in a Haml template, so there's no template system taking responsibility for anything. Since #render just returns a string, that's not a problem, because there's no template-system calls being made in the dead zone.

Except that #render doesn't always return a string. In a cruel quirk of the Rails APIs, render :layout with a block concatenates directly to the template. To do so, it calls #concat in the dead zone, which tries to access #output_buffer, which Haml isn't overriding and which thus returns nil. The hack for this in r2871122 is to not set is_haml? to false when doing render :layout with a block.

The second issue was fixed in r936d9c1. This was definitely a Haml problem. When render :layout has a block, it stores it in @_proc_for_layout. This block was created in the context of the parent template (call it T1). Its _hamlout local variable, the variable containing the buffer to which the text should be output, is pointing to the T1 buffer. When the child template (call it T2) yields, capture_haml is called on the proc, so the proc is run and all text added to the current buffer is sliced off and returned. Unfotunately, the T2 buffer is current but the proc added text to the T1 buffer, so no text is returned at all and the results of the proc are at the wrong place in the template.

My fix for this in r936d9c1 was to set the _hamlout variable of the block as the current buffer in capture_haml. There are at least two alternatives: we could compile the Haml templates so that the original code uses @haml_buffer rather than _hamlout (I just thought of that one - it's probably a better solution); or we could tweak the compiled Haml code so that all Ruby blocks have their own buffer and return the string result, thus rendering capture_haml obsolete (Yehuda Katz thought of that - it's more work but might end up being a big speed gain).

Don't worry about not figuring it out. It took me a good long time and a surprising amount of knowledge of both the Haml and ActionView internals.

- Nathan

Coder2000

unread,
Sep 29, 2008, 2:04:37 PM9/29/08
to Haml
I am having similar issues but don't know where the error lies or if
this fixed it and I don't have the current code but I keep getting a
method not found error from ActionView::CaptureHelper#capture for
block_called_from_erb? when I have a haml template. Output can be
seen here: http://pastie.org/280880
> <mislav.maroh...@gmail.com>wrote:

Nathan Weizenbaum

unread,
Oct 3, 2008, 1:50:28 AM10/3/08
to ha...@googlegroups.com
I'm not entirely sure about this, but I think this problem is caused by
air_budd_form_builder. The block_called_from_erb? method is defined in
TagHelper, which the FormBuilder apparently doesn't include.
Reply all
Reply to author
Forward
0 new messages