Class: Coradoc::AsciiDoc::Parser::MetadataDetector

Inherits:
Object
  • Object
show all
Defined in:
lib/coradoc/asciidoc/parser/metadata_detector.rb

Overview

Single Responsibility: Detect metadata markers in AsciiDoc input Stateless, no dependencies on other parser rules (Dependency Inversion)

Constant Summary collapse

METADATA_TYPES =

Metadata types (MECE - Mutually Exclusive, Collectively Exhaustive)

{
  block_title: /^\.[^ \n].*/,
  attribute_list: /^\[.+\]$/,
  element_id_double: /^\[\[.+\]\]$/,
  element_id_single: /^\[#.+\]$/,
  block_delimiter: /^(\*{4,}|={4,}|_{4,}|\+{4,}|-{4,}|--)/
}.freeze

Class Method Summary collapse

Class Method Details

.analyze_block_structure(metadata) ⇒ Hash?

Analyze block structure from metadata (MECE patterns)

Parameters:

  • metadata (Array<Hash>)

    Metadata from scan()

Returns:

  • (Hash, nil)

    title:, attributes:, delimiter: or nil



131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/coradoc/asciidoc/parser/metadata_detector.rb', line 131

def self.analyze_block_structure()
  return nil if .empty?

  # Find block delimiter (required for block)
  delim_meta = .find { |m| m[:type] == :block_delimiter }
  return nil unless delim_meta

  # Find title and attributes
  title_meta = .find { |m| m[:type] == :block_title }
  attr_meta = .find { |m| m[:type] == :attribute_list }

  # Determine MECE pattern
  pattern = if title_meta && attr_meta
              :title_attr_delim
            elsif title_meta
              :title_delim
            elsif attr_meta
              :attr_delim
            else
              :plain_delim
            end

  {
    pattern: pattern,
    title: title_meta ? detect_block_title(title_meta[:content]) : nil,
    attributes: attr_meta ? detect_attribute_list(attr_meta[:content]) : nil,
    delimiter: detect_block_delimiter(delim_meta[:content]),
    delimiter_line: delim_meta[:line]
  }
end

.detect_attribute_list(line) ⇒ Hash?

Detect attribute list (Single Responsibility)

Parameters:

  • line (String)

    Line to check

Returns:

  • (Hash, nil)

    attributes: or nil



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/coradoc/asciidoc/parser/metadata_detector.rb', line 72

def self.detect_attribute_list(line)
  return nil unless /^\[.+\]$/.match?(line)

  content = line.strip
  # Parse basic attribute structure
  inner = content[1...-1] # Remove [ and ]

  # Simple attribute parsing (positional)
  attributes = inner.split(',').map(&:strip)

  {
    content: content,
    attributes: attributes
  }
end

.detect_block_delimiter(line) ⇒ Hash?

Detect block delimiter (Single Responsibility)

Parameters:

  • line (String)

    Line to check

Returns:

  • (Hash, nil)

    count:, type: or nil



104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/coradoc/asciidoc/parser/metadata_detector.rb', line 104

def self.detect_block_delimiter(line)
  return nil unless line =~ /^(\*{4,}|={4,}|_{4,}|\+{4,}|-{4,}|--)$/

  delimiter = ::Regexp.last_match(1)
  char = delimiter[0]
  count = delimiter.length

  # Map to block type
  type = case char
         when '*' then :sidebar
         when '=' then :example
         when '_' then :quote
         when '+' then :pass
         when '-' then count == 2 ? :open : :source
         end

  {
    char: char,
    count: count,
    type: type,
    delimiter: delimiter
  }
end

.detect_block_title(line) ⇒ Hash?

Detect block title (Single Responsibility)

Parameters:

  • line (String)

    Line to check

Returns:

  • (Hash, nil)

    text: or nil



61
62
63
64
65
66
67
# File 'lib/coradoc/asciidoc/parser/metadata_detector.rb', line 61

def self.detect_block_title(line)
  return nil unless /^\.[^ \n]/.match?(line)

  # Extract title text (everything after '.')
  text = line.sub(/^\./, '').strip
  { text: text }
end

.detect_element_id(line) ⇒ Hash?

Detect element ID (Single Responsibility)

Parameters:

  • line (String)

    Line to check

Returns:

  • (Hash, nil)

    style: or nil



91
92
93
94
95
96
97
98
99
# File 'lib/coradoc/asciidoc/parser/metadata_detector.rb', line 91

def self.detect_element_id(line)
  # Double bracket style: [[id]]
  return { id: ::Regexp.last_match(1), style: :double } if line =~ /^\[\[(.+)\]\]$/

  # Single bracket style: [#id]
  return { id: ::Regexp.last_match(1), style: :single } if line =~ /^\[#(.+)\]$/

  nil
end

.scan(input, max_lines: 20) ⇒ Array<Hash>

Scan input and return metadata markers with positions

Parameters:

  • input (String)

    The input text to scan

  • max_lines (Integer) (defaults to: 20)

    Maximum lines to scan ahead

Returns:

  • (Array<Hash>)

    Array of content:, line:, position:



22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/coradoc/asciidoc/parser/metadata_detector.rb', line 22

def self.scan(input, max_lines: 20)
  lines = input.lines
   = []
  found_delimiter = false

  lines.first(max_lines).each_with_index do |line, index|
    # Skip blank lines - they are not metadata
    next if line.strip.empty?

    # Stop scanning after finding a delimiter
    # (delimiter marks the start of block content)
    break if found_delimiter

    # Calculate absolute position in input
    position = lines[0...index].sum(&:length)

    # Detect each metadata type (MECE)
    METADATA_TYPES.each do |type, pattern|
      next unless line.strip&.match?(pattern)

       << {
        type: type,
        content: line.strip,
        line: index,
        position: position,
        length: line.length
      }
      # Mark if we found a delimiter
      found_delimiter = true if type == :block_delimiter
      break # Each line has at most one metadata type
    end
  end

  
end