jb icon indicating copy to clipboard operation
jb copied to clipboard

TypeError when rendering a partial directly

Open Quintasan opened this issue 2 years ago • 2 comments

class TeasController < ApplicationController
  def create
    result = NewTeaService.new.call(params.to_unsafe_h)

    case result
    in Success[:created, Tea => tea]
      render tea, status: :created
    in Failure[_, errors]
      render json: errors, status: :unprocessable_entity
    end
  end
end

This action tries to render app/views/teas/_tea.json.jbuilder

# app/views/teas/_tea.json.jbuilder
tea.slice(*%i[
  id
  name
  country
  created_at
  updated_at
])

but fails with

TypeError (no implicit conversion of Array into String):

rack (2.2.3) lib/rack/etag.rb:69:in `<<'
rack (2.2.3) lib/rack/etag.rb:69:in `block in digest_body'
actionpack (6.1.4.1) lib/action_dispatch/http/response.rb:158:in `each'
actionpack (6.1.4.1) lib/action_dispatch/http/response.rb:158:in `each_chunk'
actionpack (6.1.4.1) lib/action_dispatch/http/response.rb:140:in `each'
actionpack (6.1.4.1) lib/action_dispatch/http/response.rb:76:in `each'
actionpack (6.1.4.1) lib/action_dispatch/http/response.rb:493:in `each'
rack (2.2.3) lib/rack/etag.rb:67:in `digest_body'
rack (2.2.3) lib/rack/etag.rb:31:in `call'
rack (2.2.3) lib/rack/conditional_get.rb:40:in `call'
rack (2.2.3) lib/rack/head.rb:12:in `call'
activerecord (6.1.4.1) lib/active_record/migration.rb:601:in `call'
actionpack (6.1.4.1) lib/action_dispatch/middleware/callbacks.rb:27:in `block in call'
activesupport (6.1.4.1) lib/active_support/callbacks.rb:98:in `run_callbacks'
actionpack (6.1.4.1) lib/action_dispatch/middleware/callbacks.rb:26:in `call'
actionpack (6.1.4.1) lib/action_dispatch/middleware/executor.rb:14:in `call'
actionpack (6.1.4.1) lib/action_dispatch/middleware/actionable_exceptions.rb:18:in `call'
actionpack (6.1.4.1) lib/action_dispatch/middleware/debug_exceptions.rb:29:in `call'
actionpack (6.1.4.1) lib/action_dispatch/middleware/show_exceptions.rb:33:in `call'
railties (6.1.4.1) lib/rails/rack/logger.rb:37:in `call_app'
railties (6.1.4.1) lib/rails/rack/logger.rb:26:in `block in call'
activesupport (6.1.4.1) lib/active_support/tagged_logging.rb:99:in `block in tagged'
activesupport (6.1.4.1) lib/active_support/tagged_logging.rb:37:in `tagged'
activesupport (6.1.4.1) lib/active_support/tagged_logging.rb:99:in `tagged'
railties (6.1.4.1) lib/rails/rack/logger.rb:26:in `call'
actionpack (6.1.4.1) lib/action_dispatch/middleware/remote_ip.rb:81:in `call'
actionpack (6.1.4.1) lib/action_dispatch/middleware/request_id.rb:26:in `call'
rack (2.2.3) lib/rack/runtime.rb:22:in `call'
activesupport (6.1.4.1) lib/active_support/cache/strategy/local_cache_middleware.rb:29:in `call'
actionpack (6.1.4.1) lib/action_dispatch/middleware/executor.rb:14:in `call'
actionpack (6.1.4.1) lib/action_dispatch/middleware/static.rb:24:in `call'
rack (2.2.3) lib/rack/sendfile.rb:110:in `call'
actionpack (6.1.4.1) lib/action_dispatch/middleware/host_authorization.rb:98:in `call'
railties (6.1.4.1) lib/rails/engine.rb:539:in `call'
puma (5.5.0) lib/puma/configuration.rb:249:in `call'
puma (5.5.0) lib/puma/request.rb:77:in `block in handle_request'
puma (5.5.0) lib/puma/thread_pool.rb:340:in `with_force_shutdown'
puma (5.5.0) lib/puma/request.rb:76:in `handle_request'
puma (5.5.0) lib/puma/server.rb:447:in `process_client'
puma (5.5.0) lib/puma/thread_pool.rb:147:in `block in spawn_thread'

unless I append .to_json to the partial. Is this working as intended?

Quintasan avatar Sep 27 '21 12:09 Quintasan

I'm getting same error when I use this:

         ApplicationController.renderer.render(
            partial: 'shared/item',
            locals: { item: item }
          )

and my file is /view/shared/_item.json.jb

fernandoiwamoto avatar Dec 21 '21 11:12 fernandoiwamoto

Only tested this for the simple ApplicationController.render(partial: ... case on Rails 7.0.4, but in case anyone else finds themselves here this patch seems to get it working:

module Jb
  module PartialRenderer
    module JSONizer
      def render_partial_template(*)
        rendered_template = super
        rendered_template.instance_variable_set :@body, rendered_template.body.to_json if rendered_template.template.respond_to?(:handler) && (rendered_template.template.handler == Jb::Handler)
        rendered_template
      end
    end
  end
end
::ActionView::PartialRenderer.prepend ::Jb::PartialRenderer::JSONizer

The underlying issue is that jb patches ActionView::TemplateRenderer, but when you call ApplicationController.render(partial: ...) it uses ActionView::PartialRenderer.

Note however that with this patch, calls to render(partial: ... within another view/partial will now also return JSON, which could mess things up with nested partials. Not sure of a good workaround yet other than using JSON.parse inside the views.

MC-Squared avatar Jan 18 '23 03:01 MC-Squared