module Asciidoctor

Methods for parsing Asciidoc input files and rendering documentsusing eRuby templates.

Asciidoc documents comprise a header followed by zero or more sections.Sections are composed of blocks of content. For example:

= Doc Title

== Section 1

This is a paragraph block in the first section.

== Section 2

This section has a paragraph block and an olist block.

. Item 1
. Item 2

Examples:

Use built-in templates:

lines = File.readlines("your_file.asc")
doc = Asciidoctor::Document.new(lines)
html = doc.render
File.open("your_file.html", "w+") do |file|
  file.puts html
end

Use custom (Tilt-supported) templates:

lines = File.readlines("your_file.asc")
doc = Asciidoctor::Document.new(lines, :template_dir => 'templates')
html = doc.render
File.open("your_file.html", "w+") do |file|
  file.puts html
end

NOTE once we decide to make DocBook 5 the default, we'll load the files the other way around

Constants

ADMONITION_STYLES
ASCIIDOC_EXTENSIONS

Set of file extensions recognized as AsciiDoc documents (stored as a truth hash)

BACKEND_ALIASES

Pointers to the preferred version for a given backend.

BLANK_LINE_PATTERN

NOTE allows for empty space in line as it could be left by the template engine

BREAK_LINES
COMPLIANCE

Flags to control compliance with the behavior of AsciiDoc

DEFAULT_BACKEND

The backend determines the format of the rendered output, default to html5

DEFAULT_DOCTYPE

The default document typeCan influence markup generated by render templates

DEFAULT_EXTENSIONS

Default extensions for the respective base backends

DEFAULT_PAGE_WIDTHS

Default page widths for calculating absolute widths

DEFAULT_STYLESHEET_KEYS
DEFAULT_STYLESHEET_NAME
DELIMITED_BLOCKS
DELIMITED_BLOCK_LEADERS
EOL

The endline character to use when rendering output

FLEXIBLE_ATTRIBUTES

attributes which be changed within the content of the document (but notheader) because it has semantic meaning; ex. numbered

FORCE_ENCODING

Flag to indicate whether encoding of external strings needs to be forced to UTF-8_All_ input data must be force encoded to UTF-8 if Encoding.default_external is not UTF-8Address failures performing string operations that are reported as “invalid byte sequence in US-ASCII” Ruby 1.8 doesn't seem to experience this problem (perhaps because it isn't validating the encodings)

FORCE_UNICODE_LINE_LENGTH

Flag to indicate that line length should be calculated using a unicode mode hint

INTRINSICS
LINE_BREAK
LINE_FEED_ENTITY
LIST_CONTINUATION
NESTABLE_LIST_CONTEXTS

LIST_CONTEXTS = [:ulist, :olist, :dlist, :colist]

ORDERED_LIST_KEYWORDS
ORDERED_LIST_MARKER_PATTERNS
ORDERED_LIST_STYLES

TODO validate use of explicit style name above ordered list (this list is for selecting an implicit style)

PARAGRAPH_STYLES
QUOTE_SUBS

unconstrained quotes:: can appear anywhereconstrained quotes:: must be bordered by non-word charactersNOTE these substituions are processed in the order they appear here andthe order in which they are replaced is important

REGEXP

The following pattern, which appears frequently, captures the contents between square brackets,ignoring escaped closing brackets (closing brackets prefixed with a backslash '' character)

Pattern:(?:|[^]])*?)])Matches:[enclosed text here] or [enclosed [text] here]

REPLACEMENTS

NOTE in Ruby 1.8.7, [^\] does not match start of line,so we need to match it explicitlyorder is significant

ROOT_PATH

The root path of the Asciidoctor gem

SECTION_LEVELS
SPECIAL_CHARS
SPECIAL_CHARS_PATTERN
VERBATIM_STYLES

Public

↑ top

Constants

VERSION

Public Class Methods

load(input, options = {}) click to toggle source

Parse the AsciiDoc source input into an Asciidoctor::Document

Accepts input as an IO (or StringIO), String or String Array object. If theinput is a File, information about the file is stored in attributes on theDocument object.

input

the AsciiDoc source as a IO, String or Array.

options

a String, Array or Hash of options to control processing (default: {})String and Array values are converted into a Hash.See Asciidoctor::Document#initialize for details about options.

returns the Asciidoctor::Document

# File lib/asciidoctor.rb, line 742
def self.load(input, options = {})
  if (monitor = options.fetch(:monitor, false))
    start = Time.now
  end

  attrs = (options[:attributes] ||= {})
  if attrs.is_a?(Hash) || (RUBY_ENGINE == 'jruby' && attrs.is_a?(Java::JavaUtil::Map))
    # all good; placed here as optimization
  elsif attrs.is_a? Array
    attrs = options[:attributes] = attrs.inject({}) do |accum, entry|
      k, v = entry.split '=', 2
      accum[k] = v || ''
      accum
    end
  elsif attrs.is_a? String
    # convert non-escaped spaces into null character, so we split on the
    # correct spaces chars, and restore escaped spaces
    attrs = attrs.gsub(REGEXP[:space_delim], "\\1\00"").gsub(REGEXP[:escaped_space], '\1')

    attrs = options[:attributes] = attrs.split("\00"").inject({}) do |accum, entry|
      k, v = entry.split '=', 2
      accum[k] = v || ''
      accum
    end
  elsif attrs.respond_to?('keys') && attrs.respond_to?('[]')
    # convert it to a Hash as we know it
    original_attrs = attrs
    attrs = options[:attributes] = {}
    original_attrs.keys.each do |key|
      attrs[key] = original_attrs[key]
    end
  else
    raise ArgumentError, "illegal type for attributes option: #{attrs.class.ancestors}"
  end

  lines = nil
  if input.is_a? File
    lines = input.readlines
    input_mtime = input.mtime
    input_path = File.expand_path(input.path)
    # hold off on setting infile and indir until we get a better sense of their purpose
    attrs['docfile'] = input_path
    attrs['docdir'] = File.dirname(input_path)
    attrs['docname'] = File.basename(input_path, File.extname(input_path))
    attrs['docdate'] = input_mtime.strftime('%Y-%m-%d')
    attrs['doctime'] = input_mtime.strftime('%H:%M:%S %Z')
    attrs['docdatetime'] = [attrs['docdate'], attrs['doctime']] * ' '
  elsif input.respond_to?(:readlines)
    input.rewind rescue nil
    lines = input.readlines
  elsif input.is_a?(String)
    lines = input.lines.entries
  elsif input.is_a?(Array)
    lines = input.dup
  else
    raise "Unsupported input type: #{input.class}"
  end

  if monitor
    read_time = Time.now - start
    start = Time.now
  end

  doc = Document.new(lines, options) 
  if monitor
    parse_time = Time.now - start
    monitor[:read] = read_time
    monitor[:parse] = parse_time
    monitor[:load] = read_time + parse_time
  end
  doc
end
load_file(filename, options = {}) click to toggle source

Parse the contents of the AsciiDoc source file into an Asciidoctor::Document

Accepts input as an IO, String or String Array object. If theinput is a File, information about the file is stored inattributes on the Document.

input

the String AsciiDoc source filename

options

a String, Array or Hash of options to control processing (default: {})String and Array values are converted into a Hash.See Asciidoctor::Document#initialize for details about options.

returns the Asciidoctor::Document

# File lib/asciidoctor.rb, line 827
def self.load_file(filename, options = {})
  Asciidoctor.load(File.new(filename), options)
end
render(input, options = {}) click to toggle source

Parse the AsciiDoc source input into an Asciidoctor::Document and render itto the specified backend format

Accepts input as an IO, String or String Array object. If theinput is a File, information about the file is stored inattributes on the Document.

If the :in_place option is true, and the input is a File, the output iswritten to a file adjacent to the input file, having an extension thatcorresponds to the backend format. Otherwise, if the :to_file option isspecified, the file is written to that file. If :to_file is not an absolutepath, it is resolved relative to :to_dir, if given, otherwise theDocument#base_dir. If the target directory does not exist, it will not becreated unless the :mkdirs option is set to true. If the file cannot bewritten because the target directory does not exist, or because it fallsoutside of the Asciidoctor::Document#base_dir in safe mode, an IOError is raised.

If the output is going to be written to a file, the header and footer arerendered unless specified otherwise (writing to a file implies creating astandalone document). Otherwise, the header and footer are not rendered bydefault and the rendered output is returned.

input

the String AsciiDoc source filename

options

a String, Array or Hash of options to control processing (default: {})String and Array values are converted into a Hash.See Asciidoctor::Document#initialize for details about options.

returns the Document object if the rendered result String is written to afile, otherwise the rendered result String

# File lib/asciidoctor.rb, line 860
def self.render(input, options = {})
  in_place = options.delete(:in_place) || false
  to_file = options.delete(:to_file)
  to_dir = options.delete(:to_dir)
  mkdirs = options.delete(:mkdirs) || false
  monitor = options.fetch(:monitor, false)

  write_in_place = in_place && input.is_a?(File)
  write_to_target = to_file || to_dir
  stream_output = !to_file.nil? && to_file.respond_to?(:write)

  if write_in_place && write_to_target
    raise ArgumentError, 'the option :in_place cannot be used with either the :to_dir or :to_file option'
  end

  if !options.has_key?(:header_footer) && (write_in_place || write_to_target)
    options[:header_footer] = true
  end

  doc = Asciidoctor.load(input, options)

  if to_file == '/dev/null'
    return doc
  elsif write_in_place
    to_file = File.join(File.dirname(input.path), "#{doc.attributes['docname']}#{doc.attributes['outfilesuffix']}")
  elsif !stream_output && write_to_target
    working_dir = options.has_key?(:base_dir) ? File.expand_path(options[:base_dir]) : File.expand_path(Dir.pwd)
    # QUESTION should the jail be the working_dir or doc.base_dir???
    jail = doc.safe >= SafeMode::SAFE ? working_dir : nil
    if to_dir
      to_dir = doc.normalize_system_path(to_dir, working_dir, jail, :target_name => 'to_dir', :recover => false)
      if to_file
        to_file = doc.normalize_system_path(to_file, to_dir, nil, :target_name => 'to_dir', :recover => false)
        # reestablish to_dir as the final target directory (in the case to_file had directory segments)
        to_dir = File.dirname(to_file)
      else
        to_file = File.join(to_dir, "#{doc.attributes['docname']}#{doc.attributes['outfilesuffix']}")
      end
    elsif to_file
      to_file = doc.normalize_system_path(to_file, working_dir, jail, :target_name => 'to_dir', :recover => false)
      # establish to_dir as the final target directory (in the case to_file had directory segments)
      to_dir = File.dirname(to_file)
    end

    if !File.directory? to_dir
      if mkdirs
        Helpers.require_library 'fileutils'
        FileUtils.mkdir_p to_dir
      else
        raise IOError, "target directory does not exist: #{to_dir}"
      end
    end
  end

  start = Time.now if monitor
  output = doc.render

  if monitor
    render_time = Time.now - start
    monitor[:render] = render_time
    monitor[:load_render] = monitor[:load] + render_time
  end

  if to_file
    start = Time.now if monitor
    if stream_output
      to_file.write output.rstrip
      # ensure there's a trailing endline
      to_file.write EOL
    else
      File.open(to_file, 'w') {|file| file.write output }
      # these assignments primarily for testing, diagnostics or reporting
      doc.attributes['outfile'] = outfile = File.expand_path(to_file)
      doc.attributes['outdir'] = File.dirname(outfile)
    end
    if monitor
      write_time = Time.now - start
      monitor[:write] = write_time
      monitor[:total] = monitor[:load_render] + write_time
    end

    # NOTE document cannot control this behavior if safe >= SafeMode::SERVER
    if !stream_output && doc.safe < SafeMode::SECURE && (doc.attr? 'basebackend-html') &&
        (doc.attr? 'linkcss') && (doc.attr? 'copycss')
      copy_asciidoctor_stylesheet = DEFAULT_STYLESHEET_KEYS.include?(stylesheet = (doc.attr 'stylesheet'))
      #copy_user_stylesheet = !copy_asciidoctor_stylesheet && (doc.attr? 'copycss')
      copy_coderay_stylesheet = (doc.attr? 'source-highlighter', 'coderay') && (doc.attr 'coderay-css', 'class') == 'class'
      copy_pygments_stylesheet = (doc.attr? 'source-highlighter', 'pygments') && (doc.attr 'pygments-css', 'class') == 'class'
      if copy_asciidoctor_stylesheet || copy_coderay_stylesheet || copy_pygments_stylesheet
        Helpers.require_library 'fileutils'
        outdir = doc.attr('outdir')
        stylesdir = doc.normalize_system_path(doc.attr('stylesdir'), outdir,
            doc.safe >= SafeMode::SAFE ? outdir : nil)
        Helpers.mkdir_p stylesdir if mkdirs
        if copy_asciidoctor_stylesheet
          File.open(File.join(stylesdir, DEFAULT_STYLESHEET_NAME), 'w') {|f|
            f.write Asciidoctor::HTML5.default_asciidoctor_stylesheet
          }
        end

        #if copy_user_stylesheet
        #end

        if copy_coderay_stylesheet
          File.open(File.join(stylesdir, 'asciidoctor-coderay.css'), 'w') {|f|
            f.write Asciidoctor::HTML5.default_coderay_stylesheet
          }
        end

        if copy_pygments_stylesheet
          File.open(File.join(stylesdir, 'asciidoctor-pygments.css'), 'w') {|f|
            f.write Asciidoctor::HTML5.pygments_stylesheet(doc.attr 'pygments-style')
          }
        end
      end
    end
    doc
  else
    output
  end
end
render_file(filename, options = {}) click to toggle source

Parse the contents of the AsciiDoc source file into an Asciidoctor::Documentand render it to the specified backend format

input

the String AsciiDoc source filename

options

a String, Array or Hash of options to control processing (default: {})String and Array values are converted into a Hash.See Asciidoctor::Document#initialize for details about options.

returns the Document object if the rendered result String is written to afile, otherwise the rendered result String

# File lib/asciidoctor.rb, line 992
def self.render_file(filename, options = {})
  Asciidoctor.render(File.new(filename), options)
end