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_methodwriter below. %i[ manifest_path strict_serialization suspense_timeout vite_dev_server dev_error_payload_enabled max_upload_bytes query_route_prefix query_parent_controller signed_global_id_default_purpose signed_global_id_default_expires_in shadcn_compatible_versions ].freeze
Instance Attribute Summary collapse
-
#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-Lengthofmultipart/form-dataandapplication/x-www-form-urlencodedrequests dispatched to aRuact::Servermutation route. -
#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 Deviseparent_controllerpattern). -
#query_route_prefix ⇒ String
readonly
Story 9.4 — URL prefix under which the
ruact_queriesrouting macro draws one named GET route per public query method (default"/q"→GET /q/<jsIdentifier>). -
#shadcn_compatible_versions ⇒ Array<Integer>
readonly
Story 10.5 — the shadcn/ui MAJOR versions the
ruact:scaffoldgenerator is regression-tested against. -
#signed_global_id_default_expires_in ⇒ ActiveSupport::Duration?
readonly
Story 13.2 (FR96) — the default
expires_in:Ruact.signed_global_iduses when the call omitsexpires_in:. -
#signed_global_id_default_purpose ⇒ Symbol, ...
readonly
Story 13.2 (FR96) — the default
for:purposeRuact.signed_global_id/Ruact.locate_signeduse when the call omitsfor:. -
#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).
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 |
# File 'lib/ruact/configuration.rb', line 164 def initialize(template: nil) if template ATTRIBUTES.each do |attr| value = template.public_send(attr) # Procs are immutable from the outside. 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" @dev_error_payload_enabled = nil @max_upload_bytes = 10 * 1024 * 1024 @query_route_prefix = "/q" @query_parent_controller = "ApplicationController" @signed_global_id_default_purpose = nil @signed_global_id_default_expires_in = nil @shadcn_compatible_versions = [1, 2] end end |
Instance Attribute Details
#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 error-rendering
layer resolves nil to Rails.env.development? || Rails.env.test?,
keeping the Configuration trivially constructible in non-Rails specs.
138 139 140 141 142 143 144 145 146 147 148 149 |
# File 'lib/ruact/configuration.rb', line 138 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.
138 139 140 141 142 143 144 145 146 147 148 149 |
# File 'lib/ruact/configuration.rb', line 138 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 to a
Ruact::Server mutation route. When the inbound Content-Length
exceeds this value, the server concern 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[:file].size / params[:file].byte_size in that case.
138 139 140 141 142 143 144 145 146 147 148 149 |
# File 'lib/ruact/configuration.rb', line 138 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).
138 139 140 141 142 143 144 145 146 147 148 149 |
# File 'lib/ruact/configuration.rb', line 138 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.
138 139 140 141 142 143 144 145 146 147 148 149 |
# File 'lib/ruact/configuration.rb', line 138 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 |
#shadcn_compatible_versions ⇒ Array<Integer> (readonly)
Returns Story 10.5 — the shadcn/ui MAJOR versions the
ruact:scaffold generator is regression-tested against. When the
generator detects an installed shadcn major (best-effort, from the
host package.json) that is NOT in this list, it emits a warning
(never a hard stop) that the generated components may import from
outdated @/components/ui/* paths. Must be a non-empty Array of
Integers. Default [1, 2] (the majors tested at gem-release time).
A dev who has manually verified a newer major adds it here to
suppress the warning — the documented "override" path.
138 139 140 141 142 143 144 145 146 147 148 149 |
# File 'lib/ruact/configuration.rb', line 138 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 |
#signed_global_id_default_expires_in ⇒ ActiveSupport::Duration? (readonly)
Returns Story 13.2 (FR96) — the default
expires_in: Ruact.signed_global_id uses when the call omits
expires_in:. Must be an ActiveSupport::Duration (e.g. 15.minutes)
— globalid calls #from_now on it. Default nil — when neither the
call nor this config supplies an expiry, the helper raises
Ruact::Error rather than mint a non-expiring token. To deliberately
mint a non-expiring token, pass an explicit expires_in: nil at the
call site (a reviewed per-call choice), never via this default.
138 139 140 141 142 143 144 145 146 147 148 149 |
# File 'lib/ruact/configuration.rb', line 138 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 |
#signed_global_id_default_purpose ⇒ Symbol, ... (readonly)
Returns Story 13.2 (FR96) — the default for:
purpose Ruact.signed_global_id / Ruact.locate_signed use when the
call omits for:. A purpose scopes a signed reference to one
use-site so a token minted for editing a post cannot be replayed
against, say, a delete endpoint. Default nil — when neither the
call nor this config supplies a purpose, the helper raises
Ruact::Error rather than sign an unscoped token (the "developer
forgot" path is a loud error, never a silent insecure default).
Prefer a per-call for: when use-sites differ; set this only for an
app-wide default purpose.
138 139 140 141 142 143 144 145 146 147 148 149 |
# File 'lib/ruact/configuration.rb', line 138 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.
138 139 140 141 142 143 144 145 146 147 148 149 |
# File 'lib/ruact/configuration.rb', line 138 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.
138 139 140 141 142 143 144 145 146 147 148 149 |
# File 'lib/ruact/configuration.rb', line 138 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: "http://localhost:5173".
138 139 140 141 142 143 144 145 146 147 148 149 |
# File 'lib/ruact/configuration.rb', line 138 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 |