class Pandocomatic::Configuration

Configuration models a pandocomatic configuration.

Constants

CONFIG_FILE

Pandocomatic’s default configuration file

Attributes

config_files[R]
data_dir[R]
input[R]
output[R]

Get the output file name

@return [String]

root_path[R]

Public Class Methods

new(options, input) click to toggle source

Create a new Configuration instance based on the command-line options

# File lib/pandocomatic/configuration.rb, line 190
def initialize(options, input)
  data_dirs = determine_data_dirs options
  @options = options
  @data_dir = data_dirs.first
  @settings = DEFAULT_SETTINGS
  @templates = {}
  @convert_patterns = {}

  load_configuration_hierarchy options, data_dirs

  @input = if input.nil? || input.empty?
             nil
           elsif input.size > 1
             MultipleFilesInput.new(input, self)
           else
             Input.new(input)
           end

  @output = if output?
              options[:output]
            elsif to_stdout? options
              Tempfile.new(@input.base) unless @input.nil?
            elsif @input.is_a? Input
              @input.base
            end

  @root_path = Path.determine_root_path options

  # Extend the command classes by setting the source tree root
  # directory, and the options quiet and dry-run, which are used when
  # executing a command: if dry-run the command is not actually
  # executed and if quiet the command is not printed to STDOUT
  Command.reset(self)
end

Public Instance Methods

clean_up!() click to toggle source

Clean up this configuration. This will remove temporary files created for the conversion process guided by this Configuration.

# File lib/pandocomatic/configuration.rb, line 408
def clean_up!
  # If a temporary file has been created while concatenating
  # multiple input files, ensure it is removed.
  @input.destroy! if @input.is_a? MultipleFilesInput
end
config?() click to toggle source

Is the config CLI option given?

@return [Boolean]

# File lib/pandocomatic/configuration.rb, line 352
def config?
  @options[:config_given]
end
configure(settings, path) click to toggle source

Configure pandocomatic based on a settings Hash

@param settings [Hash] a settings Hash to mixin in this @param path [String] the configuration’s path or filename Configuration.

# File lib/pandocomatic/configuration.rb, line 269
def configure(settings, path)
  reset_settings settings['settings'] if settings.key? 'settings'

  return unless settings.key? 'templates'

  settings['templates'].each do |name, template|
    reset_template Template.new(name, template, path)
  end
end
convert?(src) click to toggle source

Should the source file be converted given this Configuration?

@param src [String] True if this source file matches the ‘glob’

patterns in a template, false otherwise.
# File lib/pandocomatic/configuration.rb, line 465
def convert?(src)
  @convert_patterns.values.flatten.any? { |glob| File.fnmatch glob, File.basename(src) }
end
data_dir?() click to toggle source

Is the data dir CLI option given?

@return [Boolean]

# File lib/pandocomatic/configuration.rb, line 338
def data_dir?
  @options[:data_dir_given]
end
determine_template(src) click to toggle source

Determine the template to use with this source document given this Configuration.

@param src [String] path to the source document @return [String] the template’s name to use

# File lib/pandocomatic/configuration.rb, line 645
def determine_template(src)
  @convert_patterns.select do |_, globs|
    globs.any? { |glob| File.fnmatch glob, File.basename(src) }
  end.keys.first
end
determine_templates(src) click to toggle source

Determine the templates to use with this source document given this Configuration.

@param src [String] path to the source document @return [Array] the template’s name to use

# File lib/pandocomatic/configuration.rb, line 656
def determine_templates(src)
  matches = @convert_patterns.select do |_, globs|
    globs.any? { |glob| File.fnmatch glob, File.basename(src) }
  end.keys

  if matches.empty?
    []
  elsif match_all_templates?
    matches
  else
    [matches.first]
  end
end
directory?() click to toggle source

Is this Configuration for converting directories?

@return [Boolean]

# File lib/pandocomatic/configuration.rb, line 402
def directory?
  !@input.nil? and @input.directory?
end
dry_run?() click to toggle source

Is the dry run CLI option given?

@return [Boolean]

# File lib/pandocomatic/configuration.rb, line 289
def dry_run?
  @options[:dry_run_given] and @options[:dry_run]
end
feature_enabled?(feature) click to toggle source

Should given feature be enabled?

@param feature [Symbol] feature toggle to check @return [Boolean]

# File lib/pandocomatic/configuration.rb, line 360
def feature_enabled?(feature)
  @options[:enable_given] and Pandocomatic::FEATURES.include?(feature) and @options[:enable].include?(feature)
end
find_extension(template_name, metadata) click to toggle source

Find the extension of the destination file given this Confguration, template, and metadata

@param template_name [String] the name of the template used to

convert to destination

@param metadata [PandocMetadata] the metadata in the source file

@return [String] the extension to use for the destination file

# File lib/pandocomatic/configuration.rb, line 577
def find_extension(template_name, metadata)
  extension = 'html'

  # Pandoc supports enabling / disabling extensions
  # using +EXTENSION and -EXTENSION
  strip_extensions = ->(format) { format.split(/[+-]/).first }
  use_extension = lambda do |pandoc|
    pandoc['use-extension'] if pandoc.key? 'use-extension'
  end

  if template_name.nil? || template_name.empty?
    ext = use_extension.call metadata.pandoc_options
    if !ext.nil?
      extension = ext
    elsif metadata.pandoc_options.key? 'to'
      extension = strip_extensions.call(metadata.pandoc_options['to'])
    end
  elsif @templates[template_name].pandoc?
    pandoc = @templates[template_name].pandoc
    ext = use_extension.call pandoc

    if !ext.nil?
      extension = ext
    elsif pandoc.key? 'to'
      extension = strip_extensions.call(pandoc['to'])
    end
  end

  DEFAULT_EXTENSION[extension] || extension
end
get_metadata(src, template_name = nil) click to toggle source

Get a pandoc metadata object for given source file and template.

@param src [String] path to source file @param template_name [String] template used; optional parameter @return [PandocMetadata] Pandoc’s metadata for given file and template.

# File lib/pandocomatic/configuration.rb, line 432
def get_metadata(src, template_name = nil)
  if extract_metadata_from? src
    PandocMetadata.load_file src
  else
    src_format = nil

    # Determine source format based on template
    if template_name && @templates.key?(template_name) && @templates[template_name].pandoc?
      pandoc = @templates[template_name].pandoc
      src_format = pandoc['from'] if pandoc.key? 'from'
    end

    if src_format.nil?
      # Determine source format based on extension like pandoc does.
      # See https://github.com/jgm/pandoc/blob/main/src/Text/Pandoc/Format.hs
      # for that mapping
      src_extension = File.extname src
      src_format = PANDOCS_EXTENSION_TO_FORMAT_MAPPING[src_extension]
    end

    if !src_format || src_format == 'markdown'
      # Behave like pandoc: If no source format can be determined, assume markdown
      PandocMetadata.load_file src
    else
      PandocMetadata.empty src_format
    end
  end
end
get_template(template_name) click to toggle source

Get the template with template_name from this Configuration

@param template_name [String] a template’s name

@return [Template] The template with template_name.

# File lib/pandocomatic/configuration.rb, line 636
def get_template(template_name)
  @templates[template_name]
end
input?() click to toggle source

Have input CLI options be given?

# File lib/pandocomatic/configuration.rb, line 384
def input?
  @options[:input_given]
end
input_file() click to toggle source

Get the input file name

@return [String]

# File lib/pandocomatic/configuration.rb, line 391
def input_file
  if @input.nil?
    nil
  else
    @input.name
  end
end
load(filename) click to toggle source

Read a configuration file and create a pandocomatic configuration object

@param [String] filename Path to the configuration yaml file @return [Configuration] a pandocomatic configuration object

# File lib/pandocomatic/configuration.rb, line 229
def load(filename)
  begin
    path = File.absolute_path filename
    settings = PandocomaticYAML.load_file path
    if settings['settings'] && settings['settings']['data-dir']
      data_dir = settings['settings']['data-dir']
      src_dir = File.dirname filename
      @data_dir = if data_dir.start_with? '.'
                    File.absolute_path data_dir, src_dir
                  else
                    data_dir
                  end
    end
  rescue StandardError => e
    raise ConfigurationError.new(:unable_to_load_config_file, e, filename)
  end

  configure settings, filename
end
markdown_file?(filename) click to toggle source

Is filename a markdown file according to its extension?

@param filename [String] the filename to check @return [Boolean] True if filename has a markdown extension.

# File lib/pandocomatic/configuration.rb, line 612
def markdown_file?(filename)
  if filename.nil?
    false
  else
    ext = File.extname(filename).delete_prefix('.')
    DEFAULT_EXTENSION.key(ext) == 'markdown'
  end
end
match_all_templates?() click to toggle source

Should pandocomatic convert a file with all matching templates or only with the first matching template? Note. A ‘use-template’ statement in a document will overrule this setting.

@return [Boolean] True if the setting ‘match-files’ is ‘all’, false otherwise.

# File lib/pandocomatic/configuration.rb, line 491
def match_all_templates?
  @settings.key? 'match-files' and @settings['match-files'] == 'all'
end
match_first_template?() click to toggle source

Should pandocomatic convert a file with the first matching templates or with all matching templates? Note. Multiple ‘use-template’ statements in a document will overrule this setting.

@return [Boolean] True if the setting ‘match-files’ is ‘first’, false otherwise.

# File lib/pandocomatic/configuration.rb, line 501
def match_first_template?
  @settings.key? 'match-files' and @settings['match-files'] == 'first'
end
modified_only?() click to toggle source

Is the modified only CLI option given?

@return [Boolean]

# File lib/pandocomatic/configuration.rb, line 317
def modified_only?
  @options[:modified_only_given] and @options[:modified_only]
end
output?() click to toggle source

Is the output CLI option given and can that output be used?

@return [Boolean]

# File lib/pandocomatic/configuration.rb, line 367
def output?
  !@options.nil? and @options[:output_given] and @options[:output]
end
quiet?() click to toggle source

Run pandocomatic in quiet mode?

@return [Boolean]

# File lib/pandocomatic/configuration.rb, line 310
def quiet?
  [verbose?, dry_run?].none?
end
reconfigure(filename) click to toggle source

Update this configuration with a configuration file and return a new configuration

@param [String] filename path to the configuration file

@return [Configuration] a new configuration

# File lib/pandocomatic/configuration.rb, line 255
def reconfigure(filename)
  settings = PandocomaticYAML.load_file filename
  new_config = Marshal.load(Marshal.dump(self))
  new_config.configure settings, filename
  new_config
rescue StandardError => e
  raise ConfigurationError.new(:unable_to_load_config_file, e, filename)
end
recursive?() click to toggle source

Should pandocomatic be run recursively given this Configuration?

@return [Boolean] True if the setting ‘recursive’ is true, false

otherwise
# File lib/pandocomatic/configuration.rb, line 473
def recursive?
  @settings.key? 'recursive' and @settings['recursive']
end
root_path?() click to toggle source

Is the root path CLI option given?

@return [Boolean]

# File lib/pandocomatic/configuration.rb, line 345
def root_path?
  @options[:root_path_given]
end
set_destination(dst, template_name, metadata) click to toggle source

Set the destination file given this Confguration, template, and metadata

@param dst [String] path to a destination file @param template_name [String] the name of the template used to

convert to destination

@param metadata [PandocMetadata] the metadata in the source file

# File lib/pandocomatic/configuration.rb, line 526
def set_destination(dst, template_name, metadata)
  return dst if dst.is_a? Tempfile

  dir = File.dirname dst

  # Use the output option when set.
  determine_output_in_pandoc = lambda do |pandoc|
    if pandoc.key? 'output'
      output = pandoc['output']
      unless output.start_with? '/'
        # Put it relative to the current directory
        output = File.join dir, output
      end
      output
    end
  end

  # Output options in pandoc property have precedence
  destination = determine_output_in_pandoc.call metadata.pandoc_options
  rename_script = metadata.pandoc_options['rename']

  # Output option in template's pandoc property is next
  if destination.nil? && !template_name.nil? && !template_name.empty? && @templates[template_name].pandoc?
    pandoc = @templates[template_name].pandoc
    destination = determine_output_in_pandoc.call pandoc
    rename_script ||= pandoc['rename']
  end

  # Else fall back to taking the input file as output file with the
  # extension updated to the output format
  if destination.nil?
    destination = set_extension dst, template_name, metadata

    destination = rename_destination(rename_script, destination) unless rename_script.nil?
  end

  # If there is a single file input without output specified, set
  # the output now that we know what the output filename is.
  @output = destination.delete_prefix './' unless output?

  destination
end
set_extension(dst, template_name, metadata) click to toggle source

Set the extension of the destination file given this Confguration, template, and metadata

@param dst [String] path to a destination file @param template_name [String] the name of the template used to

convert to destination

@param metadata [PandocMetadata] the metadata in the source file

# File lib/pandocomatic/configuration.rb, line 512
def set_extension(dst, template_name, metadata)
  dir = File.dirname dst
  ext = File.extname dst
  basename = File.basename dst, ext
  File.join dir, "#{basename}.#{find_extension(template_name, metadata)}"
end
show_help?() click to toggle source

Is the help CLI option given?

@return [Boolean]

# File lib/pandocomatic/configuration.rb, line 331
def show_help?
  @options[:help_given]
end
show_version?() click to toggle source

Is the version CLI option given?

@return [Boolean]

# File lib/pandocomatic/configuration.rb, line 324
def show_version?
  @options[:version_given]
end
skip?(src) click to toggle source

Should the source file be skipped given this Configuration?

@param src [String] path to a source file @return [Boolean] True if this source file matches the pattern in

the 'skip' setting, false otherwise.
# File lib/pandocomatic/configuration.rb, line 419
def skip?(src)
  if @settings.key? 'skip'
    @settings['skip'].any? { |glob| File.fnmatch glob, File.basename(src) }
  else
    false
  end
end
src_root() click to toggle source

Get the source root directory

@return [String]

# File lib/pandocomatic/configuration.rb, line 379
def src_root
  @input&.absolute_path
end
stdout?() click to toggle source

Is the stdout CLI option given?

@return [Boolean]

# File lib/pandocomatic/configuration.rb, line 296
def stdout?
  !@options.nil? and @options[:stdout_given] and @options[:stdout]
end
template?(template_name) click to toggle source

Is there a template with template_name in this Configuration?

@param template_name [String] a template’s name

@return [Boolean] True if there is a template with name equal to

template_name in this Configuration
# File lib/pandocomatic/configuration.rb, line 627
def template?(template_name)
  @templates.key? template_name
end
to_s() click to toggle source

Convert this Configuration to a String

@return [String]

# File lib/pandocomatic/configuration.rb, line 282
def to_s
  marshal_dump
end
verbose?() click to toggle source

Is the verbose CLI option given?

@return [Boolean]

# File lib/pandocomatic/configuration.rb, line 303
def verbose?
  @options[:verbose_given] and @options[:verbose]
end

Private Instance Methods

determine_config_file(options, data_dir = Dir.pwd) click to toggle source
# File lib/pandocomatic/configuration.rb, line 825
def determine_config_file(options, data_dir = Dir.pwd)
  determine_config_files(options, [data_dir]).first
end
determine_config_files(options, data_dirs = []) click to toggle source
# File lib/pandocomatic/configuration.rb, line 801
def determine_config_files(options, data_dirs = [])
  config_files = []
  # Get config file from option, if any
  config_files << options[:config] if options[:config_given]

  # Get config file in each data_dir
  data_dirs.each do |data_dir|
    config_files << File.join(data_dir, CONFIG_FILE) if Dir.entries(data_dir).include? CONFIG_FILE
  end

  # Default configuration file distributes with pandocomatic
  config_files << File.join(__dir__, 'default_configuration.yaml')

  config_files.map do |config_file|
    path = File.absolute_path config_file

    raise ConfigurationError.new(:config_file_does_not_exist, nil, path) unless File.exist? path
    raise ConfigurationError.new(:config_file_is_not_a_file, nil, path) unless File.file? path
    raise ConfigurationError.new(:config_file_is_not_readable, nil, path) unless File.readable? path

    path
  end
end
determine_data_dirs(options) click to toggle source

Determine all data directories to use

# File lib/pandocomatic/configuration.rb, line 830
def determine_data_dirs(options)
  data_dirs = []

  # Data dir from CLI option
  data_dirs << options[:data_dir] if options[:data_dir_given]

  # Pandoc's default data dir
  begin
    data_dir = Paru::Pandoc.info[:data_dir]

    # If pandoc's data dir does not exist, however, fall back
    # to the current directory
    data_dirs << if File.exist? File.absolute_path(data_dir)
                   data_dir
                 else
                   Dir.pwd
                 end
  rescue Paru::Error => e
    # If pandoc cannot be run, continuing probably does not work out
    # anyway, so raise pandoc error
    raise PandocError.new(:error_running_pandoc, e, data_dir)
  rescue StandardError
    # Ignore error and use the current working directory as default working directory
    data_dirs << Dir.pwd
  end

  # check if data directories do exist and are readable
  data_dirs.uniq.map do |dir|
    path = File.absolute_path dir

    raise ConfigurationError.new(:data_dir_does_not_exist, nil, path) unless File.exist? path
    raise ConfigurationError.new(:data_dir_is_not_a_directory, nil, path) unless File.directory? path
    raise ConfigurationError.new(:data_dir_is_not_readable, nil, path) unless File.readable? path

    path
  end
end
extend_template(template) click to toggle source

Resolve the templates the templates extends and mixes them in, in order of occurrence.

@param template [Template] the template to extend @return [Template] the resolved template

# File lib/pandocomatic/configuration.rb, line 696
def extend_template(template)
  resolved_template = Template.new template.name

  missing = []

  template.extends.each do |name|
    if @templates.key? name
      resolved_template.merge! Template.clone(@templates[name])
    else
      missing << name
    end
  end

  unless missing.empty?
    if template.internal?
      warn "WARNING: Unable to find templates [#{missing.join(', ')}] while resolving internal template."
    else
      warn "WARNING: Unable to find templates [#{missing.join(', ')}] while resolving " \
           "the external template '#{template.name}' from configuration file '#{template.path}'."
    end
  end

  resolved_template.merge! template
  resolved_template
end
extract_metadata_from?(src) click to toggle source

Should we try to extract pandoc YAML metadata from source file?

# File lib/pandocomatic/configuration.rb, line 869
def extract_metadata_from?(src)
  if @settings.key? 'extract-metadata-from'
    @settings['extract-metadata-from'].any? { |glob| File.fnmatch glob, File.basename(src) }
  else
    false
  end
end
load_configuration_hierarchy(options, data_dirs) click to toggle source

Read a list of configuration files and create a pandocomatic object that mixes templates from most generic to most specific.

# File lib/pandocomatic/configuration.rb, line 788
def load_configuration_hierarchy(options, data_dirs)
  # Read and mixin templates from most generic config file to most
  # specific, thus in reverse order.
  @config_files = determine_config_files(options, data_dirs).reverse
  @config_files.each do |config_file|
    configure PandocomaticYAML.load_file(config_file), config_file
  rescue StandardError => e
    raise ConfigurationError.new(:unable_to_load_config_file, e, config_file)
  end

  load @config_files.last
end
marshal_dump() click to toggle source
# File lib/pandocomatic/configuration.rb, line 773
def marshal_dump
  [@data_dir, @settings, @templates, @convert_patterns]
end
marshal_load(array) click to toggle source
# File lib/pandocomatic/configuration.rb, line 777
def marshal_load(array)
  @data_dir, @settings, @templates, @convert_patterns = array
end
rename_destination(rename_script, dst) click to toggle source

Rename path by using rename script. If script fails somehow, warn and return the original destination.

@param rename_script [String] absolute path to script to run @param dst [String] original destination to rename

# File lib/pandocomatic/configuration.rb, line 745
def rename_destination(rename_script, dst)
  script = Path.update_path(self, rename_script)

  command, *parameters = script.shellsplit # split on spaces unless it is preceded by a backslash

  unless File.exist? command
    command = Path.which(command)
    script = "#{command} #{parameters.join(' ')}"

    raise ProcessorError.new(:script_does_not_exist, nil, command) if command.nil?
  end

  raise ProcessorError.new(:script_is_not_executable, nil, command) unless File.executable? command

  begin
    renamed_dst = Processor.run(script, dst)
    if !renamed_dst.nil? && !renamed_dst.empty?
      renamed_dst.strip
    else
      raise StandardError, new("Running rename script '#{script}' on destination '#{dst}' " \
                               'did not result in a renamed destination.')
    end
  rescue StandardError => e
    ProcessorError.new(:error_processing_script, e, [script, dst])
    dst
  end
end
reset_settings(settings) click to toggle source

Reset the settings for pandocomatic based on a new settings Hash

@param settings [Hash] the new settings to use to reset the settings in

this Configuration with.
# File lib/pandocomatic/configuration.rb, line 676
def reset_settings(settings)
  settings.each do |setting, value|
    case setting
    when 'skip'
      @settings['skip'] = @settings['skip'].concat(value).uniq
    when 'extract-metadata-from'
      @settings['extract-metadata-from'] = @settings['extract-metadata-from'].concat(value).uniq
    when 'data-dir'
      next # skip data-dir setting; is set once in initialization
    else
      @settings[setting] = value
    end
  end
end
reset_template(template) click to toggle source

Reset the template with name in this Configuration based on a new template

@param template [Template] the template to use to update the template in

this Configuarion with
# File lib/pandocomatic/configuration.rb, line 727
def reset_template(template)
  name = template.name
  extended_template = extend_template template

  if @templates.key? name
    @templates[name].merge! extended_template
  else
    @templates[name] = extended_template
  end

  @convert_patterns[name] = extended_template.glob if extended_template.glob?
end
to_stdout?(options) click to toggle source
# File lib/pandocomatic/configuration.rb, line 781
def to_stdout?(options)
  !options.nil? and options[:stdout_given] and options[:stdout]
end