philiprehberger-expiring_map

Tests Gem Version Last updated

Thread-safe hash with per-key TTL and automatic expiration

Requirements

  • Ruby >= 3.1

Installation

Add to your Gemfile:

gem "philiprehberger-expiring_map"

Or install directly:

gem install philiprehberger-expiring_map

Usage

require "philiprehberger/expiring_map"

cache = Philiprehberger::ExpiringMap.new(default_ttl: 300)
cache.set(:session, 'abc123')
cache.get(:session)  # => 'abc123'

Per-Key TTL

cache.set(:token, 'xyz', ttl: 60)    # expires in 60 seconds
cache.set(:config, data, ttl: 3600)   # expires in 1 hour
cache.ttl(:token)                      # => remaining seconds

Fetch-or-compute

cache = Philiprehberger::ExpiringMap.new(default_ttl: 300)

# On miss, the block is evaluated, the result stored, and returned.
# On hit, the stored value is returned and the block is not called.
user = cache.fetch(:user_42) { User.find(42) }

# Override the TTL for this insert only:
token = cache.fetch(:token, ttl: 60) { Auth.issue_token }

Max Size with Eviction

cache = Philiprehberger::ExpiringMap.new(default_ttl: 300, max_size: 1000)
# Oldest entries are evicted when capacity is reached

Expiration Callback

cache.on_expire do |key, value|
  logger.info("Expired: #{key}")
end

Touch to Reset TTL

cache.set(:session, data)
cache.touch(:session)  # resets TTL to default

Bulk Operations

cache.set_many({ user: "alice", role: "admin", theme: "dark" })
cache.set_many({ token: "abc", nonce: "xyz" }, ttl: 60)

result = cache.get_many(:user, :role, :missing)
# => { user: "alice", role: "admin", missing: nil }

Statistics

cache.set(:a, 1)
cache.get(:a)        # hit
cache.get(:missing)  # miss
cache.stats
# => { hits: 1, misses: 1, expirations: 0, evictions: 0, size: 1 }

Keys and Values

cache.set(:x, 10)
cache.set(:y, 20)
cache.keys    # => [:x, :y]
cache.values  # => [10, 20]

Predicate Deletion

cache.set(:a, 1)
cache.set(:b, 10)
cache.delete_if { |_key, value| value >= 10 }  # => 1

Enumerable

cache.each { |key, value| puts "#{key}: #{value}" }
cache.select { |_k, v| v > 10 }

API

Method Description
.new(default_ttl:, max_size:) Create a new expiring map
#set(key, value, ttl:) Store a value with optional per-key TTL
#get(key) Retrieve a value, nil if expired or missing
#fetch(key, ttl:) { block } Return stored value or atomically memoize block result on miss
#set_many(hash, ttl:) Bulk insert from a hash
#get_many(*keys) Bulk get returning hash of key => value
#delete(key) Remove and return a value
`#delete_if { \ k, v\
#ttl(key) Return remaining TTL in seconds
#touch(key) Reset TTL to default
#size Count of non-expired entries
#keys Array of non-expired keys
#values Array of non-expired values
#stats Hash of hits, misses, expirations, evictions, size
`#on_expire { \ k, v\
#clear Remove all entries
`#each { \ k, v\

Development

bundle install
bundle exec rspec
bundle exec rubocop

Support

If you find this project useful:

Star the repo

🐛 Report issues

💡 Suggest features

❤️ Sponsor development

🌐 All Open Source Projects

💻 GitHub Profile

🔗 LinkedIn Profile

License

MIT