Module: Ruact::ServerFunctions::QuerySource

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

Overview

Story 9.5 — derives v2 QUERY entries for the route-driven codegen, the read-side sibling of RouteSource (which derives the non-GET mutation actions). Queries come from Query subclasses mounted via the ‘ruact_queries` routing macro (Story 9.4).

## Why read the drawn route table, not all Ruact::Query subclasses

The route table is the single source of truth (FR61): a host exposes a query ONLY by mounting its class with ‘ruact_queries` in `routes.rb`. Enumerating every `Ruact::Query` subclass would over-expose query classes that are defined but never mounted (and would 404 when `useQuery` fetched their non-existent routes). Reading the routes the `ruact_queries` macro actually drew keeps codegen route-truth-consistent with dispatch by construction — and means there is no `app/queries` force-load gap to paper over (mounting a class in `routes.rb` already autoloads it).

Every generated query dispatch controller lives under the QUERY_CONTROLLER_PREFIX namespace (see Ruact::ServerFunctions::QueryDispatch.route_target_for), so the GET routes this module consumes are unambiguous: any drawn route whose controller path starts with that prefix is a mounted query method.

Pure by construction: QuerySource.collect takes the route set and a resolver callable (controller-path → the backing Query subclass). The railtie passes the real constant-resolving implementation; unit specs inject a lambda so the derivation is testable without booting controllers.

Constant Summary collapse

QUERY_CONTROLLER_PREFIX =

The controller-path prefix every generated query dispatch controller lives under (mirrors Ruact::ServerFunctions::QueryDispatch.route_target_for). A drawn GET route whose controller starts with this prefix is a mounted query.

"ruact/server_functions/query_dispatch/"
KEYWORD_PARAM_TYPES =

‘Method#parameters` types that mark keyword arguments — the FR88 query parameters (mirrors Ruact::ServerFunctions::QueryDispatch::Dispatching::KEYWORD_PARAM_TYPES).

%i[key keyreq keyrest].freeze

Class Method Summary collapse

Class Method Details

.collect(route_set, query_class_for: nil) ⇒ Array<Hash>

Collects v2 query entries from route_set.

Parameters:

  • route_set (#routes)

    anything exposing ‘#routes` (an `ActionDispatch::Routing::RouteSet`, or `Rails.application.routes`).

  • query_class_for (#call, nil) (defaults to: nil)

    ‘controller_path(String) -> (Class | nil)` — resolves a query dispatch controller path to the Query subclass it backs. Defaults to real constant resolution (reads the generated controller’s ‘__ruact_query_class`).

Returns:

  • (Array<Hash>)

    query entries (string keys) sorted by ‘js_identifier`; shape: `js_identifier`, `kind` (always `“query”`), `http_method` (always `“GET”`), `path`, `segments` (always `[]`), `accepts_params` (Boolean — does the method declare kwargs?), `controller` (the query class name — for collision origins), `action` (the Ruby method name).

Raises:



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

def collect(route_set, query_class_for: nil)
  query_class_for ||= method(:default_query_class_for)

  entries = []
  route_set.routes.each do |route|
    controller = route.defaults[:controller]
    action = route.defaults[:action]
    next if controller.nil? || action.nil?
    next unless controller.to_s.start_with?(QUERY_CONTROLLER_PREFIX)

    query_class = query_class_for.call(controller.to_s)
    next if query_class.nil?

    entries << build_entry(route, action.to_s, query_class)
  end

  entries = entries.sort_by { |entry| entry["js_identifier"] }
  detect_collisions!(entries)
  entries
end