module Asciidoctor
def const_missing name
defined prior to it being loaded.
Asciidoctor::Extensions, except that the constant isn't recognized as
This method provides the same functionality as using autoload on
Otherwise, delegates to the super method.
Requires the Asciidoctor::Extensions module if the name is :Extensions.
Internal: Automatically load the Asciidoctor::Extensions module.
def const_missing name if name == :Extensions require_relative 'asciidoctor/extensions' Extensions else super end end unless RUBY_ENGINE == 'opal'
def convert input, options = {}
Returns the Document object if the converted String is written to a
See Asciidoctor::Document#initialize for details about options.
String and Array values are converted into a Hash.
options - a String, Array or Hash of options to control processing (default: {})
input - the String AsciiDoc source filename
default and the converted result is returned.
standalone document). Otherwise, the header and footer are not included by
included unless specified otherwise (writing to a file implies creating a
If the output is going to be written to a file, the header and footer are
outside of the Document#base_dir in safe mode, an IOError is raised.
written because the target directory does not exist, or because it falls
created unless the :mkdirs option is set to true. If the file cannot be
Document#base_dir. If the target directory does not exist, it will not be
path, it is resolved relative to :to_dir, if given, otherwise the
specified, the file is written to that file. If :to_file is not an absolute
corresponds to the backend format. Otherwise, if the :to_file option is
written to a file adjacent to the input file, having an extension that
If the :to_file option is true, and the input is a File, the output is
directory name, etc) gets assigned to attributes on the Document object.
closed afterwards by this method. Information about the file (filename,
input is a File, the object is expected to be opened for reading and is not
Accepts input as an IO (or StringIO), String or String Array object. If the
convert it to the specified backend format.
Public: Parse the AsciiDoc source input into an Asciidoctor::Document and
def convert input, options = {} (options = options.merge).delete :parse to_dir = options.delete :to_dir mkdirs = options.delete :mkdirs case (to_file = options.delete :to_file) when true, nil unless (write_to_target = to_dir) sibling_path = ::File.absolute_path input.path if ::File === input end to_file = nil when false to_file = nil when '/dev/null' return self.load input, options else options[:to_file] = write_to_target = to_file unless (stream_output = to_file.respond_to? :write) end unless options.key? :standalone if sibling_path || write_to_target options[:standalone] = true elsif options.key? :header_footer options[:standalone] = options[:header_footer] end end # NOTE outfile may be controlled by document attributes, so resolve outfile after loading if sibling_path options[:to_dir] = outdir = ::File.dirname sibling_path elsif write_to_target if to_dir if to_file options[:to_dir] = ::File.dirname ::File.expand_path ::File.join to_dir, to_file else options[:to_dir] = ::File.expand_path to_dir end elsif to_file options[:to_dir] = ::File.dirname ::File.expand_path to_file end end # NOTE :to_dir is always set when outputting to a file # NOTE :to_file option only passed if assigned an explicit path doc = self.load input, options if sibling_path # write to file in same directory outfile = ::File.join outdir, %(#{doc.attributes['docname']}#{doc.outfilesuffix}) raise ::IOError, %(input file and output file cannot be the same: #{outfile}) if outfile == sibling_path elsif write_to_target # write to explicit file or directory working_dir = (options.key? :base_dir) ? (::File.expand_path options[:base_dir]) : ::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 outdir = doc.normalize_system_path(to_dir, working_dir, jail, target_name: 'to_dir', recover: false) if to_file outfile = doc.normalize_system_path(to_file, outdir, nil, target_name: 'to_dir', recover: false) # reestablish outdir as the final target directory (in the case to_file had directory segments) outdir = ::File.dirname outfile else outfile = ::File.join outdir, %(#{doc.attributes['docname']}#{doc.outfilesuffix}) end elsif to_file outfile = doc.normalize_system_path(to_file, working_dir, jail, target_name: 'to_dir', recover: false) # establish outdir as the final target directory (in the case to_file had directory segments) outdir = ::File.dirname outfile end if ::File === input && outfile == (::File.absolute_path input.path) raise ::IOError, %(input file and output file cannot be the same: #{outfile}) end if mkdirs Helpers.mkdir_p outdir else # NOTE we intentionally refer to the directory as it was passed to the API raise ::IOError, %(target directory does not exist: #{to_dir} (hint: set :mkdirs option)) unless ::File.directory? outdir end else # write to stream outfile = to_file outdir = nil end if outfile && !stream_output output = doc.convert 'outfile' => outfile, 'outdir' => outdir else output = doc.convert end if outfile doc.write output, outfile # NOTE document cannot control this behavior if safe >= SafeMode::SERVER # NOTE skip if stylesdir is a URI if !stream_output && doc.safe < SafeMode::SECURE && (doc.attr? 'linkcss') && (doc.attr? 'copycss') && (doc.basebackend? 'html') && !((stylesdir = (doc.attr 'stylesdir')) && (Helpers.uriish? stylesdir)) if (stylesheet = doc.attr 'stylesheet') if DEFAULT_STYLESHEET_KEYS.include? stylesheet copy_asciidoctor_stylesheet = true elsif !(Helpers.uriish? stylesheet) copy_user_stylesheet = true end end copy_syntax_hl_stylesheet = (syntax_hl = doc.syntax_highlighter) && (syntax_hl.write_stylesheet? doc) if copy_asciidoctor_stylesheet || copy_user_stylesheet || copy_syntax_hl_stylesheet stylesoutdir = doc.normalize_system_path(stylesdir, outdir, doc.safe >= SafeMode::SAFE ? outdir : nil) if mkdirs Helpers.mkdir_p stylesoutdir else raise ::IOError, %(target stylesheet directory does not exist: #{stylesoutdir} (hint: set :mkdirs option)) unless ::File.directory? stylesoutdir end if copy_asciidoctor_stylesheet Stylesheets.instance.write_primary_stylesheet stylesoutdir # FIXME should Stylesheets also handle the user stylesheet? elsif copy_user_stylesheet if (stylesheet_src = doc.attr 'copycss').empty? stylesheet_src = doc.normalize_system_path stylesheet else # NOTE in this case, copycss is a source location (but cannot be a URI) stylesheet_src = doc.normalize_system_path stylesheet_src end stylesheet_dest = doc.normalize_system_path stylesheet, stylesoutdir, (doc.safe >= SafeMode::SAFE ? outdir : nil) # NOTE don't warn if src can't be read and dest already exists (see #2323) if stylesheet_src != stylesheet_dest && (stylesheet_data = doc.read_asset stylesheet_src, warn_on_failure: !(::File.file? stylesheet_dest), label: 'stylesheet') ::File.write stylesheet_dest, stylesheet_data, mode: FILE_WRITE_MODE end end syntax_hl.write_stylesheet doc, stylesoutdir if copy_syntax_hl_stylesheet end end doc else output end end
def convert_file filename, options = {}
Returns the Document object if the converted String is written to a
See Asciidoctor::Document#initialize for details about options.
String and Array values are converted into a Hash.
options - a String, Array or Hash of options to control processing (default: {})
input - the String AsciiDoc source filename
Asciidoctor::Document and convert it to the specified backend format.
Public: Parse the contents of the AsciiDoc source file into an
def convert_file filename, options = {} ::File.open(filename, FILE_READ_MODE) {|file| self.convert file, options } end
def load input, options = {}
See {Document#initialize} for details about these options.
String and Array values are converted into a Hash.
options - a String, Array or Hash of options to control processing (default: {})
input - the AsciiDoc source as a IO, String or Array.
directory name, etc) gets assigned to attributes on the Document object.
closed afterwards by this method. Information about the file (filename,
input is a File, the object is expected to be opened for reading and is not
Accepts input as an IO (or StringIO), String or String Array object. If the
Public: Parse the AsciiDoc source input into a {Document}
def load input, options = {} options = options.merge if (timings = options[:timings]) timings.start :read end if (logger = options[:logger]) && logger != LoggerManager.logger LoggerManager.logger = logger end if !(attrs = options[:attributes]) attrs = {} elsif ::Hash === attrs attrs = attrs.merge elsif (defined? ::Java::JavaUtil::Map) && ::Java::JavaUtil::Map === attrs attrs = attrs.dup elsif ::Array === attrs attrs = {}.tap do |accum| attrs.each do |entry| k, _, v = entry.partition '=' accum[k] = v end end elsif ::String === attrs # condense and convert non-escaped spaces to null, unescape escaped spaces, then split on null attrs = {}.tap do |accum| attrs.gsub(SpaceDelimiterRx, '\1' + NULL).gsub(EscapedSpaceRx, '\1').split(NULL).each do |entry| k, _, v = entry.partition '=' accum[k] = v end end elsif (attrs.respond_to? :keys) && (attrs.respond_to? :[]) # coerce attrs to a real Hash attrs = {}.tap {|accum| attrs.keys.each {|k| accum[k] = attrs[k] } } else raise ::ArgumentError, %(illegal type for attributes option: #{attrs.class.ancestors.join ' < '}) end if ::File === input options[:input_mtime] = input.mtime # NOTE defer setting infile and indir until we get a better sense of their purpose # TODO cli checks if input path can be read and is file, but might want to add check to API too attrs['docfile'] = input_path = ::File.absolute_path input.path attrs['docdir'] = ::File.dirname input_path attrs['docname'] = Helpers.basename input_path, (attrs['docfilesuffix'] = ::File.extname input_path) source = input.read elsif input.respond_to? :read # NOTE tty, pipes & sockets can't be rewound, but can't be sniffed easily either # just fail the rewind operation silently to handle all cases input.rewind rescue nil source = input.read elsif ::String === input source = input elsif ::Array === input source = input.drop 0 elsif input raise ::ArgumentError, %(unsupported input type: #{input.class}) end if timings timings.record :read timings.start :parse end options[:attributes] = attrs doc = options[:parse] == false ? (Document.new source, options) : (Document.new source, options).parse timings.record :parse if timings doc rescue => ex begin context = %(asciidoctor: FAILED: #{attrs['docfile'] || '<stdin>'}: Failed to load AsciiDoc document) if ex.respond_to? :exception # The original message must be explicitly preserved when wrapping a Ruby exception wrapped_ex = ex.exception %(#{context} - #{ex.message}) # JRuby automatically sets backtrace; MRI did not until 2.6 wrapped_ex.set_backtrace ex.backtrace else # Likely a Java exception class wrapped_ex = ex.class.new context, ex wrapped_ex.stack_trace = ex.stack_trace end rescue wrapped_ex = ex end raise wrapped_ex end
def load_file filename, options = {}
See Asciidoctor::Document#initialize for details about options.
String and Array values are converted into a Hash.
options - a String, Array or Hash of options to control processing (default: {})
input - the String AsciiDoc source filename
Public: Parse the contents of the AsciiDoc source file into an Asciidoctor::Document
def load_file filename, options = {} ::File.open(filename, FILE_READ_MODE) {|file| self.load file, options } end