Class: Asciidoctor::InterdocReftext::Resolver

Inherits:
Object
  • Object
show all
Defined in:
lib/asciidoctor/interdoc_reftext/resolver.rb

Overview

Resolver of inter-document cross reference texts.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(document, asciidoc_exts: ['.adoc', '.asciidoc', '.ad'], logger: nil, raise_exceptions: true) ⇒ Resolver

Returns a new instance of Resolver.

Parameters:

  • document (Asciidoctor::Document)

    the document associated with this resolver.

  • asciidoc_exts (Array<String>) (defaults to: ['.adoc', '.asciidoc', '.ad'])

    AsciiDoc file extensions (e.g. .adoc).

  • logger (Logger, nil) (defaults to: nil)

    the logger to use for logging warning and errors. Defaults to Asciidoctor::LoggerManager.logger if using Asciidoctor 1.5.7+, or Logger.new(STDERR) otherwise.

  • raise_exceptions (Boolean) (defaults to: true)

    whether to raise exceptions, or just log them.



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/asciidoctor/interdoc_reftext/resolver.rb', line 16

def initialize(document,
               asciidoc_exts: ['.adoc', '.asciidoc', '.ad'],
               logger: nil,
               raise_exceptions: true)

  logger ||= if defined? ::Asciidoctor::LoggerManager
    ::Asciidoctor::LoggerManager.logger
  else
    ::Logger.new(STDERR)
  end

  @document = document
  @asciidoc_exts = asciidoc_exts.dup.freeze
  @logger = logger
  @raise_exceptions = raise_exceptions
  @cache = {}
end

Instance Attribute Details

#asciidoc_extsArray<String> (readonly, protected)

Returns AsciiDoc file extensions (e.g. .adoc).

Returns:

  • (Array<String>)

    AsciiDoc file extensions (e.g. .adoc).



66
67
68
# File 'lib/asciidoctor/interdoc_reftext/resolver.rb', line 66

def asciidoc_exts
  @asciidoc_exts
end

#cacheHash<String, String> (readonly, protected)

Returns a cache of resolved reftexts.

Returns:

  • (Hash<String, String>)

    a cache of resolved reftexts.



69
70
71
# File 'lib/asciidoctor/interdoc_reftext/resolver.rb', line 69

def cache
  @cache
end

#documentAsciidoctor::Document (readonly, protected)

Returns the document associated with this resolver.

Returns:

  • (Asciidoctor::Document)

    the document associated with this resolver.



72
73
74
# File 'lib/asciidoctor/interdoc_reftext/resolver.rb', line 72

def document
  @document
end

#loggerLogger (readonly, protected)

Returns the logger to use for logging warning and errors.

Returns:

  • (Logger)

    the logger to use for logging warning and errors.



75
76
77
# File 'lib/asciidoctor/interdoc_reftext/resolver.rb', line 75

def logger
  @logger
end

#raise_exceptionsBoolean (readonly, protected)

Returns whether to raise exceptions, or just log them.

Returns:

  • (Boolean)

    whether to raise exceptions, or just log them.



78
79
80
# File 'lib/asciidoctor/interdoc_reftext/resolver.rb', line 78

def raise_exceptions
  @raise_exceptions
end

Instance Method Details

#asciidoc_load(input) ⇒ Asciidoctor::Document (protected)

Returns a parsed document.

Parameters:

  • input (Enumerable<String>, String)

    lines of the AsciiDoc document to load.

Returns:

  • (Asciidoctor::Document)

    a parsed document.



128
129
130
# File 'lib/asciidoctor/interdoc_reftext/resolver.rb', line 128

def asciidoc_load(input)
  ::Asciidoctor.load(input.to_a, @document.options)
end

#parse_reftext(input, fragment = nil) ⇒ String? (protected)

Parameters:

  • input (Enumerable<String>)

    lines of the AsciiDoc document.

  • fragment (String, nil) (defaults to: nil)

    part of the target after #.

Returns:

  • (String, nil)


107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/asciidoctor/interdoc_reftext/resolver.rb', line 107

def parse_reftext(input, fragment = nil)
  unless fragment
    # Document title is typically defined at top of the document,
    # so we try to parse just the first 10 lines to save resources.
    # If document title is not here, we fallback to parsing whole document.
    title = asciidoc_load(input.take(10)).doctitle
    return title if title
  end

  doc = asciidoc_load(input)

  if fragment
    ref = doc.catalog[:refs][fragment]
    ref.xreftext if ref
  else
    doc.doctitle
  end
end

#read_file(path) {|Enumerable<String>| ... } ⇒ void (protected)

Parameters:

  • path (String)

    path of the file to read.

Yields:

  • (Enumerable<String>)

    gives lines of the file.



98
99
100
101
102
# File 'lib/asciidoctor/interdoc_reftext/resolver.rb', line 98

def read_file(path)
  ::File.open(path) do |f|
    yield f.each_line
  end
end

#resolve_reftext(refid) ⇒ String? Also known as: call

Returns reference text, or nil if not found.

Parameters:

  • refid (String)

    the target without a file extension, optionally with a fragment (e.g. intro, intro#about).

Returns:

  • (String, nil)

    reference text, or nil if not found.

Raises:

  • ArgumentError if the refid is empty or starts with # and raise_exceptions is true.



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/asciidoctor/interdoc_reftext/resolver.rb', line 39

def resolve_reftext(refid)
  if refid.empty? || refid.start_with?('#')
    msg = "interdoc-reftext: refid must not be empty or start with '#', but given: '#{refid}'"
    raise ArgumentError, msg if @raise_exceptions
    @logger.error msg
    return nil
  end

  path, fragment = refid.split('#', 2)
  path = resolve_target_path(path) or return nil

  @cache["#{path}##{fragment}".freeze] ||= begin
    read_file(path) do |lines|
      parse_reftext(lines, fragment)
    end
  rescue => e  # rubocop: disable RescueWithoutErrorClass
    raise if @raise_exceptions
    @logger.error "interdoc-reftext: #{e}"
    nil
  end
end

#resolve_target_path(target_path) ⇒ String? (protected)

Returns file path of the target_path, or nil if not found.

Parameters:

  • target_path (String)

    the target path without a file extension.

Returns:

  • (String, nil)

    file path of the target_path, or nil if not found.



82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/asciidoctor/interdoc_reftext/resolver.rb', line 82

def resolve_target_path(target_path)
  # Include file is resolved relative to dir of the current include,
  # or base_dir if within original docfile.
  path = @document.normalize_system_path(target_path, @document.reader.dir,
                                         nil, target_name: 'xref target')
  return nil unless path

  @asciidoc_exts.each do |extname|
    filename = path + extname
    return filename if ::File.file? filename
  end
  nil
end