Module: AsciidoctorExtensions::KrokiCache

Defined in:
lib/asciidoctor/extensions/asciidoctor_kroki/cache.rb

Overview

Persistent, content-addressed cache for fetched diagrams, independent of the output directory. Ports src/cache.js so both language bindings behave the same way: the cache key is derived from the diagram request itself (server URL, type, format, encoded source, options), not from the output file name, so it also survives builds that wipe the output directory between runs (e.g. Antora) and correctly detects unchanged content for diagrams with a stable, user-defined name (see #90, #113).

Constant Summary collapse

VALID_CACHE_MODES =
['', 'true', 'false', 'refresh'].freeze

Class Method Summary collapse

Class Method Details

.content_key(kroki_diagram, server_url) ⇒ Object

Computes the content-addressed cache key for a diagram.

Deliberately host-dependent: the server URL is part of the key because two Kroki servers are not guaranteed to render the same source identically (they may run different versions of the underlying diagram libraries). Options are sorted so the key does not depend on their insertion order.



56
57
58
59
60
# File 'lib/asciidoctor/extensions/asciidoctor_kroki/cache.rb', line 56

def content_key(kroki_diagram, server_url)
  sorted_opts = kroki_diagram.opts.sort_by { |k, _| k.to_s }
  material = [server_url, kroki_diagram.type, kroki_diagram.format, kroki_diagram.encode, sorted_opts.to_json].join('/')
  Digest::SHA256.hexdigest(material)
end

.exists_in_cache?(cache_dir, key, format) ⇒ Boolean

Whether a diagram is already present in the cache.

Returns:

  • (Boolean)


63
64
65
# File 'lib/asciidoctor/extensions/asciidoctor_kroki/cache.rb', line 63

def exists_in_cache?(cache_dir, key, format)
  File.exist?(cache_file_path(cache_dir, key, format))
end

.read_from_cache(cache_dir, key, format) ⇒ Object

Reads a cached diagram.



68
69
70
# File 'lib/asciidoctor/extensions/asciidoctor_kroki/cache.rb', line 68

def read_from_cache(cache_dir, key, format)
  File.read(cache_file_path(cache_dir, key, format), mode: 'rb')
end

.resolve_cache_dir(doc) ⇒ Object

Resolves the persistent cache directory. Uses the kroki-cache-dir attribute when set; otherwise defaults to the XDG cache directory ($XDG_CACHE_HOME/kroki or ~/.cache/kroki).



21
22
23
24
25
26
27
# File 'lib/asciidoctor/extensions/asciidoctor_kroki/cache.rb', line 21

def resolve_cache_dir(doc)
  configured = doc.attr('kroki-cache-dir')
  return configured if configured && !configured.empty?

  xdg_cache_home = ENV['XDG_CACHE_HOME'] || File.join(Dir.home, '.cache')
  File.join(xdg_cache_home, 'kroki')
end

.resolve_cache_mode(doc, logger) ⇒ Hash{Symbol => Boolean}

Resolves the kroki-cache attribute into a cache mode.

Recognised values are: unset (defaults to enabled), `` (set with no value, e.g. :kroki-cache:) and true (both enabled), false (disabled), and refresh (enabled, but bypasses cached reads and re-fetches + updates the cache). Any other value is invalid: it is logged and treated as if unset.

Returns:

  • (Hash{Symbol => Boolean})

    with keys :enabled and :refresh



37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/asciidoctor/extensions/asciidoctor_kroki/cache.rb', line 37

def resolve_cache_mode(doc, logger)
  raw = doc.attr('kroki-cache')
  return { enabled: true, refresh: false } if raw.nil?

  value = raw.to_s.strip.downcase
  unless VALID_CACHE_MODES.include?(value)
    logger.warn "Invalid value '#{raw}' for kroki-cache attribute. The value must be either: " \
                "'true', 'false' or 'refresh'. Proceeding using: 'true'."
    return { enabled: true, refresh: false }
  end
  { enabled: value != 'false', refresh: value == 'refresh' }
end

.write_to_cache(cache_dir, key, format, contents) ⇒ Object

Writes a diagram to the cache, creating the cache directory if needed.



73
74
75
76
# File 'lib/asciidoctor/extensions/asciidoctor_kroki/cache.rb', line 73

def write_to_cache(cache_dir, key, format, contents)
  FileUtils.mkdir_p(cache_dir)
  File.write(cache_file_path(cache_dir, key, format), contents, mode: 'wb')
end