简体   繁体   中英

Rails 6, standalone Jbuilder in a service : partials not loading

I thought I could use Jbuilder outside of Rails controllers. The documentation said so.

I wrote this module, to include into a service object. Which is integrated in a Rails 6 api app, but is not inheriting from ActiveRecord.

module JsonGenerator
  def to_json
    template = File.read("#{::Rails.root}/path/to/template.json.jbuilder")
    Jbuilder.new { |json| eval template }.target!
  end
end
class Converter::Album
  include JsonGenerator
end

This is working if I don't use any partial. But when I do, using a syntax like this:

json.artists artists do |artist|
  json.partial! 'artist', artist: artist
end

I get this error :

Failure/Error: eval template

TypeError:
{:artist=>#<Converter::Artist:0x00007fd0d1a99dc8 @name="Bob">} is not a symbol nor a string

The problem seems to be that Jbuilder partials can't accept PORO as argument ?

I don't want to use eval anyway, but it's the only way I found for loading a template which is not in the views folder.

If I copy paste all my template directly as an argument to the Jbuilder.new method, I get the same error.

Update 04/12/2019

I tried this line

json.partial! partial: 'artist', locals: { artist: artist }

and even though it loads the partial, the data it generates is wrong.

I get this

"artists" => [{"partial!"=>{"locals"=>{"name"=>"Bob", "instrument"=>"piano"}, "partial"=>"artist"}}]

instead of

"artists" => [{"name"=>"Bob", "instrument"=>"piano"}]

For whatever reason it seems like you have to explicitely name the args when outside rails magic:

json.partial! partial: 'artist', locals: { artist: artist }

To avoid the eval, I would try something with the rendered (untested):

def to_json
  ApplicationController.render(template: 'albums/show.json.jbuilder', assigns: { album: self }, formats: [:json], handlers: [:builder])
end

Or with the new renderer:

  def to_json
    renderer = ApplicationController.new
    renderer.render('albums/show.json.jbuilder', locals: { album: self }, formats: [:json], handlers: [:builder])
  end

I thought I could use Jbuilder outside of Rails controllers. The documentation said so.

You can, but not all of the methods you can use within the view context are available. This is because they are actually two different objects, Jbuilder and JbuilderTemplate , the latter of which accepts a context argument, which is an instance of ActionView::Base (or subclass thereof).

The object you're using outside of the view context is an instance of Jbuilder and doesn't have the #partial! method, which is why you have the "partial!" key in the returned JSON -- it's not treated any differently than any other key.

As far as I know, the only (easy) way to accomplish what you want to do is to use ActionController::Renderer (as @Mene said).

ApplicationController.render 'path/to/template.jbuilder', locals: {...}, assigns: {...}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM