typed_cache
A robust, type-safe caching library for Ruby that makes cache interactions explicit and predictable.
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
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
Metadata Files
README.md
TypedCache
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:
- Namespacing – hierarchical
Namespacehelpers prevent key collisions. You can create nested namespaces easily, likeNamespace.at("users", "profiles", "avatars"). - Stronger types – RBS signatures as well as monadic types like
Either,Maybe, andSnapshotwrap cache results so you always know whether you have a value, an error, or a cache-miss. - 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)– successEither.left(CacheMissError)– key not presentEither.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
- Repositories: 18
- Profile: https://github.com/glossawy
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.
- Homepage: https://github.com/glossawy/typed_cache
- Documentation: http://www.rubydoc.info/gems/typed_cache/
- License: Apache-2.0
-
Latest release: 0.3.2
published 7 months ago
Rankings
Maintainers (1)
Dependencies
- actions/checkout v4 composite
- actions/upload-artifact v4 composite
- ruby/setup-ruby v1 composite
- 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
- 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