Class: Rospatent::Cache

Inherits:
Object
  • Object
show all
Includes:
MonitorMixin
Defined in:
lib/rospatent/cache.rb

Overview

Simple in-memory cache with TTL and size limits

Defined Under Namespace

Classes: CacheEntry

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(ttl: 300, max_size: 1000) ⇒ Cache

Initialize a new cache

Parameters:

  • ttl (Integer) (defaults to: 300)

    Time to live in seconds

  • max_size (Integer) (defaults to: 1000)

    Maximum number of entries



26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/rospatent/cache.rb', line 26

def initialize(ttl: 300, max_size: 1000)
  super()
  @ttl = ttl
  @max_size = max_size
  @store = {}
  @access_order = []
  @stats = {
    hits: 0,
    misses: 0,
    evictions: 0,
    expired: 0
  }
end

Instance Attribute Details

#statsObject (readonly)

Returns the value of attribute stats.



21
22
23
# File 'lib/rospatent/cache.rb', line 21

def stats
  @stats
end

Instance Method Details

#cleanup_expiredInteger

Clean up expired entries

Returns:

  • (Integer)

    Number of expired entries removed



160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/rospatent/cache.rb', line 160

def cleanup_expired
  synchronize do
    expired_keys = []
    @store.each do |key, entry|
      expired_keys << key if entry.expired?
    end

    expired_keys.each { |key| delete_entry(key) }
    @stats[:expired] += expired_keys.size

    expired_keys.size
  end
end

#clearObject

Clear all entries from the cache



123
124
125
126
127
128
129
# File 'lib/rospatent/cache.rb', line 123

def clear
  synchronize do
    @store.clear
    @access_order.clear
    reset_stats
  end
end

#delete(key) ⇒ Object?

Delete a specific key

Parameters:

  • key (String)

    Cache key

Returns:

  • (Object, nil)

    Deleted value or nil if not found



114
115
116
117
118
119
120
# File 'lib/rospatent/cache.rb', line 114

def delete(key)
  synchronize do
    entry = @store.delete(key)
    @access_order.delete(key)
    entry&.value
  end
end

#empty?Boolean

Check if cache is empty

Returns:

  • (Boolean)

    true if cache has no entries



139
140
141
# File 'lib/rospatent/cache.rb', line 139

def empty?
  synchronize { @store.empty? }
end

#fetch(key, default_value = nil, ttl: nil) { ... } ⇒ Object

Fetch value with fallback block or default value

Parameters:

  • key (String)

    Cache key

  • default_value (Object, nil) (defaults to: nil)

    Default value to return if cache miss (optional)

  • ttl (Integer, nil) (defaults to: nil)

    Custom TTL for this entry

Yields:

  • Block to execute if cache miss

Returns:

  • (Object)

    Cached value, default value, or result of block



180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
# File 'lib/rospatent/cache.rb', line 180

def fetch(key, default_value = nil, ttl: nil)
  value = get(key)
  return value unless value.nil?

  result = if default_value
             default_value
           elsif block_given?
             yield
           else
             return nil
           end

  set(key, result, ttl: ttl) unless result.nil?
  result
end

#get(key) ⇒ Object?

Get a value from the cache

Parameters:

  • key (String)

    Cache key

Returns:

  • (Object, nil)

    Cached value or nil if not found/expired



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/rospatent/cache.rb', line 43

def get(key)
  synchronize do
    entry = @store[key]

    unless entry
      @stats[:misses] += 1
      return nil
    end

    if entry.expired?
      delete_entry(key)
      @stats[:expired] += 1
      @stats[:misses] += 1
      return nil
    end

    # Update access order for LRU
    @access_order.delete(key)
    @access_order.push(key)

    entry.touch!
    @stats[:hits] += 1
    entry.value
  end
end

#key?(key) ⇒ Boolean

Check if a key exists and is not expired

Parameters:

  • key (String)

    Cache key

Returns:

  • (Boolean)

    true if key exists and is valid



96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/rospatent/cache.rb', line 96

def key?(key)
  synchronize do
    entry = @store[key]
    return false unless entry

    if entry.expired?
      delete_entry(key)
      @stats[:expired] += 1
      return false
    end

    true
  end
end

#set(key, value, ttl: nil) ⇒ Object

Set a value in the cache

Parameters:

  • key (String)

    Cache key

  • value (Object)

    Value to cache

  • ttl (Integer, nil) (defaults to: nil)

    Custom TTL for this entry (optional)



73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/rospatent/cache.rb', line 73

def set(key, value, ttl: nil)
  synchronize do
    effective_ttl = ttl || @ttl
    expires_at = Time.now + effective_ttl

    entry = CacheEntry.new(value, expires_at, Time.now, 0)

    # Remove existing entry if present
    @access_order.delete(key) if @store.key?(key)

    @store[key] = entry
    @access_order.push(key)

    # Evict entries if over size limit
    evict_if_needed

    value
  end
end

#sizeInteger

Get current cache size

Returns:

  • (Integer)

    Number of entries in cache



133
134
135
# File 'lib/rospatent/cache.rb', line 133

def size
  synchronize { @store.size }
end

#statisticsHash

Get cache statistics

Returns:

  • (Hash)

    Statistics including hits, misses, hit rate, etc.



145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/rospatent/cache.rb', line 145

def statistics
  synchronize do
    total_requests = @stats[:hits] + @stats[:misses]
    hit_rate = total_requests.positive? ? (@stats[:hits].to_f / total_requests * 100).round(2) : 0

    @stats.merge(
      size: @store.size,
      total_requests: total_requests,
      hit_rate_percent: hit_rate
    )
  end
end