Class: Rakali::Document

Inherits:
Object
  • Object
show all
Defined in:
lib/rakali/document.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(document, config) ⇒ Document

Returns a new instance of Document.



8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/rakali/document.rb', line 8

def initialize(document, config)
  begin
    @config = config

    @from_folder = @config.fetch('from').fetch('folder')
    @from_format = @config.fetch('from').fetch('format')
    @to_folder = @config.fetch('to').fetch('folder') || @from_folder
    @to_format = @config.fetch('to').fetch('format')

    # if document is a list of files, concatenate into one input
    # use to_folder name as filename
    if document.is_a?(Array)
      @source = document.map { |file| File.basename(file) }.join(" ")
      @destination = "#{File.basename(@from_folder)}.#{@to_format}"
      content = document.map { |file| IO.read(file) }.join("\n\n")
    else
      # otherwise use source name with new extension for destination filename
      @source = File.basename(document)
      @destination = @source.sub(/\.#{@from_format}$/, ".#{@to_format}")
      content = IO.read(document)
    end

    # add pandoc options from config
    options = @config.fetch('options') || {}
    @options = options.map { |k,v| "--#{k}=#{v}" }.join(" ")

    # add pandoc variables from config
    variables = @config.fetch('variables', nil) || {}
    @variables = variables.map { |k,v| "--variable #{k}='#{v}'" }.join(" ")

    # add pandoc filters from config
    @filters = filter

    # use citeproc-pandoc if citations flag is set
    bibliography = @config.fetch('citations') ? "-f citeproc-pandoc " : ""

    # convert source document into JSON version of native AST
    # read in document and parse to Pandoc via STDIN to allow filenames with spaces
    @content = convert(content, @from_folder, "#{@source} #{bibliography}-t json #{@options} #{@variables} #{@filters}")

    # read in JSON schema, use included schemata folder if no folder is given
    @schema = scheme

    # validate JSON against schema and report errors
    @errors = validate

    # convert to destination document from JSON version of native AST
    @output = convert(@content, @to_folder, "-f json #{bibliography}-o #{@destination} #{@options} #{@variables} #{@filters}")
    Rakali.logger.abort_with "Fatal:", "Writing file #{@destination} failed" unless created?

    if @errors.empty?
      Rakali.logger.info "Success:", "Converted file #{@source} to file #{@destination}."
    else
      Rakali.logger.warn "With Errors:", "Converted file #{@source} to file #{@destination}."
    end
  rescue KeyError => e
    Rakali.logger.abort_with "Fatal:", "Configuration #{e.message}."
  rescue => e
    Rakali.logger.abort_with "Fatal:", "#{e.message}."
  end
end

Instance Attribute Details

#configObject

Returns the value of attribute config.



6
7
8
# File 'lib/rakali/document.rb', line 6

def config
  @config
end

#contentObject

Returns the value of attribute content.



6
7
8
# File 'lib/rakali/document.rb', line 6

def content
  @content
end

#destinationObject

Returns the value of attribute destination.



6
7
8
# File 'lib/rakali/document.rb', line 6

def destination
  @destination
end

#errorsObject

Returns the value of attribute errors.



6
7
8
# File 'lib/rakali/document.rb', line 6

def errors
  @errors
end

#filtersObject

Returns the value of attribute filters.



6
7
8
# File 'lib/rakali/document.rb', line 6

def filters
  @filters
end

#optionsObject

Returns the value of attribute options.



6
7
8
# File 'lib/rakali/document.rb', line 6

def options
  @options
end

#schemaObject

Returns the value of attribute schema.



6
7
8
# File 'lib/rakali/document.rb', line 6

def schema
  @schema
end

#sourceObject

Returns the value of attribute source.



6
7
8
# File 'lib/rakali/document.rb', line 6

def source
  @source
end

#to_folderObject

Returns the value of attribute to_folder.



6
7
8
# File 'lib/rakali/document.rb', line 6

def to_folder
  @to_folder
end

#variablesObject

Returns the value of attribute variables.



6
7
8
# File 'lib/rakali/document.rb', line 6

def variables
  @variables
end

Instance Method Details

#convert(string = nil, dir, args) ⇒ Object



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/rakali/document.rb', line 70

def convert(string = nil, dir, args)
  captured_stdout = ''
  captured_stderr = ''
  exit_status = Open3::popen3("pandoc #{args}", chdir: dir) do |stdin, stdout, stderr, wait_thr|
    stdin.puts string unless string.nil?
    stdin.close

    captured_stdout = stdout.read
    captured_stderr = stderr.read
    wait_thr.value
  end

  # abort with log message if non-zero exit_status
  unless exit_status.success?
    Rakali.logger.error "Pandoc Args:", "#{args}."
    Rakali.logger.abort_with "Fatal:", "#{captured_stderr}."
  end

  # otherwise return stdout
  captured_stdout
end

#created?Boolean

Returns:

  • (Boolean)


130
131
132
133
134
135
136
# File 'lib/rakali/document.rb', line 130

def created?
  # file exists
  return false unless File.exist?("#{@to_folder}/#{@destination}")

  # file was created in the last 5 seconds
  Time.now - File.mtime("#{@to_folder}/#{@destination}") < 5
end

#filterObject



102
103
104
105
106
107
108
109
110
111
112
# File 'lib/rakali/document.rb', line 102

def filter
  filters = @config.fetch('filters', nil) || []
  filters.map do |f|
    if f.include?("/")
      "--filter=#{f}"
    else
      filters_folder = File.expand_path("../../../filters", __FILE__)
      "--filter=#{filters_folder}/#{f}"
    end
  end.join(" ")
end

#from_json(string) ⇒ Object



138
139
140
141
142
# File 'lib/rakali/document.rb', line 138

def from_json(string)
  JSON.parse(string)
rescue JSON::ParserError, TypeError
  nil
end

#schemeObject



92
93
94
95
96
97
98
99
100
# File 'lib/rakali/document.rb', line 92

def scheme
  schema = @config.fetch('schema')
  if schema.include?("/")
    IO.read(schema)
  else
    schemata_folder = File.expand_path("../../../schemata", __FILE__)
    IO.read("#{schemata_folder}/#{schema}")
  end
end

#to_jsonObject



144
145
146
# File 'lib/rakali/document.rb', line 144

def to_json
  content.to_json
end

#valid?Boolean

Returns:

  • (Boolean)


126
127
128
# File 'lib/rakali/document.rb', line 126

def valid?
  errors == []
end

#validateObject



114
115
116
117
118
119
120
121
122
123
124
# File 'lib/rakali/document.rb', line 114

def validate
  errors = JSON::Validator.fully_validate(@schema, @content)
  return [] if errors.empty?

  if @config.fetch('strict', false)
    errors.each { |error| Rakali.logger.error "Validation Error:", "#{error} for file #{source}" }
    Rakali.logger.abort_with "Fatal:", "Conversion of file #{source} failed."
  else
    errors.each { |error| Rakali.logger.warn "Validation Error:", "#{error} for file #{source}" }
  end
end