typed_cache

A robust, type-safe caching library for Ruby that makes cache interactions explicit and predictable.

https://github.com/glossawy/typed_cache

Science Score: 44.0%

This score indicates how likely this project is to be science-related based on various indicators:

  • CITATION.cff file
    Found CITATION.cff file
  • codemeta.json file
    Found codemeta.json file
  • .zenodo.json file
    Found .zenodo.json file
  • DOI references
  • Academic publication links
  • Academic email domains
  • Institutional organization owner
  • JOSS paper metadata
  • Scientific vocabulary similarity
    Low similarity (9.0%) to scientific vocabulary
Last synced: 6 months ago · JSON representation ·

Repository

A robust, type-safe caching library for Ruby that makes cache interactions explicit and predictable.

Basic Info
  • Host: GitHub
  • Owner: glossawy
  • License: apache-2.0
  • Language: Ruby
  • Default Branch: main
  • Size: 1.78 MB
Statistics
  • Stars: 1
  • Watchers: 0
  • Forks: 0
  • Open Issues: 0
  • Releases: 6
Created 7 months ago · Last pushed 7 months ago
Metadata Files
Readme License Citation

README.md

TypedCache

Gem Version GitHub Release Date GitHub last commit

GitHub License

TypedCache is a lightweight, type-safe façade around your favourite Ruby cache stores. It adds three things on top of the raw back-end implementation:

  1. Namespacing – hierarchical Namespace helpers prevent key collisions. You can create nested namespaces easily, like Namespace.at("users", "profiles", "avatars").
  2. Stronger types – RBS signatures as well as monadic types like Either, Maybe, and Snapshot wrap cache results so you always know whether you have a value, an error, or a cache-miss.
  3. Composable decorators – behaviours like instrumentation can be layered on without touching the underlying store.

TL;DR – Think Faraday or Rack middlewares, but for caching.


Installation

```bash bundle add typed_cache && bundle install

or

gem install typed_cache ```

This gem does is also cryptographically signed, if you want to ensure the gem was not tampered with, make sure to use these commands:

```bash bundle add typed_cache && bundle install --trust-policy=HighSecurity

or

gem install typed_cache -P HighSecurity ```

If there are issues with unsigned gems, use MediumSecurity instead.

Quick start

```ruby require "typed_cache"

1. Build a store

store = TypedCache.builder .withbackend(:memory, shared: true) .withinstrumentation(:rails) # e.g. using ActiveSupport .build .value # unwrap Either for brevity

2. Get a reference to a key

user_ref = store.ref("users:123") # => CacheRef

3. Fetch and compute if absent

usersnapshot = userref.fetch do puts "Cache miss! Computing..." { id: 123, name: "Jane" } end.value # => Snapshot

puts "Found: #{usersnapshot.value} (fromcache?=#{usersnapshot.fromcache?})" ```

Builder API

| Step | Purpose | | ------------------------------- | -------------------------------------------------------------- | | with_backend(:name, **opts) | Mandatory. Configure the concrete Backend and its options. | | with_decorator(:key) | Optional. Add a decorator by registry key. | | with_instrumentation(:source) | Optional. Add instrumentation, e.g. :rails or :dry. | | build | Returns Either[Error, Store]. |

Back-ends vs Decorators

  • Back-end (TypedCache::Backend) – persists data (Memory, Redis, etc.).
  • Decorator (TypedCache::Decorator) – wraps an existing store to add behaviour (Instrumentation, Logging, Circuit-Breaker …).

Both include the same public Store interface, so they can be composed freely. Registries keep them separate:

ruby TypedCache::Backends.available # => [:memory, :active_support] TypedCache::Decorators.available # => [:instrumented]

Register your own

```ruby class RedisBackend include TypedCache::Backend # … implement #read, #write, etc. end

TypedCache::Backends.register(:redis, RedisBackend) ```

```ruby class LogDecorator include TypedCache::Decorator def initialize(store) = @store = store def write(key, value) puts "[cache] WRITE #{key}" @store.write(key, value) end # delegate the rest … end

TypedCache::Decorators.register(:logger, LogDecorator) ```

Error handling

All operations return one of:

  • Either.right(Snapshot) – success
  • Either.left(CacheMissError) – key not present
  • Either.left(StoreError) – transport / serialization failure

Use the monad directly or pattern-match:

ruby result.fold( ->(err) { warn err.message }, ->(snapshot) { puts snapshot.value }, )

The CacheRef and Store APIs

While you can call read, write, and fetch directly on the store, the more powerful way to work with TypedCache is via the CacheRef object. It provides a rich, monadic API for a single cache key. The Store also provides fetch_all for batch operations.

You get a CacheRef by calling store.ref(key):

ruby user_ref = store.ref("users:123") # => #<TypedCache::CacheRef ...>

Now you can operate on it:

```ruby

Fetch a value, computing it if it's missing

snapshoteither = userref.fetch do { id: 123, name: "Jane Doe" } end

The result is always an Either[Error, Snapshot]

snapshoteither.fold( ->(err) { warn "Something went wrong: #{err.message}" }, ->(snapshot) { puts "Got value: #{snapshot.value} (from cache: #{snapshot.fromcache?})" } )

You can also map over values

nameeither = userref.map { |user| user[:name] } puts "User name is: #{name_either.value.value}" # unwrap Either, then Snapshot ```

Batch Operations with fetch_all

For retrieving multiple keys at once, the Store provides a fetch_all method. This is more efficient than fetching keys one by one, especially with remote back-ends like Redis.

It takes a list of keys and a block to compute the values for any missing keys.

```ruby userrefs = store.fetchall("users:123", "users:456") do |missingkey| # This block is called for each cache miss userid = missingkey.split(":").last puts "Cache miss for #{missingkey}! Computing..." { id: userid, name: "Fetched User #{userid}" } end

userrefs.each do |key, snapshoteither| snapshot_either.fold( ->(err) { warn "Error for #{key}: #{err.message}" }, ->(snapshot) { puts "Got value for #{key}: #{snapshot.value}" } ) end ```

The CacheRef API encourages a functional style and makes composing cache operations safe and predictable.

Instrumentation

TypedCache can publish events about cache operations using different instrumenters. To enable it, use the with_instrumentation method on the builder, specifying an instrumentation backend:

```ruby

For ActiveSupport::Notifications (e.g. in Rails)

store = TypedCache.builder .withbackend(:memory) .withinstrumentation(:rails) .build.value

For Dry::Monitor

store = TypedCache.builder .withbackend(:memory) .withinstrumentation(:dry) .build.value ```

Events are published to a topic like typed_cache.<operation> (e.g., typed_cache.write). The topic namespace can be configured.

Payload keys include: :namespace, :key, :operation, :duration, and cache_hit.

You can subscribe to these events like so:

```ruby

Example for ActiveSupport

ActiveSupport::Notifications.subscribe("typed_cache.write") do |name, start, finish, id, payload|

Or you can subscribe via the store object itself

instrumenter = store.instrumenter instrumenter.subscribe("write") do |event| payload = event.payload puts "Cache WRITE for key #{payload[:key]} took #{payload[:duration]}ms. Hit? #{payload[:cache_hit]}" end ```

If you call with_instrumentation with no arguments, it uses a Null instrumenter, which has no overhead.

Custom Instrumenters

Just like with back-ends and decorators, you can write and register your own instrumenters. An instrumenter must implement an instrument and a subscribe method.

```ruby class MyCustomInstrumenter include TypedCache::Instrumenter

def instrument(operation, key, **payload, &block) # ... your logic ... end

def subscribe(event_name, **filters, &block) # ... your logic ... end end

Register it

TypedCache::Instrumenters.register(:custom, MyCustomInstrumenter)

Use it

store = TypedCache.builder .with_instrumentation(:custom) # ... ```

Further examples

For more advanced scenarios—including Rails integration, pattern matching, custom back-ends, and testing—see examples.md.

License

This work is licensed under the Apache-2.0 license.

Apache 2.0 License Key Terms

Grants

  • Perpetual, worldwide, non-exclusive, royalty-free license to:
    • Reproduce the work
    • Prepare derivative works
    • Distribute the work
    • Use and sell the work

Requirements

  • Include a copy of the Apache 2.0 License with any distribution
  • Provide attribution
  • Clearly mark any modifications made to the original work
  • Retain all original copyright and license notices

Permissions

  • Commercial use allowed
  • Modification permitted
  • Distribution of original and modified work permitted
  • Patent use granted
  • Private use allowed

Limitations

  • No warranty or liability protection
  • Trademark rights not transferred
  • Contributors not liable for damages

Compatibility

  • Can be used in closed-source and commercial projects
  • Requires preserving original license and attribution

Owner

  • Name: Autumn Winter
  • Login: glossawy
  • Kind: user
  • Location: New York

naming yourself is fun

Citation (CITATION.cff)

cff-version: 1.2.0
message:
  Please use the following metadata when citing this project in your work.
title: Typed Cache
abstract:
version: 0.3.0
license: Apache-2.0
date-released: 2025-08-15
authors:
  - family-names: Winter
    given-names: Autumn
keywords:
  - ruby
repository-code: https://github.com/glossawy/typed_cache
repository-artifact: https://rubygems.org/gems/typed_cache
url: https://github.com/glossawy/typed_cache

GitHub Events

Total
  • Release event: 4
  • Watch event: 1
  • Push event: 32
  • Create event: 5
Last Year
  • Release event: 4
  • Watch event: 1
  • Push event: 32
  • Create event: 5

Packages

  • Total packages: 1
  • Total downloads:
    • rubygems 725 total
  • Total dependent packages: 0
  • Total dependent repositories: 0
  • Total versions: 7
  • Total maintainers: 1
rubygems.org: typed_cache

TypedCache is a Ruby caching library designed to eliminate common caching pitfalls by providing a monadic, type-safe API that makes cache operations explicit and predictable. Cache interactions are first-class operations with comprehensive error handling and transparent state management. The library supports wrapping other caching libraries via custom backends and ActiveSupport::Cache is supported out of the box.

  • Versions: 7
  • Dependent Packages: 0
  • Dependent Repositories: 0
  • Downloads: 725 Total
Rankings
Dependent packages count: 14.3%
Dependent repos count: 43.9%
Average: 50.3%
Downloads: 92.8%
Maintainers (1)
Last synced: 7 months ago

Dependencies

.github/workflows/ci.yml actions
  • actions/checkout v4 composite
  • actions/upload-artifact v4 composite
  • ruby/setup-ruby v1 composite
Gemfile rubygems
  • activesupport ~> 7.1.0 development
  • pry >= 0 development
  • pry-byebug >= 0 development
  • rake ~> 13.3 development
  • rbs >= 0 development
  • rbs-inline >= 0 development
  • rspec ~> 3.13 development
  • rubocop ~> 1.74.0 development
  • rubocop-performance >= 0 development
  • rubocop-rake >= 0 development
  • rubocop-rspec >= 0 development
  • rubocop-shopify >= 0 development
  • rubocop-sorbet ~> 0.9.0 development
  • rubocop-thread_safety >= 0 development
  • steep >= 0 development
  • timecop ~> 0.9.10 development
typed_cache.gemspec rubygems
  • concurrent-ruby ~> 1.3.5
  • concurrent-ruby-edge ~> 0.7.2
  • dry-configurable ~> 1.0
  • dry-struct ~> 1.0
  • dry-types ~> 1.0
  • multi_json ~> 1.17
  • zeitwerk ~> 2.7