Class: Ruact::Configuration
- Inherits:
-
Object
- Object
- Ruact::Configuration
- Defined in:
- lib/ruact/configuration.rb
Overview
Holds gem-wide configuration. Instantiated once via Ruact.config. Configure via ‘Ruact.configure { |c| c.attr = value }` in an initializer.
Frozen after ‘Ruact.configure` returns (Story 7.3) — direct post-boot mutation (`Ruact.config.attr = value` outside the block) raises `Ruact::ConfigurationError` with the offending attribute, the caller’s file:line, and the suggested fix. Re-calling ‘Ruact.configure` after boot replaces the configuration atomically and emits a `[ruact]` warning.
Constant Summary collapse
- ATTRIBUTES =
The set of public attributes; new attributes added here automatically inherit the freeze contract via the ‘define_method` writer below.
%i[ manifest_path strict_serialization suspense_timeout vite_dev_server current_user_resolver dev_error_payload_enabled max_upload_bytes query_route_prefix query_parent_controller ].freeze
Instance Attribute Summary collapse
-
#current_user_resolver ⇒ Proc?
readonly
Story 8.3 — Lambda invoked by the standalone server-action dispatcher when a block reads ‘current_user`.
-
#dev_error_payload_enabled ⇒ Boolean?
readonly
Story 8.4 — When true, server-action failures respond with a verbose JSON payload (action name, error class, message, split backtrace, contextual suggestion, validation errors).
-
#manifest_path ⇒ String?
readonly
Path to react-client-manifest.json.
-
#max_upload_bytes ⇒ Integer?
readonly
Story 8.5 — upper bound (in bytes) on the ‘Content-Length` of `multipart/form-data` and `application/x-www-form-urlencoded` requests dispatched through `POST /__ruact/fn/:name`.
-
#query_parent_controller ⇒ String
readonly
Story 9.4 — class NAME of the controller the gem’s internal query dispatch controller inherits from (default ‘“ApplicationController”` — the Devise `parent_controller` pattern).
-
#query_route_prefix ⇒ String
readonly
Story 9.4 — URL prefix under which the ‘ruact_queries` routing macro draws one named GET route per public query method (default `“/q”` → `GET /q/<jsIdentifier>`).
-
#strict_serialization ⇒ Boolean
readonly
When true, objects without explicit ruact_props declaration raise Ruact::SerializationError.
-
#suspense_timeout ⇒ Float
readonly
Seconds before a deferred Suspense chunk times out.
-
#vite_dev_server ⇒ String
readonly
Base URL of the Vite dev server.
Instance Method Summary collapse
-
#initialize(template: nil) ⇒ Configuration
constructor
Build a fresh Configuration.
Constructor Details
#initialize(template: nil) ⇒ Configuration
Build a fresh Configuration. When template is given, dup every public attribute from it so the draft is mutable — used by ‘Ruact.configure` for atomic-replacement cloning. The dup is required because the template is always a published (frozen) Configuration with deep-frozen attribute values, and AC1 requires the DSL inside the configure block to behave identically regardless of whether this is the first call or a later one (including idiomatic in-place mutation of inherited values).
‘dup` is safe for every supported attribute type: Strings produce an unfrozen copy; nil/true/false/Numerics/Symbols dup to themselves (they are inherently immutable, so the dup is a no-op).
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 |
# File 'lib/ruact/configuration.rb', line 134 def initialize(template: nil) if template ATTRIBUTES.each do |attr| value = template.public_send(attr) # Procs are immutable from the outside (Story 8.3 — current_user_resolver). # Duping creates a different Proc instance, breaking identity comparisons # across re-configurations. Procs are inherently re-entrant safe (no # mutable internal state surface) so the dup is unnecessary; the freeze # at seal! time is enough. cloned = value.is_a?(Proc) ? value : value.dup instance_variable_set("@#{attr}", cloned) end else @manifest_path = nil @strict_serialization = begin Rails.env.production? rescue StandardError false end @suspense_timeout = 5.0 @vite_dev_server = "http://localhost:5173" @current_user_resolver = nil @dev_error_payload_enabled = nil @max_upload_bytes = 10 * 1024 * 1024 @query_route_prefix = "/q" @query_parent_controller = "ApplicationController" end end |
Instance Attribute Details
#current_user_resolver ⇒ Proc? (readonly)
Returns Story 8.3 — Lambda invoked by the standalone server-action dispatcher when a block reads ‘current_user`. Receives `request.env` (Hash) and returns the authenticated user (or nil). Memoized per-dispatch; left nil by default so apps that don’t use standalone actions never get a phantom ‘current_user` resolver.
108 109 110 111 112 113 114 115 116 117 118 119 |
# File 'lib/ruact/configuration.rb', line 108 ATTRIBUTES.each do |attr| attr_reader attr define_method("#{attr}=") do |value| if frozen? location = caller_locations(1, 1).first raise Ruact::ConfigurationError, (attr, location) end validate_attribute_value!(attr, value) instance_variable_set("@#{attr}", value) end end |
#dev_error_payload_enabled ⇒ Boolean? (readonly)
Returns Story 8.4 — When true, server-action failures respond with a verbose JSON payload (action name, error class, message, split backtrace, contextual suggestion, validation errors). When false, the wire body carries only the four baseline fields (‘_ruact_server_action_error`, `action_name`, `error_class`, `message`) so React components can render their own UI without accidental backtrace leakage. Default `nil` — the endpoint controller resolves nil to `Rails.env.development? || Rails.env.test?`, keeping the Configuration trivially constructible in non-Rails specs.
108 109 110 111 112 113 114 115 116 117 118 119 |
# File 'lib/ruact/configuration.rb', line 108 ATTRIBUTES.each do |attr| attr_reader attr define_method("#{attr}=") do |value| if frozen? location = caller_locations(1, 1).first raise Ruact::ConfigurationError, (attr, location) end validate_attribute_value!(attr, value) instance_variable_set("@#{attr}", value) end end |
#manifest_path ⇒ String? (readonly)
Returns Path to react-client-manifest.json. Defaults to Rails.root.join(“public/react-client-manifest.json”) when nil.
108 109 110 111 112 113 114 115 116 117 118 119 |
# File 'lib/ruact/configuration.rb', line 108 ATTRIBUTES.each do |attr| attr_reader attr define_method("#{attr}=") do |value| if frozen? location = caller_locations(1, 1).first raise Ruact::ConfigurationError, (attr, location) end validate_attribute_value!(attr, value) instance_variable_set("@#{attr}", value) end end |
#max_upload_bytes ⇒ Integer? (readonly)
This is a controller-level “fail fast at the boundary” knob, not a stream-safety guarantee — Rack’s multipart parser will still buffer bodies up to its own limits before the guard rejects. For very large uploads route through Active Storage Direct Upload or a presigned S3 URL; see ‘website/docs/api/server-actions.md` “File uploads” section.
Returns Story 8.5 — upper bound (in bytes) on the ‘Content-Length` of `multipart/form-data` and `application/x-www-form-urlencoded` requests dispatched through `POST /__ruact/fn/:name`. When the inbound `Content-Length` exceeds this value, the endpoint controller raises `Ruact::UploadTooLargeError` BEFORE Rack’s multipart parser runs, producing a 413 with the Story 8.4 structured error body. Default: ‘10 * 1024 * 1024` (10 MB). Set to `nil` to disable the gem-side guard — typical when a reverse proxy (`client_max_body_size`) or host middleware already owns the operational cap. Chunked-transfer requests (no `Content-Length` header) bypass the guard regardless of this setting; the action body is responsible for any belt-and-suspenders check via `params.size` / `params.byte_size` in that case.
108 109 110 111 112 113 114 115 116 117 118 119 |
# File 'lib/ruact/configuration.rb', line 108 ATTRIBUTES.each do |attr| attr_reader attr define_method("#{attr}=") do |value| if frozen? location = caller_locations(1, 1).first raise Ruact::ConfigurationError, (attr, location) end validate_attribute_value!(attr, value) instance_variable_set("@#{attr}", value) end end |
#query_parent_controller ⇒ String (readonly)
Returns Story 9.4 — class NAME of the controller the gem’s internal query dispatch controller inherits from (default ‘“ApplicationController”` — the Devise `parent_controller` pattern). Kept as a String and constantized lazily at route-draw time, NOT at configure time: `ApplicationController` does not exist when the gem loads. The host’s REAL callback chain (‘authenticate_user!`, tenant scoping, Pundit) runs before any query class is instantiated (FR89).
108 109 110 111 112 113 114 115 116 117 118 119 |
# File 'lib/ruact/configuration.rb', line 108 ATTRIBUTES.each do |attr| attr_reader attr define_method("#{attr}=") do |value| if frozen? location = caller_locations(1, 1).first raise Ruact::ConfigurationError, (attr, location) end validate_attribute_value!(attr, value) instance_variable_set("@#{attr}", value) end end |
#query_route_prefix ⇒ String (readonly)
Returns Story 9.4 — URL prefix under which the ‘ruact_queries` routing macro draws one named GET route per public query method (default `“/q”` → `GET /q/<jsIdentifier>`). Must be a String starting with `/` and without a trailing slash (the macro joins prefix and identifier with `/`). Changing the prefix is configuration, never code.
108 109 110 111 112 113 114 115 116 117 118 119 |
# File 'lib/ruact/configuration.rb', line 108 ATTRIBUTES.each do |attr| attr_reader attr define_method("#{attr}=") do |value| if frozen? location = caller_locations(1, 1).first raise Ruact::ConfigurationError, (attr, location) end validate_attribute_value!(attr, value) instance_variable_set("@#{attr}", value) end end |
#strict_serialization ⇒ Boolean (readonly)
Returns When true, objects without explicit ruact_props declaration raise Ruact::SerializationError. Defaults to false in development, true in production.
108 109 110 111 112 113 114 115 116 117 118 119 |
# File 'lib/ruact/configuration.rb', line 108 ATTRIBUTES.each do |attr| attr_reader attr define_method("#{attr}=") do |value| if frozen? location = caller_locations(1, 1).first raise Ruact::ConfigurationError, (attr, location) end validate_attribute_value!(attr, value) instance_variable_set("@#{attr}", value) end end |
#suspense_timeout ⇒ Float (readonly)
Returns Seconds before a deferred Suspense chunk times out. Default: 5.0.
108 109 110 111 112 113 114 115 116 117 118 119 |
# File 'lib/ruact/configuration.rb', line 108 ATTRIBUTES.each do |attr| attr_reader attr define_method("#{attr}=") do |value| if frozen? location = caller_locations(1, 1).first raise Ruact::ConfigurationError, (attr, location) end validate_attribute_value!(attr, value) instance_variable_set("@#{attr}", value) end end |
#vite_dev_server ⇒ String (readonly)
Returns Base URL of the Vite dev server. Default: “localhost:5173”.
108 109 110 111 112 113 114 115 116 117 118 119 |
# File 'lib/ruact/configuration.rb', line 108 ATTRIBUTES.each do |attr| attr_reader attr define_method("#{attr}=") do |value| if frozen? location = caller_locations(1, 1).first raise Ruact::ConfigurationError, (attr, location) end validate_attribute_value!(attr, value) instance_variable_set("@#{attr}", value) end end |