Probed with roundhouse 2026.9.18 (7fad14c0), Linux x86-64, CRuby 4.0.5, --target ruby (the spinel tree has the same view .rb).
A jbuilder partial! whose path is built when the template runs renders {}, with no diagnostic:
# app/views/widgets/_widget.json.jbuilder
json.id widget.id
json.name widget.name
# app/views/widgets/dynamic.json.jbuilder
kind = "widget"
json.partial!("widgets/#{kind}", widget: @widget)
Controller action: @widget = Widget.find(params[:id]) then render :dynamic; route get "widgets/:id/dynamic", to: "widgets#dynamic", defaults: { format: :json }; schema widgets(name string, size integer); ApplicationController < ActionController::Base.
| request |
Rails 8.1.4 + jbuilder 2.15.1 |
--target ruby |
GET /widgets/1/dynamic |
{"id":1,"name":"b"} |
{} |
Emitted:
def self.dynamic_json(widget)
io = String.new
io << "{"
io << ""
io << ""
io << "}"
io
end
The real-world form picks one of a few partials by a record attribute or a param (json.partial! "things/#{thing.kind}", thing: thing).
Where it goes
src/lower/jbuilder_to_library/mod.rs, classify: the partial! arm needs string_literal(path_arg), and an interpolated path is an ExprNode::StringInterp, so the statement is Unknown. (The kind = … assignment is a separate gap: a template local is also Unknown; #359 keeps it.)
A path can't name a method at compile time, but the candidates are known: the _*.json.jbuilder partials under the path's fixed prefix (widgets/). One way is a case over the interpolated string with one when "widgets/widget" arm per candidate calling its <name>_json, and an else that raises the ActionView::MissingTemplate Rails would raise. That's a design call (and the arms' arities must agree), so I've left it as an issue. Short of that, a diagnostic for Unknown statements would have pointed at it.
Found while compiling a Rails API app with --target spinel.
Probed with
roundhouse 2026.9.18 (7fad14c0), Linux x86-64, CRuby 4.0.5,--target ruby(the spinel tree has the same view.rb).A jbuilder
partial!whose path is built when the template runs renders{}, with no diagnostic:Controller action:
@widget = Widget.find(params[:id])thenrender :dynamic; routeget "widgets/:id/dynamic", to: "widgets#dynamic", defaults: { format: :json }; schemawidgets(name string, size integer);ApplicationController < ActionController::Base.--target rubyGET /widgets/1/dynamic{"id":1,"name":"b"}{}Emitted:
The real-world form picks one of a few partials by a record attribute or a param (
json.partial! "things/#{thing.kind}", thing: thing).Where it goes
src/lower/jbuilder_to_library/mod.rs,classify: thepartial!arm needsstring_literal(path_arg), and an interpolated path is anExprNode::StringInterp, so the statement isUnknown. (Thekind = …assignment is a separate gap: a template local is alsoUnknown; #359 keeps it.)A path can't name a method at compile time, but the candidates are known: the
_*.json.jbuilderpartials under the path's fixed prefix (widgets/). One way is acaseover the interpolated string with onewhen "widgets/widget"arm per candidate calling its<name>_json, and anelsethat raises theActionView::MissingTemplateRails would raise. That's a design call (and the arms' arities must agree), so I've left it as an issue. Short of that, a diagnostic forUnknownstatements would have pointed at it.Found while compiling a Rails API app with
--target spinel.