Hi @carolync! I had a quick play with a Hanami app and tilt-jbuilder. Starting from a brand new app, here’s how I got it to work.
Hanami View + tilt-builder
I added the gem:
# Update the Gemfile
gem "tilt-jbuilder"
# Bundle the new gems
$ bundle install
I set up tilt-jbuilder in the base view class (app/view.rb
):
require "hanami/view"
require "tilt/jbuilder"
Tilt.register Tilt[:jbuilder], :json
module JSONTemplates
class View < Hanami::View
end
end
Then I generated a test view:
$ bundle exec hanami g view posts.index
I placed a new jbuilder template alongside the default HTML template (app/templates/posts/index.json.jbuilder
):
json.array! ["hello world"] do |post_title|
json.title post_title
end
This just outputs some static content in an array.
Then I fired up bundle exec hanami console
and tested the view:
json_templates[development]> puts app["views.posts.index"].call(format: :json, layout: false).to_s
[{"title":"hello world"}]
=> nil
So that seems to be a working setup.
Important note: I had to pass layout: false
when calling the view. If you’re adding JSON templates to an existing view that also renders HTML within a layout, then you’ll either need pass this argument, or figure out how to set up an app/templates/layouts/app.json.jbuilder
jbuilder layout that simply passes through the result of yield
(which at this point is a string). I tried various approaches and couldn’t get it to work.
If your views are rendering JSON only, however, you can set config.layout = nil
in your view class and then you don’t need to worry about passing layout: nil
each time you call a view.
Consider yajl?
I’d actually never tried jbuilder with tilt before today! Thanks for giving me the opportunity
One thing I noticed is that jbuilder brings in a lot of “rails-y” gem dependencies that you might not otherwise have in your Hanami app (activesupport, actionview, rails-dom-testing, rails-html-sanitizer, etc.).
Instead of using jbuilder for JSON templates, perhaps you could consider using yajl? I’ve used this before, and IMHO it fits a lot better. Here’s how you can try it, again from a fresh Hanami app:
# Update the Gemfile
gem "yajl-ruby"
# Bundle the new gems
$ bundle install
Generate a test view:
$ bundle exec hanami g view posts.index
Create a template at app/templates/posts/index.json.yajl
:
json = [
{title: "hello world"}
]
Render it:
puts app["views.posts.index"].call(format: :json, layout: false).to_s
# [{"title":"hello world"}]
YAJL brings in far fewer dependencies compared to Jbuilder, and what I find the templating much more straightforward: all you need to do is operate on the json
local variable, which is later encoded into the JSON string via the templating engine. There’s no DSL like JBuilder, it’s just plain any plain Ruby you like, which I personally prefer.
Idea: conditional layout per format
One thing you might have picked up from the yajl example above is that I also had to pass layout: false
when rendering the view. I couldn’t figure out a sensble passthrough layout that would work for it either.
For yajl, this would work as an app/templates/layouts/app.json.yajl
layout, but it’s inefficient, because it’s decoding the JSON from the template only for it to be re-encoded again once the layout is rendered:
json = JSON.parse(yield)
The issue here is that Hanami View will already render the view’s main template to a string before yield
-ing it to the layout, and these JSON templating systems don’t really give you a nice way to work with raw strings.
So if you are building views where you’re rendering both HTML and JSON and still want to keep a layout configured, here’s a way to make it more ergonomic. You could add a #call
override to your relevant base view class that determines whether or not to preserve the layout based on your given format:
# auto_register: false
# frozen_string_literal: true
require "hanami/view"
module JSONTemplates
class View < Hanami::View
# Disables layout when rendering with `format: :json` option
def call(format: nil, **options)
if format == :json
super(**options, format:, layout: nil)
else
super
end
end
end
end
This way you won’t have to worry about passing layout: nil
every time you render with format: :json
.
Thanks again for using Hanami! I hope this was helpful
Please feel free to ask more questions!