Class: Markdowndocs::Documentation
- Inherits:
-
Object
- Object
- Markdowndocs::Documentation
- Defined in:
- app/models/markdowndocs/documentation.rb
Overview
Documentation PORO (Plain Old Ruby Object) Represents markdown documentation files from a configurable directory. Handles metadata extraction, frontmatter parsing, and category associations.
Instance Attribute Summary collapse
-
#category ⇒ Object
readonly
Returns the value of attribute category.
-
#description ⇒ Object
readonly
Returns the value of attribute description.
-
#file_path ⇒ Object
readonly
Returns the value of attribute file_path.
-
#keywords ⇒ Object
readonly
Returns the value of attribute keywords.
-
#slug ⇒ Object
readonly
Returns the value of attribute slug.
-
#title ⇒ Object
readonly
Returns the value of attribute title.
Class Method Summary collapse
- .all ⇒ Object
- .by_category(category) ⇒ Object
-
.find_by_slug(slug, mode: nil) ⇒ Object
When ‘mode:` is given (e.g. “guide” / “technical”), returns nil if the resolved doc’s ‘audience:` frontmatter excludes that mode.
-
.grouped_by_category(mode: nil) ⇒ Object
When ‘mode:` is given, filters out docs whose `audience:` excludes that mode AND drops categories that end up empty (so the index sidebar doesn’t render headers with no children).
Instance Method Summary collapse
-
#audience ⇒ Object
The audience(s) this doc is written for, declared via ‘audience:` frontmatter.
- #available_modes ⇒ Object
- #cache_key ⇒ Object
-
#code_content ⇒ Object
Returns text extracted from fenced code blocks for search indexing.
- #content ⇒ Object
- #default_mode ⇒ Object
-
#initialize(file_path) ⇒ Documentation
constructor
A new instance of Documentation.
- #mtime ⇒ Object
-
#plain_text_content ⇒ Object
Returns content stripped of frontmatter, markdown syntax, and HTML tags for use in search indexing.
- #supports_mode?(mode) ⇒ Boolean
-
#visible_to?(mode) ⇒ Boolean
Whether this doc should be surfaced to a viewer in the given mode.
Constructor Details
#initialize(file_path) ⇒ Documentation
Returns a new instance of Documentation.
10 11 12 13 14 15 |
# File 'app/models/markdowndocs/documentation.rb', line 10 def initialize(file_path) @file_path = file_path @slug = derive_slug @category = assign_category end |
Instance Attribute Details
#category ⇒ Object (readonly)
Returns the value of attribute category.
8 9 10 |
# File 'app/models/markdowndocs/documentation.rb', line 8 def category @category end |
#description ⇒ Object (readonly)
Returns the value of attribute description.
8 9 10 |
# File 'app/models/markdowndocs/documentation.rb', line 8 def description @description end |
#file_path ⇒ Object (readonly)
Returns the value of attribute file_path.
8 9 10 |
# File 'app/models/markdowndocs/documentation.rb', line 8 def file_path @file_path end |
#keywords ⇒ Object (readonly)
Returns the value of attribute keywords.
8 9 10 |
# File 'app/models/markdowndocs/documentation.rb', line 8 def keywords @keywords end |
#slug ⇒ Object (readonly)
Returns the value of attribute slug.
8 9 10 |
# File 'app/models/markdowndocs/documentation.rb', line 8 def slug @slug end |
#title ⇒ Object (readonly)
Returns the value of attribute title.
8 9 10 |
# File 'app/models/markdowndocs/documentation.rb', line 8 def title @title end |
Class Method Details
.all ⇒ Object
17 18 19 20 21 22 23 24 |
# File 'app/models/markdowndocs/documentation.rb', line 17 def self.all docs_path = Markdowndocs.config.resolved_docs_path return [] unless docs_path.exist? Dir.glob(docs_path.join("*.md")).map do |file| new(Pathname.new(file)) end.sort_by(&:slug) end |
.by_category(category) ⇒ Object
46 47 48 |
# File 'app/models/markdowndocs/documentation.rb', line 46 def self.by_category(category) all.select { |doc| doc.category == category } end |
.find_by_slug(slug, mode: nil) ⇒ Object
When ‘mode:` is given (e.g. “guide” / “technical”), returns nil if the resolved doc’s ‘audience:` frontmatter excludes that mode. Docs without an explicit `audience:` key default to “visible in all modes” — backward compatible with pre-0.6 docs.
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
# File 'app/models/markdowndocs/documentation.rb', line 30 def self.find_by_slug(slug, mode: nil) return nil if slug.blank? return nil if slug.include?("..") || slug.include?("/") file_path = Markdowndocs.config.resolved_docs_path.join("#{slug}.md") return nil unless file_path.exist? doc = new(file_path) return nil unless doc.visible_to?(mode) doc rescue => e Rails.logger.error("Error finding documentation by slug '#{slug}': #{e.}") nil end |
.grouped_by_category(mode: nil) ⇒ Object
When ‘mode:` is given, filters out docs whose `audience:` excludes that mode AND drops categories that end up empty (so the index sidebar doesn’t render headers with no children).
53 54 55 56 57 58 |
# File 'app/models/markdowndocs/documentation.rb', line 53 def self.grouped_by_category(mode: nil) Markdowndocs.config.categories.each_with_object({}) do |(category, slugs), hash| docs = slugs.map { |slug| find_by_slug(slug, mode: mode) }.compact hash[category] = docs unless docs.empty? end end |
Instance Method Details
#audience ⇒ Object
The audience(s) this doc is written for, declared via ‘audience:` frontmatter. Accepts a single string or an array; both are coerced to an Array<String>. When the frontmatter key is missing, defaults to all configured modes — a doc with no audience declaration is visible in every mode (backward compat with pre-0.6 docs).
102 103 104 105 106 107 108 109 110 111 112 113 |
# File 'app/models/markdowndocs/documentation.rb', line 102 def audience @audience ||= begin parsed = parse_frontmatter raw = parsed[:frontmatter]["audience"] case raw when Array then raw.map(&:to_s) when String then [ raw ] when nil then Markdowndocs.config.modes.dup else Markdowndocs.config.modes.dup end end end |
#available_modes ⇒ Object
77 78 79 80 81 82 83 |
# File 'app/models/markdowndocs/documentation.rb', line 77 def available_modes @available_modes ||= begin parsed = parse_frontmatter modes = parsed[:frontmatter]["modes"] modes.is_a?(Array) ? modes.map(&:to_s) : Markdowndocs.config.modes.dup end end |
#cache_key ⇒ Object
67 68 69 |
# File 'app/models/markdowndocs/documentation.rb', line 67 def cache_key "#{slug}-#{mtime.to_i}" end |
#code_content ⇒ Object
Returns text extracted from fenced code blocks for search indexing.
139 140 141 142 143 |
# File 'app/models/markdowndocs/documentation.rb', line 139 def code_content parsed = parse_frontmatter blocks = parsed[:markdown].scan(/```\w*\n([\s\S]*?)```/) blocks.flatten.join(" ").gsub(/\s+/, " ").strip end |
#content ⇒ Object
60 61 62 63 64 65 |
# File 'app/models/markdowndocs/documentation.rb', line 60 def content @content ||= file_path.read rescue => e Rails.logger.error("Error reading documentation file '#{file_path}': #{e.}") "" end |
#default_mode ⇒ Object
85 86 87 88 89 90 91 |
# File 'app/models/markdowndocs/documentation.rb', line 85 def default_mode @default_mode ||= begin parsed = parse_frontmatter mode = parsed[:frontmatter]["default_mode"] mode.present? ? mode.to_s : Markdowndocs.config.default_mode end end |
#mtime ⇒ Object
71 72 73 74 75 |
# File 'app/models/markdowndocs/documentation.rb', line 71 def mtime @mtime ||= file_path.mtime rescue Time.current end |
#plain_text_content ⇒ Object
Returns content stripped of frontmatter, markdown syntax, and HTML tags for use in search indexing.
125 126 127 128 129 130 131 132 133 134 135 136 |
# File 'app/models/markdowndocs/documentation.rb', line 125 def plain_text_content parsed = parse_frontmatter text = parsed[:markdown] text = text.gsub(/^#+\s*/, "") # headings text = text.gsub(/\[([^\]]+)\]\([^)]+\)/, '\1') # links text = text.gsub(/[*_~`]/, "") # emphasis markers text = text.gsub(/```[\s\S]*?```/, "") # fenced code blocks text = text.gsub(/<[^>]+>/, "") # HTML tags text = text.gsub(/^\s*[-*+]\s/, "") # list markers text = text.gsub(/\n{2,}/, "\n") # collapse blank lines text.strip end |
#supports_mode?(mode) ⇒ Boolean
93 94 95 |
# File 'app/models/markdowndocs/documentation.rb', line 93 def supports_mode?(mode) available_modes.include?(mode.to_s) end |
#visible_to?(mode) ⇒ Boolean
Whether this doc should be surfaced to a viewer in the given mode. ‘nil` mode is treated as “no filter” — useful for callers that don’t care about audience (search indexer, admin tools).
118 119 120 121 |
# File 'app/models/markdowndocs/documentation.rb', line 118 def visible_to?(mode) return true if mode.nil? audience.include?(mode.to_s) end |