rails / jbuilder

Jbuilder: generate JSON objects with a Builder-style DSL
MIT License
4.33k stars 433 forks source link

No rendering in 5.1 #405

Open msimonborg opened 7 years ago

msimonborg commented 7 years ago

After upgrading my Rails API-only app to 5.1 from ~> 5.0.0 I immediately had issues rendering my Jbuilder templates. The response body would just be blank: ""

I am also using the latest version of Jbuilder

Googling around I found that I was not the only person having this issue. One person suggested an easy fix that worked:

class ApplicationController < ActionController::API
  include ActionView::Rendering
  ...
end

Making sure that one Module was included fixed the problem. Poking around the Jbuilder source code I found some relevant lines in the jbuilder/railtie.rb

class Jbuilder
  class Railtie < ::Rails::Railtie
    initializer :jbuilder do

      ...

      if Rails::VERSION::MAJOR >= 5
        module ::ActionController
          module ApiRendering
            include ActionView::Rendering
          end
        end

        ActiveSupport.on_load :action_controller do
          if self == ActionController::API
            include ActionController::Helpers
            include ActionController::ImplicitRender
          end
        end
      end
    end

    ...

  end
end

This initializer hook should include the needed module in ApiRendering, which is included in ActionController::API. This makes sense from an ancestry POV as opposed to including directly into ActionController::API, and it probably works most of the time, but for whatever reason it's not working for me and a bunch of other people, maybe because of some other configuration or dependency. It seems that since the same initializer hook is including other modules directly into ActionController::API, we could do that for ActionView::Rendering, like this:

class Jbuilder
  class Railtie < ::Rails::Railtie
    initializer :jbuilder do

      ...

      if Rails::VERSION::MAJOR >= 5
        # module ::ActionController
        #   module ApiRendering
        #     include ActionView::Rendering
        #   end
        # end

        ActiveSupport.on_load :action_controller do
          if self == ActionController::API
            include ActionController::Helpers
            include ActionController::ImplicitRender
            include ActionView::Rendering
          end
        end
      end
    end

    ...

  end
end

I tried out this change locally in my app's gem source code and it works. I also wrote a simple test that checks if the module is included after calling Jbuilder::Railtie.run_initializers, and the tests pass both the old way and the new way. Unfortunately the old way doesn't work for everyone it seems, this seems more surefire.

nekogami commented 6 years ago

Isn't that normal ? Isn't ActionController::API made so that it's skips a a lot of view rendering inclusion in the controller since it's not supposed to render html ? (which means, no view folder).

wouldn't it be better to convert your view with ActiveModel::Serializers ?

vsai commented 6 years ago

I personally prefer using Jbuilder over ActiveModel::Serializers. It's a little simpler to maintain different serializations for different rails actions. I'm running into the same problem where when using ActionController::API, the json response is empty. But if I include: include ActionView::Rendering, it starts to work correctly.

Using rails 5.1.6.

ships commented 6 years ago

I am also experiencing this or a related issue (it provoked a stack-overflow question) where I was successfully rendering with 5.1.6, but once I started including AccessGranted gem, ActionView::Rendering no longer appears in my ancestry tree for my controller. I'm not enough of a ruby expert to say why the include logic may be changing here, but as far as the class definition goes, the only other change to ancestry is inclusion of AccessGranted::Rails::ControllerMethods.

Your fix above does work for me, however it also seems to introduce complications for controllers with actions that aren't supposed to render with jbuilder.

update

I was able to determine that the gem causing me trouble was loading ActionController::API at file load time (https://github.com/chaps-io/access-granted/blob/master/lib/access-granted.rb#L11-L21), which I believe caused relevant classes to be loaded before Jbuilder could patch ApiController as identified by @msimonborg . For myself I was able to solve this by submitting a PR to that gem which moves that loading logic into a rail tie, but that may not be affordable to all users of Jbuilder.

gsmetal commented 6 years ago

Looks like duplicate #346