Module: Plum::StaticCache

Defined in:
lib/plum/static_cache.rb,
lib/plum/static_cache/middleware.rb

Overview

Full-page static cache. Rendered public pages are written to disk keyed by host + path; subsequent requests are served from the file without touching the database or Liquid. A web server can serve the same files directly (nginx try_files) for the "full measure" where Rails never sees the hit.

Cache entries are invalidated by deletion (see StaticCacheInvalidation) — the next request re-renders and re-stores the page. Over-flushing is cheap, so anything ambiguous flushes the whole site.

Defined Under Namespace

Classes: Middleware

Constant Summary collapse

MARKER_HEADER =
"X-Plum-Static-Cache".freeze

Class Method Summary collapse

Class Method Details

.cache_rootObject



24
25
26
# File 'lib/plum/static_cache.rb', line 24

def cache_root
  Pathname(Plum.configuration.static_cache_path || Rails.root.join("storage", "plum_static_cache"))
end

.content_type_for(file) ⇒ Object



77
78
79
# File 'lib/plum/static_cache.rb', line 77

def content_type_for(file)
  Rack::Mime.mime_type(File.extname(file.to_s), "text/html")
end

.enabled?Boolean

Explicit opt-in only — see docs/static-caching.md. There is deliberately no "auto-on in production" mode: this cache is only correct on a single-server deployment, and defaulting it on would silently corrupt pages the moment an app scales to 2+ nodes.

Returns:

  • (Boolean)


20
21
22
# File 'lib/plum/static_cache.rb', line 20

def enabled?
  !!Plum.configuration.static_cache_enabled
end

.file_path(host, path) ⇒ Object

Pages are stored as host/path/index.html so a web server can map URLs to files directly. Paths with a file extension (theme assets) are stored verbatim.



31
32
33
34
35
36
37
38
39
40
41
# File 'lib/plum/static_cache.rb', line 31

def file_path(host, path)
  key = normalized_path(path)
  return nil unless key

  base = cache_root.join(sanitized_host(host))
  full = File.extname(key).present? ? base.join(key) : base.join(key, "index.html")
  full = Pathname(File.expand_path(full))
  return nil unless full.to_s.start_with?(cache_root.to_s + File::SEPARATOR)

  full
end

.flush_all!Object



71
72
73
74
75
# File 'lib/plum/static_cache.rb', line 71

def flush_all!
  return unless cache_root.exist?

  cache_root.children.each { |child| FileUtils.rm_rf(child) }
end

.flush_site!(site) ⇒ Object



62
63
64
65
66
67
68
69
# File 'lib/plum/static_cache.rb', line 62

def flush_site!(site)
  return flush_all! if site.nil? || site.domain.blank?

  [ site.domain, "www.#{site.domain}" ].each do |host|
    dir = cache_root.join(sanitized_host(host))
    FileUtils.rm_rf(dir) if dir.to_s.start_with?(cache_root.to_s) && dir.exist?
  end
end

.read(host, path) ⇒ Object



43
44
45
46
# File 'lib/plum/static_cache.rb', line 43

def read(host, path)
  file = file_path(host, path)
  file if file&.file?
end

.store(host, path, body) ⇒ Object



48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/plum/static_cache.rb', line 48

def store(host, path, body)
  file = file_path(host, path)
  return unless file

  file.dirname.mkpath
  tmp = file.sub_ext(".tmp-#{Process.pid}-#{Thread.current.object_id}")
  tmp.binwrite(body)
  File.rename(tmp, file)
  file
rescue SystemCallError => e
  Rails.logger.error("[Plum] static cache write failed for #{path}: #{e.message}")
  nil
end