class OpenUI::Rails::Surface
Immutable description of one mounted OpenUI surface.
The host Rails app owns persistence; the Gem only needs a stable, opaque identifier plus the current revision and the source to render. A surface can be built explicitly or coerced from a host record that exposes ‘id` (and, optionally, `version`, `library_version`, `response`, `context`, `message_id`).
Constants
- ATTRIBUTES
Surfacevalues copied from an explicit object, hash, or host record.
Public Class Methods
Coerce a host record, hash, or existing surface into a new Surface. Non-nil overrides replace values extracted from the input.
@param value [Surface, Hash, id, nil]
@param overrides [Hash] surface attribute overrides
@return [Surface]
# File lib/openui/rails/surface.rb, line 26 def self.coerce(value = nil, **overrides) attributes = extract(value).merge(overrides.compact) new(**attributes) end
Build an immutable surface snapshot for the browser mount.
@param id [String, to_s] stable opaque identifier
@param version [Integer, nil] non-negative revision used for stale-action checks
@param library_version [String, nil] fingerprint of the browser library manifest
@param response [String, nil] OpenUI Lang source rendered by the browser
@param context [Object, nil] JSON-compatible initial state
@param streaming [Boolean] whether a response is still streaming
@param message_id [String, nil] stream identity; defaults to id
@return [Surface]
# File lib/openui/rails/surface.rb, line 76 def initialize(id:, version: 0, library_version: nil, response: nil, context: nil, streaming: false, message_id: nil) raise ArgumentError, "surface id is required" if id.nil? || id.to_s.empty? unless version.nil? || (version.is_a?(Integer) && version >= 0) raise ArgumentError, "surface version must be a non-negative Integer" end raise ArgumentError, "surface response must be a String" unless response.nil? || response.is_a?(String) @id = id.to_s.dup.freeze @version = version || 0 @library_version = library_version&.to_s&.dup&.freeze @response = response&.dup&.freeze @context = JsonValue.copy(context) @streaming = streaming ? true : false @message_id = (message_id&.to_s || @id).dup.freeze freeze end
Public Instance Methods
# File lib/openui/rails/surface.rb, line 143 def encode_state(state) return nil if state.nil? state.is_a?(String) ? state : JSON.generate(state) end
Whether the host says this surface is still receiving a response.
@return [Boolean]
# File lib/openui/rails/surface.rb, line 97 def streaming? = @streaming # Return the surface fields as a symbol-keyed hash. # # @return [Hash] def to_h { id: id, version: version, library_version: library_version, response: response, context: context, streaming: streaming, message_id: message_id } end # The flat `data:` hash for the Stimulus mount element. Keys use # underscores so Rails renders them as `data-openui-*` attributes; nil # values are omitted so the controller can rely on presence. # # Every value is deliberately a String. DOM data attributes are strings, # and Rails only JSON-coerces *non-String* values. This path broke on # Rails 8.0 with json >= 3.1 (ActiveSupport passes a `quirks_mode:` # keyword the gem removed). Emitting strings keeps rendering independent # of ActiveSupport's JSON encoder. # # @param stream [Boolean] mark this mount as streaming # # @param actions_endpoint [String] host engine action URL # # @param initial_state [Object, nil] JSON-compatible state override # # @return [Hash] HTML data attributes with string values def to_mount_data(stream: false, actions_endpoint:, initial_state: nil) { controller: "openui", openui_surface_id: id, openui_message_id: message_id, openui_version: version.to_s, openui_library_version: library_version, openui_streaming: (streaming? || stream) ? "true" : "false", openui_actions_url: actions_endpoint, openui_response: response, openui_stream_byte_limit: OpenUI::Rails.configuration.stream_byte_limit.to_s, openui_initial_state: encode_state(initial_state || context) }.compact end private def encode_state(state) return nil if state.nil? state.is_a?(String) ? state : JSON.generate(state) end end
Return the surface fields as a symbol-keyed hash.
@return [Hash]
# File lib/openui/rails/surface.rb, line 102 def to_h { id: id, version: version, library_version: library_version, response: response, context: context, streaming: streaming, message_id: message_id } end
The flat ‘data:` hash for the Stimulus mount element. Keys use underscores so Rails renders them as `data-openui-*` attributes; nil values are omitted so the controller can rely on presence.
Every value is deliberately a String. DOM data attributes are strings, and Rails only JSON-coerces non-String values. This path broke on Rails 8.0 with json >= 3.1 (ActiveSupport passes a ‘quirks_mode:` keyword the gem removed). Emitting strings keeps rendering independent of ActiveSupport’s JSON encoder.
@param stream [Boolean] mark this mount as streaming
@param actions_endpoint [String] host engine action URL
@param initial_state [Object, nil] JSON-compatible state override
@return [Hash] HTML data attributes with string values
# File lib/openui/rails/surface.rb, line 126 def to_mount_data(stream: false, actions_endpoint:, initial_state: nil) { controller: "openui", openui_surface_id: id, openui_message_id: message_id, openui_version: version.to_s, openui_library_version: library_version, openui_streaming: (streaming? || stream) ? "true" : "false", openui_actions_url: actions_endpoint, openui_response: response, openui_stream_byte_limit: OpenUI::Rails.configuration.stream_byte_limit.to_s, openui_initial_state: encode_state(initial_state || context) }.compact end