Hi there, I’m writing a controller concern for HTMX.
I need to override ActionController::Base.render method, but I don’t know if this it’s the right way:
def render(*args, &block)
options = _normalize_render(*args, &block)
# Set flash now
if (_flash_now = options.delete :flash_now)
_flash_now.each do |type, text|
flash.now[type] = text
end
end
# Set headers
if (_headers = options.delete :headers)
_headers.each do |header, value|
response.headers[header] = value
end
end
# Avoid layout when HTMX Request
options[:layout] = false if hx_request?
super options, &block
end
I search on internet and I can’t find anything about this. There are a best way to implement this?
Instead of overriding render, it might be better to use formats and set things other ways. Your best bet is to take a look at the turbo_rails gem, since it also checks if requests are coming from turbo to change how rendering will be performed.
For example, here is how it handles the special layout for frames. Just rename what you need and replace turbo_frame_request? with hx_request?
module HtmxRequest
extend ActiveSupport::Concern
included do
layout -> { "turbo_rails/frame" if turbo_frame_request? }
etag { :frame if turbo_frame_request? }
helper_method :turbo_frame_request_id
end
private
def turbo_frame_request?
turbo_frame_request_id.present?
end
def turbo_frame_request_id
request.headers["Turbo-Frame"]
end
end
And this is how it handles both a normal response and a stream response.
def destroy
@user.destroy!
respond_to do |format|
format.turbo_stream { render turbo_stream: turbo_stream.remove(@user) }
format.html { redirect_to users_url, notice: "User removed" }
end
end
I’d like to wrap all ActionView render calls in a custom web component. This may seem like a bad idea, but still: How would you do this? Your solution doesn’t apply well to this because it mainly extends behaviour.
I tried to naively patch the render method. This gets me weird behaviour like that when I change the module code, the contents show multiple times. It seems like a caching issue. But I’m running this all in development.
# lib/render_patch.rb
module RenderPatch
def render(*args, &block)
puts "render called with #{args.inspect}"
if args.first.is_a?(String)
rendered_content = super(*args, &block)
<<~HTML.html_safe
<pre>--- #{args.first} ---
#{args.second.map { "#{_1}: #{_2.class}" }.join("\n")}
</pre>
<rasmic-debuggable>
#{rendered_content}
</rasmic-debuggable>
HTML
else
super(*args, &block)
end
end
end
# config/initializers/gui_patch.rb
Rails.application.config.to_prepare do
ActionView::Base.prepend(RenderPatch)
end
So what’s the right way to override the render method? For ActionView in my case.