Skip to content

A jbuilder partial whose body is a begin … rescue … end, and json.partial! @record, render {} #322

Description

@eddygarcas

Probed at main e0d8610, Linux x86-64, gcc 16.2.1 (Spinel f672bd97a for the binary).

Two jbuilder partial shapes render {}, with no diagnostic: a partial whose body is wrapped in begin … rescue … end, and json.partial! @record (the partial resolved from the record, as Rails does). Both show on --target ruby (CRuby, through the emitted main.rb) and on the --target spinel binary.

app/views/widgets/:

# _widget.json.jbuilder
json.id widget.id
json.name widget.name

# _guarded_widget.json.jbuilder
begin
  json.id widget.id
  json.name widget.name
rescue StandardError
  json.error "unavailable"
end

# show.json.jbuilder
json.partial! "widgets/widget", widget: @widget

# detail.json.jbuilder
json.partial! @widget

# guarded.json.jbuilder
json.partial! "widgets/guarded_widget", widget: @widget

app/controllers/widgets_controller.rb:

class WidgetsController < ApplicationController
  def show
    @widget = Widget.find(params[:id])
    render :show
  end

  def detail
    @widget = Widget.find(params[:id])
    render :detail
  end

  def guarded
    @widget = Widget.find(params[:id])
    render :guarded
  end
end
Rest of the fixture

config/routes.rb

Rails.application.routes.draw do
  get "widgets/:id", to: "widgets#show", defaults: { format: :json }
  get "widgets/:id/detail", to: "widgets#detail", defaults: { format: :json }
  get "widgets/:id/guarded", to: "widgets#guarded", defaults: { format: :json }
end

db/schema.rb

ActiveRecord::Schema[8.1].define(version: 2026_01_01_000000) do
  create_table "widgets", force: :cascade do |t|
    t.string "name"
    t.integer "size"
  end
end

app/models/widget.rb: class Widget < ApplicationRecord; end. Gemfile: rails, jbuilder. ApplicationRecord / ApplicationController (< ActionController::Base) as generated.

Seeded with one row, name: "b":

request Rails 8.1.4 + jbuilder 2.15.1 --target ruby (CRuby 4.0.5) and the spinel binary
GET /widgets/1 {"id":1,"name":"b"} {"id":1,"name":"b"}
GET /widgets/1/detail {"id":1,"name":"b"} {}
GET /widgets/1/guarded {"id":1,"name":"b"} {}

(The Rails column is the fixture booted on real Rails with sqlite in memory and the three requests sent through Rack.)

Emitted (--target ruby; the spinel tree has the same .rb):

# app/views/widgets/_guarded_widget_json.rb
def self.guarded_widget_json(widget)
  io = String.new
  io << "{"
  io << ""
  io << "}"
  io
end

# app/views/widgets/detail_json.rb
def self.detail_json(widget)
  io = String.new
  io << "{"
  io << ""
  io << "}"
  io
end

What they should be: guarded_widget_json the same body as widget_json (the "id" and "name" pairs) inside a begin … rescue StandardError … end whose rescue branch emits the "error" pair instead; and detail_json the call show_json already makes, io << Views::Widgets.widget_json(widget).

Where it goes

Both fall to JbStmt::Unknown in src/lower/jbuilder_to_library/mod.rs, which emit_object writes as an empty append inside { … }:

  • stmts_of only opens a Seq, so a template whose one statement is an ExprNode::BeginRescue is a single statement classify does not recognise.
  • The "partial!" arm needs a string-literal path (string_literal(path_arg)) and an options Hash. Jbuilder's partial! with one ActiveModel argument renders <model.to_partial_path> with the record bound to the partial's local (widgets/_widget, local widget), which for an ivar/local of a known model class is the path "<plural>/<singular>" and the arg itself.

Neither is reported (no ingest gap; with --allow-unsupported the only warnings on these templates are unresolved_type for the widget reads), so the template silently answers {}. A diagnostic for Unknown statements would have pointed at both.

Found while compiling a Rails API app with --target spinel.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions