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.



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 = 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 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

#renderObject



103
104
105
# File 'lib/okf/render/graph.rb', line 103

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