safe_memoize 0.1.0 → 0.1.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4806562a18a6f5143379d638d270ea3552280637dd33ed322c0f667f1257d836
4
- data.tar.gz: bdb12e7ae3a41d230c85fd24750edcc5eeba4d90cf2ac93b59cd0bccc8ba0325
3
+ metadata.gz: 00d20095844739078f88cd287d4389b749f9953b9b6055939d733f2da139c19d
4
+ data.tar.gz: 5c4336dbe5e3ee1e1e2b822b92a3abd4f6d5452dedd36d99706c54eb8528ed7a
5
5
  SHA512:
6
- metadata.gz: 4799eb0209980d5fee072ae05ff1661d70b8db5869e8701cac3fcd9241c1106f17fa84a798f85bcb084c197c24c646e2996fe60fa7375d4db8d780ad3bc4b71a
7
- data.tar.gz: 6d64a7e4bef4d5b3825d903294bc955c1ec483157aee479496e001cb500e6128804e148791793d862938543bf5e9e7ee05cca128adb4bb37e68a9d4858bcb517
6
+ metadata.gz: 5d14486d2a280e2e243e2c54184875d175447f8dc921ff9e152374e9f8327061b0b4ce6de0e9213871000c0950a8cab146db7d303a0de8242d081c30e54c53ce
7
+ data.tar.gz: 67be0eb15e01782f7dc6b0a35cb79c24677f8d56ebf042a9dfdd49d9ec88a44c08711af24063150688c0b1ad9047c70a6c3754fd1cc662ba54a83000c1c5247a
@@ -0,0 +1,38 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ pull_request:
8
+ workflow_dispatch:
9
+
10
+ permissions:
11
+ contents: read
12
+
13
+ jobs:
14
+ test:
15
+ name: Ruby ${{ matrix.ruby }}
16
+ runs-on: ubuntu-latest
17
+ strategy:
18
+ fail-fast: false
19
+ matrix:
20
+ ruby:
21
+ - "3.2"
22
+ - "3.3"
23
+ - "3.4"
24
+ - "4.0"
25
+
26
+ steps:
27
+ - name: Check out repository
28
+ uses: actions/checkout@v5
29
+
30
+ - name: Set up Ruby
31
+ uses: ruby/setup-ruby@v1
32
+ with:
33
+ ruby-version: ${{ matrix.ruby }}
34
+ bundler-cache: true
35
+
36
+ - name: Run test and lint suite
37
+ run: bundle exec rake
38
+
@@ -0,0 +1,91 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ permissions:
9
+ contents: write
10
+
11
+ jobs:
12
+ release:
13
+ runs-on: ubuntu-latest
14
+
15
+ steps:
16
+ - name: Check out repository
17
+ uses: actions/checkout@v5
18
+ with:
19
+ fetch-depth: 0
20
+
21
+ - name: Set up Ruby
22
+ uses: ruby/setup-ruby@v1
23
+ with:
24
+ ruby-version: "3.4"
25
+ bundler-cache: true
26
+
27
+ - name: Verify tag matches gem version
28
+ env:
29
+ RELEASE_TAG: ${{ github.ref_name }}
30
+ run: |
31
+ expected_version="${RELEASE_TAG#v}"
32
+ actual_version="$(bundle exec ruby -e 'require_relative "lib/safe_memoize/version"; print SafeMemoize::VERSION')"
33
+
34
+ if [ "$actual_version" != "$expected_version" ]; then
35
+ echo "Tag $expected_version does not match gem version $actual_version"
36
+ exit 1
37
+ fi
38
+
39
+ - name: Run test suite
40
+ run: bundle exec rake
41
+
42
+ - name: Build gem artifact
43
+ env:
44
+ RELEASE_TAG: ${{ github.ref_name }}
45
+ run: |
46
+ version="${RELEASE_TAG#v}"
47
+ mkdir -p pkg
48
+ gem build safe_memoize.gemspec --output "pkg/safe_memoize-${version}.gem"
49
+
50
+ - name: Check whether the gem version is already on RubyGems
51
+ id: rubygems
52
+ env:
53
+ RELEASE_TAG: ${{ github.ref_name }}
54
+ run: |
55
+ version="${RELEASE_TAG#v}"
56
+
57
+ if ruby -rjson -ropen-uri -e 'version = ARGV.fetch(0); versions = JSON.parse(URI.open("https://rubygems.org/api/v1/versions/safe_memoize.json", &:read)); exit(versions.any? { |item| item["number"] == version } ? 0 : 1)' "$version"; then
58
+ echo "already_published=true" >> "$GITHUB_OUTPUT"
59
+ else
60
+ echo "already_published=false" >> "$GITHUB_OUTPUT"
61
+ fi
62
+
63
+ - name: Skip RubyGems publish for existing versions
64
+ if: steps.rubygems.outputs.already_published == 'true'
65
+ env:
66
+ RELEASE_TAG: ${{ github.ref_name }}
67
+ run: echo "RubyGems already has version ${RELEASE_TAG#v}; skipping gem push."
68
+
69
+ - name: Publish gem to RubyGems
70
+ if: steps.rubygems.outputs.already_published != 'true'
71
+ env:
72
+ GEM_HOST_API_KEY: ${{ secrets.RUBYGEMS_API_KEY }}
73
+ RELEASE_TAG: ${{ github.ref_name }}
74
+ run: |
75
+ version="${RELEASE_TAG#v}"
76
+ gem push "pkg/safe_memoize-${version}.gem"
77
+
78
+ - name: Generate GitHub release notes from CHANGELOG
79
+ env:
80
+ RELEASE_TAG: ${{ github.ref_name }}
81
+ run: |
82
+ version="${RELEASE_TAG#v}"
83
+ bundle exec ruby bin/release_notes "$version" "${RUNNER_TEMP}/release_notes.md"
84
+
85
+ - name: Create GitHub release
86
+ uses: softprops/action-gh-release@v2
87
+ with:
88
+ body_path: ${{ runner.temp }}/release_notes.md
89
+ files: pkg/*.gem
90
+ tag_name: ${{ github.ref_name }}
91
+
data/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.1.2] - 2026-05-13
4
+
5
+ - Preserve public, protected, and private visibility for memoized methods
6
+ - Allow reset_memo to clear one cached argument combination or all entries for a method
7
+ - Add a memoized? helper for checking whether a method call is already cached
8
+ - Add a memo_count helper for inspecting cache size per instance or method
9
+ - Add a memo_keys helper for inspecting cached argument signatures
10
+ - Add a memo_values helper for inspecting cached signatures and their values
11
+
12
+ ## [0.1.1] - 2026-05-13
13
+
14
+ - Add automated release tooling plus a GitHub Actions workflow for RubyGems publishing and GitHub releases
15
+
3
16
  ## [0.1.0] - 2026-02-26
4
17
 
5
18
  - Initial release
data/README.md CHANGED
@@ -21,6 +21,12 @@ SafeMemoize uses `Hash#key?` to distinguish "not yet cached" from "cached nil/fa
21
21
  - Thread-safe via double-check locking
22
22
  - Zero runtime dependencies
23
23
  - Simple `prepend` + `memoize` API
24
+ - Preserves public, protected, and private method visibility
25
+ - Supports targeted cache invalidation by argument combination
26
+ - Includes a `memoized?` helper for cache inspection
27
+ - Includes a `memo_count` helper for cache size stats
28
+ - Includes a `memo_keys` helper for inspecting cached signatures
29
+ - Includes a `memo_values` helper for inspecting cached signatures and values
24
30
  - Block arguments bypass cache (blocks aren't comparable)
25
31
 
26
32
  ## Installation
@@ -94,12 +100,51 @@ class Config
94
100
  end
95
101
  ```
96
102
 
103
+ ### Works with private methods
104
+
105
+ ```ruby
106
+ class TokenProvider
107
+ prepend SafeMemoize
108
+
109
+ def bearer_token
110
+ token
111
+ end
112
+
113
+ private
114
+
115
+ def token
116
+ fetch_token_from_service
117
+ end
118
+ memoize :token
119
+ end
120
+ ```
121
+
97
122
  ### Cache reset
98
123
 
99
124
  ```ruby
100
125
  obj = MyService.new
101
- obj.reset_memo(:current_user) # Clears cache for one method
102
- obj.reset_all_memos # Clears all memoized values
126
+ obj.reset_memo(:current_user) # Clears all cached entries for one method
127
+ obj.reset_memo(:find_user, 42) # Clears only the cached call for find_user(42)
128
+ obj.reset_memo(:search, "ruby", page: 2) # Clears one positional/keyword combination
129
+ obj.reset_all_memos # Clears all memoized values
130
+ ```
131
+
132
+ ### Cache inspection
133
+
134
+ ```ruby
135
+ obj = MyService.new
136
+
137
+ obj.memoized?(:current_user) # => false
138
+ obj.current_user
139
+ obj.memoized?(:current_user) # => true
140
+
141
+ obj.memoized?(:search, "ruby", page: 2) # Checks one cached argument combination
142
+ obj.memo_count # Total cached entries for this instance
143
+ obj.memo_count(:search) # Cached entries for one method
144
+ obj.memo_keys # All cached signatures with method, args, kwargs
145
+ obj.memo_keys(:search) # Cached signatures for one method
146
+ obj.memo_values # Cached signatures and values for all methods
147
+ obj.memo_values(:search) # Cached signatures and values for one method
103
148
  ```
104
149
 
105
150
  ## How It Works
@@ -110,6 +155,41 @@ SafeMemoize uses Ruby's `prepend` mechanism. When you call `memoize :method_name
110
155
 
111
156
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `bundle exec rspec` to run the tests. You can also run `bin/console` for an interactive prompt.
112
157
 
158
+ GitHub Actions also runs the full `bundle exec rake` suite automatically for pull requests, manual workflow runs, and pushes to `main` via `.github/workflows/ci.yml`.
159
+
160
+ ## Releasing
161
+
162
+ Releases are automated in two parts:
163
+
164
+ 1. Run `bin/release VERSION` locally to:
165
+ - update `lib/safe_memoize/version.rb`
166
+ - convert the current `## [Unreleased]` section in `CHANGELOG.md` into a dated release entry
167
+ - create the release commit and annotated tag
168
+ 2. Push the branch and tag to GitHub. The workflow in `.github/workflows/release.yml` will:
169
+ - run the test and lint suite
170
+ - build the gem
171
+ - push it to RubyGems when that version is not already published
172
+ - create a GitHub release using the matching section from `CHANGELOG.md`
173
+
174
+ One-time setup:
175
+
176
+ - add a `RUBYGEMS_API_KEY` repository secret in GitHub
177
+
178
+ Typical release flow:
179
+
180
+ ```bash
181
+ bundle exec rake
182
+ bin/release 0.1.1
183
+ git push origin HEAD
184
+ git push origin v0.1.1
185
+ ```
186
+
187
+ To preview the changelog/version update without changing anything, use:
188
+
189
+ ```bash
190
+ bin/release 0.1.1 --dry-run
191
+ ```
192
+
113
193
  ## Contributing
114
194
 
115
195
  Bug reports and pull requests are welcome on GitHub at https://github.com/eclectic-coding/safe_memoize.
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "date"
4
+
5
+ module SafeMemoize
6
+ module ReleaseTooling
7
+ module_function
8
+
9
+ VERSION_PATTERN = /VERSION = "[^"]+"/
10
+ SEMVER_PATTERN = /\A\d+\.\d+\.\d+(?:[-.][0-9A-Za-z]+(?:[.-][0-9A-Za-z]+)*)?\z/
11
+ UNRELEASED_HEADING = "## [Unreleased]"
12
+
13
+ def normalize_version(version)
14
+ normalized_version = version.to_s.sub(/\Av/, "")
15
+
16
+ unless normalized_version.match?(SEMVER_PATTERN)
17
+ raise ArgumentError, "version must look like x.y.z"
18
+ end
19
+
20
+ normalized_version
21
+ end
22
+
23
+ def update_version_file(contents, version)
24
+ normalized_version = normalize_version(version)
25
+
26
+ unless contents.match?(VERSION_PATTERN)
27
+ raise ArgumentError, "version file does not define VERSION"
28
+ end
29
+
30
+ contents.sub(VERSION_PATTERN, %(VERSION = "#{normalized_version}"))
31
+ end
32
+
33
+ def finalize_changelog(contents, version, date = Date.today)
34
+ normalized_version = normalize_version(version)
35
+ release_heading = "## [#{normalized_version}] - #{date.iso8601}"
36
+
37
+ unless contents.include?(UNRELEASED_HEADING)
38
+ raise ArgumentError, "CHANGELOG.md must contain an Unreleased heading"
39
+ end
40
+
41
+ if contents.match?(/^## \[#{Regexp.escape(normalized_version)}\](?: - .+)?$/)
42
+ raise ArgumentError, "CHANGELOG.md already contains #{normalized_version}"
43
+ end
44
+
45
+ contents.sub(UNRELEASED_HEADING, "#{UNRELEASED_HEADING}\n\n#{release_heading}")
46
+ end
47
+
48
+ def extract_release_notes(contents, version)
49
+ normalized_version = normalize_version(version)
50
+ lines = contents.lines
51
+ release_heading = /^## \[#{Regexp.escape(normalized_version)}\](?: - .+)?$/
52
+ start_index = lines.index { |line| line.match?(release_heading) }
53
+
54
+ raise ArgumentError, "CHANGELOG.md is missing release notes for #{normalized_version}" unless start_index
55
+
56
+ body = lines[(start_index + 1)..].take_while { |line| !line.start_with?("## [") }.join.strip
57
+ body = "- No changes listed." if body.empty?
58
+
59
+ <<~MARKDOWN
60
+ ## SafeMemoize #{normalized_version}
61
+
62
+ #{body}
63
+ MARKDOWN
64
+ end
65
+ end
66
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SafeMemoize
4
- VERSION = "0.1.0"
4
+ VERSION = "0.1.2"
5
5
  end
data/lib/safe_memoize.rb CHANGED
@@ -12,59 +12,195 @@ module SafeMemoize
12
12
  module ClassMethods
13
13
  def memoize(method_name)
14
14
  method_name = method_name.to_sym
15
+ visibility = memoized_method_visibility(method_name)
15
16
 
16
17
  mod = Module.new do
17
18
  define_method(method_name) do |*args, **kwargs, &block|
18
19
  # Blocks bypass cache entirely — they aren't comparable
19
20
  return super(*args, **kwargs, &block) if block
20
21
 
21
- cache_key = [method_name, args, kwargs]
22
-
23
- @__safe_memo_mutex__ ||= Mutex.new
22
+ cache_key = safe_memo_cache_key(method_name, args, kwargs)
24
23
 
25
24
  # Fast path: check without lock
26
- if defined?(@__safe_memo_cache__) && @__safe_memo_cache__.key?(cache_key)
27
- return @__safe_memo_cache__[cache_key]
28
- end
29
-
30
- # Slow path: lock and double-check
31
- @__safe_memo_mutex__.synchronize do
32
- @__safe_memo_cache__ ||= {}
33
-
34
- if @__safe_memo_cache__.key?(cache_key)
35
- @__safe_memo_cache__[cache_key]
36
- else
37
- @__safe_memo_cache__[cache_key] = super(*args, **kwargs)
38
- end
39
- end
25
+ return memo_cache_read(cache_key) if memo_cache_hit?(cache_key)
26
+
27
+ memo_fetch_or_store(cache_key) { super(*args, **kwargs) }
40
28
  end
29
+
30
+ send(visibility, method_name)
41
31
  end
42
32
 
43
33
  prepend mod
44
34
  end
35
+
36
+ private
37
+
38
+ def memoized_method_visibility(method_name)
39
+ return :private if private_method_defined?(method_name)
40
+ return :protected if protected_method_defined?(method_name)
41
+
42
+ :public
43
+ end
45
44
  end
46
45
 
47
- def reset_memo(method_name)
46
+ def memoized?(method_name, *args, **kwargs, &block)
47
+ return false if block
48
+
49
+ cache_key = safe_memo_cache_key(method_name, args, kwargs)
50
+
51
+ with_memo_lock do
52
+ with_memo_cache { |cache| cache.key?(cache_key) } || false
53
+ end
54
+ end
55
+
56
+ def memo_count(*method_name)
57
+ scoped_method = safe_memo_scoped_method(method_name)
58
+
59
+ with_memo_lock do
60
+ safe_memo_count_for(scoped_method)
61
+ end
62
+ end
63
+
64
+ def memo_keys(*method_name)
65
+ scoped_method = safe_memo_scoped_method(method_name)
66
+
67
+ with_memo_lock do
68
+ safe_memo_keys_for(scoped_method)
69
+ end
70
+ end
71
+
72
+ def memo_values(*method_name)
73
+ scoped_method = safe_memo_scoped_method(method_name)
74
+
75
+ with_memo_lock do
76
+ safe_memo_values_for(scoped_method)
77
+ end
78
+ end
79
+
80
+ def reset_memo(method_name, *args, **kwargs)
48
81
  method_name = method_name.to_sym
49
82
 
50
- return unless defined?(@__safe_memo_cache__)
83
+ matcher = memo_matcher_for(method_name, args, kwargs)
51
84
 
52
- if defined?(@__safe_memo_mutex__) && @__safe_memo_mutex__
53
- @__safe_memo_mutex__.synchronize do
54
- @__safe_memo_cache__.delete_if { |key, _| key[0] == method_name }
85
+ with_memo_lock do
86
+ with_memo_cache do |cache|
87
+ cache.delete_if { |key, _| matcher.call(key) }
55
88
  end
56
- else
57
- @__safe_memo_cache__.delete_if { |key, _| key[0] == method_name }
58
89
  end
59
90
  end
60
91
 
61
92
  def reset_all_memos
93
+ with_memo_lock do
94
+ @__safe_memo_cache__ = {}
95
+ end
96
+ end
97
+
98
+ private
99
+
100
+ def safe_memo_scoped_method(method_name)
101
+ raise ArgumentError, "expected 0 or 1 arguments" if method_name.length > 1
102
+
103
+ method_name.first&.to_sym
104
+ end
105
+
106
+ def with_memo_lock
62
107
  if defined?(@__safe_memo_mutex__) && @__safe_memo_mutex__
63
- @__safe_memo_mutex__.synchronize do
64
- @__safe_memo_cache__ = {}
108
+ @__safe_memo_mutex__.synchronize { yield }
109
+ else
110
+ yield
111
+ end
112
+ end
113
+
114
+ def memo_cache_or_nil
115
+ return nil unless defined?(@__safe_memo_cache__)
116
+
117
+ @__safe_memo_cache__
118
+ end
119
+
120
+ def memo_cache_hit?(cache_key)
121
+ cache = memo_cache_or_nil
122
+ cache&.key?(cache_key)
123
+ end
124
+
125
+ def memo_cache_read(cache_key)
126
+ cache = memo_cache_or_nil
127
+ cache && cache[cache_key]
128
+ end
129
+
130
+ def memo_fetch_or_store(cache_key)
131
+ memo_mutex!.synchronize do
132
+ @__safe_memo_cache__ ||= {}
133
+
134
+ if @__safe_memo_cache__.key?(cache_key)
135
+ @__safe_memo_cache__[cache_key]
136
+ else
137
+ @__safe_memo_cache__[cache_key] = yield
65
138
  end
139
+ end
140
+ end
141
+
142
+ def memo_mutex!
143
+ @__safe_memo_mutex__ ||= Mutex.new
144
+ end
145
+
146
+ def with_memo_cache
147
+ cache = memo_cache_or_nil
148
+ return nil unless cache
149
+
150
+ yield cache
151
+ end
152
+
153
+ def memo_matcher_for(method_name, args, kwargs)
154
+ if args.empty? && kwargs.empty?
155
+ ->(key) { key[0] == method_name }
66
156
  else
67
- @__safe_memo_cache__ = {}
157
+ cache_key = safe_memo_cache_key(method_name, args, kwargs)
158
+ ->(key) { key == cache_key }
68
159
  end
69
160
  end
161
+
162
+ def memo_entries_for(method_name)
163
+ cache = memo_cache_or_nil
164
+ return [] unless cache
165
+
166
+ entries = cache.to_a
167
+ return entries unless method_name
168
+
169
+ entries.select { |(cache_key, _)| cache_key[0] == method_name }
170
+ end
171
+
172
+ def safe_memo_count_for(method_name)
173
+ memo_entries_for(method_name).length
174
+ end
175
+
176
+ def safe_memo_keys_for(method_name)
177
+ entries = memo_entries_for(method_name)
178
+ include_method = method_name.nil?
179
+
180
+ entries.map do |(cache_key, value)|
181
+ memo_projection(cache_key, value, include_method: include_method, include_value: false)
182
+ end
183
+ end
184
+
185
+ def safe_memo_values_for(method_name)
186
+ entries = memo_entries_for(method_name)
187
+ include_method = method_name.nil?
188
+
189
+ entries.map do |(cache_key, value)|
190
+ memo_projection(cache_key, value, include_method: include_method, include_value: true)
191
+ end
192
+ end
193
+
194
+ def memo_projection(cache_key, value, include_method:, include_value:)
195
+ method_name, args, kwargs = cache_key
196
+
197
+ payload = {args: args, kwargs: kwargs}
198
+ payload[:method] = method_name if include_method
199
+ payload[:value] = value if include_value
200
+ payload
201
+ end
202
+
203
+ def safe_memo_cache_key(method_name, args, kwargs)
204
+ [method_name.to_sym, args, kwargs]
205
+ end
70
206
  end
data/sig/safe_memoize.rbs CHANGED
@@ -1,12 +1,47 @@
1
1
  module SafeMemoize
2
2
  VERSION: String
3
3
 
4
+ type memo_key = [Symbol, Array[untyped], Hash[Symbol, untyped]]
5
+ type memo_entry = [memo_key, untyped]
6
+
7
+ @__safe_memo_cache__: Hash[memo_key, untyped]?
8
+ @__safe_memo_mutex__: Mutex?
9
+
4
10
  def self.prepended: (Class base) -> void
5
11
 
6
- def reset_memo: (Symbol method_name) -> void
12
+ def memoized?: (Symbol | String method_name, *untyped args, **untyped kwargs) -> bool
13
+ def memo_count: () -> Integer
14
+ def memo_count: (Symbol | String method_name) -> Integer
15
+ def memo_keys: () -> Array[untyped]
16
+ def memo_keys: (Symbol | String method_name) -> Array[untyped]
17
+ def memo_values: () -> Array[untyped]
18
+ def memo_values: (Symbol | String method_name) -> Array[untyped]
19
+ def reset_memo: (Symbol | String method_name, *untyped args, **untyped kwargs) -> void
7
20
  def reset_all_memos: () -> void
8
21
 
22
+ private
23
+
24
+ def safe_memo_scoped_method: (Array[untyped] method_name) -> Symbol?
25
+ def with_memo_lock: { () -> untyped } -> untyped
26
+ def memo_cache_or_nil: () -> Hash[memo_key, untyped]?
27
+ def memo_cache_hit?: (memo_key cache_key) -> bool
28
+ def memo_cache_read: (memo_key cache_key) -> untyped?
29
+ def memo_fetch_or_store: (memo_key cache_key) { () -> untyped } -> untyped
30
+ def memo_mutex!: () -> Mutex
31
+ def with_memo_cache: { (Hash[memo_key, untyped] cache) -> untyped } -> untyped?
32
+ def memo_matcher_for: (Symbol method_name, Array[untyped] args, Hash[Symbol, untyped] kwargs) -> ((memo_key) -> bool)
33
+ def memo_entries_for: (Symbol? method_name) -> Array[memo_entry]
34
+ def safe_memo_count_for: (Symbol? method_name) -> Integer
35
+ def safe_memo_keys_for: (Symbol? method_name) -> Array[untyped]
36
+ def safe_memo_values_for: (Symbol? method_name) -> Array[untyped]
37
+ def memo_projection: (memo_key cache_key, untyped value, include_method: bool, include_value: bool) -> Hash[Symbol, untyped]
38
+ def safe_memo_cache_key: (Symbol | String method_name, Array[untyped] args, Hash[Symbol, untyped] kwargs) -> memo_key
39
+
9
40
  module ClassMethods
10
41
  def memoize: (Symbol | String method_name) -> void
42
+
43
+ private
44
+
45
+ def memoized_method_visibility: (Symbol method_name) -> (:private | :protected | :public)
11
46
  end
12
47
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: safe_memoize
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chuck Smith
@@ -18,11 +18,14 @@ executables: []
18
18
  extensions: []
19
19
  extra_rdoc_files: []
20
20
  files:
21
+ - ".github/workflows/ci.yml"
22
+ - ".github/workflows/release.yml"
21
23
  - CHANGELOG.md
22
24
  - LICENSE.txt
23
25
  - README.md
24
26
  - Rakefile
25
27
  - lib/safe_memoize.rb
28
+ - lib/safe_memoize/release_tooling.rb
26
29
  - lib/safe_memoize/version.rb
27
30
  - sig/safe_memoize.rbs
28
31
  homepage: https://github.com/eclectic-coding/safe_memoize
@@ -47,7 +50,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
47
50
  - !ruby/object:Gem::Version
48
51
  version: '0'
49
52
  requirements: []
50
- rubygems_version: 4.0.2
53
+ rubygems_version: 3.6.9
51
54
  specification_version: 4
52
55
  summary: Thread-safe memoization that correctly handles nil and false values
53
56
  test_files: []