Class: Jekyll::AsciiDoc::Converter

Inherits:
Converter
  • Object
show all
Defined in:
lib/jekyll-asciidoc/converter.rb

Constant Summary collapse

DefaultAttributes =
{
  'idprefix' => '',
  'idseparator' => '-',
  'linkattrs' => '@',
}
DefaultFileExtensions =
%w(asciidoc adoc ad)
DefaultPageAttributePrefix =
'page'
ImplicitAttributes =
{
  'env' => 'site',
  'env-site' => '',
  'site-gen' => 'jekyll',
  'site-gen-jekyll' => '',
  'builder' => 'jekyll',
  'builder-jekyll' => '',
  'jekyll-version' => ::Jekyll::VERSION,
}
MessageTopic =
Utils::MessageTopic
NewLine =
Utils::NewLine
AttributeReferenceRx =
/\\?\{(\p{Word}[-\p{Word}]*)\}/
HeaderBoundaryRx =
/(?<=\p{Graph}#{NewLine * 2})/
HeaderLineRx =
/^=[ \t]+.|^:!?\w[-\w]*!?:(?:[ \t]+.)?/

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config) ⇒ Converter

Returns a new instance of Converter.



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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/jekyll-asciidoc/converter.rb', line 34

def initialize config
  @config = config
  @logger = ::Jekyll.logger
  @page_context = {}

  # NOTE jekyll-watch reinitializes plugins using a shallow clone of config, so no need to reconfigure
  # NOTE check for Configured only works if value of key is defined in _config.yml as Hash
  unless Configured === (asciidoc_config = (config['asciidoc'] ||= {}))
    if ::String === asciidoc_config
      @logger.warn MessageTopic,
          'The AsciiDoc configuration should be defined using Hash on asciidoc key instead of discrete entries.'
      asciidoc_config = config['asciidoc'] = { 'processor' => asciidoc_config }
    else
      asciidoc_config['processor'] ||= 'asciidoctor'
    end
    old_asciidoc_ext = config.delete 'asciidoc_ext'
    asciidoc_ext = (asciidoc_config['ext'] ||= (old_asciidoc_ext || (DefaultFileExtensions * ',')))
    asciidoc_ext_re = asciidoc_config['ext_re'] = /^\.(?:#{asciidoc_ext.tr ',', '|'})$/ix
    old_page_attr_prefix_def = config.key? 'asciidoc_page_attribute_prefix'
    old_page_attr_prefix_val = config.delete 'asciidoc_page_attribute_prefix'
    unless (page_attr_prefix = asciidoc_config['page_attribute_prefix'])
      page_attr_prefix = old_page_attr_prefix_def ? old_page_attr_prefix_val || '' :
          (asciidoc_config.key? 'page_attribute_prefix') ? '' : DefaultPageAttributePrefix
    end
    asciidoc_config['page_attribute_prefix'] = (page_attr_prefix = page_attr_prefix.chomp '-').empty? ?
        '' : %(#{page_attr_prefix}-)
    asciidoc_config['require_front_matter_header'] = !!asciidoc_config['require_front_matter_header']
    asciidoc_config.extend Configured

    if asciidoc_config['require_front_matter_header']
      unless (::Jekyll::Utils.method :has_yaml_header?).owner == ::Jekyll::Utils
        # NOTE restore original method
        ::Jekyll::Utils.extend (::Module.new do
          define_method :has_yaml_header?, &(Utils.method :has_yaml_header?)
        end)
      end
    else
      ::Jekyll::Utils.extend (::Module.new do
        define_method :has_yaml_header?,
            (Utils.method :has_front_matter?).curry[Utils.method :has_yaml_header?][asciidoc_ext_re]
      end)
    end
  end

  if (@asciidoc_config = asciidoc_config)['processor'] == 'asciidoctor'
    unless Configured === (@asciidoctor_config = (config['asciidoctor'] ||= {}))
      asciidoctor_config = @asciidoctor_config
      asciidoctor_config.replace symbolize_keys asciidoctor_config
      source = ::File.expand_path config['source']
      dest = ::File.expand_path config['destination']
      case (base = asciidoctor_config[:base_dir])
      when ':source'
        asciidoctor_config[:base_dir] = source
      when ':docdir'
        asciidoctor_config[:base_dir] = :docdir
      else
        asciidoctor_config[:base_dir] = ::File.expand_path base if base
      end
      asciidoctor_config[:safe] ||= 'safe'
      site_attributes = {
        'site-root' => ::Dir.pwd,
        'site-source' => source,
        'site-destination' => dest,
        'site-baseurl' => (baseurl = config['baseurl']),
        'site-url' => config['url'],
      }
      attrs = asciidoctor_config[:attributes] = compile_attributes asciidoctor_config[:attributes],
          (compile_attributes asciidoc_config['attributes'],
              ((site_attributes.merge ImplicitAttributes).merge DefaultAttributes))
      if (imagesdir = attrs['imagesdir']) && (imagesdir.start_with? '/')
        attrs['imagesoutdir'] = ::File.join dest, (imagesdir.chomp '@') unless attrs.key? 'imagesoutdir'
        attrs['imagesdir'] = baseurl + imagesdir unless baseurl.to_s.empty?
      end
      asciidoctor_config.extend Configured
    end
  end

  load_processor
end

Class Method Details

.after_render(document) ⇒ Object



148
149
150
# File 'lib/jekyll-asciidoc/converter.rb', line 148

def self.after_render document
  (get_instance document.site).after_render document if Document === document || Excerpt === document
end

.before_render(document, payload) ⇒ Object



144
145
146
# File 'lib/jekyll-asciidoc/converter.rb', line 144

def self.before_render document, payload
  (get_instance document.site).before_render document, payload if Document === document || Excerpt === document
end

.get_instance(site) ⇒ Object



132
133
134
# File 'lib/jekyll-asciidoc/converter.rb', line 132

def self.get_instance site
  site.find_converter_instance self
end

Instance Method Details

#after_render(_document) ⇒ Object



158
159
160
# File 'lib/jekyll-asciidoc/converter.rb', line 158

def after_render _document
  @page_context.clear
end

#before_render(document, payload) ⇒ Object



152
153
154
155
156
# File 'lib/jekyll-asciidoc/converter.rb', line 152

def before_render document, payload
  # NOTE Jekyll 3.1 incorrectly maps the page payload to document.data instead of payload['page']
  @page_context[:data] = ::Jekyll::AsciiDoc::Jekyll3_1 ? document.data : payload['page']
  record_paths document
end

#clear_pathsObject



175
176
177
# File 'lib/jekyll-asciidoc/converter.rb', line 175

def clear_paths
  @page_context.delete :paths
end

#convert(content) ⇒ Object



207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
# File 'lib/jekyll-asciidoc/converter.rb', line 207

def convert content
  # NOTE don't use nil_or_empty? since that's only provided only by Asciidoctor
  return '' unless content && !content.empty?

  case @asciidoc_config['processor']
  when 'asciidoctor'
    opts = @asciidoctor_config.merge header_footer: (data = @page_context[:data] || {})['standalone']
    if (paths = @page_context[:paths])
      if opts[:base_dir] == :docdir
        opts[:base_dir] = paths['docdir'] # NOTE this assignment happens inside the processor anyway
      else
        paths.delete 'docdir'
      end
      opts[:attributes] = opts[:attributes].merge paths
    elsif opts[:base_dir] == :docdir
      opts.delete :base_dir
    end
    if (doctype = data['doctype'])
      opts[:doctype] = doctype
    end
    (data['document'] = ::Asciidoctor.load content, opts).extend(Liquidable).convert
  else
    @logger.warn MessageTopic,
        %(Unknown AsciiDoc processor: #{@asciidoc_config['processor']}. Passing through unparsed content.)
    content
  end
end

#load_header(document) ⇒ Object



179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
# File 'lib/jekyll-asciidoc/converter.rb', line 179

def load_header document
  record_paths document, source_only: true
  case @asciidoc_config['processor']
  when 'asciidoctor'
    opts = @asciidoctor_config.merge parse_header_only: true
    header = extract_header document
    if (paths = @page_context[:paths])
      if opts[:base_dir] == :docdir
        opts[:base_dir] = paths['docdir'] # NOTE this assignment happens inside the processor anyway
      else
        paths.delete 'docdir'
      end
      opts[:attributes] = opts[:attributes].merge paths
    end
    if (layout_attr = resolve_default_layout document, opts[:attributes])
      opts[:attributes] = opts[:attributes].merge layout_attr
    end
    # NOTE return instance even if header is empty since attributes may be inherited from config
    doc = ::Asciidoctor.load header, opts
  else
    @logger.warn MessageTopic,
        %(Unknown AsciiDoc processor: #{@asciidoc_config['processor']}. Cannot load document header.)
    doc = nil
  end
  clear_paths
  doc
end

#load_processorObject



114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/jekyll-asciidoc/converter.rb', line 114

def load_processor
  case @asciidoc_config['processor']
  when 'asciidoctor'
    begin
      require 'asciidoctor' unless defined? ::Asciidoctor::VERSION
    rescue ::LoadError
      @logger.error MessageTopic, 'You\'re missing a library required to convert AsciiDoc files. Install using:'
      @logger.error '', '$ [sudo] gem install asciidoctor'
      @logger.abort_with 'Bailing out; missing required dependency: asciidoctor'
    end
  else
    @logger.error MessageTopic, %(Invalid AsciiDoc processor given: #{@asciidoc_config['processor']})
    @logger.error '', 'Valid options are: asciidoctor'
    @logger.abort_with 'Bailing out; invalid Asciidoctor processor.'
  end
  nil
end

#matches(ext) ⇒ Object



136
137
138
# File 'lib/jekyll-asciidoc/converter.rb', line 136

def matches ext
  @asciidoc_config['ext_re'].match? ext
end

#output_ext(_ext) ⇒ Object



140
141
142
# File 'lib/jekyll-asciidoc/converter.rb', line 140

def output_ext _ext
  '.html'
end

#record_paths(document, opts = {}) ⇒ Object



162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/jekyll-asciidoc/converter.rb', line 162

def record_paths document, opts = {}
  @page_context[:paths] = paths = {
    'docfile' => (docfile = ::File.join document.site.source, document.relative_path),
    'docdir' => (::File.dirname docfile),
    'docname' => (::File.basename docfile, (::File.extname docfile)),
  }
  paths.update(
    'outfile' => (outfile = document.destination document.site.dest),
    'outdir' => (::File.dirname outfile),
    'outpath' => document.url
  ) unless opts[:source_only]
end