memoizable 0.4.2 → 0.5.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 +5 -5
- data/CHANGELOG.md +100 -0
- data/LICENSE.md +1 -1
- data/README.md +20 -35
- data/lib/memoizable/instance_methods.rb +7 -10
- data/lib/memoizable/memory.rb +99 -26
- data/lib/memoizable/method_builder.rb +29 -31
- data/lib/memoizable/module_methods.rb +9 -45
- data/lib/memoizable/version.rb +3 -5
- data/lib/memoizable.rb +10 -12
- metadata +16 -90
- data/Rakefile +0 -10
- data/memoizable.gemspec +0 -24
- data/spec/integration/serializable_spec.rb +0 -34
- data/spec/shared/call_super_shared_spec.rb +0 -23
- data/spec/shared/command_method_behavior.rb +0 -7
- data/spec/spec_helper.rb +0 -33
- data/spec/unit/memoizable/class_methods/included_spec.rb +0 -18
- data/spec/unit/memoizable/fixtures/classes.rb +0 -41
- data/spec/unit/memoizable/instance_methods/freeze_spec.rb +0 -27
- data/spec/unit/memoizable/instance_methods/memoize_spec.rb +0 -40
- data/spec/unit/memoizable/memory_spec.rb +0 -24
- data/spec/unit/memoizable/method_builder/call_spec.rb +0 -93
- data/spec/unit/memoizable/method_builder/class_methods/new_spec.rb +0 -34
- data/spec/unit/memoizable/method_builder/original_method_spec.rb +0 -31
- data/spec/unit/memoizable/module_methods/included_spec.rb +0 -23
- data/spec/unit/memoizable/module_methods/memoize_spec.rb +0 -123
- data/spec/unit/memoizable/module_methods/memoized_predicate_spec.rb +0 -28
- data/spec/unit/memoizable/module_methods/unmemoized_instance_method_spec.rb +0 -43
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 9b9efbfc2d856bca857e97e84678b1e49977e2b1f4d0a0d93a239cacea9b95ef
|
|
4
|
+
data.tar.gz: 93a3c69fb399accdc6a8f684c315284c46450af48c5df0d4eb05117af1e2ea2e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: de5d824ac3bd2b23970c50463cc27ba3a83b2f102d0293475edde45b1cebcccb87e5fac3411b6d509ce8866307d8f6eb213b8dc2c81eb17874df162c93329797
|
|
7
|
+
data.tar.gz: 5ae626655e57e079b0a6ea42d9a194e300fc681ec0b3403d1e7c08dea362075a1678041133df583ce296011baaa28da72c1f6e67bab47b067aa633146580658b
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
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
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.5.0] - 2026-02-09
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- `Memory#delete` method to remove a specific memoized value from cache
|
|
15
|
+
- `Memory#clear` method to remove all memoized values from cache
|
|
16
|
+
- `Memory#fetch` now accepts an optional default argument
|
|
17
|
+
- Steep for static type checking
|
|
18
|
+
- Mutant for mutation testing
|
|
19
|
+
- RuboCop and Standard Ruby for linting
|
|
20
|
+
- YARD and Yardstick for documentation coverage
|
|
21
|
+
- GitHub Actions CI pipeline (replacing Travis CI)
|
|
22
|
+
- GitHub Actions workflow for publishing gems to RubyGems with Sigstore attestation
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
|
|
26
|
+
- Renamed `Memory#[]=` to `Memory#store`
|
|
27
|
+
- `ModuleMethods#memoize` now raises `ArgumentError` when method is already memoized
|
|
28
|
+
- Memoization cache now uses composite keys `[class, method_name]` to support inheritance ([#13](https://github.com/dkubb/memoizable/issues/13))
|
|
29
|
+
- Replaced `thread_safe` gem dependency with Ruby's built-in `Monitor`
|
|
30
|
+
|
|
31
|
+
### Removed
|
|
32
|
+
|
|
33
|
+
- `Memory#key?` method
|
|
34
|
+
- `ModuleMethods#memoized?` method
|
|
35
|
+
- `ModuleMethods#included` method
|
|
36
|
+
- `thread_safe` gem dependency
|
|
37
|
+
|
|
38
|
+
## [0.4.2] - 2014-03-27
|
|
39
|
+
|
|
40
|
+
### Changed
|
|
41
|
+
|
|
42
|
+
- Updated `thread_safe` dependency to use semantic versioning compatible version
|
|
43
|
+
|
|
44
|
+
## [0.4.1] - 2014-03-04
|
|
45
|
+
|
|
46
|
+
### Added
|
|
47
|
+
|
|
48
|
+
- Support for Ruby 2.1.0
|
|
49
|
+
|
|
50
|
+
### Changed
|
|
51
|
+
|
|
52
|
+
- Updated `thread_safe` dependency to ~> 0.2.0
|
|
53
|
+
|
|
54
|
+
## [0.4.0] - 2013-12-24
|
|
55
|
+
|
|
56
|
+
### Added
|
|
57
|
+
|
|
58
|
+
- `Memory#marshal_dump` and `Memory#marshal_load` methods for Marshal serialization support ([#10](https://github.com/dkubb/memoizable/issues/10))
|
|
59
|
+
|
|
60
|
+
## [0.3.1] - 2013-12-18
|
|
61
|
+
|
|
62
|
+
### Changed
|
|
63
|
+
|
|
64
|
+
- Added double-checked locking to `Memory#fetch` for improved thread safety
|
|
65
|
+
|
|
66
|
+
### Removed
|
|
67
|
+
|
|
68
|
+
- Unnecessary `Memory#set` method
|
|
69
|
+
|
|
70
|
+
## [0.3.0] - 2013-12-15
|
|
71
|
+
|
|
72
|
+
### Added
|
|
73
|
+
|
|
74
|
+
- Thread-safe memory operations using `thread_safe` gem
|
|
75
|
+
- `BlockNotAllowedError` raised when passing a block to memoized methods
|
|
76
|
+
- `ModuleMethods#included` to allow module methods to be memoized
|
|
77
|
+
|
|
78
|
+
### Changed
|
|
79
|
+
|
|
80
|
+
- Memory is now shallowly frozen after initialization
|
|
81
|
+
|
|
82
|
+
## [0.2.0] - 2013-11-18
|
|
83
|
+
|
|
84
|
+
### Added
|
|
85
|
+
|
|
86
|
+
- Core memoization functionality
|
|
87
|
+
- `Memoizable::MethodBuilder` for memoized method creation
|
|
88
|
+
- `InstanceMethods#memoize` to manually set memoized values
|
|
89
|
+
- `InstanceMethods#freeze` support for frozen objects
|
|
90
|
+
- `ModuleMethods#unmemoized_instance_method` to access original method
|
|
91
|
+
- Thread-safe cache using `ThreadSafe::Cache`
|
|
92
|
+
|
|
93
|
+
[Unreleased]: https://github.com/dkubb/memoizable/compare/v0.5.0...HEAD
|
|
94
|
+
[0.5.0]: https://github.com/dkubb/memoizable/compare/v0.4.2...v0.5.0
|
|
95
|
+
[0.4.2]: https://github.com/dkubb/memoizable/compare/v0.4.1...v0.4.2
|
|
96
|
+
[0.4.1]: https://github.com/dkubb/memoizable/compare/v0.4.0...v0.4.1
|
|
97
|
+
[0.4.0]: https://github.com/dkubb/memoizable/compare/v0.3.1...v0.4.0
|
|
98
|
+
[0.3.1]: https://github.com/dkubb/memoizable/compare/v0.3.0...v0.3.1
|
|
99
|
+
[0.3.0]: https://github.com/dkubb/memoizable/compare/v0.2.0...v0.3.0
|
|
100
|
+
[0.2.0]: https://github.com/dkubb/memoizable/compare/v0.0.0...v0.2.0
|
data/LICENSE.md
CHANGED
data/README.md
CHANGED
|
@@ -1,19 +1,25 @@
|
|
|
1
1
|
# Memoizable
|
|
2
2
|
|
|
3
3
|
[][gem]
|
|
4
|
-
[][test]
|
|
5
|
+
[][lint]
|
|
6
|
+
[][mutant]
|
|
7
|
+
[][docs]
|
|
8
|
+
[][steep]
|
|
8
9
|
|
|
9
10
|
[gem]: https://rubygems.org/gems/memoizable
|
|
10
|
-
[
|
|
11
|
-
[
|
|
12
|
-
[
|
|
13
|
-
[
|
|
11
|
+
[test]: https://github.com/dkubb/memoizable/actions/workflows/test.yml
|
|
12
|
+
[lint]: https://github.com/dkubb/memoizable/actions/workflows/lint.yml
|
|
13
|
+
[mutant]: https://github.com/dkubb/memoizable/actions/workflows/mutant.yml
|
|
14
|
+
[docs]: https://github.com/dkubb/memoizable/actions/workflows/docs.yml
|
|
15
|
+
[steep]: https://github.com/dkubb/memoizable/actions/workflows/steep.yml
|
|
14
16
|
|
|
15
17
|
Memoize method return values
|
|
16
18
|
|
|
19
|
+
## Changelog
|
|
20
|
+
|
|
21
|
+
See [CHANGELOG.md](CHANGELOG.md) for details.
|
|
22
|
+
|
|
17
23
|
## Contributing
|
|
18
24
|
|
|
19
25
|
See [CONTRIBUTING.md](CONTRIBUTING.md) for details.
|
|
@@ -76,36 +82,15 @@ result. Please keep this in mind when considering which methods to memoize.
|
|
|
76
82
|
Supported Ruby Versions
|
|
77
83
|
-----------------------
|
|
78
84
|
|
|
79
|
-
This library aims to support and is
|
|
80
|
-
implementations:
|
|
81
|
-
|
|
82
|
-
* Ruby 1.8.7
|
|
83
|
-
* Ruby 1.9.2
|
|
84
|
-
* Ruby 1.9.3
|
|
85
|
-
* Ruby 2.0.0
|
|
86
|
-
* Ruby 2.1.0
|
|
87
|
-
* [JRuby][]
|
|
88
|
-
* [Rubinius][]
|
|
89
|
-
* [Ruby Enterprise Edition][ree]
|
|
85
|
+
This library aims to support and is tested against the following Ruby versions:
|
|
90
86
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
87
|
+
* Ruby 3.2
|
|
88
|
+
* Ruby 3.3
|
|
89
|
+
* Ruby 3.4
|
|
90
|
+
* Ruby 4.0
|
|
94
91
|
|
|
95
92
|
If something doesn't work on one of these versions, it's a bug.
|
|
96
93
|
|
|
97
|
-
This library may inadvertently work (or seem to work) on other Ruby versions or
|
|
98
|
-
implementations, however support will only be provided for the implementations
|
|
99
|
-
listed above.
|
|
100
|
-
|
|
101
|
-
If you would like this library to support another Ruby version or
|
|
102
|
-
implementation, you may volunteer to be a maintainer. Being a maintainer
|
|
103
|
-
entails making sure all tests run and pass on that implementation. When
|
|
104
|
-
something breaks on your implementation, you will be responsible for providing
|
|
105
|
-
patches in a timely fashion. If critical issues for a particular implementation
|
|
106
|
-
exist at the time of a major release, support for that Ruby version may be
|
|
107
|
-
dropped.
|
|
108
|
-
|
|
109
94
|
## Copyright
|
|
110
95
|
|
|
111
|
-
Copyright © 2013 Dan Kubb, Erik
|
|
96
|
+
Copyright © 2013-2026 Dan Kubb, Erik Berlin. See LICENSE for details.
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
#
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Memoizable
|
|
4
|
-
|
|
5
4
|
# Methods mixed in to memoizable instances
|
|
6
5
|
module InstanceMethods
|
|
7
|
-
|
|
8
6
|
# Freeze the object
|
|
9
7
|
#
|
|
10
8
|
# @example
|
|
@@ -14,7 +12,7 @@ module Memoizable
|
|
|
14
12
|
#
|
|
15
13
|
# @api public
|
|
16
14
|
def freeze
|
|
17
|
-
memoized_method_cache
|
|
15
|
+
memoized_method_cache # initialize method cache
|
|
18
16
|
super
|
|
19
17
|
end
|
|
20
18
|
|
|
@@ -30,11 +28,11 @@ module Memoizable
|
|
|
30
28
|
#
|
|
31
29
|
# @api public
|
|
32
30
|
def memoize(data)
|
|
33
|
-
data.each { |name, value| memoized_method_cache[name]
|
|
31
|
+
data.each { |name, value| memoized_method_cache.store([self.class, name], value) }
|
|
34
32
|
self
|
|
35
33
|
end
|
|
36
34
|
|
|
37
|
-
|
|
35
|
+
private
|
|
38
36
|
|
|
39
37
|
# The memoized method results
|
|
40
38
|
#
|
|
@@ -42,8 +40,7 @@ module Memoizable
|
|
|
42
40
|
#
|
|
43
41
|
# @api private
|
|
44
42
|
def memoized_method_cache
|
|
45
|
-
@_memoized_method_cache ||= Memory.new
|
|
43
|
+
@_memoized_method_cache ||= Memory.new({}) # rubocop:disable Naming/MemoizedInstanceVariableName
|
|
46
44
|
end
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
end # Memoizable
|
|
45
|
+
end
|
|
46
|
+
end
|
data/lib/memoizable/memory.rb
CHANGED
|
@@ -1,94 +1,169 @@
|
|
|
1
|
-
#
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Memoizable
|
|
4
|
-
|
|
5
4
|
# Storage for memoized methods
|
|
6
5
|
class Memory
|
|
7
|
-
|
|
8
6
|
# Initialize the memory storage for memoized methods
|
|
9
7
|
#
|
|
10
|
-
# @param [
|
|
8
|
+
# @param [Hash] memory
|
|
11
9
|
#
|
|
12
10
|
# @return [undefined]
|
|
13
11
|
#
|
|
14
12
|
# @api private
|
|
15
|
-
def initialize
|
|
16
|
-
@memory
|
|
13
|
+
def initialize(memory)
|
|
14
|
+
@memory = memory
|
|
17
15
|
@monitor = Monitor.new
|
|
18
16
|
freeze
|
|
19
17
|
end
|
|
20
18
|
|
|
21
19
|
# Get the value from memory
|
|
22
20
|
#
|
|
21
|
+
# @example
|
|
22
|
+
#
|
|
23
|
+
# memory = Memoizable::Memory.new(foo: 1)
|
|
24
|
+
# memory[:foo] # => 1
|
|
25
|
+
#
|
|
23
26
|
# @param [Symbol] name
|
|
24
27
|
#
|
|
25
28
|
# @return [Object]
|
|
26
29
|
#
|
|
27
30
|
# @api public
|
|
28
31
|
def [](name)
|
|
29
|
-
|
|
30
|
-
|
|
32
|
+
fetch(name) do
|
|
33
|
+
raise NameError, "No method #{name} is memoized"
|
|
31
34
|
end
|
|
32
35
|
end
|
|
33
36
|
|
|
34
37
|
# Store the value in memory
|
|
35
38
|
#
|
|
39
|
+
# @example
|
|
40
|
+
# memory = Memoizable::Memory.new(foo: 1)
|
|
41
|
+
# memory.store(:foo, 2)
|
|
42
|
+
# memory[:foo] # => 2
|
|
43
|
+
#
|
|
36
44
|
# @param [Symbol] name
|
|
37
45
|
# @param [Object] value
|
|
38
46
|
#
|
|
39
47
|
# @return [undefined]
|
|
40
48
|
#
|
|
41
49
|
# @api public
|
|
42
|
-
def
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
value
|
|
50
|
+
def store(name, value)
|
|
51
|
+
@monitor.synchronize do
|
|
52
|
+
raise ArgumentError, "The method #{name} is already memoized" if @memory.key?(name)
|
|
53
|
+
|
|
54
|
+
@memory[name] = value
|
|
47
55
|
end
|
|
48
|
-
fail ArgumentError, "The method #{name} is already memoized" if memoized
|
|
49
56
|
end
|
|
50
57
|
|
|
51
58
|
# Fetch the value from memory, or store it if it does not exist
|
|
52
59
|
#
|
|
60
|
+
# @example
|
|
61
|
+
# memory = Memoizable::Memory.new(foo: 1)
|
|
62
|
+
# memory.fetch(:foo) { 2 } # => 1
|
|
63
|
+
# memory.fetch(:bar) { 2 } # => 2
|
|
64
|
+
# memory[:bar] # => 2
|
|
65
|
+
# memory.fetch(:baz, 3) # => 3
|
|
66
|
+
#
|
|
53
67
|
# @param [Symbol] name
|
|
68
|
+
# @param [Object] default
|
|
69
|
+
# optional default value to return if the key is not found
|
|
54
70
|
#
|
|
55
71
|
# @yieldreturn [Object]
|
|
56
72
|
# the value to memoize
|
|
57
73
|
#
|
|
74
|
+
# @return [Object]
|
|
75
|
+
#
|
|
58
76
|
# @api public
|
|
59
|
-
def fetch(name)
|
|
77
|
+
def fetch(name, default = (no_default = true), &block)
|
|
60
78
|
@memory.fetch(name) do # check for the key
|
|
61
79
|
@monitor.synchronize do # acquire a lock if the key is not found
|
|
62
80
|
@memory.fetch(name) do # recheck under lock
|
|
63
|
-
|
|
81
|
+
@memory[name] = resolve_fetch_value(name, no_default, default, &block)
|
|
64
82
|
end
|
|
65
83
|
end
|
|
66
84
|
end
|
|
67
85
|
end
|
|
68
86
|
|
|
69
|
-
|
|
87
|
+
private
|
|
88
|
+
|
|
89
|
+
# Resolve the value for a fetch operation
|
|
70
90
|
#
|
|
71
91
|
# @param [Symbol] name
|
|
92
|
+
# @param [Boolean] no_default
|
|
93
|
+
# @param [Object] default
|
|
72
94
|
#
|
|
73
|
-
# @
|
|
95
|
+
# @yieldreturn [Object]
|
|
96
|
+
#
|
|
97
|
+
# @return [Object]
|
|
98
|
+
#
|
|
99
|
+
# @api private
|
|
100
|
+
def resolve_fetch_value(name, no_default, default)
|
|
101
|
+
if block_given?
|
|
102
|
+
yield
|
|
103
|
+
elsif no_default
|
|
104
|
+
raise KeyError, "key not found: #{name.inspect}"
|
|
105
|
+
else
|
|
106
|
+
default
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
public
|
|
111
|
+
|
|
112
|
+
# Remove a specific value from memory
|
|
113
|
+
#
|
|
114
|
+
# @example
|
|
115
|
+
# memory = Memoizable::Memory.new(foo: 1)
|
|
116
|
+
# memory.delete(:foo)
|
|
117
|
+
#
|
|
118
|
+
# @param [Symbol] name
|
|
119
|
+
#
|
|
120
|
+
# @return [Object]
|
|
74
121
|
#
|
|
75
122
|
# @api public
|
|
76
|
-
def
|
|
77
|
-
@
|
|
123
|
+
def delete(name)
|
|
124
|
+
@monitor.synchronize do
|
|
125
|
+
@memory.delete(name)
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Remove all values from memory
|
|
130
|
+
#
|
|
131
|
+
# @example
|
|
132
|
+
# memory = Memoizable::Memory.new(foo: 1)
|
|
133
|
+
# memory.clear # => memory
|
|
134
|
+
#
|
|
135
|
+
# @return [self]
|
|
136
|
+
#
|
|
137
|
+
# @api public
|
|
138
|
+
def clear
|
|
139
|
+
@monitor.synchronize do
|
|
140
|
+
@memory.clear
|
|
141
|
+
end
|
|
142
|
+
self
|
|
78
143
|
end
|
|
79
144
|
|
|
80
145
|
# A hook that allows Marshal to dump the object
|
|
81
146
|
#
|
|
147
|
+
# @example
|
|
148
|
+
# memory = Memoizable::Memory.new(foo: 1)
|
|
149
|
+
# Marshal.dump(memory)
|
|
150
|
+
# # => "\x04\bU:\x17Memoizable::Memory{\x06:\bfooi\x06"
|
|
151
|
+
#
|
|
82
152
|
# @return [Hash]
|
|
83
153
|
# A hash used to populate the internal memory
|
|
84
154
|
#
|
|
85
155
|
# @api public
|
|
86
156
|
def marshal_dump
|
|
87
|
-
@memory
|
|
157
|
+
@memory
|
|
88
158
|
end
|
|
89
159
|
|
|
90
160
|
# A hook that allows Marshal to load the object
|
|
91
161
|
#
|
|
162
|
+
# @example
|
|
163
|
+
# memory = Memoizable::Memory.new(foo: 1)
|
|
164
|
+
# Marshal.load(Marshal.dump(memory))
|
|
165
|
+
# # => #<Memoizable::Memory:0x007f9c2a8b6b20 @memory={:foo=>1}>
|
|
166
|
+
#
|
|
92
167
|
# @param [Hash] hash
|
|
93
168
|
# A hash used to populate the internal memory
|
|
94
169
|
#
|
|
@@ -96,9 +171,7 @@ module Memoizable
|
|
|
96
171
|
#
|
|
97
172
|
# @api public
|
|
98
173
|
def marshal_load(hash)
|
|
99
|
-
initialize
|
|
100
|
-
@memory.marshal_load(hash)
|
|
174
|
+
initialize(hash)
|
|
101
175
|
end
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
end # Memoizable
|
|
176
|
+
end
|
|
177
|
+
end
|
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
#
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Memoizable
|
|
4
|
-
|
|
5
4
|
# Build the memoized method
|
|
6
5
|
class MethodBuilder
|
|
7
|
-
|
|
8
6
|
# Raised when the method arity is invalid
|
|
9
7
|
class InvalidArityError < ArgumentError
|
|
10
|
-
|
|
11
8
|
# Initialize an invalid arity exception
|
|
12
9
|
#
|
|
13
10
|
# @param [Module] descendant
|
|
@@ -18,12 +15,10 @@ module Memoizable
|
|
|
18
15
|
def initialize(descendant, method, arity)
|
|
19
16
|
super("Cannot memoize #{descendant}##{method}, its arity is #{arity}")
|
|
20
17
|
end
|
|
21
|
-
|
|
22
|
-
end # InvalidArityError
|
|
18
|
+
end
|
|
23
19
|
|
|
24
20
|
# Raised when a block is passed to a memoized method
|
|
25
21
|
class BlockNotAllowedError < ArgumentError
|
|
26
|
-
|
|
27
22
|
# Initialize a block not allowed exception
|
|
28
23
|
#
|
|
29
24
|
# @param [Module] descendant
|
|
@@ -33,11 +28,13 @@ module Memoizable
|
|
|
33
28
|
def initialize(descendant, method)
|
|
34
29
|
super("Cannot pass a block to #{descendant}##{method}, it is memoized")
|
|
35
30
|
end
|
|
36
|
-
|
|
37
|
-
end # BlockNotAllowedError
|
|
31
|
+
end
|
|
38
32
|
|
|
39
33
|
# The original method before memoization
|
|
40
34
|
#
|
|
35
|
+
# @example
|
|
36
|
+
# method_builder.original_method # => :foo
|
|
37
|
+
#
|
|
41
38
|
# @return [UnboundMethod]
|
|
42
39
|
#
|
|
43
40
|
# @api public
|
|
@@ -53,12 +50,12 @@ module Memoizable
|
|
|
53
50
|
#
|
|
54
51
|
# @api private
|
|
55
52
|
def initialize(descendant, method_name, freezer)
|
|
56
|
-
@descendant
|
|
57
|
-
@method_name
|
|
58
|
-
@freezer
|
|
53
|
+
@descendant = descendant
|
|
54
|
+
@method_name = method_name
|
|
55
|
+
@freezer = freezer
|
|
59
56
|
@original_visibility = visibility
|
|
60
|
-
@original_method
|
|
61
|
-
assert_arity(
|
|
57
|
+
@original_method = descendant.instance_method(@method_name)
|
|
58
|
+
assert_arity(original_method.arity)
|
|
62
59
|
end
|
|
63
60
|
|
|
64
61
|
# Build a new memoized method
|
|
@@ -76,7 +73,7 @@ module Memoizable
|
|
|
76
73
|
self
|
|
77
74
|
end
|
|
78
75
|
|
|
79
|
-
|
|
76
|
+
private
|
|
80
77
|
|
|
81
78
|
# Assert the method arity is zero
|
|
82
79
|
#
|
|
@@ -88,9 +85,9 @@ module Memoizable
|
|
|
88
85
|
#
|
|
89
86
|
# @api private
|
|
90
87
|
def assert_arity(arity)
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
88
|
+
return unless arity.nonzero?
|
|
89
|
+
|
|
90
|
+
raise InvalidArityError.new(@descendant, @method_name, arity)
|
|
94
91
|
end
|
|
95
92
|
|
|
96
93
|
# Remove the original method
|
|
@@ -109,13 +106,15 @@ module Memoizable
|
|
|
109
106
|
#
|
|
110
107
|
# @api private
|
|
111
108
|
def create_memoized_method
|
|
112
|
-
name
|
|
113
|
-
@
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
109
|
+
name = @method_name
|
|
110
|
+
method = @original_method
|
|
111
|
+
freezer = @freezer
|
|
112
|
+
descendant = @descendant
|
|
113
|
+
@descendant.define_method(name) do |&block|
|
|
114
|
+
raise BlockNotAllowedError.new(self.class, name) if block # steep:ignore NoMethod
|
|
115
|
+
|
|
116
|
+
memoized_method_cache.fetch([descendant, name]) do # steep:ignore NoMethod
|
|
117
|
+
freezer.call(method.bind_call(self))
|
|
119
118
|
end
|
|
120
119
|
end
|
|
121
120
|
end
|
|
@@ -126,7 +125,7 @@ module Memoizable
|
|
|
126
125
|
#
|
|
127
126
|
# @api private
|
|
128
127
|
def set_method_visibility
|
|
129
|
-
@descendant.
|
|
128
|
+
@descendant.__send__(@original_visibility, @method_name)
|
|
130
129
|
end
|
|
131
130
|
|
|
132
131
|
# Get the visibility of the original method
|
|
@@ -135,11 +134,10 @@ module Memoizable
|
|
|
135
134
|
#
|
|
136
135
|
# @api private
|
|
137
136
|
def visibility
|
|
138
|
-
if
|
|
137
|
+
if @descendant.private_method_defined?(@method_name) then :private
|
|
139
138
|
elsif @descendant.protected_method_defined?(@method_name) then :protected
|
|
140
|
-
else
|
|
139
|
+
else :public
|
|
141
140
|
end
|
|
142
141
|
end
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
end # Memoizable
|
|
142
|
+
end
|
|
143
|
+
end
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
#
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Memoizable
|
|
4
|
-
|
|
5
4
|
# Methods mixed in to memoizable singleton classes
|
|
6
5
|
module ModuleMethods
|
|
6
|
+
include Memoizable
|
|
7
7
|
|
|
8
8
|
# Return default deep freezer
|
|
9
9
|
#
|
|
@@ -26,34 +26,10 @@ module Memoizable
|
|
|
26
26
|
#
|
|
27
27
|
# @api public
|
|
28
28
|
def memoize(*methods)
|
|
29
|
-
methods.each(
|
|
29
|
+
methods.each { |method| memoize_method(method) }
|
|
30
30
|
self
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
-
# Test if an instance method is memoized
|
|
34
|
-
#
|
|
35
|
-
# @example
|
|
36
|
-
# class Foo
|
|
37
|
-
# include Memoizable
|
|
38
|
-
#
|
|
39
|
-
# def bar
|
|
40
|
-
# end
|
|
41
|
-
# memoize :bar
|
|
42
|
-
# end
|
|
43
|
-
#
|
|
44
|
-
# Foo.memoized?(:bar) # true
|
|
45
|
-
# Foo.memoized?(:baz) # false
|
|
46
|
-
#
|
|
47
|
-
# @param [Symbol] name
|
|
48
|
-
#
|
|
49
|
-
# @return [Boolean]
|
|
50
|
-
# true if method is memoized, false if not
|
|
51
|
-
#
|
|
52
|
-
# @api private
|
|
53
|
-
def memoized?(name)
|
|
54
|
-
memoized_methods.key?(name)
|
|
55
|
-
end
|
|
56
|
-
|
|
57
33
|
# Return unmemoized instance method
|
|
58
34
|
#
|
|
59
35
|
# @example
|
|
@@ -81,20 +57,7 @@ module Memoizable
|
|
|
81
57
|
memoized_methods[name].original_method
|
|
82
58
|
end
|
|
83
59
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
# Hook called when module is included
|
|
87
|
-
#
|
|
88
|
-
# @param [Module] descendant
|
|
89
|
-
# the module including ModuleMethods
|
|
90
|
-
#
|
|
91
|
-
# @return [self]
|
|
92
|
-
#
|
|
93
|
-
# @api private
|
|
94
|
-
def included(descendant)
|
|
95
|
-
super
|
|
96
|
-
descendant.module_eval { include Memoizable }
|
|
97
|
-
end
|
|
60
|
+
private
|
|
98
61
|
|
|
99
62
|
# Memoize the named method
|
|
100
63
|
#
|
|
@@ -105,6 +68,8 @@ module Memoizable
|
|
|
105
68
|
#
|
|
106
69
|
# @api private
|
|
107
70
|
def memoize_method(method_name)
|
|
71
|
+
raise ArgumentError, "The method #{method_name} is already memoized" if memoized_methods.key?(method_name)
|
|
72
|
+
|
|
108
73
|
memoized_methods[method_name] = MethodBuilder.new(
|
|
109
74
|
self,
|
|
110
75
|
method_name,
|
|
@@ -118,8 +83,7 @@ module Memoizable
|
|
|
118
83
|
#
|
|
119
84
|
# @api private
|
|
120
85
|
def memoized_methods
|
|
121
|
-
@
|
|
86
|
+
@memoized_methods ||= {}
|
|
122
87
|
end
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
end # Memoizable
|
|
88
|
+
end
|
|
89
|
+
end
|