require 'net/http' require 'json' require_relative "inertia_rails" module InertiaRails class Renderer attr_reader :component, :view_data def initialize(component, controller, request, response, render_method, props: nil, view_data: nil, deep_merge: nil) @component = component.is_a?(TrueClass) ? "#{controller.controller_path}/#{controller.action_name}" : component @controller = controller @request = request @response = response @render_method = render_method @props = props ? props.with_indifferent_access : controller.inertia_view_assigns.with_indifferent_access @view_data = view_data || {} @deep_merge = !deep_merge.nil? ? deep_merge : InertiaRails.deep_merge_shared_data? end def render if @request.headers['X-Inertia'] @response.set_header('Vary', 'Accept') @response.set_header('X-Inertia', 'true') @render_method.call json: page, status: @response.status, content_type: Mime[:json] else return render_ssr if ::InertiaRails.ssr_enabled? rescue nil @render_method.call template: 'inertia', layout: layout, locals: (view_data).merge({page: page}) end end private def render_ssr uri = URI("#{::InertiaRails.ssr_url}/render") res = JSON.parse(Net::HTTP.post(uri, page.to_json, 'Content-Type' => 'application/json').body) ::InertiaRails.html_headers = res['head'] @render_method.call html: res['body'].html_safe, layout: layout, locals: (view_data).merge({page: page}) end def layout @controller.send(:inertia_layout) end def computed_props _props = ::InertiaRails.shared_data(@controller).send(prop_merge_method, @props).select do |key, prop| if rendering_partial_component? key.in? partial_keys else !prop.is_a?(InertiaRails::Lazy) end end deep_transform_values(_props, lambda {|prop| prop.respond_to?(:call) ? @controller.instance_exec(&prop) : prop }).with_indifferent_access end def page { component: component, props: computed_props, url: @request.original_fullpath, version: ::InertiaRails.version, } end def deep_transform_values(hash, proc) return proc.call(hash) unless hash.is_a? Hash hash.transform_values {|value| deep_transform_values(value, proc)} end def partial_keys (@request.headers['X-Inertia-Partial-Data'] || '').split(',').compact.map(&:to_sym) end def rendering_partial_component? @request.inertia_partial? && @request.headers['X-Inertia-Partial-Component'] == component end def prop_merge_method @deep_merge ? :deep_merge : :merge end end end