Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

Already on GitHub? Sign in to your account

Weird error reporting when an error occur in a JBuilder partial #40

Closed
Sephi-Chan opened this Issue Apr 28, 2012 · 13 comments

Comments

Projects
None yet
9 participants

Hi,

I noticed two errors in the reporting of errors with jbuilder partials rendered from a view.
The first is weird but not a big deal, the second is really problematic for debugging.

I have a view and two partials.

In views/application/home.html.haml, I have:

- json = render(partial: 'users/user', formats: [:json], locals: { user: @user })
= javascript_tag { "window.data = {}; window.data = #{raw(json)};" }

In views/users/_user.html.haml, I have:

= user.undefined_method_called_from_haml_template

In views/users/_user.json.jbuilder, I have:

json.name user.undefined_method_called_from_jbuilder_template

Displaying the page raises:

NoMethodError in Application#home
Showing ~/MyTest/app/views/users/_user.html.haml where line # raised:
undefined method `undefined_method_called_from_jbuilder_template' for #User:0x007fb2bea96de8

The reported file is wrong: it should be the JSON version.

So now, the second error is visible when there is no other version of the same partial (in my case, by removing _user.html.haml).

ActionView::MissingTemplate in Application#data
Showing ~/MyTest/app/views/application/home.html.haml where line #1 raised:
Missing partial users/user with {:locale=>[:en], :formats=>[:html], :handlers=>[:erb, :builder, :jbuilder, :coffee, :haml]}.

What the hell? Why does any errors occurring in the partial are ignored and reported as a missing template (plus, the formats and handlers are not correct?
If I fix the error (not easy with this weird report) in the template, it works!

Any idea?

ariera commented Oct 16, 2012

+1

fsainz commented Oct 16, 2012

+1

Ouch. Just got bit by this one!

Collaborator

rwz commented Dec 25, 2012

This is a rails bug. I'm trying to fix it here: rails/rails#8406

@rwz rwz closed this Jan 1, 2013

earnold commented May 2, 2015

I am seeing this again in rails 3.2.21 - is there any known workaround?

earnold commented May 2, 2015

I've created a small demo app to precisely demonstrate the problem: https://github.com/earnold/error-demo

When there is an error in the jbuilder template, you get a missing template error. The real error vanishes.

Contributor

yuki24 commented May 2, 2015

@earnold Rails 3.2 is no longer maintained. You should upgrade to 4.1 or higher.

earnold commented May 2, 2015

@yuki24 - unfortunately, I'm trying to use this in a large app and it would be prohibitively difficult to upgrade rails. I guess I was hoping that since this issue had surface with earlier versions of 3.2 with Jbuilder, there would be a know fix.

@earnold I've also run into this issue in the past. I'm not a big fan of monkey patches, but this one to ActionView::Template will surface the original error. Just put it in an initializer, and you should be good.

if Rails.env.development?
  module ActionView
    class Template

      protected

      def handle_render_error(view, e) #:nodoc:
        if e.is_a?(Template::Error)
          e.sub_template_of(self)
          raise e
        else
          assigns  = view.respond_to?(:assigns) ? view.assigns : {}
          template = self
          unless template.source
            # If an error occurs while the Jbuilder template is being rendered, 
            # this block of code would raise a false exception (ActionView::MissingTemplate) 
            # and swallow the original error. This monkey patch wraps the first exception
            # in a begin/rescue/ignore block so the original exception can bubble up.
            begin
              template = refresh(view)
              template.encode!
            rescue Exception => ignore; end
          end
          raise Template::Error.new(template, assigns, e)
        end
      end

    end
  end
end
Collaborator

rwz commented May 3, 2015

If you're still on Rails 3.2 I suggest to instantiate a new view context each time you need to render a jbuilder view into an HTML view.

Somewhat like this:

def render_with_next_context(*args)
  context = ApplicationController.new.view_context
  context.render(*args)
end

and then in your HAML/Erb template:

- json = render_with_new_context(partial: 'users/user', formats: [:json], locals: { user: @user })
= javascript_tag { "window.data = #{json.html_safe};" }

This is probably far from being the most effective way of doing it, but just something from the top of my head.

earnold commented May 5, 2015

@rwz and @brettfishman - thank you both so much for the help! I was banging my head against this for ages.

I've implemented the monkeypatch for now. In the second case, using the new context, gave me this error ActionView::Template::Error (undefined methodhost' for nil:NilClass):` when I called url helpers.

earnold commented May 5, 2015

I know I'm beating a dead horse at this point, but...

@brettfishman - I found I could get a more accurate stack trace by using this monkey patch:

if Rails.env.development? || Rails.env.staging?
  module ActionView
    class Template

      protected

      def handle_render_error(view, e) #:nodoc:
        if e.is_a?(Template::Error)
          e.sub_template_of(self)
          raise e
        else
          assigns  = view.respond_to?(:assigns) ? view.assigns : {}
          template = self
          # If an error occurs while the Jbuilder template is being rendered in 
          # in a nested context, you have a mismatch between the template format
          # and the view context. Therefore, this block of code would raise 
          # a false exception (ActionView::MissingTemplate) and swallow the original 
          # error. This monkey patch tricks rails into thinking it was a json request
          # so that refreshing the source hits the right partial
          if template.formats == [:json]
            view.lookup_context.formats = [:json]
          end
          unless template.source
            template = refresh(view)
            template.encode!
          end
          raise Template::Error.new(template, assigns, e)
        end
      end

    end
  end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment