Module: Ruact::ServerFunctions

Defined in:
lib/ruact/server_functions.rb,
lib/ruact/server_functions/codegen.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/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/validation_errors.rb,
lib/ruact/server_functions/bucket_two_payload.rb,
lib/ruact/server_functions/codegen_v2_query_params.rb

Defined Under Namespace

Modules: BacktraceCleaner, BucketTwoPayload, Codegen, ErrorPayload, ErrorRendering, ErrorSuggestion, NameBridge, QueryDispatch, QuerySource, RouteSource, Snapshot, SnapshotWriter, ValidationErrors Classes: QueryContext

Class Method Summary collapse

Class Method Details

.default_loggerObject



78
79
80
# File 'lib/ruact/server_functions.rb', line 78

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:



94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/ruact/server_functions.rb', line 94

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 / 9.9 — orchestrates the route-driven (v2) codegen. Reads the route table via RouteSource + QuerySource, writes the version-2 bridge to the REAL path (write-if-changed), and renders the TS via the Ruby Codegen. As of Story 9.9 this is the SOLE writer of the real bridge — the v1 registry path and the parallel .next target were demolished (epic decision #6: route-driven codegen owns server-functions.ts unconditionally; the module path @/.ruact/server-functions never changed, so React imports are unchanged by construction).

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 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).



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/ruact/server_functions.rb', line 57

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.json")
  ts_path   = root.join("app/javascript/.ruact/server-functions.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