memorb 0.1.0 → 0.2.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/CHANGELOG.md +24 -0
- data/Gemfile +1 -1
- data/README.md +17 -13
- data/lib/memorb/integration.rb +1 -32
- data/lib/memorb/integrator_class_methods.rb +1 -3
- data/lib/memorb/version.rb +1 -1
- data/spec/memorb/integration_spec.rb +0 -43
- data/spec/memorb/integrator_class_methods_spec.rb +2 -22
- metadata +7 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: eddf3e947817bc71d168593c818b5a983a060cd62e21936e62595a9b9400d918
|
4
|
+
data.tar.gz: 1d306ffd9b7068f27de0bdfe9267dc7a5a243a7bcfbe1e90380b3cb6a68bd229
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a9a012843efd2230b6a6927be7048025e1c6d993df11e60d92824acad0b1400159883d14a49ef32b586cdd5057bb98f5e502c747b25151fa8996e60f055f04cc
|
7
|
+
data.tar.gz: b0010e430dd6f80385b369eff2e7ca7962481401a592882509a7351120fc0f6eb6891518a0c85197fef0ce04f3d954a73b71c14c59fb965199d8cac81c0feecf
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,24 @@
|
|
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.0.0/),
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
7
|
+
|
8
|
+
## [Unreleased]
|
9
|
+
|
10
|
+
## [0.2.0] - 2021-05-11
|
11
|
+
|
12
|
+
### Changed
|
13
|
+
|
14
|
+
- BREAKING CHANGE: Changed the activation alias from `memorb!` to `memoize`.
|
15
|
+
- No longer keep a global registry of instance agents to reduce the risk of memory leakage.
|
16
|
+
- Updated Gemfile source to HTTPS.
|
17
|
+
|
18
|
+
### Removed
|
19
|
+
|
20
|
+
- Removed undocumented `purge` method.
|
21
|
+
|
22
|
+
## [0.1.0] - 2020-06-08
|
23
|
+
|
24
|
+
Initial release.
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -2,7 +2,11 @@
|
|
2
2
|
|
3
3
|
Memoize instance methods with ease.
|
4
4
|
|
5
|
-
[](https://circleci.com/gh/pjrebsch/memorb/tree/master)
|
5
|
+
[](https://badge.fury.io/rb/memorb) [](https://circleci.com/gh/pjrebsch/memorb/tree/master)
|
6
|
+
|
7
|
+
```bash
|
8
|
+
gem install memorb
|
9
|
+
```
|
6
10
|
|
7
11
|
## Overview
|
8
12
|
|
@@ -57,19 +61,19 @@ Memorb exists to make memoization in these cases much easier to implement. Simpl
|
|
57
61
|
class WeekForecast
|
58
62
|
extend Memorb
|
59
63
|
|
60
|
-
|
64
|
+
memoize def data
|
61
65
|
...
|
62
66
|
end
|
63
67
|
|
64
|
-
|
68
|
+
memoize def week_days
|
65
69
|
...
|
66
70
|
end
|
67
71
|
|
68
|
-
|
72
|
+
memoize def rain_on?(day)
|
69
73
|
...
|
70
74
|
end
|
71
75
|
|
72
|
-
|
76
|
+
memoize def will_rain?
|
73
77
|
...
|
74
78
|
end
|
75
79
|
end
|
@@ -79,13 +83,13 @@ These methods' return values will now be memoized for each instance of `WeekFore
|
|
79
83
|
|
80
84
|
## Usage
|
81
85
|
|
82
|
-
First, integrate Memorb into a class with `extend Memorb`. Then, use the `
|
86
|
+
First, integrate Memorb into a class with `extend Memorb`. Then, use the `memoize` class method to register instance methods for memoization.
|
83
87
|
|
84
88
|
### Integrating Class Methods
|
85
89
|
|
86
90
|
These methods are available as class methods on the integrating class.
|
87
91
|
|
88
|
-
#### `
|
92
|
+
#### `memoize`
|
89
93
|
|
90
94
|
Use this method to register instance methods for memoization. When a method is both registered and defined, Memorb will override it. Once the method is overridden, it's considered "enabled" for memoization. On initial invocation with a given set of arguments, the method's return value is cached based on the given arguments and returned. Then, subsequent invocations of that method with the same arguments return the cached value.
|
91
95
|
|
@@ -96,15 +100,15 @@ Internally, calls to the overriding method implementation are serialized with a
|
|
96
100
|
Conveniently, methods defined using the `def` keyword return the method name, so the method definition can just be prefixed with a registration directive. This approach helps make apparent the fact that the method is being memoized when reading the method.
|
97
101
|
|
98
102
|
```ruby
|
99
|
-
|
103
|
+
memoize def data
|
100
104
|
...
|
101
105
|
end
|
102
106
|
```
|
103
107
|
|
104
|
-
If you prefer `def` and `end` to align, you can move `
|
108
|
+
If you prefer `def` and `end` to align, you can move `memoize` up to a new line and escape the line break. The Memorb registration methods require arguments, so if you forget to escape the line break, you'll be made aware with an exception when the class is loaded.
|
105
109
|
|
106
110
|
```ruby
|
107
|
-
|
111
|
+
memoize \
|
108
112
|
def data
|
109
113
|
...
|
110
114
|
end
|
@@ -115,7 +119,7 @@ end
|
|
115
119
|
If you wish to enumerate the methods to register all at once, or don't have access to a method's implementation source to use the Prefix form, you can supply a list of method names instead.
|
116
120
|
|
117
121
|
```ruby
|
118
|
-
|
122
|
+
memoize :data, :week_days, :rain_on?, :will_rain?
|
119
123
|
```
|
120
124
|
|
121
125
|
Typos are a potential problem. Memorb can't know when a registered method's definition is going to occur, so if you mistype the name of a method you intend to define later, Memorb will anticipate that method's definition indefinitely and the method that you intended to register won't end up being memoized. The Prefix form is recommended for this reason.
|
@@ -127,7 +131,7 @@ If you do use this form, you can check that all registered methods were enabled
|
|
127
131
|
Instead of listing out method names or decorating their definitions, you can just define them within a block.
|
128
132
|
|
129
133
|
```ruby
|
130
|
-
|
134
|
+
memoize do
|
131
135
|
def data
|
132
136
|
...
|
133
137
|
end
|
@@ -157,7 +161,7 @@ These methods are available on the `Memorb::Integration` instance for an integra
|
|
157
161
|
|
158
162
|
#### `register`
|
159
163
|
|
160
|
-
Alias of `
|
164
|
+
Alias of `memoize`.
|
161
165
|
|
162
166
|
#### `registered_methods`
|
163
167
|
|
data/lib/memorb/integration.rb
CHANGED
@@ -87,10 +87,6 @@ module Memorb
|
|
87
87
|
_enabled?(_identifier(name))
|
88
88
|
end
|
89
89
|
|
90
|
-
def purge(name)
|
91
|
-
_purge(_identifier(name))
|
92
|
-
end
|
93
|
-
|
94
90
|
def auto_register?
|
95
91
|
_auto_registration.value > 0
|
96
92
|
end
|
@@ -125,17 +121,8 @@ module Memorb
|
|
125
121
|
|
126
122
|
alias_method :inspect, :name
|
127
123
|
|
128
|
-
# Never save reference to the integrator instance or it may
|
129
|
-
# never be garbage collected!
|
130
124
|
def create_agent(integrator_instance)
|
131
|
-
Agent.new(integrator_instance.object_id)
|
132
|
-
_agents.write(agent.id, agent)
|
133
|
-
|
134
|
-
# The proc must not be made here because it would save a
|
135
|
-
# reference to `integrator_instance`.
|
136
|
-
finalizer = _agent_finalizer(agent.id)
|
137
|
-
::ObjectSpace.define_finalizer(integrator_instance, finalizer)
|
138
|
-
end
|
125
|
+
Agent.new(integrator_instance.object_id)
|
139
126
|
end
|
140
127
|
|
141
128
|
private
|
@@ -190,14 +177,6 @@ module Memorb
|
|
190
177
|
_overrides.keys.include?(method_id)
|
191
178
|
end
|
192
179
|
|
193
|
-
def _purge(method_id)
|
194
|
-
_agents.keys.each do |id|
|
195
|
-
agent = _agents.read(id)
|
196
|
-
store = agent&.method_store&.read(method_id)
|
197
|
-
store&.reset!
|
198
|
-
end
|
199
|
-
end
|
200
|
-
|
201
180
|
def _remove_override(method_id)
|
202
181
|
# Ruby will raise an exception if the method doesn't exist.
|
203
182
|
# Catching it is the safest thing to do for thread-safety.
|
@@ -233,11 +212,6 @@ module Memorb
|
|
233
212
|
visibility
|
234
213
|
end
|
235
214
|
|
236
|
-
def _agent_finalizer(agent_id)
|
237
|
-
# This must not be a lambda proc, otherwise GC hangs!
|
238
|
-
::Proc.new { _agents.forget(agent_id) }
|
239
|
-
end
|
240
|
-
|
241
215
|
def _registrations
|
242
216
|
RubyCompatibility.module_constant(self, :registrations)
|
243
217
|
end
|
@@ -246,10 +220,6 @@ module Memorb
|
|
246
220
|
RubyCompatibility.module_constant(self, :overrides)
|
247
221
|
end
|
248
222
|
|
249
|
-
def _agents
|
250
|
-
RubyCompatibility.module_constant(self, :agents)
|
251
|
-
end
|
252
|
-
|
253
223
|
def _auto_registration
|
254
224
|
RubyCompatibility.module_constant(self, :auto_registration)
|
255
225
|
end
|
@@ -259,7 +229,6 @@ module Memorb
|
|
259
229
|
|
260
230
|
RubyCompatibility.module_constant_set(mixin, :registrations, KeyValueStore.new)
|
261
231
|
RubyCompatibility.module_constant_set(mixin, :overrides, KeyValueStore.new)
|
262
|
-
RubyCompatibility.module_constant_set(mixin, :agents, KeyValueStore.new)
|
263
232
|
RubyCompatibility.module_constant_set(mixin,
|
264
233
|
:auto_registration,
|
265
234
|
::Concurrent::AtomicFixnum.new,
|
@@ -7,7 +7,7 @@ module Memorb
|
|
7
7
|
Integration[self]
|
8
8
|
end
|
9
9
|
|
10
|
-
def
|
10
|
+
def memoize(*args, &block)
|
11
11
|
memorb.register(*args, &block)
|
12
12
|
end
|
13
13
|
|
@@ -29,14 +29,12 @@ module Memorb
|
|
29
29
|
def method_removed(name)
|
30
30
|
super.tap do
|
31
31
|
memorb.disable(name)
|
32
|
-
memorb.purge(name)
|
33
32
|
end
|
34
33
|
end
|
35
34
|
|
36
35
|
def method_undefined(name)
|
37
36
|
super.tap do
|
38
37
|
memorb.disable(name)
|
39
|
-
memorb.purge(name)
|
40
38
|
end
|
41
39
|
end
|
42
40
|
|
data/lib/memorb/version.rb
CHANGED
@@ -64,7 +64,6 @@ describe ::Memorb::Integration do
|
|
64
64
|
let(:integrator) { target.tap { |x| x.extend(::Memorb) } }
|
65
65
|
let(:integrator_singleton) { integrator.singleton_class }
|
66
66
|
let(:instance) { integrator.new }
|
67
|
-
let(:agent_registry) { subject.send(:_agents) }
|
68
67
|
subject { described_class[integrator] }
|
69
68
|
|
70
69
|
describe 'integrator instance methods' do
|
@@ -76,10 +75,6 @@ describe ::Memorb::Integration do
|
|
76
75
|
agent = instance.memorb
|
77
76
|
expect(agent.id).to equal(instance.object_id)
|
78
77
|
end
|
79
|
-
it 'adds the agent to the global registry' do
|
80
|
-
agent = instance.memorb
|
81
|
-
expect(agent_registry.keys).to contain_exactly(agent.id)
|
82
|
-
end
|
83
78
|
end
|
84
79
|
describe '#memorb' do
|
85
80
|
it 'returns the agent for the instance' do
|
@@ -355,27 +350,6 @@ describe ::Memorb::Integration do
|
|
355
350
|
end
|
356
351
|
end
|
357
352
|
end
|
358
|
-
describe '::purge' do
|
359
|
-
let(:method_name) { :increment }
|
360
|
-
let(:method_id) { ::Memorb::MethodIdentifier.new(method_name) }
|
361
|
-
|
362
|
-
it 'clears cached data for the given method in all instances' do
|
363
|
-
subject.register(method_name)
|
364
|
-
instance.send(method_name)
|
365
|
-
store = instance.memorb.method_store.read(method_id)
|
366
|
-
expect(store.keys).not_to be_empty
|
367
|
-
subject.purge(method_name)
|
368
|
-
expect(store.keys).to be_empty
|
369
|
-
end
|
370
|
-
context 'when the given method has no cache record' do
|
371
|
-
it 'does not raise an error' do
|
372
|
-
subject.register(method_name)
|
373
|
-
store = instance.memorb.method_store.read(method_id)
|
374
|
-
expect(store).to be(nil)
|
375
|
-
expect { subject.purge(method_name) }.not_to raise_error
|
376
|
-
end
|
377
|
-
end
|
378
|
-
end
|
379
353
|
describe '::auto_register?' do
|
380
354
|
context 'by default' do
|
381
355
|
it 'returns false' do
|
@@ -476,11 +450,6 @@ describe ::Memorb::Integration do
|
|
476
450
|
agent = subject.create_agent(instance)
|
477
451
|
expect(agent).to be_an_instance_of(::Memorb::Agent)
|
478
452
|
end
|
479
|
-
it 'writes the agent to the global agent registry' do
|
480
|
-
agent = subject.create_agent(instance)
|
481
|
-
registry = subject.send(:_agents)
|
482
|
-
expect(registry.keys).to contain_exactly(agent.id)
|
483
|
-
end
|
484
453
|
end
|
485
454
|
it 'supports regularly invalid method names' do
|
486
455
|
invalid_starting_chars = [0x00..0x40, 0x5b..0x60, 0x7b..0xff]
|
@@ -513,17 +482,5 @@ describe ::Memorb::Integration do
|
|
513
482
|
expect { klass.include(subject) }.to raise_error(error, error_message)
|
514
483
|
end
|
515
484
|
end
|
516
|
-
::SpecHelper.for_testing_garbage_collection do
|
517
|
-
context 'when freed by the garbage collector' do
|
518
|
-
it 'removes its agent from the global registry' do
|
519
|
-
ref = ::WeakRef.new(integrator.new)
|
520
|
-
agent = ref.__getobj__.memorb
|
521
|
-
expect(agent_registry.keys).to include(agent.id)
|
522
|
-
::SpecHelper.force_garbage_collection
|
523
|
-
expect(ref.weakref_alive?).to be_falsey
|
524
|
-
expect(agent_registry.keys).to be_empty
|
525
|
-
end
|
526
|
-
end
|
527
|
-
end
|
528
485
|
end
|
529
486
|
end
|
@@ -10,7 +10,7 @@ describe ::Memorb::IntegratorClassMethods do
|
|
10
10
|
expect(integrator.memorb).to be(integration)
|
11
11
|
end
|
12
12
|
end
|
13
|
-
describe '::
|
13
|
+
describe '::memoize' do
|
14
14
|
it 'calls register with the same arguments' do
|
15
15
|
spy = double('spy', register: nil)
|
16
16
|
mod = Module.new
|
@@ -18,7 +18,7 @@ describe ::Memorb::IntegratorClassMethods do
|
|
18
18
|
integrator.singleton_class.prepend(mod)
|
19
19
|
block = Proc.new { nil }
|
20
20
|
expect(spy).to receive(:register).with(:a, &block)
|
21
|
-
integrator.
|
21
|
+
integrator.memoize(:a, &block)
|
22
22
|
end
|
23
23
|
end
|
24
24
|
describe '::inherited' do
|
@@ -90,16 +90,6 @@ describe ::Memorb::IntegratorClassMethods do
|
|
90
90
|
expect(integration.enabled_methods).not_to include(method_name)
|
91
91
|
expect(integration.public_instance_methods).not_to include(method_name)
|
92
92
|
end
|
93
|
-
it 'clears cached data for the method in all instances' do
|
94
|
-
::Memorb::RubyCompatibility
|
95
|
-
.define_method(integrator, method_name) { nil }
|
96
|
-
integration.register(method_name)
|
97
|
-
instance.send(method_name)
|
98
|
-
store = instance.memorb.method_store.read(method_id)
|
99
|
-
expect(store.keys).not_to be_empty
|
100
|
-
::Memorb::RubyCompatibility.remove_method(integrator, method_name)
|
101
|
-
expect(store.keys).to be_empty
|
102
|
-
end
|
103
93
|
end
|
104
94
|
describe '::method_undefined' do
|
105
95
|
let(:method_name) { :method_1 }
|
@@ -128,15 +118,5 @@ describe ::Memorb::IntegratorClassMethods do
|
|
128
118
|
instance.send(method_name)
|
129
119
|
}.to raise_error(::NoMethodError, /undefined method/)
|
130
120
|
end
|
131
|
-
it 'clears cached data for the method in all instances' do
|
132
|
-
::Memorb::RubyCompatibility
|
133
|
-
.define_method(integrator, method_name) { nil }
|
134
|
-
integration.register(method_name)
|
135
|
-
instance.send(method_name)
|
136
|
-
store = instance.memorb.method_store.read(method_id)
|
137
|
-
expect(store.keys).not_to be_empty
|
138
|
-
::Memorb::RubyCompatibility.undef_method(integrator, method_name)
|
139
|
-
expect(store.keys).to be_empty
|
140
|
-
end
|
141
121
|
end
|
142
122
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: memorb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Patrick Rebsch
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-05-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -64,6 +64,7 @@ files:
|
|
64
64
|
- ".circleci/config.yml"
|
65
65
|
- ".gitignore"
|
66
66
|
- ".rspec"
|
67
|
+
- CHANGELOG.md
|
67
68
|
- Gemfile
|
68
69
|
- LICENSE.txt
|
69
70
|
- README.md
|
@@ -89,7 +90,7 @@ homepage: https://github.com/pjrebsch/memorb
|
|
89
90
|
licenses:
|
90
91
|
- MIT
|
91
92
|
metadata: {}
|
92
|
-
post_install_message:
|
93
|
+
post_install_message:
|
93
94
|
rdoc_options: []
|
94
95
|
require_paths:
|
95
96
|
- lib
|
@@ -104,8 +105,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
104
105
|
- !ruby/object:Gem::Version
|
105
106
|
version: '2.6'
|
106
107
|
requirements: []
|
107
|
-
rubygems_version: 3.
|
108
|
-
signing_key:
|
108
|
+
rubygems_version: 3.1.2
|
109
|
+
signing_key:
|
109
110
|
specification_version: 4
|
110
111
|
summary: Memoization made easy
|
111
112
|
test_files: []
|