Class: Woods::Obsidian::NameMapper

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

Overview

Centralizes the identifier -> note-filename mapping for an Obsidian vault.

This is the single source of truth for three things that must always agree: the filename a note is written to, the wikilink target that points at it, and the inverse path -> id map shipped in the machine sidecar. Building them in one place means a link can never point at a filename the writer didn't produce.

Rules:

  • Sanitize :: to __ and every other non [A-Za-z0-9_-] char to _, so the basename is always safe both as a filename and as a wikilink target (Obsidian's # | ^ : %% link-breaking chars are all removed).
  • Collisions are resolved per folder, case-insensitively (macOS/Windows fold case): the colliding note gets a short content hash appended. Two identical basenames in different type folders are distinct paths and need no hash.
  • The MOC basename _index is reserved in every folder so a (pathological) unit named _index can never overwrite a folder's index note.
  • Filenames are capped to 255 bytes, truncated on a UTF-8 char boundary.
  • The alias (the human-readable display half of a wikilink) keeps the original identifier, with only the link-structural chars [ ] | replaced.

Determinism: ids are processed in sorted order, so the same input always produces the same collision-hash assignments.

Constant Summary collapse

MAX_FILENAME_BYTES =
255
EXTENSION =
'.md'
HASH_SUFFIX_BYTES =

"-" + 8 hex chars

9
RESERVED_BASENAMES =
%w[_index].freeze

Instance Method Summary collapse

Constructor Details

#initialize(id_to_dir) ⇒ NameMapper

Returns a new instance of NameMapper.

Parameters:

  • id_to_dir (Hash{String=>String})

    map of unit identifier -> type folder name (e.g. "User" => "models")



40
41
42
43
44
# File 'lib/woods/obsidian/name_mapper.rb', line 40

def initialize(id_to_dir)
  @map = {}
  @paths = {}
  build(id_to_dir)
end

Instance Method Details

#idsArray<String>

Returns exported identifiers, sorted.

Returns:

  • (Array<String>)

    exported identifiers, sorted



47
48
49
# File 'lib/woods/obsidian/name_mapper.rb', line 47

def ids
  @map.keys
end

#known?(id) ⇒ Boolean

Returns whether this id has an emitted note.

Parameters:

  • id (String)

Returns:

  • (Boolean)

    whether this id has an emitted note



53
54
55
# File 'lib/woods/obsidian/name_mapper.rb', line 53

def known?(id)
  @map.key?(id)
end

#path_for(id) ⇒ String?

Returns vault-relative note path ("models/User.md").

Parameters:

  • id (String)

Returns:

  • (String, nil)

    vault-relative note path ("models/User.md")



59
60
61
# File 'lib/woods/obsidian/name_mapper.rb', line 59

def path_for(id)
  @map[id]&.fetch(:path)
end

#paths_to_idsHash{String=>String}

Returns inverse path -> id map (sorted).

Returns:

  • (Hash{String=>String})

    inverse path -> id map (sorted)



79
80
81
# File 'lib/woods/obsidian/name_mapper.rb', line 79

def paths_to_ids
  @paths
end

#target_for(id) ⇒ String?

Returns wikilink target with no extension ("models/User").

Parameters:

  • id (String)

Returns:

  • (String, nil)

    wikilink target with no extension ("models/User")



65
66
67
# File 'lib/woods/obsidian/name_mapper.rb', line 65

def target_for(id)
  @map[id]&.fetch(:target)
end

Returns full wikilink ("[[models/User|User]]") or nil if unknown.

Parameters:

  • id (String)

Returns:

  • (String, nil)

    full wikilink ("[[models/User|User]]") or nil if unknown



71
72
73
74
75
76
# File 'lib/woods/obsidian/name_mapper.rb', line 71

def wikilink(id)
  entry = @map[id]
  return nil unless entry

  "[[#{entry[:target]}|#{entry[:alias]}]]"
end