Class: GemCP::Cache
- Inherits:
-
Object
- Object
- GemCP::Cache
- Defined in:
- lib/gemcp/cache.rb
Overview
Persistent JSON file cache with conditional-request metadata (ETag, Last-Modified). Uses atomic temp+rename writes to prevent partial reads on POSIX systems.
Defined Under Namespace
Classes: Entry
Instance Method Summary collapse
-
#backdate(key, seconds:) ⇒ void
Artificially age a cache entry by the given number of seconds.
-
#initialize(directory:) ⇒ Cache
constructor
A new instance of Cache.
-
#read(key) ⇒ Entry?
The cached entry, or nil if missing/corrupt.
-
#touch(key, entry) ⇒ Entry
Re-writes an entry with a fresh timestamp, preserving body and headers.
-
#write(key, body:, etag: nil, last_modified: nil) ⇒ Entry
The newly written entry.
Constructor Details
#initialize(directory:) ⇒ Cache
Returns a new instance of Cache.
23 24 25 26 |
# File 'lib/gemcp/cache.rb', line 23 def initialize(directory:) @directory = directory FileUtils.mkdir_p(directory) end |
Instance Method Details
#backdate(key, seconds:) ⇒ void
This method returns an undefined value.
Artificially age a cache entry by the given number of seconds. Intended for testing staleness without coupling to internal storage format.
85 86 87 88 89 90 91 92 |
# File 'lib/gemcp/cache.rb', line 85 def backdate(key, seconds:) path = path_for(key) return unless File.file?(path) data = JSON.parse(File.read(path)) data["stored_at"] = data["stored_at"] - seconds File.write(path, JSON.generate(data)) end |
#read(key) ⇒ Entry?
Returns the cached entry, or nil if missing/corrupt.
30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
# File 'lib/gemcp/cache.rb', line 30 def read(key) path = path_for(key) return unless File.file?(path) data = JSON.parse(File.read(path)) Entry.new( body: data.fetch("body"), etag: data["etag"], last_modified: data["last_modified"], stored_at: Time.at(data.fetch("stored_at")) ) rescue JSON::ParserError, KeyError, Errno::ENOENT nil end |
#touch(key, entry) ⇒ Entry
Re-writes an entry with a fresh timestamp, preserving body and headers.
75 76 77 |
# File 'lib/gemcp/cache.rb', line 75 def touch(key, entry) write(key, body: entry.body, etag: entry.etag, last_modified: entry.last_modified) end |
#write(key, body:, etag: nil, last_modified: nil) ⇒ Entry
Returns the newly written entry.
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
# File 'lib/gemcp/cache.rb', line 50 def write(key, body:, etag: nil, last_modified: nil) path = path_for(key) temp = "#{path}.#{SecureRandom.hex(8)}.tmp" now = Time.now payload = { body: body, etag: etag, last_modified: last_modified, stored_at: now.to_i } File.write(temp, JSON.generate(payload)) File.rename(temp, path) Entry.new(body: body, etag: etag, last_modified: last_modified, stored_at: now) ensure File.delete(temp) if temp && File.exist?(temp) end |