Module: Ruact::ServerFunctions

Defined in:
lib/ruact/server_functions.rb,
lib/ruact/server_functions/codegen.rb,
lib/ruact/server_functions/registry.rb,
lib/ruact/server_functions/snapshot.rb,
lib/ruact/server_functions/codegen_v2.rb,
lib/ruact/server_functions/name_bridge.rb,
lib/ruact/server_functions/query_source.rb,
lib/ruact/server_functions/route_source.rb,
lib/ruact/server_functions/error_payload.rb,
lib/ruact/server_functions/query_context.rb,
lib/ruact/server_functions/query_dispatch.rb,
lib/ruact/server_functions/registry_entry.rb,
lib/ruact/server_functions/error_rendering.rb,
lib/ruact/server_functions/snapshot_writer.rb,
lib/ruact/server_functions/error_suggestion.rb,
lib/ruact/server_functions/backtrace_cleaner.rb,
lib/ruact/server_functions/bucket_two_payload.rb,
lib/ruact/server_functions/standalone_context.rb,
lib/ruact/server_functions/endpoint_controller.rb,
lib/ruact/server_functions/standalone_dispatcher.rb

Defined Under Namespace

Modules: BacktraceCleaner, BucketTwoPayload, Codegen, ErrorPayload, ErrorRendering, ErrorSuggestion, NameBridge, QueryDispatch, QuerySource, RouteSource, Snapshot, SnapshotWriter Classes: EndpointController, QueryContext, Registry, RegistryEntry, StandaloneContext, StandaloneDispatcher

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Attribute Details

#blockProc? (readonly)

Returns the implementation block supplied by the controller macro. Story 8.0a stores it untouched; Stories 8.1 / 9.1 invoke it.

Returns:

  • (Proc, nil)

    the implementation block supplied by the controller macro. Story 8.0a stores it untouched; Stories 8.1 / 9.1 invoke it.



24
# File 'lib/ruact/server_functions/registry_entry.rb', line 24

RegistryEntry = Data.define(:ruby_symbol, :js_identifier, :kind, :controller, :block)

#controllerClass? (readonly)

Returns the controller class that registered the function; used for collision-error messages and downstream tooling. Nil is allowed for tests / Rails-console registrations.

Returns:

  • (Class, nil)

    the controller class that registered the function; used for collision-error messages and downstream tooling. Nil is allowed for tests / Rails-console registrations.



24
# File 'lib/ruact/server_functions/registry_entry.rb', line 24

RegistryEntry = Data.define(:ruby_symbol, :js_identifier, :kind, :controller, :block)

#js_identifierString (readonly)

Returns result of Ruact::ServerFunctions::NameBridge.to_js_identifier — cached at registration time so the snapshot writer never re-derives it.

Returns:



24
# File 'lib/ruact/server_functions/registry_entry.rb', line 24

RegistryEntry = Data.define(:ruby_symbol, :js_identifier, :kind, :controller, :block)

#kindSymbol (readonly)

Returns ‘:action` or `:query`. Informational at codegen time (Story 8.0 decision 2A-i: both kinds POST through the same accessor).

Returns:

  • (Symbol)

    ‘:action` or `:query`. Informational at codegen time (Story 8.0 decision 2A-i: both kinds POST through the same accessor).



24
# File 'lib/ruact/server_functions/registry_entry.rb', line 24

RegistryEntry = Data.define(:ruby_symbol, :js_identifier, :kind, :controller, :block)

#ruby_symbolSymbol (readonly)

Returns the symbol the controller registered (e.g. ‘:create_post`).

Returns:

  • (Symbol)

    the symbol the controller registered (e.g. ‘:create_post`).



24
# File 'lib/ruact/server_functions/registry_entry.rb', line 24

RegistryEntry = Data.define(:ruby_symbol, :js_identifier, :kind, :controller, :block)

Class Method Details

.default_loggerObject



82
83
84
# File 'lib/ruact/server_functions.rb', line 82

def self.default_logger
  defined?(Rails) && Rails.respond_to?(:logger) ? Rails.logger : nil
end

.detect_merged_namespace_collisions!(entries) ⇒ Object

Story 9.5 (Task 2) — the merged JS namespace covers route (action) entries AND query entries. RouteSource already rejects action×action collisions and QuerySource rejects query×query; this final pass catches a route×query clash — two distinct origins (one a mutation route, one a query method) mapping to the same ‘js_identifier` would emit two `export const <id>` lines and crash the generated module at load. Fail loudly at boot naming BOTH origins. The escape hatch is the `ruact_function_name :<action>, as: “<id>”` rename macro on the mutation controller (Story 9.3) or renaming the colliding query method.

Parameters:

  • entries (Array<Hash>)

    merged action + query entries.

Raises:



98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/ruact/server_functions.rb', line 98

def self.detect_merged_namespace_collisions!(entries)
  entries.group_by { |entry| entry["js_identifier"] }.each do |js_id, group|
    next if group.size < 2

    origins = group.map { |entry| "#{entry['controller']}##{entry['action']}" }
    raise Ruact::ConfigurationError,
          "server-function naming collision: #{origins.join(' and ')} " \
          "both map to JS identifier \"#{js_id}\" — disambiguate with " \
          "`ruact_function_name :<action>, as: \"<other-name>\"` on the mutation " \
          "controller, or rename the query method."
  end
end

.write_v2_snapshot!(route_set:, root:, logger: default_logger) ⇒ Array<Hash>

Story 9.3 — orchestrates the route-driven (v2) codegen target. Reads the route table via RouteSource, writes the version-2 bridge to the PARALLEL ‘.next` path (write-if-changed), and renders the inspection TS via the Ruby Codegen (Vite does not watch `.next`). Per AC5 the `.next` target is for parity tests + inspection only — never imported by application code —so the real `server-functions.ts` (v1, rendered by Vite) is untouched (AC6). The Decision-#6 ownership flip (zero v1 declarations → v2 owns the real file) is Story 9.8’s job.

AC2 — transparency over silence: the exposed names are ALWAYS logged so a routed non-GET action never becomes a callable server function silently.

Story 9.5 — the ‘entries` array now carries BOTH mutation actions (from RouteSource) and read queries (from QuerySource); they share ONE merged JS namespace. detect_merged_namespace_collisions! catches a route×query clash (each source already catches its own intra-kind collisions); the rename-override macro `ruact_function_name` on the mutation side (or renaming the query method) resolves it.

Parameters:

  • route_set (#routes)

    the Rails route set.

  • root (Pathname)

    the app root (for ‘tmp/cache` + `app/javascript`).

  • logger (#info, nil) (defaults to: default_logger)

    logger for the exposure line; defaults to ‘Rails.logger` when Rails is loaded, else nil.

Returns:

  • (Array<Hash>)

    the exposed v2 entries (actions + queries).



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/ruact/server_functions.rb', line 61

def self.write_v2_snapshot!(route_set:, root:, logger: default_logger)
  actions = RouteSource.collect(route_set)
  queries = QuerySource.collect(route_set)
  entries = (actions + queries).sort_by { |entry| entry["js_identifier"] }
  detect_merged_namespace_collisions!(entries)

  json_path = root.join("tmp/cache/ruact/server-functions.next.json")
  ts_path   = root.join("app/javascript/.ruact/server-functions.next.ts")

  # Read back from the on-disk bridge (not a fresh dump) so a stable route
  # table never churns the timestamp baked into the rendered TS header.
  Snapshot.generate_v2!(entries: entries, path: json_path)
  Codegen.generate_ts!(snapshot: JSON.parse(File.read(json_path)), output_path: ts_path)

  # AC2 — ALWAYS log what is exposed (even "(none)"), so a routed non-GET
  # action never becomes a callable server function silently.
  names = entries.empty? ? "(none)" : entries.map { |e| e["js_identifier"] }.join(", ")
  logger&.info "[ruact] codegen: exposing #{names}"
  entries
end