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 =
'Jekyll AsciiDoc:'
StandaloneOptionLine =
Utils::StandaloneOptionLine
HeaderBoundaryRx =
/(?<=\p{Graph})#{Utils::NewLine * 2}/

Instance Method Summary collapse

Constructor Details

#initialize(config) ⇒ Converter

Returns a new instance of Converter.



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

def initialize config
  @config = config
  @logger = ::Jekyll.logger
  @path_info = nil
  @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 Utils.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] = Utils.hashify_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
      end
      asciidoctor_config.extend Configured
    end
  end
end

Instance Method Details

#after_render(document) ⇒ Object



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

def after_render document
  clear_path_info if Document === document
end

#before_render(document, payload) ⇒ Object



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

def before_render document, payload
  record_path_info document if Document === document
end

#clear_path_infoObject



163
164
165
# File 'lib/jekyll-asciidoc/converter.rb', line 163

def clear_path_info
  @path_info = nil
end

#convert(content) ⇒ Object



193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
# File 'lib/jekyll-asciidoc/converter.rb', line 193

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 @path_info
      if opts[:base_dir] == :docdir
        opts[:base_dir] = @path_info['docdir'] # NOTE this assignment happens inside the processor anyway
      else
        @path_info.delete 'docdir'
      end
      opts[:attributes] = opts[:attributes].merge @path_info
    end
    ::Asciidoctor.convert content, opts
  else
    @logger.warn MessageTopic, %(Unknown AsciiDoc processor: #{@asciidoc_config['processor']}. Passing through unparsed content.)
    content
  end
end

#load_header(document) ⇒ Object



167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/jekyll-asciidoc/converter.rb', line 167

def load_header document
  setup
  record_path_info 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 @path_info
      if opts[:base_dir] == :docdir
        opts[:base_dir] = @path_info['docdir'] # NOTE this assignment happens inside the processor anyway
      else
        @path_info.delete 'docdir'
      end
      opts[:attributes] = opts[:attributes].merge @path_info
    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_path_info if defined? ::Jekyll::Hooks
  doc
end

#matches(ext) ⇒ Object



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

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

#output_ext(ext) ⇒ Object



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

def output_ext ext
  '.html'
end

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



148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/jekyll-asciidoc/converter.rb', line 148

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

#setupObject



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

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