|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## Project |
| 6 | + |
| 7 | +Cacheable is a Ruby gem by Splitwise that adds method caching via an AOP (Aspect-Oriented Programming) pattern. Include `Cacheable` in a class, annotate methods with `cacheable :method_name`, and results are automatically cached. |
| 8 | + |
| 9 | +## Commands |
| 10 | + |
| 11 | +```bash |
| 12 | +# Install dependencies |
| 13 | +bundle install |
| 14 | + |
| 15 | +# Run full default task (rubocop + rspec) |
| 16 | +bundle exec rake |
| 17 | + |
| 18 | +# Run tests only |
| 19 | +bundle exec rspec |
| 20 | + |
| 21 | +# Run a single test file |
| 22 | +bundle exec rspec spec/cacheable/cacheable_spec.rb |
| 23 | + |
| 24 | +# Run a single test by line number |
| 25 | +bundle exec rspec spec/cacheable/cacheable_spec.rb:45 |
| 26 | + |
| 27 | +# Run linter only |
| 28 | +bundle exec rubocop |
| 29 | + |
| 30 | +# Auto-fix lint issues |
| 31 | +bundle exec rubocop -a |
| 32 | + |
| 33 | +# Watch and auto-run tests/lint on file changes |
| 34 | +bundle exec guard |
| 35 | +``` |
| 36 | + |
| 37 | +## Architecture |
| 38 | + |
| 39 | +The gem uses **module prepending with dynamic method generation** to intercept and cache method calls. |
| 40 | + |
| 41 | +### Core flow |
| 42 | + |
| 43 | +1. **`Cacheable`** (`lib/cacheable.rb`) — The module users include. On `included`, it extends the host class with `MethodGenerator` and creates a unique anonymous interceptor module that gets prepended to the class. |
| 44 | + |
| 45 | +2. **`MethodGenerator`** (`lib/cacheable/method_generator.rb`) — When `cacheable :method_name` is called, this generates five methods on the interceptor module: |
| 46 | + - `method_name` (override) — dispatcher that routes to `with_cache` or `without_cache` based on the `unless:` condition |
| 47 | + - `method_with_cache` — fetch from cache or compute and store |
| 48 | + - `method_without_cache` — bypass cache, call original |
| 49 | + - `method_key_format` — generate the cache key |
| 50 | + - `clear_method_cache` — invalidate the cache entry |
| 51 | + |
| 52 | +3. **`CacheAdapter`** (`lib/cacheable/cache_adapter.rb`) — Protocol for cache backends. Default is `:memory`. Required interface: `fetch(key, options, &block)` and `delete(key)`. |
| 53 | + |
| 54 | +4. **`MemoryAdapter`** (`lib/cacheable/cache_adapters/memory_adapter.rb`) — Built-in hash-backed in-memory cache. Production use typically wires in `Rails.cache` or a custom adapter. |
| 55 | + |
| 56 | +### Key design details |
| 57 | + |
| 58 | +- Each class that includes `Cacheable` gets its own unique interceptor module (created via `Module.new`), which is prepended to the class. This is how `super` chains through to the original method. |
| 59 | +- The `unless:` option accepts a proc/symbol that, when truthy, skips caching and calls the original method directly. |
| 60 | +- `key_format:` accepts a proc receiving `(target, method_name, args, **kwargs)` for custom cache key generation. |
| 61 | + |
| 62 | +## Style |
| 63 | + |
| 64 | +- Max line length: 120 characters |
| 65 | +- Max method length: 25 lines |
| 66 | +- Rubocop enforced with `NewCops: enable` |
| 67 | +- No frozen string literal comments required |
0 commit comments