Class: OKF::Render::Graph

Inherits:
Object
  • Object
show all
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.expand_path("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

Instance Method Summary collapse

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.



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/okf/render/graph.rb', line 91

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 = meta_endpoint
  @embed = 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 trust line is composed on the client from the catalog row, so no map is baked for it). bodies and sources have no bare endpoint at all — they exist so a static file searches offline what the server-mode index deliberately leaves out; the sources text is the same join the Ruby engines index, so --engine index and the baked page rank identically. Empty strings are baked too: an undefined getter throws client-side.



65
66
67
68
69
70
71
72
73
74
75
# File 'lib/okf/render/graph.rb', line 65

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 },
    sources: folder.concepts.each_with_object({}) do |concept, map|
      map[concept.id] = OKF::Bundle::Search.field_texts(concept)["sources"]
    end
  }
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

#renderObject



111
112
113
# File 'lib/okf/render/graph.rb', line 111

def render
  ERB.new(File.read(TEMPLATE, encoding: "UTF-8")).result(binding)
end