-
Notifications
You must be signed in to change notification settings - Fork 37
Description
Description
Out of the box, Rage supports rendering JSON, plaintext, and SSE. However, many applications need to render other formats — HTML via Phlex or ERB, CSV, XML, Protocol Buffers, etc. Today, this is possible but requires manually setting the content type:
render plain: MyPhlexComponent.call
headers["content-type"] = "text/html"This works, but it's not ergonomic for frequent use, and it doesn't provide a consistent pattern across an application. The goal of this issue is to introduce a standard way to register custom renderers, making it straightforward to plug in view-level gems and enable server-side rendering workflows. This work will also serve as the foundation for the Inertia.js integration.
Proposed interface
Register renderers in configuration:
# config/application.rb or config/environments/<environment>.rb
Rage.configure do
config.renderer(:phlex) do |object, **options|
headers["content-type"] = "text/html"
object.new(**options).call
end
config.renderer(:csv) do |object, delimiter: ","|
headers["content-type"] = "text/csv"
CSV.generate(object, col_sep: delimiter)
end
endUse in controllers:
class ArticlesController < RageController::API
def index
render_phlex MyComponent, articles: Article.all
end
def export
render_csv my_csv_data, delimiter: ";"
end
endRegistering a renderer named :phlex would define a render_phlex method available in all controllers. The proc's return value becomes the response body.
Design considerations
These are open questions that should be addressed in the design proposal:
- Execution context. The proc references
headersdirectly, which means it needs to execute in the controller's context. This gives the proc access torequest,params,cookies, etc. - Return value semantics. In the proposed interface the proc's return value becomes the response body as a string. Consider whether a more flexible approach would be to explicitly use
renderinstead. This would allow to support streaming bodies and custom status codes, but could be more verbose. - Name conflicts. What happens if someone registers duplicate renderers?
- Method definition timing. Since renderers are registered at config time and define controller methods, ensure the methods are available after code loading. Consider how this interacts with Rage's code reloading in development.
Before You Start
This issue requires a design proposal before implementation. Please open a discussion or comment on this issue with a high-level plan that outlines:
- The public API (confirming or adjusting the interface above).
- How renderer procs will be stored and how controller methods will be defined.
- The execution context strategy (
instance_execor alternative). - How the edge cases above will be handled.
PRs without prior design discussion are unlikely to be accepted.
Tips
- Look at how
render json:andrender plain:are currently implemented in the codebase to understand the rendering pipeline and where custom renderers would hook in. - Check the architecture doc that shows how Rage's core components interact with each other and outlines the design principles.
- For context on the Inertia.js use case, see inertia-rails to understand what the rendering integration needs to support.
- Feel free to ask any questions or request help in the comments below!