safe_memoize 0.7.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +29 -2
- data/.github/workflows/release.yml +5 -1
- data/CHANGELOG.md +119 -100
- data/README.md +385 -7
- data/ROADMAP.md +92 -0
- data/benchmarks/README.md +68 -0
- data/benchmarks/benchmark.rb +225 -0
- data/codecov.yml +17 -0
- data/lib/safe_memoize/adapters/opentelemetry.rb +19 -0
- data/lib/safe_memoize/adapters/statsd.rb +25 -0
- data/lib/safe_memoize/class_methods.rb +15 -4
- data/lib/safe_memoize/configuration.rb +7 -1
- data/lib/safe_memoize/hooks_methods.rb +40 -1
- data/lib/safe_memoize/inspection_methods.rb +14 -1
- data/lib/safe_memoize/public_methods.rb +43 -0
- data/lib/safe_memoize/rails/middleware.rb +23 -0
- data/lib/safe_memoize/rails/request_scoped.rb +37 -0
- data/lib/safe_memoize/rails.rb +28 -0
- data/lib/safe_memoize/version.rb +1 -1
- data/lib/safe_memoize.rb +8 -0
- data/sig/safe_memoize.rbs +32 -1
- metadata +11 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0b47b6031a8f395991376eec0407b93d1cf02caecad23f4d77e4d68234ef87b5
|
|
4
|
+
data.tar.gz: 7542678912a0a39425a75e3addfc718f2abb35068e28e0cdc0444c294dd4c4c1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a304040bf86b1bc359c91f3d687a9f45e020bdaddded53b82a87e473553491aa9daba1ac45f8abadffb8ede8af270479f73762ab47357486aef9b46043afacde
|
|
7
|
+
data.tar.gz: 79d6a552f98f9f2ef1482832c211cc7b0a58f6481c989320c19db63b870a1723b482304955a133c661a0082a07c17de539258a9f869da0b5cbd0aaa2acacd96a
|
data/.github/workflows/ci.yml
CHANGED
|
@@ -10,7 +10,27 @@ on:
|
|
|
10
10
|
permissions:
|
|
11
11
|
contents: read
|
|
12
12
|
|
|
13
|
+
env:
|
|
14
|
+
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
|
15
|
+
|
|
13
16
|
jobs:
|
|
17
|
+
lint:
|
|
18
|
+
name: Lint
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
|
|
21
|
+
steps:
|
|
22
|
+
- name: Check out repository
|
|
23
|
+
uses: actions/checkout@v5
|
|
24
|
+
|
|
25
|
+
- name: Set up Ruby
|
|
26
|
+
uses: ruby/setup-ruby@v1
|
|
27
|
+
with:
|
|
28
|
+
ruby-version: "3.4"
|
|
29
|
+
bundler-cache: true
|
|
30
|
+
|
|
31
|
+
- name: Run StandardRB
|
|
32
|
+
run: bundle exec standardrb
|
|
33
|
+
|
|
14
34
|
test:
|
|
15
35
|
name: Ruby ${{ matrix.ruby }}
|
|
16
36
|
runs-on: ubuntu-latest
|
|
@@ -32,6 +52,13 @@ jobs:
|
|
|
32
52
|
ruby-version: ${{ matrix.ruby }}
|
|
33
53
|
bundler-cache: true
|
|
34
54
|
|
|
35
|
-
- name: Run test
|
|
36
|
-
run: bundle exec
|
|
55
|
+
- name: Run test suite
|
|
56
|
+
run: bundle exec rspec
|
|
57
|
+
|
|
58
|
+
- name: Upload coverage to Codecov
|
|
59
|
+
uses: codecov/codecov-action@v5
|
|
60
|
+
if: matrix.ruby == '3.4'
|
|
61
|
+
with:
|
|
62
|
+
token: ${{ secrets.CODECOV_TOKEN }}
|
|
63
|
+
files: coverage/.resultset.json
|
|
37
64
|
|
|
@@ -7,6 +7,7 @@ on:
|
|
|
7
7
|
|
|
8
8
|
permissions:
|
|
9
9
|
contents: write
|
|
10
|
+
id-token: write
|
|
10
11
|
|
|
11
12
|
jobs:
|
|
12
13
|
release:
|
|
@@ -66,10 +67,13 @@ jobs:
|
|
|
66
67
|
RELEASE_TAG: ${{ github.ref_name }}
|
|
67
68
|
run: echo "RubyGems already has version ${RELEASE_TAG#v}; skipping gem push."
|
|
68
69
|
|
|
70
|
+
- name: Configure RubyGems credentials (trusted publishing)
|
|
71
|
+
if: steps.rubygems.outputs.already_published != 'true'
|
|
72
|
+
uses: rubygems/configure-rubygems-credentials@v1.0.0
|
|
73
|
+
|
|
69
74
|
- name: Publish gem to RubyGems
|
|
70
75
|
if: steps.rubygems.outputs.already_published != 'true'
|
|
71
76
|
env:
|
|
72
|
-
GEM_HOST_API_KEY: ${{ secrets.RUBYGEMS_API_KEY }}
|
|
73
77
|
RELEASE_TAG: ${{ github.ref_name }}
|
|
74
78
|
run: |
|
|
75
79
|
version="${RELEASE_TAG#v}"
|
data/CHANGELOG.md
CHANGED
|
@@ -1,139 +1,158 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html)
|
|
7
|
+
from v1.0.0 onwards. Prior 0.x releases may include breaking changes between minor versions.
|
|
8
|
+
|
|
1
9
|
## [Unreleased]
|
|
2
10
|
|
|
11
|
+
## [0.9.0] - 2026-05-22
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- `ActiveSupport::Notifications` integration — opt-in via `SafeMemoize.configure { |c| c.active_support_notifications = true }`; emits `cache_hit.safe_memoize`, `cache_miss.safe_memoize`, `cache_evict.safe_memoize`, `cache_expire.safe_memoize`, and `cache_store.safe_memoize` events; each payload includes `:method`, `:key`, and `:class`; zero overhead when ActiveSupport is not loaded
|
|
16
|
+
- `SafeMemoize::Adapters::StatsD` — thin optional adapter that routes lifecycle events to any StatsD client via `SafeMemoize.configure { |c| c.statsd_client = my_client }`; emits `safe_memoize.hit`, `safe_memoize.miss`, `safe_memoize.evict`, `safe_memoize.expire`, and `safe_memoize.store` with `method:` and `class:` tags; client errors are rescued and warned rather than raised
|
|
17
|
+
- Formal benchmark suite (`benchmarks/benchmark.rb`) — six scenarios covering zero-arg cache hit/miss, with-argument hit, fast vs locked path, shared vs instance cache, and concurrent throughput under 8-thread contention; optional comparisons against `memery` and `memo_wise`; run with `bundle exec ruby benchmarks/benchmark.rb`
|
|
18
|
+
- Concurrency stress test suite (`spec/concurrency_spec.rb`) — 18 barrier-synchronized examples hammering the fast path, locked path, and shared cache under 30 concurrent threads; covers exactly-once computation, LRU size invariant, hook count integrity, metric accuracy, TTL pruning, and deadlock detection (10-second timeout per run)
|
|
19
|
+
- `SafeMemoize::Adapters::OpenTelemetry` — optional adapter that wraps each cache-miss computation in an OpenTelemetry span; configure via `SafeMemoize.configure { |c| c.opentelemetry_tracer = OpenTelemetry.tracer_provider.tracer("safe_memoize") }`; span name is `"safe_memoize.compute"` with attributes `safe_memoize.method`, `safe_memoize.class`, and `safe_memoize.cache_hit`; falls back to untraced execution when the tracer is absent or does not respond to `in_span`
|
|
20
|
+
- `SafeMemoize::Rails` — opt-in request-scope helpers (`require "safe_memoize/rails"`): `SafeMemoize::Rails::RequestScoped` concern auto-registers `after_action :reset_all_memos` in controllers and exposes `reset_request_memos` elsewhere; `SafeMemoize::Rails::Middleware` Rack middleware resets all thread-tracked instances (`SafeMemoize::Rails.track(self)`) at the end of each request even on error
|
|
21
|
+
|
|
22
|
+
## [0.8.0] - 2026-05-21
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
|
|
26
|
+
- Raise `ArgumentError` at definition time when `memoize` is called on a method that does not exist on the class — previously the error only surfaced at runtime when `super` had nothing to call
|
|
27
|
+
- Key serialization safety: argument arrays, hashes, and strings are deep-frozen into an independent copy when the cache key is built, so callers that mutate their arguments after a call can no longer corrupt or miss the cached entry
|
|
28
|
+
- `memo_inspect` — single-entry deep-inspection helper returning all metadata for one cached call in one mutex-held read: `cached`, `value`, `hits`, `misses`, `ttl_remaining`, `age`, `custom_key`, and `lru_position`; returns `nil` when the entry is not cached
|
|
29
|
+
- Deprecation infrastructure: `SafeMemoize.deprecate(subject, message:, horizon:)` emits a structured `[SafeMemoize]` warning to stderr by default; configurable via `SafeMemoize.configure { |c| c.on_deprecation = ->(msg) { ... } }` to raise, log, or collect warnings
|
|
30
|
+
- `memoize_all only:` — symmetric counterpart to `except:`; explicitly lists the methods to memoize and skips all others; raises `ArgumentError` when both `only:` and `except:` are given
|
|
31
|
+
- Hook error isolation: exceptions raised inside lifecycle hooks no longer propagate to the caller; by default a `[SafeMemoize] Hook error in <type>: <message>` warning is emitted to stderr; configurable via `SafeMemoize.configure { |c| c.on_hook_error = ->(error, hook_type, cache_key) { ... } }` to raise, log, or silence
|
|
32
|
+
|
|
3
33
|
## [0.7.0] - 2026-05-18
|
|
4
34
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
-
|
|
16
|
-
|
|
17
|
-
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
-
|
|
23
|
-
|
|
24
|
-
- `memo_touch(:method, *args, ttl: 30)` sets a new TTL explicitly
|
|
25
|
-
- Returns `true` on success, `false` if the entry is not cached or already expired
|
|
26
|
-
- Add `shared_memo_age` class method to inspect how long ago a shared entry was cached
|
|
27
|
-
- Add `shared_memo_stale?` class method to check whether a shared entry's TTL has elapsed
|
|
28
|
-
- Update RBS type signatures for all new methods and the `Configuration` class
|
|
29
|
-
- Add `key:` option to `memoize` for class-level cache key generation
|
|
30
|
-
- `memoize :method, key: ->(a, b) { a }` defines a key generator at the class level — calls whose key block returns the same value share one cache entry
|
|
31
|
-
- Instance-level `memoize_with_custom_key` still takes priority over `key:`
|
|
32
|
-
- Composes with all existing options (`ttl:`, `max_size:`, `shared:`, `if:`, etc.)
|
|
33
|
-
- Raises `ArgumentError` if `key:` is not callable
|
|
34
|
-
- Add `shared:` support to `memoize_all` (was already functional via `**options` passthrough; now tested and documented)
|
|
35
|
-
- Add `memo_refresh` to force-recompute a cached entry and store the new value in one call
|
|
36
|
-
- Add `memo_age` to return how many seconds ago an entry was cached (`nil` if not cached or expired)
|
|
37
|
-
- Add `memo_stale?` to check whether a cached entry exists but its TTL has elapsed
|
|
35
|
+
### Added
|
|
36
|
+
|
|
37
|
+
- `memo_preload` to batch-warm multiple cache entries in one call — `obj.memo_preload(:find, [1], [2], [3])` calls the memoized method for each arg set, caches all results, and returns them in input order
|
|
38
|
+
- `on_memo_store` hook that fires whenever a value is written to the cache (miss, `warm_memo`, or `load_memo`); completes the full lifecycle hook set alongside `on_hit`, `on_miss`, `on_expire`, and `on_evict`
|
|
39
|
+
- `SafeMemoize.configure` for global default options — `default_ttl` and `default_max_size` apply to all subsequently memoized methods; per-call options override the global defaults
|
|
40
|
+
- `SafeMemoize.reset_configuration!` to restore all global defaults to `nil`
|
|
41
|
+
- `memo_touch` to reset the expiry clock on a cached entry without recomputing — accepts an optional `ttl:` override; returns `true` on success, `false` if the entry is not cached or already expired
|
|
42
|
+
- `shared_memo_age` class method to inspect how long ago a shared entry was cached
|
|
43
|
+
- `shared_memo_stale?` class method to check whether a shared entry's TTL has elapsed
|
|
44
|
+
- `key:` option on `memoize` for class-level cache key generation — calls whose key block returns the same value share one cache entry; instance-level `memoize_with_custom_key` still takes priority
|
|
45
|
+
- `memo_refresh` to force-recompute a cached entry and store the new value in one call
|
|
46
|
+
- `memo_age` to return how many seconds ago an entry was cached (`nil` if not cached or expired)
|
|
47
|
+
- `memo_stale?` to check whether a cached entry exists but its TTL has elapsed
|
|
48
|
+
|
|
49
|
+
### Changed
|
|
50
|
+
|
|
51
|
+
- `cache_metrics_reset` now accepts an optional method name to clear stats for a single method only; calling without arguments still clears all metrics
|
|
52
|
+
- `shared:` support in `memoize_all` is now tested and documented (was already functional via `**options` passthrough)
|
|
53
|
+
- RBS type signatures updated for all new methods and the `Configuration` class
|
|
38
54
|
|
|
39
55
|
## [0.6.3] - 2026-05-18
|
|
40
56
|
|
|
57
|
+
### Changed
|
|
58
|
+
|
|
41
59
|
- Upgrade `softprops/action-gh-release` from v2 to v3 to resolve Node.js 20 deprecation warning in release workflow
|
|
42
60
|
|
|
43
61
|
## [0.6.2] - 2026-05-18
|
|
44
62
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
- Add tests for private `memo_cache_read` in `CacheStoreMethods` covering nil cache, live hit, and expired entry
|
|
49
|
-
- Add tests for `memo_keys` / `memo_values` with custom-key entries, covering the `custom_key:` projection branch in `InspectionMethods`
|
|
50
|
-
- Add missing error-case tests for `ReleaseTooling.update_version_file` (no VERSION constant) and `finalize_changelog` (no Unreleased heading)
|
|
63
|
+
### Added
|
|
64
|
+
|
|
65
|
+
- 100% line coverage across all lib files — added tests for edge cases in `CacheRecordMethods`, `CacheStoreMethods`, `InspectionMethods`, and `ReleaseTooling`; added SimpleCov filter to exclude `/spec` from coverage reporting
|
|
51
66
|
|
|
52
67
|
## [0.6.1] - 2026-05-17
|
|
53
68
|
|
|
54
|
-
|
|
55
|
-
|
|
69
|
+
### Changed
|
|
70
|
+
|
|
71
|
+
- Refactored `cache_stats` / `cache_stats_for` to share aggregation logic via private helpers
|
|
72
|
+
|
|
73
|
+
### Fixed
|
|
74
|
+
|
|
75
|
+
- `memo_keys` and `memo_values` showed `args: custom_key, kwargs: nil` for methods using `memoize_with_custom_key` — now correctly surfaces as `custom_key:`
|
|
56
76
|
|
|
57
77
|
## [0.6.0] - 2026-05-17
|
|
58
78
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
-
|
|
62
|
-
-
|
|
63
|
-
-
|
|
64
|
-
-
|
|
65
|
-
-
|
|
66
|
-
|
|
79
|
+
### Added
|
|
80
|
+
|
|
81
|
+
- `ttl:` option on `warm_memo` so warmed entries can be given an expiry
|
|
82
|
+
- `max_size:` support for `shared: true` memoization (class-level LRU eviction)
|
|
83
|
+
- `ttl_refresh: true` option on `memoize` for sliding window TTL — resets the expiry clock on every cache hit so the entry only expires after a full TTL of inactivity
|
|
84
|
+
- `include_protected:` and `include_private:` options on `memoize_all`
|
|
85
|
+
- `memo_ttl_remaining` for TTL introspection — returns seconds until expiry, `nil` for no TTL, `0` for uncached or expired
|
|
86
|
+
|
|
87
|
+
### Fixed
|
|
88
|
+
|
|
89
|
+
- TTL clock started at `memoize` definition time instead of at first method call
|
|
90
|
+
- Metrics key silently dropped kwargs, causing methods that differ only in kwargs to share a metrics bucket
|
|
91
|
+
- Stale LRU references remained in the order list after expired entries were pruned
|
|
67
92
|
|
|
68
93
|
## [0.5.0] - 2026-05-17
|
|
69
94
|
|
|
70
|
-
|
|
95
|
+
### Removed
|
|
96
|
+
|
|
97
|
+
- Support for Ruby 3.2 (EOL); minimum required version is now Ruby 3.3
|
|
71
98
|
|
|
72
99
|
## [0.4.0] - 2026-05-17
|
|
73
100
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
-
|
|
80
|
-
- All instances share one cache; the method is computed only once regardless of how many objects exist
|
|
81
|
-
- Class-level invalidation: `reset_shared_memo`, `reset_all_shared_memos`
|
|
82
|
-
- Class-level inspection: `shared_memoized?`, `shared_memo_count`
|
|
83
|
-
- Supports `ttl:`, `if:`, and `unless:` options
|
|
84
|
-
- Instance hooks (`on_memo_hit`, `on_memo_miss`, `on_memo_expire`) fire on the calling instance
|
|
85
|
-
- Add `memoize_all` to memoize every public method defined on the class in one call
|
|
86
|
-
- Accepts all options supported by `memoize` (`ttl:`, `max_size:`, `if:`, `unless:`)
|
|
87
|
-
- `except:` option to skip specific methods by name
|
|
88
|
-
- Only affects public methods defined directly on the class
|
|
89
|
-
- Add `on_memo_miss` hook that fires on every cache miss, completing the full lifecycle hook set alongside `on_memo_hit`, `on_memo_evict`, and `on_memo_expire`
|
|
101
|
+
### Added
|
|
102
|
+
|
|
103
|
+
- `warm_memo`, `dump_memo`, and `load_memo` for cache warm-up and persistence — pre-populate entries without calling the method, export live entries as a plain hash, and restore from a snapshot
|
|
104
|
+
- `shared: true` option on `memoize` to store results on the class instead of per-instance — includes `reset_shared_memo`, `reset_all_shared_memos`, `shared_memoized?`, and `shared_memo_count`; supports `ttl:`, `if:`, and `unless:`
|
|
105
|
+
- `memoize_all` to memoize every public method defined on the class in one call — accepts all `memoize` options plus `except:` to skip specific methods
|
|
106
|
+
- `on_memo_miss` hook that fires on every cache miss, completing the full lifecycle hook set
|
|
90
107
|
|
|
91
108
|
## [0.3.0] - 2026-05-15
|
|
92
109
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
- Compatible with `ttl:`, `max_size:`, hooks, and all inspection APIs
|
|
99
|
-
- Add LRU cache size limit via `max_size:` option on `memoize`
|
|
100
|
-
- Evicts the least-recently-used entry per method when the limit is reached
|
|
101
|
-
- Cache hits promote entries to most-recently-used, preventing premature eviction
|
|
102
|
-
- Fires the existing `on_evict` hook for LRU-evicted entries
|
|
103
|
-
- Self-healing: stale LRU references left by `reset_memo` are pruned automatically
|
|
104
|
-
- Compatible with `ttl:` option and all existing inspection/reset APIs
|
|
105
|
-
- Thread-safe under concurrent access
|
|
110
|
+
### Added
|
|
111
|
+
|
|
112
|
+
- `on_memo_hit` hook that fires on every cache hit
|
|
113
|
+
- Conditional memoization via `if:` and `unless:` predicates on `memoize` — uncached calls recompute on every invocation until the condition is satisfied; composes with `ttl:`, `max_size:`, and hooks
|
|
114
|
+
- LRU cache size limit via `max_size:` on `memoize` — evicts the least-recently-used entry when the limit is reached; cache hits promote entries; fires `on_evict`; thread-safe
|
|
106
115
|
|
|
107
116
|
## [0.2.0] - 2026-05-14
|
|
108
117
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
-
|
|
115
|
-
- `cache_stats` for comprehensive cache metrics
|
|
116
|
-
- `cache_stats_for(method_name)` for per-method statistics
|
|
117
|
-
- `cache_hit_rate` and `cache_miss_rate` for performance analysis
|
|
118
|
-
- `cache_metrics_reset` to clear collected metrics
|
|
119
|
-
- Add manual cache key generation support
|
|
120
|
-
- `memoize_with_custom_key` to define custom cache key logic
|
|
121
|
-
- `clear_custom_keys` to remove custom key generators
|
|
122
|
-
- Support for complex and computed keys based on arguments
|
|
118
|
+
### Added
|
|
119
|
+
|
|
120
|
+
- Optional TTL expiration for memoized entries
|
|
121
|
+
- `on_memo_expire` and `on_memo_evict` lifecycle hooks; `clear_memo_hooks` to remove registered hooks
|
|
122
|
+
- Cache metrics: `cache_stats`, `cache_stats_for`, `cache_hit_rate`, `cache_miss_rate`, and `cache_metrics_reset`
|
|
123
|
+
- Custom cache key generation via `memoize_with_custom_key` and `clear_custom_keys`
|
|
123
124
|
|
|
124
125
|
## [0.1.2] - 2026-05-13
|
|
125
126
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
-
|
|
129
|
-
-
|
|
130
|
-
-
|
|
131
|
-
-
|
|
127
|
+
### Added
|
|
128
|
+
|
|
129
|
+
- Method visibility preservation (public, protected, private) for memoized methods
|
|
130
|
+
- Targeted `reset_memo` — clear one cached argument combination or all entries for a method
|
|
131
|
+
- `memoized?` helper to check whether a specific call is cached
|
|
132
|
+
- `memo_count`, `memo_keys`, and `memo_values` helpers for cache introspection
|
|
132
133
|
|
|
133
134
|
## [0.1.1] - 2026-05-13
|
|
134
135
|
|
|
135
|
-
|
|
136
|
+
### Added
|
|
137
|
+
|
|
138
|
+
- Automated release tooling (`bin/release`) and GitHub Actions workflow for RubyGems publishing and GitHub releases
|
|
136
139
|
|
|
137
140
|
## [0.1.0] - 2026-02-26
|
|
138
141
|
|
|
142
|
+
### Added
|
|
143
|
+
|
|
139
144
|
- Initial release
|
|
145
|
+
|
|
146
|
+
[Unreleased]: https://github.com/eclectic-coding/safe_memoize/compare/v0.7.0...HEAD
|
|
147
|
+
[0.7.0]: https://github.com/eclectic-coding/safe_memoize/compare/v0.6.3...v0.7.0
|
|
148
|
+
[0.6.3]: https://github.com/eclectic-coding/safe_memoize/compare/v0.6.2...v0.6.3
|
|
149
|
+
[0.6.2]: https://github.com/eclectic-coding/safe_memoize/compare/v0.6.1...v0.6.2
|
|
150
|
+
[0.6.1]: https://github.com/eclectic-coding/safe_memoize/compare/v0.6.0...v0.6.1
|
|
151
|
+
[0.6.0]: https://github.com/eclectic-coding/safe_memoize/compare/v0.5.0...v0.6.0
|
|
152
|
+
[0.5.0]: https://github.com/eclectic-coding/safe_memoize/compare/v0.4.0...v0.5.0
|
|
153
|
+
[0.4.0]: https://github.com/eclectic-coding/safe_memoize/compare/v0.3.0...v0.4.0
|
|
154
|
+
[0.3.0]: https://github.com/eclectic-coding/safe_memoize/compare/v0.2.0...v0.3.0
|
|
155
|
+
[0.2.0]: https://github.com/eclectic-coding/safe_memoize/compare/v0.1.2...v0.2.0
|
|
156
|
+
[0.1.2]: https://github.com/eclectic-coding/safe_memoize/compare/v0.1.1...v0.1.2
|
|
157
|
+
[0.1.1]: https://github.com/eclectic-coding/safe_memoize/compare/v0.1.0...v0.1.1
|
|
158
|
+
[0.1.0]: https://github.com/eclectic-coding/safe_memoize/releases/tag/v0.1.0
|