The right way to override render method

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?

Gracias|Thanks.

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
1 Like

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.