Class: Lutaml::Hal::Cache::CacheEntry

Inherits:
Object
  • Object
show all
Defined in:
lib/lutaml/hal/cache/cache_entry.rb

Overview

Represents a complete cached entry with metadata and HAL resource

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(url: nil, cached_at: nil, metadata: nil, hal_resource: nil) ⇒ CacheEntry

Returns a new instance of CacheEntry.



13
14
15
16
17
18
# File 'lib/lutaml/hal/cache/cache_entry.rb', line 13

def initialize(url: nil, cached_at: nil, metadata: nil, hal_resource: nil)
  @url = url
  @cached_at = cached_at
  @metadata = 
  @hal_resource = hal_resource
end

Instance Attribute Details

#cached_atObject

Returns the value of attribute cached_at.



11
12
13
# File 'lib/lutaml/hal/cache/cache_entry.rb', line 11

def cached_at
  @cached_at
end

#hal_resourceObject

Returns the value of attribute hal_resource.



11
12
13
# File 'lib/lutaml/hal/cache/cache_entry.rb', line 11

def hal_resource
  @hal_resource
end

#metadataObject

Returns the value of attribute metadata.



11
12
13
# File 'lib/lutaml/hal/cache/cache_entry.rb', line 11

def 
  @metadata
end

#urlObject

Returns the value of attribute url.



11
12
13
# File 'lib/lutaml/hal/cache/cache_entry.rb', line 11

def url
  @url
end

Class Method Details

.create(url, response, hal_resource) ⇒ Object

Create a cache entry from a URL, response, and realized HAL resource



67
68
69
70
71
72
73
74
# File 'lib/lutaml/hal/cache/cache_entry.rb', line 67

def self.create(url, response, hal_resource)
  new(
    url: url,
    cached_at: Time.now.to_s,
    metadata: CacheMetadata.from_response(response),
    hal_resource: hal_resource
  )
end

.from_storage_h(hash) ⇒ Object

Rebuild a CacheEntry from a to_storage_h document. Tolerates string or symbol keys, since lutaml-store parses persisted JSON with symbolize_names.



48
49
50
51
52
53
54
55
56
# File 'lib/lutaml/hal/cache/cache_entry.rb', line 48

def self.from_storage_h(hash)
  h = hash.transform_keys(&:to_s)
  new(
    url: h['url'],
    cached_at: h['cached_at'],
    metadata: h['metadata'] ? CacheMetadata.from_json(h['metadata']) : nil,
    hal_resource: rebuild_model(h['model_class'], h['model'])
  )
end

.rebuild_model(class_name, model_json) ⇒ Object



58
59
60
61
62
63
64
# File 'lib/lutaml/hal/cache/cache_entry.rb', line 58

def self.rebuild_model(class_name, model_json)
  return nil unless class_name && model_json

  Object.const_get(class_name).from_json(model_json)
rescue NameError
  nil
end

.storage_format?(hash) ⇒ Boolean

True if the given hash looks like a to_storage_h document (as opposed to a legacy in-memory cache hash holding a live :realized_model).

Returns:

  • (Boolean)


40
41
42
43
# File 'lib/lutaml/hal/cache/cache_entry.rb', line 40

def self.storage_format?(hash)
  hash.key?('model') || hash.key?(:model) ||
    hash.key?('model_class') || hash.key?(:model_class)
end

Instance Method Details

#ageObject

Get cache age in seconds



116
117
118
119
120
121
# File 'lib/lutaml/hal/cache/cache_entry.rb', line 116

def age
  return 0 unless cached_at

  cached_time = cached_at.is_a?(String) ? Time.parse(cached_at) : cached_at
  Time.now - cached_time
end

#cacheable?Boolean

Check if the response is cacheable based on metadata

Returns:

  • (Boolean)


105
106
107
# File 'lib/lutaml/hal/cache/cache_entry.rb', line 105

def cacheable?
  &.cacheable? != false
end

#conditional_headersObject

Get conditional headers for revalidation



100
101
102
# File 'lib/lutaml/hal/cache/cache_entry.rb', line 100

def conditional_headers
  &.conditional_headers || {}
end

#expired?(default_ttl) ⇒ Boolean

Check if the entry is expired and needs revalidation

Returns:

  • (Boolean)


88
89
90
# File 'lib/lutaml/hal/cache/cache_entry.rb', line 88

def expired?(default_ttl)
  !valid?(default_ttl)
end

#refresh_metadata(response) ⇒ Object

Update the cache entry with fresh metadata (for 304 responses)



110
111
112
113
# File 'lib/lutaml/hal/cache/cache_entry.rb', line 110

def (response)
  self.cached_at = Time.now.to_s
  self. = CacheMetadata.from_response(response)
end

#revalidatable?Boolean

Check if the entry can be revalidated with conditional requests

Returns:

  • (Boolean)


93
94
95
96
97
# File 'lib/lutaml/hal/cache/cache_entry.rb', line 93

def revalidatable?
  return false unless 

  !!(.etag || .last_modified)
end

#serve_stale?(max_stale = nil) ⇒ Boolean

Check if entry should be served stale (useful for error scenarios)

Returns:

  • (Boolean)


124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/lutaml/hal/cache/cache_entry.rb', line 124

def serve_stale?(max_stale = nil)
  return false unless max_stale
  return false if valid?(Float::INFINITY) # Still fresh

  cached_time = cached_at.is_a?(String) ? Time.parse(cached_at) : cached_at
  current_age = Time.now - cached_time
  ttl = &.max_age || 0

  # Entry is stale if current_age > ttl
  # But we can serve it if the staleness is within the max_stale window
  staleness = current_age - ttl
  current_age > ttl && staleness < max_stale
end

#to_json(*_args) ⇒ Object

Called by lutaml-store’s CacheStore when serializing a persisted entry.



34
35
36
# File 'lib/lutaml/hal/cache/cache_entry.rb', line 34

def to_json(*_args)
  JSON.generate(to_storage_h)
end

#to_storage_hObject

Plain-hash representation suitable for JSON persistence. The HAL resource and its class are recorded so the model can be rebuilt; the metadata is kept as its own JSON document.



23
24
25
26
27
28
29
30
31
# File 'lib/lutaml/hal/cache/cache_entry.rb', line 23

def to_storage_h
  {
    'url' => url,
    'cached_at' => cached_at,
    'metadata' => &.to_json,
    'model_class' => hal_resource&.class&.name,
    'model' => hal_resource&.to_json
  }
end

#valid?(default_ttl) ⇒ Boolean

Check if the cache entry is still valid based on TTL

Returns:

  • (Boolean)


77
78
79
80
81
82
83
84
85
# File 'lib/lutaml/hal/cache/cache_entry.rb', line 77

def valid?(default_ttl)
  return false unless cached_at

  cached_time = cached_at.is_a?(String) ? Time.parse(cached_at) : cached_at
  age = Time.now - cached_time
  ttl = &.max_age || default_ttl

  age < ttl
end