memoist 0.12.0 → 0.16.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.travis.yml +15 -8
- data/CHANGELOG.md +130 -0
- data/README.md +75 -50
- data/Rakefile +6 -5
- data/lib/memoist/version.rb +3 -1
- data/lib/memoist.rb +76 -39
- data/memoist.gemspec +31 -23
- data/script/benchmark.rb +48 -0
- data/test/memoist_test.rb +225 -33
- data/test/test_helper.rb +1 -1
- metadata +26 -12
- data/lib/memoist/core_ext/singleton_class.rb +0 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: '058dae917e46d13306497f0bcd18df607cb1d5dca2d85ee6f9cbc18739172e27'
|
4
|
+
data.tar.gz: c0874b135f70db573a863e4b54bb141905c003d9e451d7c3637d8f67c96a0696
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 409f37c0861015cc81704bf4fbbb1002d7a011a5f3dccb0e1430baf8b5eac601f5caae99cf4e1634fe044ea01123c1a0319692fad6d64c848026329f9a4dfa7a
|
7
|
+
data.tar.gz: 7f3134101c02c37ce88fd6a814c6bce19f5ffc4085fcfbb08e22ce720f125b7f8c2c2644c6a694cf4bb5dc4feb40974785f1209d598c18ba098bc055bca33146
|
data/.travis.yml
CHANGED
@@ -1,14 +1,21 @@
|
|
1
|
+
sudo: false
|
2
|
+
cache: bundler
|
1
3
|
language: ruby
|
2
4
|
rvm:
|
3
|
-
- 1.8.7
|
4
|
-
- 1.9.2
|
5
5
|
- 1.9.3
|
6
6
|
- 2.0.0
|
7
|
-
- 2.1.
|
8
|
-
- 2.2.
|
9
|
-
-
|
10
|
-
-
|
11
|
-
-
|
7
|
+
- 2.1.10
|
8
|
+
- 2.2.7
|
9
|
+
- 2.3.4
|
10
|
+
- 2.4.1
|
11
|
+
- 2.5.1
|
12
|
+
- 2.6.0
|
12
13
|
- ruby-head
|
14
|
+
- jruby-19mode
|
15
|
+
- jruby-9.1.9.0
|
13
16
|
- jruby-head
|
14
|
-
|
17
|
+
|
18
|
+
before_install:
|
19
|
+
- gem install bundler --no-document -v '~> 1.13'
|
20
|
+
before_script:
|
21
|
+
- unset JRUBY_OPTS
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
## [Unreleased](https://github.com/matthewrudy/memoist/tree/HEAD)
|
4
|
+
|
5
|
+
[Full Changelog](https://github.com/matthewrudy/memoist/compare/v0.16.1...HEAD)
|
6
|
+
|
7
|
+
**Merged pull requests:**
|
8
|
+
|
9
|
+
- Fix regression introduced by frozen symbol fix [\#86](https://github.com/matthewrudy/memoist/pull/86) ([sebjacobs](https://github.com/sebjacobs))
|
10
|
+
|
11
|
+
## [v0.16.1](https://github.com/matthewrudy/memoist/tree/v0.16.1) (2019-11-08)
|
12
|
+
|
13
|
+
[Full Changelog](https://github.com/matthewrudy/memoist/compare/v0.16.0...v0.16.1)
|
14
|
+
|
15
|
+
**Merged pull requests:**
|
16
|
+
|
17
|
+
- Remove ruby 1.9.2 from travis build matrix [\#84](https://github.com/matthewrudy/memoist/pull/84) ([unasuke](https://github.com/unasuke))
|
18
|
+
- Make Memoist.escape\_punctuation compatible with MRI 2.7 [\#82](https://github.com/matthewrudy/memoist/pull/82) ([casperisfine](https://github.com/casperisfine))
|
19
|
+
- add 2.5.1 to travis [\#77](https://github.com/matthewrudy/memoist/pull/77) ([matthewrudy](https://github.com/matthewrudy))
|
20
|
+
- Remove ghit.me [\#74](https://github.com/matthewrudy/memoist/pull/74) ([matthewrudy](https://github.com/matthewrudy))
|
21
|
+
- Place sample code for execution in README.md [\#73](https://github.com/matthewrudy/memoist/pull/73) ([3nan3](https://github.com/3nan3))
|
22
|
+
- Require Ruby \>=1.9.2 [\#69](https://github.com/matthewrudy/memoist/pull/69) ([matthewrudy](https://github.com/matthewrudy))
|
23
|
+
|
24
|
+
## [v0.16.0](https://github.com/matthewrudy/memoist/tree/v0.16.0) (2017-06-20)
|
25
|
+
|
26
|
+
[Full Changelog](https://github.com/matthewrudy/memoist/compare/v0.15.0...v0.16.0)
|
27
|
+
|
28
|
+
**Merged pull requests:**
|
29
|
+
|
30
|
+
- Fix undefined `memoized\_methods` error raised when a parent class has not call `memoize` [\#68](https://github.com/matthewrudy/memoist/pull/68) ([PikachuEXE](https://github.com/PikachuEXE))
|
31
|
+
- Add support for class-level cache flushing. [\#67](https://github.com/matthewrudy/memoist/pull/67) ([JoeMcB](https://github.com/JoeMcB))
|
32
|
+
- Add ruby 2.4 to travis \(bump 2.2 and 2.3 versions\) [\#64](https://github.com/matthewrudy/memoist/pull/64) ([jrafanie](https://github.com/jrafanie))
|
33
|
+
- Fix tests for Ruby \< 1.9.3 [\#56](https://github.com/matthewrudy/memoist/pull/56) ([matthewrudy](https://github.com/matthewrudy))
|
34
|
+
- Add return in comments for `flush\_cache`. [\#55](https://github.com/matthewrudy/memoist/pull/55) ([joshuapinter](https://github.com/joshuapinter))
|
35
|
+
- Update readme [\#53](https://github.com/matthewrudy/memoist/pull/53) ([biow0lf](https://github.com/biow0lf))
|
36
|
+
|
37
|
+
## [v0.15.0](https://github.com/matthewrudy/memoist/tree/v0.15.0) (2016-08-23)
|
38
|
+
|
39
|
+
[Full Changelog](https://github.com/matthewrudy/memoist/compare/v0.14.0...v0.15.0)
|
40
|
+
|
41
|
+
**Merged pull requests:**
|
42
|
+
|
43
|
+
- Remove test warnings [\#52](https://github.com/matthewrudy/memoist/pull/52) ([matthewrudy](https://github.com/matthewrudy))
|
44
|
+
- Use SVG badge over PNG [\#44](https://github.com/matthewrudy/memoist/pull/44) ([olivierlacan](https://github.com/olivierlacan))
|
45
|
+
|
46
|
+
## [v0.14.0](https://github.com/matthewrudy/memoist/tree/v0.14.0) (2015-12-15)
|
47
|
+
|
48
|
+
[Full Changelog](https://github.com/matthewrudy/memoist/compare/v0.13.0...v0.14.0)
|
49
|
+
|
50
|
+
**Merged pull requests:**
|
51
|
+
|
52
|
+
- Faster2: Cache the method, ivar, and arity and the ancestry memoized methods [\#38](https://github.com/matthewrudy/memoist/pull/38) ([jrafanie](https://github.com/jrafanie))
|
53
|
+
|
54
|
+
## [v0.13.0](https://github.com/matthewrudy/memoist/tree/v0.13.0) (2015-11-26)
|
55
|
+
|
56
|
+
[Full Changelog](https://github.com/matthewrudy/memoist/compare/v0.12.0...v0.13.0)
|
57
|
+
|
58
|
+
**Merged pull requests:**
|
59
|
+
|
60
|
+
- Faster memoist with less object allocations [\#36](https://github.com/matthewrudy/memoist/pull/36) ([jrafanie](https://github.com/jrafanie))
|
61
|
+
- Be optimistic about bundler version [\#35](https://github.com/matthewrudy/memoist/pull/35) ([lotyrin](https://github.com/lotyrin))
|
62
|
+
- Add syntax highlighting for code blocks. [\#34](https://github.com/matthewrudy/memoist/pull/34) ([joshuapinter](https://github.com/joshuapinter))
|
63
|
+
|
64
|
+
## [v0.12.0](https://github.com/matthewrudy/memoist/tree/v0.12.0) (2015-04-13)
|
65
|
+
|
66
|
+
[Full Changelog](https://github.com/matthewrudy/memoist/compare/v0.11.0...v0.12.0)
|
67
|
+
|
68
|
+
**Merged pull requests:**
|
69
|
+
|
70
|
+
- Fix forking link [\#30](https://github.com/matthewrudy/memoist/pull/30) ([brandondrew](https://github.com/brandondrew))
|
71
|
+
- Update README with :identifier info [\#29](https://github.com/matthewrudy/memoist/pull/29) ([fervic](https://github.com/fervic))
|
72
|
+
|
73
|
+
## [v0.11.0](https://github.com/matthewrudy/memoist/tree/v0.11.0) (2014-10-10)
|
74
|
+
|
75
|
+
[Full Changelog](https://github.com/matthewrudy/memoist/compare/v0.10.0...v0.11.0)
|
76
|
+
|
77
|
+
**Merged pull requests:**
|
78
|
+
|
79
|
+
- Call abs on arity when extracting reload [\#27](https://github.com/matthewrudy/memoist/pull/27) ([bradylove](https://github.com/bradylove))
|
80
|
+
|
81
|
+
## [v0.10.0](https://github.com/matthewrudy/memoist/tree/v0.10.0) (2014-08-13)
|
82
|
+
|
83
|
+
[Full Changelog](https://github.com/matthewrudy/memoist/compare/v0.9.3...v0.10.0)
|
84
|
+
|
85
|
+
**Merged pull requests:**
|
86
|
+
|
87
|
+
- Make memoize return a :symbol [\#24](https://github.com/matthewrudy/memoist/pull/24) ([matthewrudy](https://github.com/matthewrudy))
|
88
|
+
- Use Minitest [\#19](https://github.com/matthewrudy/memoist/pull/19) ([matthewrudy](https://github.com/matthewrudy))
|
89
|
+
|
90
|
+
## [v0.9.3](https://github.com/matthewrudy/memoist/tree/v0.9.3) (2014-06-01)
|
91
|
+
|
92
|
+
[Full Changelog](https://github.com/matthewrudy/memoist/compare/v0.9.2...v0.9.3)
|
93
|
+
|
94
|
+
**Merged pull requests:**
|
95
|
+
|
96
|
+
- Remove Array caching hack [\#17](https://github.com/matthewrudy/memoist/pull/17) ([matthewrudy](https://github.com/matthewrudy))
|
97
|
+
|
98
|
+
## [v0.9.2](https://github.com/matthewrudy/memoist/tree/v0.9.2) (2014-04-16)
|
99
|
+
|
100
|
+
[Full Changelog](https://github.com/matthewrudy/memoist/compare/0.9.0...v0.9.2)
|
101
|
+
|
102
|
+
**Merged pull requests:**
|
103
|
+
|
104
|
+
- Give double-memoize errors their own error class [\#15](https://github.com/matthewrudy/memoist/pull/15) ([zachhale](https://github.com/zachhale))
|
105
|
+
- Add tax-themed example for class method memoization fixes \#9 [\#10](https://github.com/matthewrudy/memoist/pull/10) ([fny](https://github.com/fny))
|
106
|
+
|
107
|
+
## [0.9.0](https://github.com/matthewrudy/memoist/tree/0.9.0) (2013-03-20)
|
108
|
+
|
109
|
+
[Full Changelog](https://github.com/matthewrudy/memoist/compare/0.2.0...0.9.0)
|
110
|
+
|
111
|
+
**Merged pull requests:**
|
112
|
+
|
113
|
+
- Update README.md to include memoization bypass description [\#6](https://github.com/matthewrudy/memoist/pull/6) ([andreychernih](https://github.com/andreychernih))
|
114
|
+
- Adds a note about the MIT License [\#4](https://github.com/matthewrudy/memoist/pull/4) ([matiaskorhonen](https://github.com/matiaskorhonen))
|
115
|
+
|
116
|
+
## [0.2.0](https://github.com/matthewrudy/memoist/tree/0.2.0) (2012-08-15)
|
117
|
+
|
118
|
+
[Full Changelog](https://github.com/matthewrudy/memoist/compare/0.1.0...0.2.0)
|
119
|
+
|
120
|
+
**Merged pull requests:**
|
121
|
+
|
122
|
+
- Improved performance of flush\_cache and prime\_cache when parameters are passed to them. [\#2](https://github.com/matthewrudy/memoist/pull/2) ([jrafanie](https://github.com/jrafanie))
|
123
|
+
|
124
|
+
## [0.1.0](https://github.com/matthewrudy/memoist/tree/0.1.0) (2012-01-24)
|
125
|
+
|
126
|
+
[Full Changelog](https://github.com/matthewrudy/memoist/compare/7a5352d6b6c4219f37f329d2422985961c749748...0.1.0)
|
127
|
+
|
128
|
+
|
129
|
+
|
130
|
+
\* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)*
|
data/README.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
Memoist
|
2
2
|
=============
|
3
3
|
|
4
|
-
[![Build Status](https://travis-ci.org/matthewrudy/memoist.
|
4
|
+
[![Build Status](https://travis-ci.org/matthewrudy/memoist.svg?branch=master)](https://travis-ci.org/matthewrudy/memoist)
|
5
5
|
|
6
6
|
Memoist is an extraction of ActiveSupport::Memoizable.
|
7
7
|
|
@@ -14,72 +14,91 @@ Usage
|
|
14
14
|
|
15
15
|
Just extend with the Memoist module
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
|
17
|
+
```ruby
|
18
|
+
require 'memoist'
|
19
|
+
class Person
|
20
|
+
extend Memoist
|
20
21
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
22
|
+
def social_security
|
23
|
+
puts "execute!"
|
24
|
+
decrypt_social_security
|
25
|
+
end
|
26
|
+
memoize :social_security
|
27
|
+
end
|
28
|
+
|
29
|
+
person = Person.new
|
30
|
+
|
31
|
+
person.social_security
|
32
|
+
# execute!
|
33
|
+
# => (returns decrypt_social_security)
|
34
|
+
|
35
|
+
person.social_security
|
36
|
+
# => (returns the memoized value)
|
37
|
+
```
|
26
38
|
|
27
39
|
And person.social_security will only be calculated once.
|
28
40
|
|
29
41
|
Every memoized function (which initially was not accepting any arguments) has a ```(reload)```
|
30
42
|
argument you can pass in to bypass and reset the memoization:
|
31
43
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
44
|
+
```ruby
|
45
|
+
def some_method
|
46
|
+
Time.now
|
47
|
+
end
|
48
|
+
memoize :some_method
|
49
|
+
```
|
36
50
|
|
37
51
|
Calling ```some_method``` will be memoized, but calling ```some_method(true)``` will rememoize each time.
|
38
52
|
|
39
53
|
You can even memoize method that takes arguments.
|
40
54
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
55
|
+
```ruby
|
56
|
+
class Person
|
57
|
+
def taxes_due(income)
|
58
|
+
income * 0.40
|
59
|
+
end
|
60
|
+
memoize :taxes_due
|
61
|
+
end
|
62
|
+
```
|
48
63
|
|
49
64
|
This will only be calculated once per value of income.
|
50
65
|
|
51
66
|
You can also memoize class methods.
|
52
67
|
|
53
|
-
|
54
|
-
|
55
|
-
class << self
|
56
|
-
extend Memoist
|
57
|
-
def with_overdue_taxes
|
58
|
-
# ...
|
59
|
-
end
|
60
|
-
memoize :with_overdue_taxes
|
61
|
-
end
|
68
|
+
```ruby
|
69
|
+
class Person
|
62
70
|
|
71
|
+
class << self
|
72
|
+
extend Memoist
|
73
|
+
def with_overdue_taxes
|
74
|
+
# ...
|
63
75
|
end
|
76
|
+
memoize :with_overdue_taxes
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
```
|
64
81
|
|
65
82
|
When a sub-class overrides one of its parent's methods and you need to memoize both.
|
66
83
|
Then you can use the `:identifier` parameter in order to help _Memoist_ distinguish between the two.
|
67
84
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
85
|
+
```ruby
|
86
|
+
class Clock
|
87
|
+
extend Memoist
|
88
|
+
def now
|
89
|
+
"The time now is #{Time.now.hour} o'clock and #{Time.now.min} minutes"
|
90
|
+
end
|
91
|
+
memoize :now
|
92
|
+
end
|
93
|
+
|
94
|
+
class AccurateClock < Clock
|
95
|
+
extend Memoist
|
96
|
+
def now
|
97
|
+
"#{super} and #{Time.now.sec} seconds"
|
98
|
+
end
|
99
|
+
memoize :now, :identifier => :accurate_clock
|
100
|
+
end
|
101
|
+
```
|
83
102
|
|
84
103
|
|
85
104
|
Reload
|
@@ -87,17 +106,23 @@ Reload
|
|
87
106
|
|
88
107
|
Each memoized function comes with a way to flush the existing value.
|
89
108
|
|
90
|
-
|
91
|
-
|
109
|
+
```ruby
|
110
|
+
person.social_security # returns the memoized value
|
111
|
+
person.social_security(true) # bypasses the memoized value and rememoizes it
|
112
|
+
```
|
92
113
|
|
93
114
|
This also works with a memoized method with arguments
|
94
115
|
|
95
|
-
|
96
|
-
|
116
|
+
```ruby
|
117
|
+
person.taxes_due(100_000) # returns the memoized value
|
118
|
+
person.taxes_due(100_000, true) # bypasses the memoized value and rememoizes it
|
119
|
+
```
|
97
120
|
|
98
121
|
If you want to flush the entire memoization cache for an object
|
99
122
|
|
100
|
-
|
123
|
+
```ruby
|
124
|
+
person.flush_cache # returns an array of flushed memoized methods, e.g. ["social_security", "some_method"]
|
125
|
+
```
|
101
126
|
|
102
127
|
Authors
|
103
128
|
===========
|
@@ -120,7 +145,7 @@ Everyone who contributed to it in the rails repository.
|
|
120
145
|
Contributing
|
121
146
|
============
|
122
147
|
|
123
|
-
1. Fork it (
|
148
|
+
1. Fork it ( https://github.com/matthewrudy/memoist/fork )
|
124
149
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
125
150
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
126
151
|
4. Push to the branch (`git push origin my-new-feature`)
|
data/Rakefile
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
-
require "bundler/gem_tasks"
|
3
2
|
|
4
|
-
require
|
3
|
+
require 'bundler/gem_tasks'
|
4
|
+
|
5
|
+
require 'rake/testtask'
|
5
6
|
Rake::TestTask.new do |t|
|
6
|
-
t.libs <<
|
7
|
-
t.test_files = FileList[
|
7
|
+
t.libs << 'test'
|
8
|
+
t.test_files = FileList['test/**/*_test.rb']
|
8
9
|
t.verbose = true
|
9
10
|
end
|
10
11
|
|
11
|
-
task :
|
12
|
+
task default: ['test']
|
data/lib/memoist/version.rb
CHANGED
data/lib/memoist.rb
CHANGED
@@ -1,25 +1,53 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'memoist/version'
|
2
4
|
|
3
5
|
module Memoist
|
6
|
+
def self.extended(extender)
|
7
|
+
Memoist.memoist_eval(extender) do
|
8
|
+
unless singleton_class.method_defined?(:memoized_methods)
|
9
|
+
def self.memoized_methods
|
10
|
+
@_memoized_methods ||= []
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
4
15
|
|
5
|
-
def self.memoized_ivar_for(method_name, identifier=nil)
|
6
|
-
|
16
|
+
def self.memoized_ivar_for(method_name, identifier = nil)
|
17
|
+
"@#{memoized_prefix(identifier)}_#{escape_punctuation(method_name)}"
|
7
18
|
end
|
8
19
|
|
9
|
-
def self.unmemoized_method_for(method_name, identifier=nil)
|
10
|
-
|
20
|
+
def self.unmemoized_method_for(method_name, identifier = nil)
|
21
|
+
"#{unmemoized_prefix(identifier)}_#{method_name}".to_sym
|
11
22
|
end
|
12
23
|
|
13
|
-
def self.memoized_prefix(identifier=nil)
|
14
|
-
|
24
|
+
def self.memoized_prefix(identifier = nil)
|
25
|
+
if identifier
|
26
|
+
"_memoized_#{identifier}"
|
27
|
+
else
|
28
|
+
'_memoized'.freeze
|
29
|
+
end
|
15
30
|
end
|
16
31
|
|
17
|
-
def self.unmemoized_prefix(identifier=nil)
|
18
|
-
|
32
|
+
def self.unmemoized_prefix(identifier = nil)
|
33
|
+
if identifier
|
34
|
+
"_unmemoized_#{identifier}"
|
35
|
+
else
|
36
|
+
'_unmemoized'.freeze
|
37
|
+
end
|
19
38
|
end
|
20
39
|
|
21
40
|
def self.escape_punctuation(string)
|
22
|
-
string.
|
41
|
+
string = string.is_a?(String) ? string.dup : string.to_s.dup
|
42
|
+
|
43
|
+
return string unless string.end_with?('?'.freeze, '!'.freeze)
|
44
|
+
|
45
|
+
# A String can't end in both ? and !
|
46
|
+
if string.sub!(/\?\Z/, '_query'.freeze)
|
47
|
+
else
|
48
|
+
string.sub!(/!\Z/, '_bang'.freeze)
|
49
|
+
end
|
50
|
+
string
|
23
51
|
end
|
24
52
|
|
25
53
|
def self.memoist_eval(klass, *args, &block)
|
@@ -46,47 +74,55 @@ module Memoist
|
|
46
74
|
flush_cache
|
47
75
|
end
|
48
76
|
|
49
|
-
def
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
end.compact
|
57
|
-
end
|
77
|
+
def memoized_structs(names)
|
78
|
+
ref_obj = self.class.respond_to?(:class_eval) ? singleton_class : self
|
79
|
+
structs = ref_obj.all_memoized_structs
|
80
|
+
return structs if names.empty?
|
81
|
+
|
82
|
+
structs.select { |s| names.include?(s.memoized_method) }
|
83
|
+
end
|
58
84
|
|
59
|
-
|
60
|
-
|
61
|
-
|
85
|
+
def prime_cache(*method_names)
|
86
|
+
memoized_structs(method_names).each do |struct|
|
87
|
+
if struct.arity == 0
|
88
|
+
__send__(struct.memoized_method)
|
62
89
|
else
|
63
|
-
ivar
|
64
|
-
instance_variable_set(ivar, {})
|
90
|
+
instance_variable_set(struct.ivar, {})
|
65
91
|
end
|
66
92
|
end
|
67
93
|
end
|
68
94
|
|
69
95
|
def flush_cache(*method_names)
|
70
|
-
|
71
|
-
|
72
|
-
method_names = (methods + private_methods + protected_methods).collect do |method_name|
|
73
|
-
if method_name.to_s.start_with?(prefix)
|
74
|
-
method_name[prefix.length..-1]
|
75
|
-
end
|
76
|
-
end.compact
|
96
|
+
memoized_structs(method_names).each do |struct|
|
97
|
+
remove_instance_variable(struct.ivar) if instance_variable_defined?(struct.ivar)
|
77
98
|
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
MemoizedMethod = Struct.new(:memoized_method, :ivar, :arity)
|
78
103
|
|
79
|
-
|
80
|
-
|
81
|
-
|
104
|
+
def all_memoized_structs
|
105
|
+
@all_memoized_structs ||= begin
|
106
|
+
structs = memoized_methods.dup
|
107
|
+
|
108
|
+
# Collect the memoized_methods of ancestors in ancestor order
|
109
|
+
# unless we already have it since self or parents could be overriding
|
110
|
+
# an ancestor method.
|
111
|
+
ancestors.grep(Memoist).each do |ancestor|
|
112
|
+
ancestor.memoized_methods.each do |m|
|
113
|
+
structs << m unless structs.any? { |am| am.memoized_method == m.memoized_method }
|
114
|
+
end
|
82
115
|
end
|
116
|
+
structs
|
83
117
|
end
|
84
118
|
end
|
85
119
|
|
120
|
+
def clear_structs
|
121
|
+
@all_memoized_structs = nil
|
122
|
+
end
|
123
|
+
|
86
124
|
def memoize(*method_names)
|
87
|
-
if method_names.last.is_a?(Hash)
|
88
|
-
identifier = method_names.pop[:identifier]
|
89
|
-
end
|
125
|
+
identifier = method_names.pop[:identifier] if method_names.last.is_a?(Hash)
|
90
126
|
|
91
127
|
method_names.each do |method_name|
|
92
128
|
unmemoized_method = Memoist.unmemoized_method_for(method_name, identifier)
|
@@ -101,7 +137,9 @@ module Memoist
|
|
101
137
|
end
|
102
138
|
alias_method unmemoized_method, method_name
|
103
139
|
|
104
|
-
|
140
|
+
mm = MemoizedMethod.new(method_name, memoized_ivar, instance_method(method_name).arity)
|
141
|
+
memoized_methods << mm
|
142
|
+
if mm.arity == 0
|
105
143
|
|
106
144
|
# define a method like this;
|
107
145
|
|
@@ -197,5 +235,4 @@ module Memoist
|
|
197
235
|
# return a chainable method_name symbol if we can
|
198
236
|
method_names.length == 1 ? method_names.first : method_names
|
199
237
|
end
|
200
|
-
|
201
238
|
end
|
data/memoist.gemspec
CHANGED
@@ -1,38 +1,46 @@
|
|
1
1
|
# coding: utf-8
|
2
|
+
|
2
3
|
lib = File.expand_path('../lib', __FILE__)
|
3
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
5
|
require 'memoist/version'
|
5
6
|
|
6
7
|
AUTHORS = [
|
7
|
-
[
|
8
|
-
[
|
9
|
-
[
|
10
|
-
[
|
11
|
-
[
|
12
|
-
[
|
13
|
-
[
|
14
|
-
[
|
15
|
-
[
|
16
|
-
[
|
17
|
-
[
|
18
|
-
[
|
19
|
-
]
|
8
|
+
['Joshua Peek', 'josh@joshpeek.com'],
|
9
|
+
['Tarmo Tänav', 'tarmo@itech.ee'],
|
10
|
+
['Jeremy Kemper', 'jeremy@bitsweat.net'],
|
11
|
+
['Eugene Pimenov', 'libc@mac.com'],
|
12
|
+
['Xavier Noria', 'fxn@hashref.com'],
|
13
|
+
['Niels Ganser', 'niels@herimedia.co'],
|
14
|
+
['Carl Lerche & Yehuda Katz', 'wycats@gmail.com'],
|
15
|
+
['jeem', 'jeem@hughesorama.com'],
|
16
|
+
['Jay Pignata', 'john.pignata@gmail.com'],
|
17
|
+
['Damien Mathieu', '42@dmathieu.com'],
|
18
|
+
['José Valim', 'jose.valim@gmail.com'],
|
19
|
+
['Matthew Rudy Jacobs', 'matthewrudyjacobs@gmail.com']
|
20
|
+
].freeze
|
20
21
|
|
21
22
|
Gem::Specification.new do |spec|
|
22
|
-
spec.name =
|
23
|
+
spec.name = 'memoist'
|
23
24
|
spec.version = Memoist::VERSION
|
24
|
-
spec.authors = AUTHORS.map{ |name,
|
25
|
-
spec.email = AUTHORS.map{ |
|
26
|
-
spec.summary =
|
27
|
-
spec.homepage =
|
28
|
-
spec.license =
|
25
|
+
spec.authors = AUTHORS.map { |name, _email| name }
|
26
|
+
spec.email = AUTHORS.map { |_name, email| email }
|
27
|
+
spec.summary = 'memoize methods invocation'
|
28
|
+
spec.homepage = 'https://github.com/matthewrudy/memoist'
|
29
|
+
spec.license = 'MIT'
|
29
30
|
|
30
31
|
spec.files = `git ls-files -z`.split("\x0")
|
31
32
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
32
33
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
33
|
-
spec.require_paths = [
|
34
|
+
spec.require_paths = ['lib']
|
35
|
+
|
36
|
+
spec.required_ruby_version = '>= 1.9.2'
|
34
37
|
|
35
|
-
spec.add_development_dependency
|
36
|
-
spec.add_development_dependency
|
37
|
-
|
38
|
+
spec.add_development_dependency 'benchmark-ips'
|
39
|
+
spec.add_development_dependency 'bundler'
|
40
|
+
if RUBY_VERSION < '1.9.3'
|
41
|
+
spec.add_development_dependency 'rake', '~> 10.4'
|
42
|
+
else
|
43
|
+
spec.add_development_dependency 'rake'
|
44
|
+
end
|
45
|
+
spec.add_development_dependency 'minitest', '~> 5.10'
|
38
46
|
end
|
data/script/benchmark.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
$LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
|
2
|
+
require 'benchmark/ips'
|
3
|
+
|
4
|
+
require 'memoist'
|
5
|
+
|
6
|
+
class Benchy
|
7
|
+
extend Memoist
|
8
|
+
|
9
|
+
def arity_0
|
10
|
+
'Hello World'
|
11
|
+
end
|
12
|
+
memoize :arity_0
|
13
|
+
|
14
|
+
def arity_1(name)
|
15
|
+
"Hello #{name}"
|
16
|
+
end
|
17
|
+
memoize :arity_1
|
18
|
+
end
|
19
|
+
|
20
|
+
OBJECT = Benchy.new
|
21
|
+
|
22
|
+
puts "Benchmarking: #{Memoist::VERSION}"
|
23
|
+
|
24
|
+
Benchmark.ips do |x|
|
25
|
+
x.report('arity 0 - memoized') do |times|
|
26
|
+
times.times do
|
27
|
+
OBJECT.arity_0
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# x.report("arity 0 - unmemoized") do |times|
|
32
|
+
# times.times do
|
33
|
+
# OBJECT._unmemoized_arity_0
|
34
|
+
# end
|
35
|
+
# end
|
36
|
+
|
37
|
+
x.report('arity 1 - memoized') do |times|
|
38
|
+
times.times do
|
39
|
+
OBJECT.arity_1(:World)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# x.report("arity 1 - unmemoized") do |times|
|
44
|
+
# times.times do
|
45
|
+
# OBJECT._unmemoized_arity_1(:World)
|
46
|
+
# end
|
47
|
+
# end
|
48
|
+
end
|
data/test/memoist_test.rb
CHANGED
@@ -1,10 +1,8 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
require 'memoist'
|
3
3
|
|
4
|
-
class MemoistTest < Minitest::
|
5
|
-
|
4
|
+
class MemoistTest < Minitest::Test
|
6
5
|
class CallCounter
|
7
|
-
|
8
6
|
def initialize
|
9
7
|
@calls = {}
|
10
8
|
end
|
@@ -17,7 +15,6 @@ class MemoistTest < Minitest::Unit::TestCase
|
|
17
15
|
def count(method_name)
|
18
16
|
@calls[method_name] ||= 0
|
19
17
|
end
|
20
|
-
|
21
18
|
end
|
22
19
|
|
23
20
|
class Person
|
@@ -49,7 +46,7 @@ class MemoistTest < Minitest::Unit::TestCase
|
|
49
46
|
|
50
47
|
def name
|
51
48
|
@counter.call(:name)
|
52
|
-
|
49
|
+
'Josh'
|
53
50
|
end
|
54
51
|
|
55
52
|
def name?
|
@@ -58,8 +55,8 @@ class MemoistTest < Minitest::Unit::TestCase
|
|
58
55
|
end
|
59
56
|
memoize :name?
|
60
57
|
|
61
|
-
def update(
|
62
|
-
|
58
|
+
def update(_name)
|
59
|
+
'Joshua'
|
63
60
|
end
|
64
61
|
memoize :update
|
65
62
|
|
@@ -70,6 +67,12 @@ class MemoistTest < Minitest::Unit::TestCase
|
|
70
67
|
|
71
68
|
memoize :name, :age
|
72
69
|
|
70
|
+
def age?
|
71
|
+
@counter.call(:age?)
|
72
|
+
true
|
73
|
+
end
|
74
|
+
memoize 'age?'
|
75
|
+
|
73
76
|
def sleep(hours = 8)
|
74
77
|
@counter.call(:sleep)
|
75
78
|
hours
|
@@ -80,7 +83,7 @@ class MemoistTest < Minitest::Unit::TestCase
|
|
80
83
|
@counter.count(:sleep)
|
81
84
|
end
|
82
85
|
|
83
|
-
def update_attributes(
|
86
|
+
def update_attributes(_options = {})
|
84
87
|
@counter.call(:update_attributes)
|
85
88
|
true
|
86
89
|
end
|
@@ -101,7 +104,7 @@ class MemoistTest < Minitest::Unit::TestCase
|
|
101
104
|
|
102
105
|
def is_developer?
|
103
106
|
@counter.call(:is_developer?)
|
104
|
-
|
107
|
+
'Yes'
|
105
108
|
end
|
106
109
|
memoize :is_developer?
|
107
110
|
end
|
@@ -111,7 +114,14 @@ class MemoistTest < Minitest::Unit::TestCase
|
|
111
114
|
@counter.call(:student_name)
|
112
115
|
"Student #{super}"
|
113
116
|
end
|
114
|
-
memoize :name, :
|
117
|
+
memoize :name, identifier: :student
|
118
|
+
end
|
119
|
+
|
120
|
+
class Teacher < Person
|
121
|
+
def seniority
|
122
|
+
'very_senior'
|
123
|
+
end
|
124
|
+
memoize :seniority
|
115
125
|
end
|
116
126
|
|
117
127
|
class Company
|
@@ -122,7 +132,7 @@ class MemoistTest < Minitest::Unit::TestCase
|
|
122
132
|
|
123
133
|
def name
|
124
134
|
@name_calls += 1
|
125
|
-
|
135
|
+
'37signals'
|
126
136
|
end
|
127
137
|
end
|
128
138
|
|
@@ -174,16 +184,68 @@ class MemoistTest < Minitest::Unit::TestCase
|
|
174
184
|
memoize :counter
|
175
185
|
end
|
176
186
|
|
187
|
+
class Book
|
188
|
+
extend Memoist
|
189
|
+
STATUSES = %w[new used].freeze
|
190
|
+
CLASSIFICATION = %w[fiction nonfiction].freeze
|
191
|
+
GENRES = %w[humor romance reference sci-fi classic philosophy].freeze
|
192
|
+
|
193
|
+
attr_reader :title, :author
|
194
|
+
def initialize(title, author)
|
195
|
+
@title = title
|
196
|
+
@author = author
|
197
|
+
end
|
198
|
+
|
199
|
+
def full_title
|
200
|
+
"#{@title} by #{@author}"
|
201
|
+
end
|
202
|
+
memoize :full_title
|
203
|
+
|
204
|
+
class << self
|
205
|
+
extend Memoist
|
206
|
+
|
207
|
+
def all_types
|
208
|
+
STATUSES.product(CLASSIFICATION).product(GENRES).collect(&:flatten)
|
209
|
+
end
|
210
|
+
memoize :all_types
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
class Abb
|
215
|
+
extend Memoist
|
216
|
+
|
217
|
+
def run(*_args)
|
218
|
+
flush_cache if respond_to?(:flush_cache)
|
219
|
+
execute
|
220
|
+
end
|
221
|
+
|
222
|
+
def execute
|
223
|
+
some_method
|
224
|
+
end
|
225
|
+
|
226
|
+
def some_method
|
227
|
+
# Override this
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
class Bbb < Abb
|
232
|
+
def some_method
|
233
|
+
:foo
|
234
|
+
end
|
235
|
+
memoize :some_method
|
236
|
+
end
|
237
|
+
|
177
238
|
def setup
|
178
239
|
@person = Person.new
|
179
240
|
@calculator = Calculator.new
|
241
|
+
@book = Book.new('My Life', "Brian 'Fudge' Turmuck")
|
180
242
|
end
|
181
243
|
|
182
244
|
def test_memoization
|
183
|
-
assert_equal
|
245
|
+
assert_equal 'Josh', @person.name
|
184
246
|
assert_equal 1, @person.name_calls
|
185
247
|
|
186
|
-
3.times { assert_equal
|
248
|
+
3.times { assert_equal 'Josh', @person.name }
|
187
249
|
assert_equal 1, @person.name_calls
|
188
250
|
end
|
189
251
|
|
@@ -199,13 +261,13 @@ class MemoistTest < Minitest::Unit::TestCase
|
|
199
261
|
end
|
200
262
|
|
201
263
|
def test_memoize_with_options_hash
|
202
|
-
assert_equal true, @person.update_attributes(:
|
264
|
+
assert_equal true, @person.update_attributes(age: 21, name: 'James')
|
203
265
|
assert_equal 1, @person.update_attributes_calls
|
204
266
|
|
205
|
-
3.times { assert_equal true, @person.update_attributes(:
|
267
|
+
3.times { assert_equal true, @person.update_attributes(age: 21, name: 'James') }
|
206
268
|
assert_equal 1, @person.update_attributes_calls
|
207
269
|
|
208
|
-
3.times { assert_equal true, @person.update_attributes({:
|
270
|
+
3.times { assert_equal true, @person.update_attributes({ age: 21, name: 'James' }, :reload) }
|
209
271
|
assert_equal 4, @person.update_attributes_calls
|
210
272
|
end
|
211
273
|
|
@@ -216,6 +278,13 @@ class MemoistTest < Minitest::Unit::TestCase
|
|
216
278
|
@person.unmemoize_all
|
217
279
|
end
|
218
280
|
|
281
|
+
def test_memoization_when_memoize_is_called_with_punctuated_string
|
282
|
+
assert_equal true, @person.age?
|
283
|
+
|
284
|
+
@person.memoize_all
|
285
|
+
@person.unmemoize_all
|
286
|
+
end
|
287
|
+
|
219
288
|
def test_memoization_flush_with_punctuation
|
220
289
|
assert_equal true, @person.name?
|
221
290
|
@person.flush_cache(:name?)
|
@@ -224,10 +293,10 @@ class MemoistTest < Minitest::Unit::TestCase
|
|
224
293
|
end
|
225
294
|
|
226
295
|
def test_memoization_with_nil_value
|
227
|
-
|
296
|
+
assert_nil @person.age
|
228
297
|
assert_equal 1, @person.age_calls
|
229
298
|
|
230
|
-
3.times {
|
299
|
+
3.times { assert_nil @person.age }
|
231
300
|
assert_equal 1, @person.age_calls
|
232
301
|
end
|
233
302
|
|
@@ -244,26 +313,116 @@ class MemoistTest < Minitest::Unit::TestCase
|
|
244
313
|
|
245
314
|
assert @calculator.instance_variable_get(:@_memoized_counter)
|
246
315
|
@calculator.flush_cache(:counter)
|
247
|
-
|
248
|
-
assert !@calculator.instance_variable_defined?(:@_memoized_counter)
|
316
|
+
assert_equal false, @calculator.instance_variable_defined?(:@_memoized_counter)
|
249
317
|
|
250
318
|
assert_equal 2, @calculator.counter
|
251
319
|
end
|
252
320
|
|
321
|
+
def test_class_flush_cache
|
322
|
+
@book.memoize_all
|
323
|
+
assert_equal "My Life by Brian 'Fudge' Turmuck", @book.full_title
|
324
|
+
|
325
|
+
Book.memoize_all
|
326
|
+
assert_instance_of Array, Book.instance_variable_get(:@_memoized_all_types)
|
327
|
+
Book.flush_cache
|
328
|
+
assert_equal false, Book.instance_variable_defined?(:@_memoized_all_types)
|
329
|
+
end
|
330
|
+
|
331
|
+
def test_class_flush_cache_preserves_instances
|
332
|
+
@book.memoize_all
|
333
|
+
Book.memoize_all
|
334
|
+
assert_equal "My Life by Brian 'Fudge' Turmuck", @book.full_title
|
335
|
+
|
336
|
+
Book.flush_cache
|
337
|
+
assert_equal false, Book.instance_variable_defined?(:@_memoized_all_types)
|
338
|
+
assert_equal "My Life by Brian 'Fudge' Turmuck", @book.full_title
|
339
|
+
end
|
340
|
+
|
341
|
+
def test_flush_cache_in_child_class
|
342
|
+
x = Bbb.new
|
343
|
+
|
344
|
+
# This should not throw error
|
345
|
+
x.run
|
346
|
+
end
|
347
|
+
|
253
348
|
def test_unmemoize_all
|
254
349
|
assert_equal 1, @calculator.counter
|
255
350
|
|
351
|
+
assert_equal true, @calculator.instance_variable_defined?(:@_memoized_counter)
|
256
352
|
assert @calculator.instance_variable_get(:@_memoized_counter)
|
257
353
|
@calculator.unmemoize_all
|
258
|
-
|
259
|
-
assert !@calculator.instance_variable_defined?(:@_memoized_counter)
|
354
|
+
assert_equal false, @calculator.instance_variable_defined?(:@_memoized_counter)
|
260
355
|
|
261
356
|
assert_equal 2, @calculator.counter
|
262
357
|
end
|
263
358
|
|
359
|
+
def test_all_memoized_structs
|
360
|
+
# Person memoize :age, :age?, :is_developer?, :memoize_protected_test, :name, :name?, :sleep, :update, :update_attributes
|
361
|
+
# Student < Person memoize :name, :identifier => :student
|
362
|
+
# Teacher < Person memoize :seniority
|
363
|
+
|
364
|
+
expected = %w[age age? is_developer? memoize_protected_test name name? sleep update update_attributes]
|
365
|
+
structs = Person.all_memoized_structs
|
366
|
+
assert_equal expected, structs.collect(&:memoized_method).collect(&:to_s).sort
|
367
|
+
assert_equal '@_memoized_name', structs.detect { |s| s.memoized_method == :name }.ivar
|
368
|
+
|
369
|
+
# Same expected methods
|
370
|
+
structs = Student.all_memoized_structs
|
371
|
+
assert_equal expected, structs.collect(&:memoized_method).collect(&:to_s).sort
|
372
|
+
assert_equal '@_memoized_student_name', structs.detect { |s| s.memoized_method == :name }.ivar
|
373
|
+
|
374
|
+
expected = (expected << 'seniority').sort
|
375
|
+
structs = Teacher.all_memoized_structs
|
376
|
+
assert_equal expected, structs.collect(&:memoized_method).collect(&:to_s).sort
|
377
|
+
assert_equal '@_memoized_name', structs.detect { |s| s.memoized_method == :name }.ivar
|
378
|
+
end
|
379
|
+
|
380
|
+
def test_unmemoize_all_subclasses
|
381
|
+
# Person memoize :age, :is_developer?, :memoize_protected_test, :name, :name?, :sleep, :update, :update_attributes
|
382
|
+
# Student < Person memoize :name, :identifier => :student
|
383
|
+
# Teacher < Person memoize :seniority
|
384
|
+
|
385
|
+
teacher = Teacher.new
|
386
|
+
assert_equal 'Josh', teacher.name
|
387
|
+
assert_equal 'Josh', teacher.instance_variable_get(:@_memoized_name)
|
388
|
+
assert_equal 'very_senior', teacher.seniority
|
389
|
+
assert_equal 'very_senior', teacher.instance_variable_get(:@_memoized_seniority)
|
390
|
+
|
391
|
+
teacher.unmemoize_all
|
392
|
+
assert_equal false, teacher.instance_variable_defined?(:@_memoized_name)
|
393
|
+
assert_equal false, teacher.instance_variable_defined?(:@_memoized_seniority)
|
394
|
+
|
395
|
+
student = Student.new
|
396
|
+
assert_equal 'Student Josh', student.name
|
397
|
+
assert_equal 'Student Josh', student.instance_variable_get(:@_memoized_student_name)
|
398
|
+
assert_equal false, student.instance_variable_defined?(:@_memoized_seniority)
|
399
|
+
|
400
|
+
student.unmemoize_all
|
401
|
+
assert_equal false, @calculator.instance_variable_defined?(:@_memoized_student_name)
|
402
|
+
end
|
403
|
+
|
264
404
|
def test_memoize_all
|
265
405
|
@calculator.memoize_all
|
266
|
-
|
406
|
+
assert_equal true, @calculator.instance_variable_defined?(:@_memoized_counter)
|
407
|
+
end
|
408
|
+
|
409
|
+
def test_memoize_all_subclasses
|
410
|
+
# Person memoize :age, :is_developer?, :memoize_protected_test, :name, :name?, :sleep, :update, :update_attributes
|
411
|
+
# Student < Person memoize :name, :identifier => :student
|
412
|
+
# Teacher < Person memoize :seniority
|
413
|
+
|
414
|
+
teacher = Teacher.new
|
415
|
+
teacher.memoize_all
|
416
|
+
|
417
|
+
assert_equal 'very_senior', teacher.instance_variable_get(:@_memoized_seniority)
|
418
|
+
assert_equal 'Josh', teacher.instance_variable_get(:@_memoized_name)
|
419
|
+
|
420
|
+
student = Student.new
|
421
|
+
student.memoize_all
|
422
|
+
|
423
|
+
assert_equal 'Student Josh', student.instance_variable_get(:@_memoized_student_name)
|
424
|
+
assert_equal 'Student Josh', student.name
|
425
|
+
assert_equal false, student.instance_variable_defined?(:@_memoized_seniority)
|
267
426
|
end
|
268
427
|
|
269
428
|
def test_memoization_cache_is_different_for_each_instance
|
@@ -272,10 +431,20 @@ class MemoistTest < Minitest::Unit::TestCase
|
|
272
431
|
assert_equal 1, Calculator.new.counter
|
273
432
|
end
|
274
433
|
|
434
|
+
def test_memoization_class_variables
|
435
|
+
@book.memoize_all
|
436
|
+
assert_equal "My Life by Brian 'Fudge' Turmuck", @book.instance_variable_get(:@_memoized_full_title)
|
437
|
+
assert_equal "My Life by Brian 'Fudge' Turmuck", @book.full_title
|
438
|
+
|
439
|
+
Book.memoize_all
|
440
|
+
assert_instance_of Array, Book.instance_variable_get(:@_memoized_all_types)
|
441
|
+
assert_equal 24, Book.all_types.count
|
442
|
+
end
|
443
|
+
|
275
444
|
def test_memoized_is_not_affected_by_freeze
|
276
445
|
@person.freeze
|
277
|
-
assert_equal
|
278
|
-
assert_equal
|
446
|
+
assert_equal 'Josh', @person.name
|
447
|
+
assert_equal 'Joshua', @person.update('Joshua')
|
279
448
|
end
|
280
449
|
|
281
450
|
def test_memoization_with_args
|
@@ -302,9 +471,9 @@ class MemoistTest < Minitest::Unit::TestCase
|
|
302
471
|
company.extend Memoist
|
303
472
|
company.memoize :name
|
304
473
|
|
305
|
-
assert_equal
|
474
|
+
assert_equal '37signals', company.name
|
306
475
|
assert_equal 1, company.name_calls
|
307
|
-
assert_equal
|
476
|
+
assert_equal '37signals', company.name
|
308
477
|
assert_equal 1, company.name_calls
|
309
478
|
end
|
310
479
|
end
|
@@ -331,7 +500,29 @@ class MemoistTest < Minitest::Unit::TestCase
|
|
331
500
|
end
|
332
501
|
|
333
502
|
def test_double_memoization_with_identifier
|
334
|
-
Person
|
503
|
+
# Person memoize :age, :is_developer?, :memoize_protected_test, :name, :name?, :sleep, :update, :update_attributes
|
504
|
+
# Student < Person memoize :name, :identifier => :student
|
505
|
+
# Teacher < Person memoize :seniority
|
506
|
+
|
507
|
+
Person.memoize :name, identifier: :again
|
508
|
+
p = Person.new
|
509
|
+
assert_equal 'Josh', p.name
|
510
|
+
assert p.instance_variable_get(:@_memoized_again_name)
|
511
|
+
|
512
|
+
# HACK: tl;dr: Don't memoize classes in test that are used elsewhere.
|
513
|
+
# Calling Person.memoize :name, :identifier => :again pollutes Person
|
514
|
+
# and descendents since we cache the memoized method structures.
|
515
|
+
# This populates those structs, verifies Person is polluted, resets the
|
516
|
+
# structs, cleans up cached memoized_methods
|
517
|
+
Student.all_memoized_structs
|
518
|
+
Person.all_memoized_structs
|
519
|
+
Teacher.all_memoized_structs
|
520
|
+
assert Person.memoized_methods.any? { |m| m.ivar == '@_memoized_again_name' }
|
521
|
+
|
522
|
+
[Student, Teacher, Person].each(&:clear_structs)
|
523
|
+
assert Person.memoized_methods.reject! { |m| m.ivar == '@_memoized_again_name' }
|
524
|
+
assert_nil Student.memoized_methods.reject! { |m| m.ivar == '@_memoized_again_name' }
|
525
|
+
assert_nil Teacher.memoized_methods.reject! { |m| m.ivar == '@_memoized_again_name' }
|
335
526
|
end
|
336
527
|
|
337
528
|
def test_memoization_with_a_subclass
|
@@ -344,7 +535,9 @@ class MemoistTest < Minitest::Unit::TestCase
|
|
344
535
|
|
345
536
|
def test_memoization_is_chainable
|
346
537
|
klass = Class.new do
|
347
|
-
def foo
|
538
|
+
def foo
|
539
|
+
'bar'
|
540
|
+
end
|
348
541
|
end
|
349
542
|
klass.extend Memoist
|
350
543
|
chainable = klass.memoize :foo
|
@@ -355,17 +548,16 @@ class MemoistTest < Minitest::Unit::TestCase
|
|
355
548
|
person = Person.new
|
356
549
|
|
357
550
|
assert_raises(NoMethodError) { person.memoize_protected_test }
|
358
|
-
assert_equal
|
551
|
+
assert_equal 'protected', person.send(:memoize_protected_test)
|
359
552
|
end
|
360
553
|
|
361
554
|
def test_private_method_memoization
|
362
555
|
person = Person.new
|
363
556
|
|
364
557
|
assert_raises(NoMethodError) { person.is_developer? }
|
365
|
-
assert_equal
|
558
|
+
assert_equal 'Yes', person.send(:is_developer?)
|
366
559
|
assert_equal 1, person.is_developer_calls
|
367
|
-
assert_equal
|
560
|
+
assert_equal 'Yes', person.send(:is_developer?)
|
368
561
|
assert_equal 1, person.is_developer_calls
|
369
562
|
end
|
370
|
-
|
371
563
|
end
|
data/test/test_helper.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: memoist
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.16.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joshua Peek
|
@@ -19,22 +19,36 @@ authors:
|
|
19
19
|
autorequire:
|
20
20
|
bindir: bin
|
21
21
|
cert_chain: []
|
22
|
-
date:
|
22
|
+
date: 2019-12-04 00:00:00.000000000 Z
|
23
23
|
dependencies:
|
24
|
+
- !ruby/object:Gem::Dependency
|
25
|
+
name: benchmark-ips
|
26
|
+
requirement: !ruby/object:Gem::Requirement
|
27
|
+
requirements:
|
28
|
+
- - ">="
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
version: '0'
|
31
|
+
type: :development
|
32
|
+
prerelease: false
|
33
|
+
version_requirements: !ruby/object:Gem::Requirement
|
34
|
+
requirements:
|
35
|
+
- - ">="
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
24
38
|
- !ruby/object:Gem::Dependency
|
25
39
|
name: bundler
|
26
40
|
requirement: !ruby/object:Gem::Requirement
|
27
41
|
requirements:
|
28
|
-
- - "
|
42
|
+
- - ">="
|
29
43
|
- !ruby/object:Gem::Version
|
30
|
-
version: '
|
44
|
+
version: '0'
|
31
45
|
type: :development
|
32
46
|
prerelease: false
|
33
47
|
version_requirements: !ruby/object:Gem::Requirement
|
34
48
|
requirements:
|
35
|
-
- - "
|
49
|
+
- - ">="
|
36
50
|
- !ruby/object:Gem::Version
|
37
|
-
version: '
|
51
|
+
version: '0'
|
38
52
|
- !ruby/object:Gem::Dependency
|
39
53
|
name: rake
|
40
54
|
requirement: !ruby/object:Gem::Requirement
|
@@ -55,14 +69,14 @@ dependencies:
|
|
55
69
|
requirements:
|
56
70
|
- - "~>"
|
57
71
|
- !ruby/object:Gem::Version
|
58
|
-
version: '
|
72
|
+
version: '5.10'
|
59
73
|
type: :development
|
60
74
|
prerelease: false
|
61
75
|
version_requirements: !ruby/object:Gem::Requirement
|
62
76
|
requirements:
|
63
77
|
- - "~>"
|
64
78
|
- !ruby/object:Gem::Version
|
65
|
-
version: '
|
79
|
+
version: '5.10'
|
66
80
|
description:
|
67
81
|
email:
|
68
82
|
- josh@joshpeek.com
|
@@ -83,14 +97,15 @@ extra_rdoc_files: []
|
|
83
97
|
files:
|
84
98
|
- ".gitignore"
|
85
99
|
- ".travis.yml"
|
100
|
+
- CHANGELOG.md
|
86
101
|
- Gemfile
|
87
102
|
- LICENSE.md
|
88
103
|
- README.md
|
89
104
|
- Rakefile
|
90
105
|
- lib/memoist.rb
|
91
|
-
- lib/memoist/core_ext/singleton_class.rb
|
92
106
|
- lib/memoist/version.rb
|
93
107
|
- memoist.gemspec
|
108
|
+
- script/benchmark.rb
|
94
109
|
- test/memoist_test.rb
|
95
110
|
- test/test_helper.rb
|
96
111
|
homepage: https://github.com/matthewrudy/memoist
|
@@ -105,15 +120,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
105
120
|
requirements:
|
106
121
|
- - ">="
|
107
122
|
- !ruby/object:Gem::Version
|
108
|
-
version:
|
123
|
+
version: 1.9.2
|
109
124
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
110
125
|
requirements:
|
111
126
|
- - ">="
|
112
127
|
- !ruby/object:Gem::Version
|
113
128
|
version: '0'
|
114
129
|
requirements: []
|
115
|
-
|
116
|
-
rubygems_version: 2.4.5
|
130
|
+
rubygems_version: 3.0.4
|
117
131
|
signing_key:
|
118
132
|
specification_version: 4
|
119
133
|
summary: memoize methods invocation
|