Class: RSX::CompileCache

Inherits:
Object
  • Object
show all
Defined in:
lib/rsx/compile_cache.rb

Overview

Caches the Ruby produced from .rsx files on disk, keyed by a digest of the source. A warm cache turns loading a template into reading a .rb file, so booting a large application does no transformation work at all.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(directory) ⇒ CompileCache

Returns a new instance of CompileCache.



13
14
15
16
17
18
# File 'lib/rsx/compile_cache.rb', line 13

def initialize(directory)
  @directory = directory
  @enabled = !directory.nil?
  @memory = {}
  @lock = Mutex.new
end

Instance Attribute Details

#directoryObject (readonly)

Returns the value of attribute directory.



11
12
13
# File 'lib/rsx/compile_cache.rb', line 11

def directory
  @directory
end

Instance Method Details

#clearObject



76
77
78
79
80
81
82
# File 'lib/rsx/compile_cache.rb', line 76

def clear
  @lock.synchronize { @memory.clear }
  return unless enabled? && File.directory?(@directory)

  Dir.glob(File.join(@directory, "*.rb")).each { |file| File.delete(file) }
  Dir.glob(File.join(@directory, "*.tmp")).each { |file| File.delete(file) }
end

#digest(source) ⇒ Object



24
25
26
# File 'lib/rsx/compile_cache.rb', line 24

def digest(source)
  Digest::SHA256.hexdigest("#{COMPILER_VERSION}\0#{source}")[0, 32]
end

#enabled?Boolean

Returns:

  • (Boolean)


20
21
22
# File 'lib/rsx/compile_cache.rb', line 20

def enabled?
  @enabled
end

#fetch(path, source) ⇒ Object

Returns the compiled Ruby for source, compiling only on a cache miss.



29
30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/rsx/compile_cache.rb', line 29

def fetch(path, source)
  key = digest(source)
  cached = @lock.synchronize { @memory[key] }
  return cached if cached

  ruby = read(path, key) || begin
    compiled = yield
    write(path, key, compiled)
    compiled
  end

  @lock.synchronize { @memory[key] = ruby }
  ruby
end

#path_for(source_path, key) ⇒ Object



44
45
46
47
48
49
50
# File 'lib/rsx/compile_cache.rb', line 44

def path_for(source_path, key)
  return nil unless enabled?

  basename = File.basename(source_path.to_s, ".*")
  basename = "template" if basename.empty?
  File.join(@directory, "#{basename}-#{key}.rb")
end

#read(source_path, key) ⇒ Object



52
53
54
55
56
57
58
59
# File 'lib/rsx/compile_cache.rb', line 52

def read(source_path, key)
  target = path_for(source_path, key)
  return nil unless target && File.file?(target)

  File.read(target)
rescue SystemCallError
  nil
end

#write(source_path, key, ruby) ⇒ Object



61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/rsx/compile_cache.rb', line 61

def write(source_path, key, ruby)
  target = path_for(source_path, key)
  return ruby unless target

  FileUtils.mkdir_p(File.dirname(target))
  temporary = "#{target}.#{Process.pid}.#{rand(1 << 24)}.tmp"
  File.binwrite(temporary, ruby)
  File.rename(temporary, target)
  ruby
rescue SystemCallError
  # A read-only or missing cache directory must never break rendering.
  @enabled = false
  ruby
end