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 CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- YWI4NDI2YmQ5NTc1NjMyOGM5Y2UyZjk1NmFhYTcwODQyNWYzMzQ0Mw==
4
+ ZDgxZjQ2MjE0MmM1ODk2ZWMzODVlM2ExMmQyZjg5YmQ1NzUwNWRlZQ==
5
5
  data.tar.gz: !binary |-
6
- NTFmOTRhYTg3NjNjZjAwM2JmNzIyYmRkNmI3YzM5NjJlNWY1ZjI4NA==
6
+ MzNkM2Y0OWIyNTE4Yzk4NWZlNDI2Nzc1YjI4MzhhZjUwYjMyMTZjMQ==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- MWJiNjdmODBmOTIwZGEzOThjNThjZDU2YjJlODk0M2RhN2Q2MjJkNjU5MjVj
10
- YTZmYmU2ZDViYjg4MTc1Yzc0YjQ4M2ZmMjBjOTkxYzViZTAwZmQ4NzUzZTgw
11
- OWQzMzRkODE0ZWMwN2VlNjIwNjk4NGJlYzAxMDY4NjE4ZjhhNjk=
9
+ YzI2OTAyOWZhZjU5ZmQxODNjYzE0OWFjZDAzNmQ4NWU5YmU4YWY2YjBmMDhj
10
+ YTFiMmEyZjNkNWRiM2IyNWZiYmY2MTVkN2IwOTkzNGM4OWM2N2UzMTE1OGJl
11
+ MjViZWE2NGZhZmQzNjMxZjA0MDA2YWE0YzA1ZTUxN2FlNDdlNDE=
12
12
  data.tar.gz: !binary |-
13
- M2MwOWE5MjFlN2U4ODRlN2VjMDg0MWQzYWZlODk0YmE2YmY5MmU5MjlhNmJj
14
- MmJhNzk1N2RiMzM4YjMxNzY3MTlhYWI3MTYyM2FhYjViZTA4MTU5MDI4OWU0
15
- ZGRiNWIxOTJkZWI5MmQxY2FjYzU3MzUwMTE3NDg4OGViM2NlYzA=
13
+ Yzc5NTExM2YzNjI1MjdlYzZkNmUwYzg1MzZjNWIzMGU3Nzg3MWM2NzY4MDdi
14
+ ZTViODhjYzg2YWQxNDE4MjdhOGYzMDZiOGI0MWU2YTY4ZmZjZjNkZTdlOTVh
15
+ ODZlYmI0ZDVmYjMwZGJhOTdmMTQ4ZTc5ZGQ0MzYzOTYyNmEzZjg=
data/CHANGELOG.md CHANGED
@@ -1 +1,2 @@
1
- ## 0.0.1.0 - Pre Release
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 some_other_method(with_an_arg)
63
+ def method_with_args(with_an_arg)
64
64
  "result2-#{with_an_arg}"
65
65
  end
66
66
 
67
- memoize :some_method, :some_other_method
67
+ memoize :some_method
68
+ memoize_with_args :method_with_args
68
69
  end
69
70
 
70
- Calls to both some_method and some_other_method are memoized. Notice that some_other_method takes an argument, differing
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. This is intended to provide a reasonably
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, options)
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
 
@@ -1,3 +1,3 @@
1
1
  module ForgetMeNot
2
- VERSION = '0.1.0'
2
+ VERSION = '0.2.0'
3
3
  end
@@ -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, :method2, :method3, :method4, :method5
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.1.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-18 00:00:00.000000000 Z
11
+ date: 2013-11-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport