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
StandaloneOptionLine =
%([%standalone]#{NewLine})
AttributeReferenceRx =
/\\?\{(\w+(?:[\-]\w+)*)\}/
HeaderBoundaryRx =
/(?<=\p{Graph})#{NewLine * 2}/

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
# File 'lib/jekyll-asciidoc/converter.rb', line 34

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

  # 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 as Hash under asciidoc key instead of as 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.chomp '-'
    asciidoc_config['require_front_matter_header'] = !!asciidoc_config['require_front_matter_header']
    asciidoc_config.extend Configured

    begin
      if (dlg_method = Utils.method :has_yaml_header?) && asciidoc_config['require_front_matter_header']
        if (::Jekyll::Utils.method dlg_method.name).arity == -1 # not original method
          ::Jekyll::Utils.define_singleton_method dlg_method.name, &dlg_method
        end
      else
        unless (new_method = dlg_method.owner.method :has_front_matter?).respond_to? :curry
          new_method = new_method.to_proc # Ruby < 2.2
        end
        ::Jekyll::Utils.define_singleton_method dlg_method.name, new_method.curry[dlg_method][asciidoc_ext_re]
      end
    rescue ::NameError; 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'
        if defined? ::Jekyll::Hooks
          asciidoctor_config[:base_dir] = :docdir
        else
          @logger.warn MessageTopic, 'Using :docdir as value of base_dir option requires Jekyll 3. Falling back to source directory.'
          asciidoctor_config[:base_dir] = source
        end
      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' => config['baseurl'],
        'site-url' => config['url']
      }
      attrs = asciidoctor_config[:attributes] = assemble_attributes asciidoctor_config[:attributes],
          ((site_attributes.merge ImplicitAttributes).merge DefaultAttributes)
      if (imagesdir = attrs['imagesdir']) && !(attrs.key? 'imagesoutdir') && (imagesdir.start_with? '/')
        attrs['imagesoutdir'] = ::File.join dest, (imagesdir.chomp '@')
      end
      asciidoctor_config.extend Configured
    end
  end
end

Class Method Details

.after_render(document) ⇒ Object



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

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

.before_render(document, payload) ⇒ Object



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

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

.get_instance(site) ⇒ Object



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

def self.get_instance site
  site.find_converter_instance self
end

Instance Method Details

#after_render(document) ⇒ Object



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

def after_render document
  @page_context.clear
end

#before_render(document, payload) ⇒ Object



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

def before_render document, payload
  # NOTE Jekyll 3.1 incorrectly mapped 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



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

def clear_paths
  @page_context.delete :paths
end

#convert(content) ⇒ Object



206
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
# File 'lib/jekyll-asciidoc/converter.rb', line 206

def convert content
  return '' if content.nil_or_empty?
  setup
  if (standalone = content.start_with? StandaloneOptionLine)
    content = content[StandaloneOptionLine.length..-1]
  end
  case @asciidoc_config['processor']
  when 'asciidoctor'
    opts = @asciidoctor_config.merge header_footer: 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
    # for auto-extracted excerpt, paths are't available since hooks don't get triggered
    elsif opts[:base_dir] == :docdir
      opts.delete :base_dir
    end
    ((@page_context[: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



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
# File 'lib/jekyll-asciidoc/converter.rb', line 180

def load_header document
  setup
  record_paths document, source_only: true if defined? ::Jekyll::Hooks
  # NOTE merely an optimization; if this doesn't match, the header still gets isolated by the processor
  header = (document.content.split HeaderBoundaryRx, 2)[0]
  case @asciidoc_config['processor']
  when 'asciidoctor'
    opts = @asciidoctor_config.merge parse_header_only: true
    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
    # 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 if defined? ::Jekyll::Hooks
  doc
end

#matches(ext) ⇒ Object



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

def matches ext
  ext =~ @asciidoc_config['ext_re']
end

#output_ext(ext) ⇒ Object



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

def output_ext ext
  '.html'
end

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



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

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

#setupObject



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

def setup
  return self if @setup
  @setup = true
  case @asciidoc_config['processor']
  when 'asciidoctor'
    begin
      require 'asciidoctor' unless defined? ::Asciidoctor::VERSION
    rescue ::LoadError
      @logger.error MessageTopic, 'You are missing a library required to convert AsciiDoc files. Please 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
  self
end