Module: Ruact::ServerFunctions::Snapshot
- Defined in:
- lib/ruact/server_functions/snapshot.rb
Overview
Pure functions that build the JSON-shaped Hash representing both server- function registries. Serialized to ‘tmp/cache/ruact/server-functions.json` by Snapshot.generate!; the Vite plugin reads that file and emits the TS module.
The “functions” array is sorted by ‘ruby_symbol` for deterministic output so that fingerprint comparisons (used by the write-if-changed guard) are stable across runs. Cross-registry JS-identifier collisions are detected here (the per-registry `Registry#register` only sees its own entries; a `ruact_action :foo` colliding with a `ruact_query :foo` is invisible to both registries in isolation).
Constant Summary collapse
- VERSION =
Bump only when the on-disk schema changes incompatibly. The Vite plugin must be updated in lockstep.
1- VERSION_V2 =
Story 9.3 — the route-driven snapshot schema. v2 entries are produced by RouteSource (route table), not the registries.
2
Class Method Summary collapse
-
.dump(action_registry, query_registry, now: Time.now.utc) ⇒ Hash
Builds the snapshot Hash for both registries.
-
.dump_v2(entries, now: Time.now.utc) ⇒ Hash
Story 9.3 — wraps route-derived
entriesinto a version-2 snapshot Hash (the shape Codegen.render dispatches on). -
.functions_payload(action_registry, query_registry) ⇒ Array<Hash>
Returns the payload-only array of function entries, sorted by ‘ruby_symbol`.
-
.generate!(action_registry:, query_registry:, path:, now: Time.now.utc) ⇒ Boolean
Builds the snapshot and writes it to
path, but only if the functions list differs from the on-disk snapshot. -
.generate_v2!(entries:, path:, now: Time.now.utc) ⇒ Boolean
Story 9.3 — write-if-changed for the route-driven (v2) bridge.
-
.v1_declarations?(action_registry, query_registry) ⇒ Boolean
Story 9.3 — the Decision-#6 ownership primitive.
Class Method Details
.dump(action_registry, query_registry, now: Time.now.utc) ⇒ Hash
Builds the snapshot Hash for both registries. Pure. See also generate! (writes to disk) and functions_payload (fingerprint surface).
38 39 40 41 42 43 44 |
# File 'lib/ruact/server_functions/snapshot.rb', line 38 def dump(action_registry, query_registry, now: Time.now.utc) { version: VERSION, generated_at: now.utc.iso8601, functions: functions_payload(action_registry, query_registry) } end |
.dump_v2(entries, now: Time.now.utc) ⇒ Hash
Story 9.3 — wraps route-derived entries into a version-2 snapshot Hash (the shape Codegen.render dispatches on). Pure.
105 106 107 108 109 110 111 |
# File 'lib/ruact/server_functions/snapshot.rb', line 105 def dump_v2(entries, now: Time.now.utc) { version: VERSION_V2, generated_at: now.utc.iso8601, functions: entries } end |
.functions_payload(action_registry, query_registry) ⇒ Array<Hash>
Returns the payload-only array of function entries, sorted by ‘ruby_symbol`. Used both inside dump and as the fingerprint surface by generate!’s short-circuit (so timestamp churn alone never causes a rewrite). Detects cross-registry JS-identifier collisions and raises before emitting — a ‘ruact_action :foo` and `ruact_query :foo` would emit two `export const foo` lines at codegen, which `tsc` rejects.
56 57 58 59 60 61 62 63 64 65 66 67 |
# File 'lib/ruact/server_functions/snapshot.rb', line 56 def functions_payload(action_registry, query_registry) combined = action_registry.entries.values + query_registry.entries.values detect_cross_registry_collision!(combined) combined.sort_by { |entry| entry.ruby_symbol.to_s }.map do |entry| { "ruby_symbol" => entry.ruby_symbol.to_s, "js_identifier" => entry.js_identifier, "kind" => entry.kind.to_s, "controller" => describe_controller(entry.controller) } end end |
.generate!(action_registry:, query_registry:, path:, now: Time.now.utc) ⇒ Boolean
Builds the snapshot and writes it to path, but only if the functions list differs from the on-disk snapshot. This is the central short-circuit that prevents ‘config.to_prepare` from rewriting the file on every request (Story 8.0a pitfall #1): the JSON’s ‘generated_at` is freshly stamped only when the registry actually changed; otherwise the on-disk content stays byte-identical.
The short-circuit compares both ‘version` and `functions` against the on-disk snapshot — a schema bump (`VERSION` increment) forces a rewrite even when the registry payload is unchanged, so the Vite plugin never reads a stale-version snapshot after a gem upgrade.
86 87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/ruact/server_functions/snapshot.rb', line 86 def generate!(action_registry:, query_registry:, path:, now: Time.now.utc) new_functions = functions_payload(action_registry, query_registry) existing_version, existing_functions = read_existing_snapshot(path) return false if existing_version == VERSION && existing_functions == new_functions snapshot = { version: VERSION, generated_at: now.utc.iso8601, functions: new_functions } SnapshotWriter.write_if_changed!(path: path, content: "#{JSON.pretty_generate(snapshot)}\n") end |
.generate_v2!(entries:, path:, now: Time.now.utc) ⇒ Boolean
Story 9.3 — write-if-changed for the route-driven (v2) bridge. Mirrors generate!: ‘generated_at` is freshly stamped only when the entries changed, so a stable route table never churns the file (and never re-triggers downstream TS rendering). A schema mismatch (`version`) forces a rewrite even when entries are unchanged.
122 123 124 125 126 127 128 |
# File 'lib/ruact/server_functions/snapshot.rb', line 122 def generate_v2!(entries:, path:, now: Time.now.utc) existing_version, existing_functions = read_existing_snapshot(path) return false if existing_version == VERSION_V2 && existing_functions == entries snapshot = { version: VERSION_V2, generated_at: now.utc.iso8601, functions: entries } SnapshotWriter.write_if_changed!(path: path, content: "#{JSON.pretty_generate(snapshot)}\n") end |
.v1_declarations?(action_registry, query_registry) ⇒ Boolean
Story 9.3 — the Decision-#6 ownership primitive. True when the app has ANY v1 declaration (‘ruact_action` / `ruact_query`). Story 9.8 consults this to decide whether route-driven codegen takes over the real `server-functions.ts`; in Story 9.3 the v2 codegen always writes the parallel `.next` target regardless, so this is informational here.
137 138 139 |
# File 'lib/ruact/server_functions/snapshot.rb', line 137 def v1_declarations?(action_registry, query_registry) !(action_registry.entries.empty? && query_registry.entries.empty?) end |