Module: Trane::Docs::Cache
- Defined in:
- lib/trane/docs/cache.rb
Overview
Memoized JSON + HTML output of the ServiceDefinition. The pair lives
inside a single frozen Snapshot ivar built atomically by
precompute!: every Snapshot instance carries a (json, html)
pair derived from one ServiceDefinition.generate call, so no
caller can observe a torn pair WITHIN a single snapshot
generation. The previous design wrote @json and @html in
sequence, leaving a window where a reader saw new JSON paired
with old HTML across a Rails reload.
Consistency is per-snapshot-instance, not per-call-pair: a thread
that reads Cache.json then Cache.html may observe two
different snapshots if precompute! runs between the two calls.
In production this is unobservable (each Rack request hits ONE of
/docs.json or /docs, never both), and cross-request
ordering across reloads is out of scope for this module.
precompute! is ALWAYS lazy — triggered by the first json/html
read via ensure_snapshot. Trane::Engine's to_prepare block calls
invalidate! (not precompute!): during to_prepare the host routes
are not drawn yet (it runs before the Finisher's
set_routes_reloader_hook, in every environment), so precomputing there
would build a snapshot from an empty route set and every operation would
fall back to method "GET" with an empty path. Deferring to the first
post-boot read — inside a request, with the routes drawn — is what makes
the docs report each operation's real HTTP verb and path. Concurrent
cold readers may both build a snapshot; one assignment wins, the other is
GC'd — wasted work, never inconsistent state.
Defined Under Namespace
Classes: Snapshot
Class Method Summary collapse
- .html ⇒ Object
- .invalidate! ⇒ Object
- .json ⇒ Object
-
.precompute! ⇒ void
Build a fresh (json, html) Snapshot from the current ServiceDefinition and swap it in atomically.
Class Method Details
.html ⇒ Object
43 44 45 46 |
# File 'lib/trane/docs/cache.rb', line 43 def html ensure_snapshot @snapshot&.html end |
.invalidate! ⇒ Object
64 65 66 |
# File 'lib/trane/docs/cache.rb', line 64 def invalidate! @snapshot = nil end |
.json ⇒ Object
38 39 40 41 |
# File 'lib/trane/docs/cache.rb', line 38 def json ensure_snapshot @snapshot&.json end |
.precompute! ⇒ void
This method returns an undefined value.
Build a fresh (json, html) Snapshot from the current ServiceDefinition and swap it in atomically.
52 53 54 55 56 57 58 59 60 61 62 |
# File 'lib/trane/docs/cache.rb', line 52 def precompute! # The only Rails read in the docs layer: the service name is # Rails.application.name, never Trane configuration. app = Rails.application definition = ServiceDefinition.generate(app.routes.routes, service_name: app.name) @snapshot = Snapshot.new( json: JSON.generate(definition), html: HtmlRenderer.render(definition) ) nil end |