class Asciidoctor::IncludeExt::IncludeProcessor

Asciidoctor preprocessor for processing `include::<target>[]` directives in the source document.

@see asciidoctor.org/docs/user-manual/#include-directive

Attributes

logger[R]

Public Class Methods

new(selectors: [LinenoLinesSelector, TagLinesSelector], logger: Logging.default_logger, **) click to toggle source

@param selectors [Array<Class>] an array of selectors that can filter

specified portions of the document to include
(see <http://asciidoctor.org/docs/user-manual#include-partial>).

@param logger [Logger] the logger to use for logging warning and errors

from this object and selectors.
Calls superclass method
# File lib/asciidoctor/include_ext/include_processor.rb, line 25
def initialize(selectors: [LinenoLinesSelector, TagLinesSelector],
               logger: Logging.default_logger, **)
  super
  @selectors = selectors.dup.freeze
  @logger = logger
end

Public Instance Methods

process(_, reader, target, attributes) click to toggle source

@param reader [Asciidoctor::Reader] @param target [String] name of the source file to include as specified

in the target slot of the `include::[]` directive.

@param attributes [Hash<String, String>] parsed attributes of the

`include::[]` directive.
# File lib/asciidoctor/include_ext/include_processor.rb, line 37
def process(_, reader, target, attributes)
  unless include_allowed? target, reader
    reader.unshift_line("link:#{target}[]")
    return
  end

  if (max_depth = reader.exceeded_max_depth?)
    logger.error "#{reader.line_info}: maximum include depth of #{max_depth} exceeded"
    return
  end

  unless (path = resolve_target_path(target, reader))
    if attributes.key? 'optional-option'
      reader.shift
    else
      logger.error "#{reader.line_info}: include target not found: #{target}"
      unresolved_include!(target, reader)
    end
    return
  end

  selector = lines_selector_for(target, attributes)
  begin
    lines = read_lines(path, selector)
  rescue => e  # rubocop:disable RescueWithoutErrorClass
    logger.error "#{reader.line_info}: failed to read include file: #{path}: #{e}"
    unresolved_include!(target, reader)
    return
  end

  if selector && selector.respond_to?(:first_included_lineno)
    incl_offset = selector.first_included_lineno
  end

  unless lines.empty?
    reader.push_include(lines, path, target, incl_offset || 1, attributes)
  end
end

Protected Instance Methods

include_allowed?(target, reader) click to toggle source

@param target (see process) @param reader (see process) @return [Boolean] `true` if it's allowed to include the target,

`false` otherwise.
# File lib/asciidoctor/include_ext/include_processor.rb, line 84
def include_allowed?(target, reader)
  doc = reader.document

  return false if doc.safe >= ::Asciidoctor::SafeMode::SECURE
  return false if doc.attributes.fetch('max-include-depth', 64).to_i < 1
  return false if target_uri?(target) && !doc.attributes.key?('allow-uri-read')
  true
end
lines_selector_for(target, attributes) click to toggle source

Finds and initializes a lines selector that can handle the specified include.

@param target (see process) @param attributes (see process) @return [#to_proc, nil] an instance of lines selector, or `nil` if not found.

# File lib/asciidoctor/include_ext/include_processor.rb, line 127
def lines_selector_for(target, attributes)
  if (klass = @selectors.find { |s| s.handles? target, attributes })
    klass.new(target, attributes, logger: logger)
  end
end
read_lines(filename, selector) click to toggle source

Reads the specified file as individual lines, filters them using the selector (if provided) and returns those lines in an array.

@param filename [String] path of the file to be read. @param selector [#to_proc, nil] predicate to filter lines that should be

included in the output. It must accept two arguments: line and
the line number. If `nil` is given, all lines are passed.

@return [Array<String>] an array of read lines.

# File lib/asciidoctor/include_ext/include_processor.rb, line 114
def read_lines(filename, selector)
  if selector
    IO.foreach(filename).select.with_index(1, &selector)
  else
    open(filename, &:read)
  end
end
resolve_target_path(target, reader) click to toggle source

@param target (see process) @param reader (see process) @return [String, nil] file path or URI of the target, or `nil` if not found.

# File lib/asciidoctor/include_ext/include_processor.rb, line 96
def resolve_target_path(target, reader)
  return target if target_uri? target

  # Include file is resolved relative to dir of the current include,
  # or base_dir if within original docfile.
  path = reader.document.normalize_system_path(target, reader.dir, nil,
                                               target_name: 'include file')
  path if ::File.file?(path)
end
unresolved_include!(target, reader) click to toggle source

Replaces the include directive in ouput with a notice that it has not been resolved.

@param target (see process) @param reader (see process)

# File lib/asciidoctor/include_ext/include_processor.rb, line 138
def unresolved_include!(target, reader)
  reader.unshift_line("Unresolved directive in #{reader.path} - include::#{target}[]")
end

Private Instance Methods

target_uri?(target) click to toggle source

@param target (see process) @return [Boolean] `true` if the target is an URI, `false` otherwise.

# File lib/asciidoctor/include_ext/include_processor.rb, line 146
def target_uri?(target)
  ::Asciidoctor::Helpers.uriish?(target)
end