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?), params (Array — Story 13.4: one { "name" => String, "required" => Boolean } per declared keyreq/key keyword, in declaration order; empty when none), params_rest (Boolean — Story 13.4: does the method declare a **keyrest?), controller (the query class name — for collision origins), action (the Ruby method name).

Raises:



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/ruact/server_functions/query_source.rb', line 67

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