Class: OKF::Concept

Inherits:
Object
  • Object
show all
Defined in:
lib/okf/concept.rb,
lib/okf/concept/file.rb

Defined Under Namespace

Classes: File

Constant Summary collapse

RESERVED_FILENAMES =

Reserved filenames (spec §3.1): defined at any level of the hierarchy and never concept documents. The single source of truth for "concept vs reserved" — OKF::Bundle and OKF::Bundle::Validator ask through Concept.reserved?.

%w[index.md log.md].freeze
CONCEPT_SCOPED_CHECKS =

The lint checks that apply to a single concept out of bundle context. The rest (orphan, backlog, duplicate_title, …) need the whole bundle.

%i[
  stub missing_title missing_description missing_timestamp
  uncited_external self_link unused_reference_def undefined_reference
].freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(path:, frontmatter:, body:) ⇒ Concept

Returns a new instance of Concept.



26
27
28
29
30
# File 'lib/okf/concept.rb', line 26

def initialize(path:, frontmatter:, body:)
  @path = Path.normalize_relative!(path)
  @frontmatter = Markdown::Frontmatter.stringify_keys(frontmatter)
  @body = body.to_s
end

Instance Attribute Details

#bodyObject (readonly)

Returns the value of attribute body.



24
25
26
# File 'lib/okf/concept.rb', line 24

def body
  @body
end

#frontmatterObject (readonly)

Returns the value of attribute frontmatter.



24
25
26
# File 'lib/okf/concept.rb', line 24

def frontmatter
  @frontmatter
end

#pathObject (readonly)

Returns the value of attribute path.



24
25
26
# File 'lib/okf/concept.rb', line 24

def path
  @path
end

Class Method Details

.reserved?(path) ⇒ Boolean

Whether a bundle-relative path names a reserved file rather than a concept. ::File is explicit: OKF::Concept::File (the on-disk handle) shadows Ruby's File inside this namespace.

Returns:

  • (Boolean)


20
21
22
# File 'lib/okf/concept.rb', line 20

def self.reserved?(path)
  RESERVED_FILENAMES.include?(::File.basename(path))
end

Instance Method Details

#citationsObject

Citation link targets under the # Citations section (spec §8), or [].



80
81
82
# File 'lib/okf/concept.rb', line 80

def citations
  Markdown::Citations.targets(body)
end

#descriptionObject



50
51
52
# File 'lib/okf/concept.rb', line 50

def description
  frontmatter["description"]
end

Body links that point outside the bundle — external URLs and mailto:.



85
86
87
# File 'lib/okf/concept.rb', line 85

def external_links
  links.select { |raw| raw.match?(Markdown::Links::SCHEME) || raw.start_with?("mailto:") }
end

#idObject

Stable node identity. A concept may pin an explicit id in its frontmatter (any scalar; blank is ignored); otherwise it is the bundle-relative path with the .md suffix stripped — i.e. "folder/filename". Because cross-links are file paths, OKF::Bundle maps a resolved link path back to the concept there and uses its id, so a custom id still resolves edges correctly.



37
38
39
40
# File 'lib/okf/concept.rb', line 37

def id
  explicit = frontmatter["id"].to_s.strip
  explicit.empty? ? path.sub(/\.md\z/, "") : explicit
end

Raw markdown cross-link targets in the body, in document order (spec §5).



75
76
77
# File 'lib/okf/concept.rb', line 75

def links
  Markdown::Links.extract(body)
end

#lint(**options) ⇒ Object

Lint this concept in isolation: the concept-scoped checks only (a lone concept has no bundle to judge reachability, backlog, or duplicate titles).



97
98
99
# File 'lib/okf/concept.rb', line 97

def lint(**options)
  Bundle.new(concepts: [ self ]).lint(only: CONCEPT_SCOPED_CHECKS, **options)
end

#reserved?Boolean

Returns:

  • (Boolean)


68
69
70
# File 'lib/okf/concept.rb', line 68

def reserved?
  self.class.reserved?(path)
end

#resourceObject

Canonical URI of the underlying asset (spec §4.1), when the concept is bound to one. Absent for concepts describing purely abstract ideas.



56
57
58
# File 'lib/okf/concept.rb', line 56

def resource
  frontmatter["resource"]
end

#tagsObject



60
61
62
# File 'lib/okf/concept.rb', line 60

def tags
  frontmatter["tags"]
end

#timestampObject



64
65
66
# File 'lib/okf/concept.rb', line 64

def timestamp
  frontmatter["timestamp"]
end

#titleObject



46
47
48
# File 'lib/okf/concept.rb', line 46

def title
  frontmatter["title"]
end

#to_markdownObject

Serialize back to a markdown document (frontmatter + body) — the inverse of Markdown::Frontmatter.parse.



91
92
93
# File 'lib/okf/concept.rb', line 91

def to_markdown
  Markdown::Frontmatter.dump(frontmatter, body)
end

#typeObject



42
43
44
# File 'lib/okf/concept.rb', line 42

def type
  frontmatter["type"]
end