memorb 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9724352c954ae84fd4004bc0cdc534fd71c847fdd5ceeb241a1b92855cd3374f
4
- data.tar.gz: 3d1b910ae44c6680eb4ae5a997800630da89bffed0f851849b32beedfbbeb257
3
+ metadata.gz: eddf3e947817bc71d168593c818b5a983a060cd62e21936e62595a9b9400d918
4
+ data.tar.gz: 1d306ffd9b7068f27de0bdfe9267dc7a5a243a7bcfbe1e90380b3cb6a68bd229
5
5
  SHA512:
6
- metadata.gz: 62d3610c4f5591da319e16bef054ad79acc32329c4ea95e2a9fe793b1124e3059fdf6743a19130e1c6fd5a380bd387c3b0ea8b67b39e3fc13c01c4880b1b23ce
7
- data.tar.gz: ece13fcec090a805cd010217e85d6ff3b42868156c47856f58781dc79faa1edb30eb88b06da0039869aac93cc59229c86c98c3c2d308376c39cf970538622983
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
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- source :rubygems
3
+ source 'https://rubygems.org'
4
4
  gemspec
data/README.md CHANGED
@@ -2,7 +2,11 @@
2
2
 
3
3
  Memoize instance methods with ease.
4
4
 
5
- [![CircleCI](https://circleci.com/gh/pjrebsch/memorb/tree/master.svg?style=svg)](https://circleci.com/gh/pjrebsch/memorb/tree/master)
5
+ [![Gem Version](https://badge.fury.io/rb/memorb.svg)](https://badge.fury.io/rb/memorb) [![CircleCI](https://circleci.com/gh/pjrebsch/memorb/tree/master.svg?style=svg)](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
- memorb! def data
64
+ memoize def data
61
65
  ...
62
66
  end
63
67
 
64
- memorb! def week_days
68
+ memoize def week_days
65
69
  ...
66
70
  end
67
71
 
68
- memorb! def rain_on?(day)
72
+ memoize def rain_on?(day)
69
73
  ...
70
74
  end
71
75
 
72
- memorb! def will_rain?
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 `memorb!` class method to register instance methods for memoization.
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
- #### `memorb!`
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
- memorb! def data
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 `memorb!` 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.
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
- memorb! \
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
- memorb! :data, :week_days, :rain_on?, :will_rain?
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
- memorb! do
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 `memorb!`.
164
+ Alias of `memoize`.
161
165
 
162
166
  #### `registered_methods`
163
167
 
@@ -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).tap do |agent|
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 memorb!(*args, &block)
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
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Memorb
4
- VERSION = '0.1.0'
4
+ VERSION = '0.2.0'
5
5
  end
@@ -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 '::memorb!' do
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.memorb!(:a, &block)
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.1.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: 2020-06-08 00:00:00.000000000 Z
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.0.4
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: []