forget-me-not 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 +8 -8
- data/CHANGELOG.md +2 -1
- data/README.md +36 -7
- data/lib/forget-me-not/memoizable.rb +8 -1
- data/lib/forget-me-not/version.rb +1 -1
- data/spec/memoizable_spec.rb +42 -2
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
ZDgxZjQ2MjE0MmM1ODk2ZWMzODVlM2ExMmQyZjg5YmQ1NzUwNWRlZQ==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
MzNkM2Y0OWIyNTE4Yzk4NWZlNDI2Nzc1YjI4MzhhZjUwYjMyMTZjMQ==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
YzI2OTAyOWZhZjU5ZmQxODNjYzE0OWFjZDAzNmQ4NWU5YmU4YWY2YjBmMDhj
|
10
|
+
YTFiMmEyZjNkNWRiM2IyNWZiYmY2MTVkN2IwOTkzNGM4OWM2N2UzMTE1OGJl
|
11
|
+
MjViZWE2NGZhZmQzNjMxZjA0MDA2YWE0YzA1ZTUxN2FlNDdlNDE=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
Yzc5NTExM2YzNjI1MjdlYzZkNmUwYzg1MzZjNWIzMGU3Nzg3MWM2NzY4MDdi
|
14
|
+
ZTViODhjYzg2YWQxNDE4MjdhOGYzMDZiOGI0MWU2YTY4ZmZjZjNkZTdlOTVh
|
15
|
+
ODZlYmI0ZDVmYjMwZGJhOTdmMTQ4ZTc5ZGQ0MzYzOTYyNmEzZjg=
|
data/CHANGELOG.md
CHANGED
@@ -1 +1,2 @@
|
|
1
|
-
## 0.0.
|
1
|
+
## 0.2.0.0 - Still pre release, but made a new method for memoizing methods with args.
|
2
|
+
## 0.1.0.0 - Pre Release
|
data/README.md
CHANGED
@@ -60,16 +60,40 @@ called once.
|
|
60
60
|
'result'
|
61
61
|
end
|
62
62
|
|
63
|
-
def
|
63
|
+
def method_with_args(with_an_arg)
|
64
64
|
"result2-#{with_an_arg}"
|
65
65
|
end
|
66
66
|
|
67
|
-
memoize :some_method
|
67
|
+
memoize :some_method
|
68
|
+
memoize_with_args :method_with_args
|
68
69
|
end
|
69
70
|
|
70
|
-
Calls to both some_method and
|
71
|
-
argument values will result in different results being memoized.
|
71
|
+
Calls to both some_method and method_with_args are memoized.
|
72
72
|
|
73
|
+
#### Caution!
|
74
|
+
Use caution when memoizing results on long-lived objects (e.g., objects that live for the life-time of the process).
|
75
|
+
Memoized results never expire and the initially calculated result will always be the one that is returned. This can
|
76
|
+
lead to incorrect application logic if the memoized results would differ if calculated at a later time.
|
77
|
+
|
78
|
+
_Consider the case of the application that memoizes a user's privileges the first time they are needed. If the privilege
|
79
|
+
container were long lived, it would never reflect a change in the user's privileges. (There are more subtle ways to
|
80
|
+
lose, this is just an obvious example)._
|
81
|
+
|
82
|
+
An additional caution is warranted when memoizing methods that take arguments. Because different argument values result
|
83
|
+
in different results being memoized, if the set of arguments is unbounded, a great deal of memory can be consumed to
|
84
|
+
hold on to the results.
|
85
|
+
|
86
|
+
The easiest way to prevent stale results and excessive consumption of memory when memoizing is to ensure that two
|
87
|
+
things are true:
|
88
|
+
- The memoizing object has a finite, well-known life span (e.g., a request)
|
89
|
+
- If arguments are used, the set of possible arguments is bounded
|
90
|
+
|
91
|
+
In order to draw attention to the potential issues with argument driven memoization, you must call the memoize_with_args
|
92
|
+
method if the method being memoized has arity > 0.
|
93
|
+
|
94
|
+
Memoization is a powerful tool, but like all powerful tools, needs to be used with knowledge and respect.
|
95
|
+
|
96
|
+
#### Storage
|
73
97
|
By default, the memoization code stores results in a simple Hash based cache. If you have other requirements, perhaps
|
74
98
|
a thread-safe storage, then set the ForgetMeNot::Memoization.storage_builder property to a proc that will create a new
|
75
99
|
instance of whatever storage you desire.
|
@@ -128,11 +152,16 @@ key members.
|
|
128
152
|
cache :some_method, :include => :important_property
|
129
153
|
end
|
130
154
|
|
155
|
+
#### Caution!
|
156
|
+
Of course, every argument variation and every included instance property amplifies the potential amount of cache
|
157
|
+
memory that is consumed.
|
158
|
+
|
159
|
+
#### Storage
|
131
160
|
By default, the cache will attempt to use the Rails cache. If that isn't found, but ActiveSupport is available, a new
|
132
|
-
instance of MemoryStore will be used. Failing that, cache will raise an error.
|
133
|
-
sane default, but really, set the ForgetMeNot::Cacheable.cache. Cacheable expects a cache shaped like an
|
134
|
-
ActiveSupport::Cacheable::Store
|
161
|
+
instance of MemoryStore will be used. Failing that, cache will raise an error.
|
135
162
|
|
163
|
+
This is intended to provide a reasonably sane default, but really, set the ForgetMeNot::Cacheable.cache with something
|
164
|
+
shaped like an ActiveSupport::Cacheable::Store
|
136
165
|
|
137
166
|
## Origins
|
138
167
|
This is an extension of the ideas and approach found here:
|
@@ -13,15 +13,22 @@ module ForgetMeNot
|
|
13
13
|
methods.each { |m| memoize_method(m, options) }
|
14
14
|
end
|
15
15
|
|
16
|
+
def memoize_with_args(*methods)
|
17
|
+
options = methods.last.is_a?(Hash) ? methods.pop : {}
|
18
|
+
options = options.merge(allow_args: true)
|
19
|
+
methods.each { |m| memoize_method(m, options) }
|
20
|
+
end
|
21
|
+
|
16
22
|
private
|
17
23
|
def memoize_method(method_name, options)
|
18
24
|
method = instance_method(method_name)
|
25
|
+
raise 'Cannot memoize with arity > 0. Use memoize_with_args instead.' if method.arity > 0 && ! options[:allow_args]
|
19
26
|
visibility = method_visibility(method_name)
|
20
27
|
define_memoized_method(method, options)
|
21
28
|
send(visibility, method_name)
|
22
29
|
end
|
23
30
|
|
24
|
-
def define_memoized_method(method,
|
31
|
+
def define_memoized_method(method, _)
|
25
32
|
method_name = method.name.to_sym
|
26
33
|
key_prefix = "/memoized_method_result/#{self.name}"
|
27
34
|
|
data/spec/memoizable_spec.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
module ForgetMeNot
|
4
|
+
|
4
5
|
class MemoizeTestClass
|
5
6
|
include Memoizable
|
6
7
|
include MethodCounter
|
@@ -30,13 +31,13 @@ module ForgetMeNot
|
|
30
31
|
"method5({#{hash_arg.map { |k, v| "#{k}/#{v}" }.join '|' }})"
|
31
32
|
end
|
32
33
|
|
33
|
-
memoize :method1
|
34
|
+
memoize :method1
|
35
|
+
memoize_with_args :method2, :method3, :method4, :method5
|
34
36
|
end
|
35
37
|
|
36
38
|
describe Memoizable do
|
37
39
|
before do
|
38
40
|
MemoizeTestClass.clear_calls
|
39
|
-
TestClass2.clear_calls
|
40
41
|
end
|
41
42
|
|
42
43
|
|
@@ -60,6 +61,45 @@ module ForgetMeNot
|
|
60
61
|
|
61
62
|
end
|
62
63
|
|
64
|
+
describe 'memoize arity > 0 calls' do
|
65
|
+
it 'throws an exception if memoize is called for a function with arguments' do
|
66
|
+
expect do
|
67
|
+
Class.new do
|
68
|
+
include Memoizable
|
69
|
+
|
70
|
+
def method(arg)
|
71
|
+
end
|
72
|
+
memoize :method
|
73
|
+
end
|
74
|
+
end.to raise_error 'Cannot memoize with arity > 0. Use memoize_with_args instead.'
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'allows memoization if the allow_args option is true' do
|
78
|
+
expect do
|
79
|
+
Class.new do
|
80
|
+
include Memoizable
|
81
|
+
|
82
|
+
def method(arg)
|
83
|
+
end
|
84
|
+
memoize :method, allow_args: true
|
85
|
+
end
|
86
|
+
end.not_to raise_error
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'allows memoization if memoize_with_args is called' do
|
90
|
+
expect do
|
91
|
+
Class.new do
|
92
|
+
include Memoizable
|
93
|
+
|
94
|
+
def method(arg)
|
95
|
+
end
|
96
|
+
memoize_with_args :method
|
97
|
+
end
|
98
|
+
end.not_to raise_error
|
99
|
+
end
|
100
|
+
|
101
|
+
|
102
|
+
end
|
63
103
|
describe 'memoize arity-1 calls' do
|
64
104
|
it 'should memoize calls from the same instance, same argument' do
|
65
105
|
foo = MemoizeTestClass.new
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: forget-me-not
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Koan Health
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-11-
|
11
|
+
date: 2013-11-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|