Module: Ruact::ServerFunctions::Codegen::V2

Defined in:
lib/ruact/server_functions/codegen_v2.rb

Overview

Story 9.3 — renders a version-2 (route-driven) snapshot into the TS module. Each entry is an action targeting a real path + verb, emitted as ‘_makeServerFunction({ method, path, segments })` instead of v1’s ‘_makeRef(“<sym>”)`. Lives in its own module (nested in Ruact::ServerFunctions::Codegen) so the v1 singleton class stays within its size budget; render delegates here when `snapshot.version == 2`.

The output MUST stay byte-identical to the JS-side ‘renderV2` in `gem/vendor/javascript/vite-plugin-ruact/server-functions-codegen.mjs`; the parity test (“Story 9.3 — route-driven (v2) render + parity”) asserts this. Constants (ACTION_SIGNATURE, RUNTIME_IMPORT, REVALIDATE_REEXPORT, LINE_TERMINATORS, VALID_JS_IDENTIFIER) are reused from Ruact::ServerFunctions::Codegen via lexical scope.

Constant Summary collapse

HTTP_METHODS =

Verbs a v2 ACTION entry may carry (mirrors RouteSource::MUTATION_VERBS).

%w[POST PUT PATCH DELETE].to_set.freeze
QUERY_HTTP_METHODS =

Story 9.5 — the verb a v2 QUERY entry may carry. Queries are reads, mounted by Routing#ruact_queries as named GET routes; the 2026-06-02 ADR addendum voided the old ‘POST /__ruact/fn/:id` query mechanism, so a query entry is GET-only.

%w[GET].to_set.freeze

Class Method Summary collapse

Class Method Details

.render(version, generated_at, functions) ⇒ String

Returns TS module bytes (single trailing newline).

Parameters:

  • version (Integer, String)
  • generated_at (String)
  • functions (Array<Hash>)

    route-derived entries (actions AND, Story 9.5, queries).

Returns:

  • (String)

    TS module bytes (single trailing newline).

Raises:



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/ruact/server_functions/codegen_v2.rb', line 39

def render(version, generated_at, functions)
  validate_functions!(functions)

  has_query  = functions.any? { |entry| fetch(entry, "kind").to_s == "query" }
  has_action = functions.any? { |entry| fetch(entry, "kind").to_s == "action" }

  # Import only what is used. Keep `_makeServerFunction` in the empty
  # case so the no-functions module stays byte-identical to Story 9.3.
  imports = []
  imports << "_makeServerFunction" if has_action || functions.empty?
  imports << "_makeQuery" if has_query

  io = +""
  io << "// AUTO-GENERATED by vite-plugin-ruact (Story 9.3). DO NOT EDIT.\n"
  io << "// Source: Rails route table (version #{version})\n"
  io << "// Generated at: #{generated_at}\n"
  io << "import { #{imports.join(', ')} } from #{RUNTIME_IMPORT};\n"

  if functions.empty?
    io << "\n// (no server functions exposed yet — add a non-GET route on a Ruact::Server controller)\n"
    io << "void _makeServerFunction;\n"
  else
    io << "\n"
    functions.each { |entry| io << render_export(entry) }
  end

  io << "\n"
  io << REVALIDATE_REEXPORT
  io << USEQUERY_REEXPORT if has_query
  io
end