Class: OKF::Render::Graph
- Inherits:
-
Object
- Object
- OKF::Render::Graph
- Defined in:
- lib/okf/render/graph.rb
Overview
Renders an OKF::Bundle::Graph as the interactive graph page. The markup lives in graph/template.html.erb; #render returns the HTML string.
The page boots from a minimal payload — nodes carry only id + title, plus compact TYPES/TAGS inverted indexes for colouring and filtering. It has two data modes, both driven by one template:
server mode (embed: nil) — the default. Each concept's markdown body,
metadata, catalog, index and log are pulled from OKF::Server::App on
demand via fetch, so the initial payload stays small and bodies read
live from disk (edits show without a restart). Nothing extra embedded.
render mode (embed: payload) — `okf render` bakes the whole bundle in via
.static below: the same fetch getters resolve from the injected payload
instead, so the single file needs no server (e.g. GitHub Pages).
NOTE (trust boundary): the page loads Cytoscape + marked from a CDN, so it needs network for those libraries even in render mode. Fetched/embedded markdown is sanitized client-side (DOMPurify.sanitize(marked.parse(...))) and all inline--escaped by #json_for_script (stdlib ERB does not auto-escape). Still, only serve bundles you trust.
Constant Summary collapse
- TEMPLATE =
File.("graph/template.html.erb", __dir__)
- LAYOUTS =
%w[cose concentric breadthfirst circle grid].freeze
- MIN_SIZE =
Node-diameter range in px; the template scales within it by node degree.
14- MAX_SIZE =
44- LT_ESCAPE =
The 6-character JSON unicode escape for
<(backslash, u, 0, 0, 3, c), built from the backslash code point so no literal escape appears here. (92.chr(Encoding::UTF_8) + "u003c").freeze
Class Method Summary collapse
-
.payload(folder) ⇒ Object
What the baked page carries in place of the endpoints a live server would answer.
-
.static(folder, title: nil, link: nil, layout: "cose", map: false) ⇒ Object
okf render: the whole page as one self-contained file, the bundle baked in, so it hosts where no server answers a fetch.
Instance Method Summary collapse
-
#initialize(graph, title: nil, link: nil, layout: "cose", node_endpoint: "node", meta_endpoint: "node/meta", embed: nil, siblings: nil, self_slug: nil, hub_path: nil, search_endpoint: nil, manage_root: nil, manage_token: nil, cuts: nil, map: false) ⇒ Graph
constructor
+node_endpoint+/+meta_endpoint+ are the (mount-relative) URLs the page fetches a concept's raw markdown and metadata fragment from — relative so the page works whether served at "/" or mounted under a Rails prefix.
- #render ⇒ Object
Constructor Details
#initialize(graph, title: nil, link: nil, layout: "cose", node_endpoint: "node", meta_endpoint: "node/meta", embed: nil, siblings: nil, self_slug: nil, hub_path: nil, search_endpoint: nil, manage_root: nil, manage_token: nil, cuts: nil, map: false) ⇒ Graph
+node_endpoint+/+meta_endpoint+ are the (mount-relative) URLs the page
fetches a concept's raw markdown and metadata fragment from — relative so
the page works whether served at "/" or mounted under a Rails prefix.
embed is the render-mode payload (nil = server mode); see the class doc.
+siblings+/+self_slug+/+hub_path+ carry the hub's bundle switcher into the
page (server mode only). nil — the standalone-server and okf render
default — injects an empty SIBLINGS, so the switcher never appears in a
single bundle or a static file. search_endpoint rides along with them:
the hub's cross-bundle /search, which only a hub can answer.
cuts is one integer per edge, in @graph.edges order: the cut that edge
survives (OKF::Bundle::Skeleton). It rides inline rather than being
fetched because its only consumer needs it before the first layout
runs, which is earlier than any request could answer. nil is allowed and
simply turns the reduced first layout off.
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
# File 'lib/okf/render/graph.rb', line 83 def initialize(graph, title: nil, link: nil, layout: "cose", node_endpoint: "node", meta_endpoint: "node/meta", embed: nil, siblings: nil, self_slug: nil, hub_path: nil, search_endpoint: nil, manage_root: nil, manage_token: nil, cuts: nil, map: false) @graph = graph @cuts = cuts @map = map @title = title @link = link @layout = layout @node_endpoint = node_endpoint @meta_endpoint = @embed = @siblings = siblings @self_slug = self_slug @hub_path = hub_path @search_endpoint = search_endpoint @manage_root = manage_root @manage_token = manage_token end |
Class Method Details
.payload(folder) ⇒ Object
What the baked page carries in place of the endpoints a live server would answer. Every key here is data a client getter reads from EMBED instead of fetching — and each derives from the same folder method the matching OKF::Server::App endpoint uses, so the bake and the live server cannot drift (/node/meta is the exception: the fragment is derived on the client from the catalog's raw description, so no map is baked for it).
60 61 62 63 64 65 66 67 |
# File 'lib/okf/render/graph.rb', line 60 def self.payload(folder) { catalog: folder.catalog, index: folder.directory_index, logs: folder.log_entries, bodies: folder.concepts.each_with_object({}) { |concept, map| map[concept.id] = concept.body.to_s } } end |
.static(folder, title: nil, link: nil, layout: "cose", map: false) ⇒ Object
okf render: the whole page as one self-contained file, the bundle baked
in, so it hosts where no server answers a fetch. Takes any bundle handle
(an OKF::Bundle::Folder) and returns the HTML string.
48 49 50 51 52 |
# File 'lib/okf/render/graph.rb', line 48 def self.static(folder, title: nil, link: nil, layout: "cose", map: false) graph = folder.graph(minimal: true) new(graph, title: title || folder.name, link: link, layout: layout, embed: payload(folder), cuts: folder.skeleton.cuts_for(graph.edges), map: map).render end |
Instance Method Details
#render ⇒ Object
103 104 105 |
# File 'lib/okf/render/graph.rb', line 103 def render ERB.new(File.read(TEMPLATE, encoding: "UTF-8")).result(binding) end |