Class: Woods::Obsidian::NoteBuilder

Inherits:
Object
  • Object
show all
Defined in:
lib/woods/obsidian/note_builder.rb

Overview

Renders one extracted unit into a single Obsidian note: flat YAML frontmatter (the machine-queryable surface) followed by a Markdown body with wikilinks (the human surface).

Two invariants from the design:

  • Edges come from the graph, not the unit. The authoritative "Depends on" / "Used by" sections are rendered from +depends_on+/+used_by+ passed in by the exporter (derived from the dependency graph). The "Associations" section is an explicit, human-only nicety read from unit metadata and is not part of the machine edge contract.
  • Frontmatter is flat. Obsidian's Properties UI cannot represent nested objects, so only scalars and flat lists go in YAML; structured edges live in the _woods/ sidecar. Frontmatter is emitted via Psych so any identifier (quotes, colons, hashes, newlines) is escaped correctly.

Constant Summary collapse

USED_BY_DISPLAY_CAP =

Cap on how many "Used by" links a single note renders before summarizing, so a hub with hundreds of dependents doesn't dominate the note or graph.

50
ASSOCIATION_ORDER =
%w[belongs_to has_one has_many has_and_belongs_to_many].freeze

Instance Method Summary collapse

Constructor Details

#initialize(name_mapper:, nodes:, pagerank: {}, analysis: nil, include_source: false, scanner: nil) ⇒ NoteBuilder

Returns a new instance of NoteBuilder.

Parameters:

  • name_mapper (NameMapper)

    resolves ids to wikilinks/paths

  • nodes (Hash)

    graph nodes ({ id => { 'type' => , ... } }) for edge-endpoint types

  • pagerank (Hash{String=>Float}) (defaults to: {})

    persisted pagerank scores

  • analysis (Hash, nil) (defaults to: nil)

    parsed graph_analysis.json (hubs/cycles/orphans/bridges) or nil

  • include_source (Boolean) (defaults to: false)

    embed scrubbed source code

  • scanner (#scan, nil) (defaults to: nil)

    credential scanner (required when include_source)



36
37
38
39
40
41
42
43
44
45
46
# File 'lib/woods/obsidian/note_builder.rb', line 36

def initialize(name_mapper:, nodes:, pagerank: {}, analysis: nil, include_source: false, scanner: nil)
  @mapper = name_mapper
  @nodes = nodes || {}
  @pagerank = pagerank || {}
  @include_source = include_source
  @scanner = scanner
  @hubs = identifier_set(analysis, 'hubs')
  @bridges = identifier_set(analysis, 'bridges')
  @orphans = plain_set(analysis, 'orphans')
  @cycle_members = cycle_member_set(analysis)
end

Instance Method Details

#build(id:, unit:, depends_on:, used_by:) ⇒ String

Returns the rendered note. A failed source scrub omits the source section (the note is still written); build never returns nil.

Parameters:

  • id (String)

    bare unit identifier (graph node key)

  • unit (Hash)

    parsed unit JSON (used only for body facts)

  • depends_on (Array<Hash>)

    [{ target:, via: }] filtered to the emitted set

  • used_by (Array<String>)

    unique dependent ids filtered to the emitted set

Returns:

  • (String)

    the rendered note. A failed source scrub omits the source section (the note is still written); build never returns nil.



54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/woods/obsidian/note_builder.rb', line 54

def build(id:, unit:, depends_on:, used_by:)
  sections = [
    frontmatter(id, unit, depends_on, used_by),
    heading(id, unit),
    meta_line(unit),
    callout(id, used_by),
    depends_section(depends_on),
    used_by_section(used_by),
    associations_section(unit),
    schema_section(unit),
    source_section(unit)
  ].compact

  "#{sections.join("\n\n")}\n"
end