memoist3 1.0.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 +7 -0
- data/.github/workflows/ci.yml +46 -0
- data/.gitignore +18 -0
- data/CHANGELOG.md +136 -0
- data/Gemfile +4 -0
- data/LICENSE.md +20 -0
- data/README.md +172 -0
- data/Rakefile +12 -0
- data/lib/memoist/version.rb +5 -0
- data/lib/memoist.rb +238 -0
- data/memoist.gemspec +41 -0
- data/script/benchmark.rb +48 -0
- data/test/memoist_test.rb +590 -0
- data/test/test_helper.rb +3 -0
- metadata +138 -0
data/memoist.gemspec
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
lib = File.expand_path('../lib', __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require 'memoist/version'
|
4
|
+
|
5
|
+
AUTHORS = [
|
6
|
+
['Joshua Peek', 'josh@joshpeek.com'],
|
7
|
+
['Tarmo Tänav', 'tarmo@itech.ee'],
|
8
|
+
['Jeremy Kemper', 'jeremy@bitsweat.net'],
|
9
|
+
['Eugene Pimenov', 'libc@mac.com'],
|
10
|
+
['Xavier Noria', 'fxn@hashref.com'],
|
11
|
+
['Niels Ganser', 'niels@herimedia.co'],
|
12
|
+
['Carl Lerche & Yehuda Katz', 'wycats@gmail.com'],
|
13
|
+
['jeem', 'jeem@hughesorama.com'],
|
14
|
+
['Jay Pignata', 'john.pignata@gmail.com'],
|
15
|
+
['Damien Mathieu', '42@dmathieu.com'],
|
16
|
+
['José Valim', 'jose.valim@gmail.com'],
|
17
|
+
['Matthew Rudy Jacobs', 'matthewrudyjacobs@gmail.com'],
|
18
|
+
['Jan Sterba', 'info@jansterba.com']
|
19
|
+
].freeze
|
20
|
+
|
21
|
+
Gem::Specification.new do |spec|
|
22
|
+
spec.name = 'memoist3'
|
23
|
+
spec.version = Memoist::VERSION
|
24
|
+
spec.authors = AUTHORS.map { |name, _email| name }
|
25
|
+
spec.email = AUTHORS.map { |_name, email| email }
|
26
|
+
spec.summary = 'memoize methods invocation'
|
27
|
+
spec.homepage = 'https://github.com/honzasterba/memoist'
|
28
|
+
spec.license = 'MIT'
|
29
|
+
|
30
|
+
spec.files = `git ls-files -z`.split("\x0")
|
31
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
32
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
33
|
+
spec.require_paths = ['lib']
|
34
|
+
|
35
|
+
spec.required_ruby_version = '>= 2.7.2'
|
36
|
+
|
37
|
+
spec.add_development_dependency 'benchmark-ips'
|
38
|
+
spec.add_development_dependency 'bundler'
|
39
|
+
spec.add_development_dependency 'minitest', '~> 5.10'
|
40
|
+
spec.add_development_dependency 'rake'
|
41
|
+
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
|
@@ -0,0 +1,590 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'memoist'
|
3
|
+
|
4
|
+
class MemoistTest < Minitest::Test
|
5
|
+
class CallCounter
|
6
|
+
def initialize
|
7
|
+
@calls = {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(method_name)
|
11
|
+
@calls[method_name] ||= 0
|
12
|
+
@calls[method_name] += 1
|
13
|
+
end
|
14
|
+
|
15
|
+
def count(method_name)
|
16
|
+
@calls[method_name] ||= 0
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class Person
|
21
|
+
extend Memoist
|
22
|
+
|
23
|
+
def initialize
|
24
|
+
@counter = CallCounter.new
|
25
|
+
end
|
26
|
+
|
27
|
+
def name_calls
|
28
|
+
@counter.count(:name)
|
29
|
+
end
|
30
|
+
|
31
|
+
def student_name_calls
|
32
|
+
@counter.count(:student_name)
|
33
|
+
end
|
34
|
+
|
35
|
+
def name_query_calls
|
36
|
+
@counter.count(:name?)
|
37
|
+
end
|
38
|
+
|
39
|
+
def is_developer_calls
|
40
|
+
@counter.count(:is_developer?)
|
41
|
+
end
|
42
|
+
|
43
|
+
def age_calls
|
44
|
+
@counter.count(:age)
|
45
|
+
end
|
46
|
+
|
47
|
+
def name
|
48
|
+
@counter.call(:name)
|
49
|
+
'Josh'
|
50
|
+
end
|
51
|
+
|
52
|
+
def name?
|
53
|
+
@counter.call(:name?)
|
54
|
+
true
|
55
|
+
end
|
56
|
+
memoize :name?
|
57
|
+
|
58
|
+
def update(_name)
|
59
|
+
'Joshua'
|
60
|
+
end
|
61
|
+
memoize :update
|
62
|
+
|
63
|
+
def age
|
64
|
+
@counter.call(:age)
|
65
|
+
nil
|
66
|
+
end
|
67
|
+
|
68
|
+
memoize :name, :age
|
69
|
+
|
70
|
+
def age?
|
71
|
+
@counter.call(:age?)
|
72
|
+
true
|
73
|
+
end
|
74
|
+
memoize 'age?'
|
75
|
+
|
76
|
+
def sleep(hours = 8)
|
77
|
+
@counter.call(:sleep)
|
78
|
+
hours
|
79
|
+
end
|
80
|
+
memoize :sleep
|
81
|
+
|
82
|
+
def sleep_calls
|
83
|
+
@counter.count(:sleep)
|
84
|
+
end
|
85
|
+
|
86
|
+
def update_attributes(_options = {})
|
87
|
+
@counter.call(:update_attributes)
|
88
|
+
true
|
89
|
+
end
|
90
|
+
memoize :update_attributes
|
91
|
+
|
92
|
+
def update_attributes_calls
|
93
|
+
@counter.count(:update_attributes)
|
94
|
+
end
|
95
|
+
|
96
|
+
def do_with_special(_regular = 10, special_one: true, special_two: true)
|
97
|
+
@counter.call(:do_with_special)
|
98
|
+
true
|
99
|
+
end
|
100
|
+
memoize :do_with_special
|
101
|
+
|
102
|
+
def do_with_special_calls
|
103
|
+
@counter.count(:do_with_special)
|
104
|
+
end
|
105
|
+
|
106
|
+
protected
|
107
|
+
|
108
|
+
def memoize_protected_test
|
109
|
+
'protected'
|
110
|
+
end
|
111
|
+
memoize :memoize_protected_test
|
112
|
+
|
113
|
+
private
|
114
|
+
|
115
|
+
def is_developer?
|
116
|
+
@counter.call(:is_developer?)
|
117
|
+
'Yes'
|
118
|
+
end
|
119
|
+
memoize :is_developer?
|
120
|
+
end
|
121
|
+
|
122
|
+
class Student < Person
|
123
|
+
def name
|
124
|
+
@counter.call(:student_name)
|
125
|
+
"Student #{super}"
|
126
|
+
end
|
127
|
+
memoize :name, identifier: :student
|
128
|
+
end
|
129
|
+
|
130
|
+
class Teacher < Person
|
131
|
+
def seniority
|
132
|
+
'very_senior'
|
133
|
+
end
|
134
|
+
memoize :seniority
|
135
|
+
end
|
136
|
+
|
137
|
+
class Company
|
138
|
+
attr_reader :name_calls
|
139
|
+
def initialize
|
140
|
+
@name_calls = 0
|
141
|
+
end
|
142
|
+
|
143
|
+
def name
|
144
|
+
@name_calls += 1
|
145
|
+
'37signals'
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
module Rates
|
150
|
+
extend Memoist
|
151
|
+
|
152
|
+
attr_reader :sales_tax_calls
|
153
|
+
def sales_tax(price)
|
154
|
+
@sales_tax_calls ||= 0
|
155
|
+
@sales_tax_calls += 1
|
156
|
+
price * 0.1025
|
157
|
+
end
|
158
|
+
memoize :sales_tax
|
159
|
+
end
|
160
|
+
|
161
|
+
class Calculator
|
162
|
+
extend Memoist
|
163
|
+
include Rates
|
164
|
+
|
165
|
+
attr_reader :fib_calls
|
166
|
+
def initialize
|
167
|
+
@fib_calls = 0
|
168
|
+
end
|
169
|
+
|
170
|
+
def fib(n)
|
171
|
+
@fib_calls += 1
|
172
|
+
|
173
|
+
if n == 0 || n == 1
|
174
|
+
n
|
175
|
+
else
|
176
|
+
fib(n - 1) + fib(n - 2)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
memoize :fib
|
180
|
+
|
181
|
+
def add_or_subtract(i, j, add)
|
182
|
+
if add
|
183
|
+
i + j
|
184
|
+
else
|
185
|
+
i - j
|
186
|
+
end
|
187
|
+
end
|
188
|
+
memoize :add_or_subtract
|
189
|
+
|
190
|
+
def counter
|
191
|
+
@count ||= 0
|
192
|
+
@count += 1
|
193
|
+
end
|
194
|
+
memoize :counter
|
195
|
+
end
|
196
|
+
|
197
|
+
class Book
|
198
|
+
extend Memoist
|
199
|
+
STATUSES = %w[new used].freeze
|
200
|
+
CLASSIFICATION = %w[fiction nonfiction].freeze
|
201
|
+
GENRES = %w[humor romance reference sci-fi classic philosophy].freeze
|
202
|
+
|
203
|
+
attr_reader :title, :author
|
204
|
+
def initialize(title, author)
|
205
|
+
@title = title
|
206
|
+
@author = author
|
207
|
+
end
|
208
|
+
|
209
|
+
def full_title
|
210
|
+
"#{@title} by #{@author}"
|
211
|
+
end
|
212
|
+
memoize :full_title
|
213
|
+
|
214
|
+
class << self
|
215
|
+
extend Memoist
|
216
|
+
|
217
|
+
def all_types
|
218
|
+
STATUSES.product(CLASSIFICATION).product(GENRES).collect(&:flatten)
|
219
|
+
end
|
220
|
+
memoize :all_types
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
class Abb
|
225
|
+
extend Memoist
|
226
|
+
|
227
|
+
def run(*_args)
|
228
|
+
flush_cache if respond_to?(:flush_cache)
|
229
|
+
execute
|
230
|
+
end
|
231
|
+
|
232
|
+
def execute
|
233
|
+
some_method
|
234
|
+
end
|
235
|
+
|
236
|
+
def some_method
|
237
|
+
# Override this
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
class Bbb < Abb
|
242
|
+
def some_method
|
243
|
+
:foo
|
244
|
+
end
|
245
|
+
memoize :some_method
|
246
|
+
end
|
247
|
+
|
248
|
+
def setup
|
249
|
+
@person = Person.new
|
250
|
+
@calculator = Calculator.new
|
251
|
+
@book = Book.new('My Life', "Brian 'Fudge' Turmuck")
|
252
|
+
end
|
253
|
+
|
254
|
+
def test_memoization
|
255
|
+
assert_equal 'Josh', @person.name
|
256
|
+
assert_equal 1, @person.name_calls
|
257
|
+
|
258
|
+
3.times { assert_equal 'Josh', @person.name }
|
259
|
+
assert_equal 1, @person.name_calls
|
260
|
+
end
|
261
|
+
|
262
|
+
def test_memoize_with_optional_arguments
|
263
|
+
assert_equal 4, @person.sleep(4)
|
264
|
+
assert_equal 1, @person.sleep_calls
|
265
|
+
|
266
|
+
3.times { assert_equal 4, @person.sleep(4) }
|
267
|
+
assert_equal 1, @person.sleep_calls
|
268
|
+
|
269
|
+
3.times { assert_equal 4, @person.sleep(4, :reload) }
|
270
|
+
assert_equal 4, @person.sleep_calls
|
271
|
+
end
|
272
|
+
|
273
|
+
def test_memoize_with_options_hash
|
274
|
+
assert_equal true, @person.update_attributes(age: 21, name: 'James')
|
275
|
+
assert_equal 1, @person.update_attributes_calls
|
276
|
+
|
277
|
+
3.times { assert_equal true, @person.update_attributes(age: 21, name: 'James') }
|
278
|
+
assert_equal 1, @person.update_attributes_calls
|
279
|
+
|
280
|
+
3.times { assert_equal true, @person.update_attributes({ age: 21, name: 'James' }, :reload) }
|
281
|
+
assert_equal 4, @person.update_attributes_calls
|
282
|
+
end
|
283
|
+
|
284
|
+
def test_memoize_with_kwargs
|
285
|
+
assert_equal true, @person.do_with_special(1, special_one: true)
|
286
|
+
assert_equal 1, @person.do_with_special_calls
|
287
|
+
|
288
|
+
3.times { assert_equal true, @person.do_with_special(1, special_one: true) }
|
289
|
+
assert_equal 1, @person.do_with_special_calls
|
290
|
+
|
291
|
+
assert_equal true, @person.do_with_special(2)
|
292
|
+
assert_equal 2, @person.do_with_special_calls
|
293
|
+
|
294
|
+
assert_equal true, @person.do_with_special(1, special_one: false)
|
295
|
+
assert_equal 3, @person.do_with_special_calls
|
296
|
+
|
297
|
+
assert_equal true, @person.do_with_special(1, special_two: false)
|
298
|
+
assert_equal 4, @person.do_with_special_calls
|
299
|
+
end
|
300
|
+
|
301
|
+
def test_memoization_with_punctuation
|
302
|
+
assert_equal true, @person.name?
|
303
|
+
|
304
|
+
@person.memoize_all
|
305
|
+
@person.unmemoize_all
|
306
|
+
end
|
307
|
+
|
308
|
+
def test_memoization_when_memoize_is_called_with_punctuated_string
|
309
|
+
assert_equal true, @person.age?
|
310
|
+
|
311
|
+
@person.memoize_all
|
312
|
+
@person.unmemoize_all
|
313
|
+
end
|
314
|
+
|
315
|
+
def test_memoization_flush_with_punctuation
|
316
|
+
assert_equal true, @person.name?
|
317
|
+
@person.flush_cache(:name?)
|
318
|
+
3.times { assert_equal true, @person.name? }
|
319
|
+
assert_equal 2, @person.name_query_calls
|
320
|
+
end
|
321
|
+
|
322
|
+
def test_memoization_with_nil_value
|
323
|
+
assert_nil @person.age
|
324
|
+
assert_equal 1, @person.age_calls
|
325
|
+
|
326
|
+
3.times { assert_nil @person.age }
|
327
|
+
assert_equal 1, @person.age_calls
|
328
|
+
end
|
329
|
+
|
330
|
+
def test_reloadable
|
331
|
+
assert_equal 1, @calculator.counter
|
332
|
+
assert_equal 2, @calculator.counter(:reload)
|
333
|
+
assert_equal 2, @calculator.counter
|
334
|
+
assert_equal 3, @calculator.counter(true)
|
335
|
+
assert_equal 3, @calculator.counter
|
336
|
+
end
|
337
|
+
|
338
|
+
def test_flush_cache
|
339
|
+
assert_equal 1, @calculator.counter
|
340
|
+
|
341
|
+
assert @calculator.instance_variable_get(:@_memoized_counter)
|
342
|
+
@calculator.flush_cache(:counter)
|
343
|
+
assert_equal false, @calculator.instance_variable_defined?(:@_memoized_counter)
|
344
|
+
|
345
|
+
assert_equal 2, @calculator.counter
|
346
|
+
end
|
347
|
+
|
348
|
+
def test_class_flush_cache
|
349
|
+
@book.memoize_all
|
350
|
+
assert_equal "My Life by Brian 'Fudge' Turmuck", @book.full_title
|
351
|
+
|
352
|
+
Book.memoize_all
|
353
|
+
assert_instance_of Array, Book.instance_variable_get(:@_memoized_all_types)
|
354
|
+
Book.flush_cache
|
355
|
+
assert_equal false, Book.instance_variable_defined?(:@_memoized_all_types)
|
356
|
+
end
|
357
|
+
|
358
|
+
def test_class_flush_cache_preserves_instances
|
359
|
+
@book.memoize_all
|
360
|
+
Book.memoize_all
|
361
|
+
assert_equal "My Life by Brian 'Fudge' Turmuck", @book.full_title
|
362
|
+
|
363
|
+
Book.flush_cache
|
364
|
+
assert_equal false, Book.instance_variable_defined?(:@_memoized_all_types)
|
365
|
+
assert_equal "My Life by Brian 'Fudge' Turmuck", @book.full_title
|
366
|
+
end
|
367
|
+
|
368
|
+
def test_flush_cache_in_child_class
|
369
|
+
x = Bbb.new
|
370
|
+
|
371
|
+
# This should not throw error
|
372
|
+
x.run
|
373
|
+
end
|
374
|
+
|
375
|
+
def test_unmemoize_all
|
376
|
+
assert_equal 1, @calculator.counter
|
377
|
+
|
378
|
+
assert_equal true, @calculator.instance_variable_defined?(:@_memoized_counter)
|
379
|
+
assert @calculator.instance_variable_get(:@_memoized_counter)
|
380
|
+
@calculator.unmemoize_all
|
381
|
+
assert_equal false, @calculator.instance_variable_defined?(:@_memoized_counter)
|
382
|
+
|
383
|
+
assert_equal 2, @calculator.counter
|
384
|
+
end
|
385
|
+
|
386
|
+
def test_all_memoized_structs
|
387
|
+
# Person memoize :age, :age?, :is_developer?, :memoize_protected_test, :name, :name?, :sleep, :update, :update_attributes
|
388
|
+
# Student < Person memoize :name, :identifier => :student
|
389
|
+
# Teacher < Person memoize :seniority
|
390
|
+
|
391
|
+
expected = %w[age age? do_with_special is_developer? memoize_protected_test name name? sleep update update_attributes]
|
392
|
+
structs = Person.all_memoized_structs
|
393
|
+
assert_equal expected, structs.collect(&:memoized_method).collect(&:to_s).sort
|
394
|
+
assert_equal '@_memoized_name', structs.detect { |s| s.memoized_method == :name }.ivar
|
395
|
+
|
396
|
+
# Same expected methods
|
397
|
+
structs = Student.all_memoized_structs
|
398
|
+
assert_equal expected, structs.collect(&:memoized_method).collect(&:to_s).sort
|
399
|
+
assert_equal '@_memoized_student_name', structs.detect { |s| s.memoized_method == :name }.ivar
|
400
|
+
|
401
|
+
expected = (expected << 'seniority').sort
|
402
|
+
structs = Teacher.all_memoized_structs
|
403
|
+
assert_equal expected, structs.collect(&:memoized_method).collect(&:to_s).sort
|
404
|
+
assert_equal '@_memoized_name', structs.detect { |s| s.memoized_method == :name }.ivar
|
405
|
+
end
|
406
|
+
|
407
|
+
def test_unmemoize_all_subclasses
|
408
|
+
# Person memoize :age, :is_developer?, :memoize_protected_test, :name, :name?, :sleep, :update, :update_attributes
|
409
|
+
# Student < Person memoize :name, :identifier => :student
|
410
|
+
# Teacher < Person memoize :seniority
|
411
|
+
|
412
|
+
teacher = Teacher.new
|
413
|
+
assert_equal 'Josh', teacher.name
|
414
|
+
assert_equal 'Josh', teacher.instance_variable_get(:@_memoized_name)
|
415
|
+
assert_equal 'very_senior', teacher.seniority
|
416
|
+
assert_equal 'very_senior', teacher.instance_variable_get(:@_memoized_seniority)
|
417
|
+
|
418
|
+
teacher.unmemoize_all
|
419
|
+
assert_equal false, teacher.instance_variable_defined?(:@_memoized_name)
|
420
|
+
assert_equal false, teacher.instance_variable_defined?(:@_memoized_seniority)
|
421
|
+
|
422
|
+
student = Student.new
|
423
|
+
assert_equal 'Student Josh', student.name
|
424
|
+
assert_equal 'Student Josh', student.instance_variable_get(:@_memoized_student_name)
|
425
|
+
assert_equal false, student.instance_variable_defined?(:@_memoized_seniority)
|
426
|
+
|
427
|
+
student.unmemoize_all
|
428
|
+
assert_equal false, @calculator.instance_variable_defined?(:@_memoized_student_name)
|
429
|
+
end
|
430
|
+
|
431
|
+
def test_memoize_all
|
432
|
+
@calculator.memoize_all
|
433
|
+
assert_equal true, @calculator.instance_variable_defined?(:@_memoized_counter)
|
434
|
+
end
|
435
|
+
|
436
|
+
def test_memoize_all_subclasses
|
437
|
+
# Person memoize :age, :is_developer?, :memoize_protected_test, :name, :name?, :sleep, :update, :update_attributes
|
438
|
+
# Student < Person memoize :name, :identifier => :student
|
439
|
+
# Teacher < Person memoize :seniority
|
440
|
+
|
441
|
+
teacher = Teacher.new
|
442
|
+
teacher.memoize_all
|
443
|
+
|
444
|
+
assert_equal 'very_senior', teacher.instance_variable_get(:@_memoized_seniority)
|
445
|
+
assert_equal 'Josh', teacher.instance_variable_get(:@_memoized_name)
|
446
|
+
|
447
|
+
student = Student.new
|
448
|
+
student.memoize_all
|
449
|
+
|
450
|
+
assert_equal 'Student Josh', student.instance_variable_get(:@_memoized_student_name)
|
451
|
+
assert_equal 'Student Josh', student.name
|
452
|
+
assert_equal false, student.instance_variable_defined?(:@_memoized_seniority)
|
453
|
+
end
|
454
|
+
|
455
|
+
def test_memoization_cache_is_different_for_each_instance
|
456
|
+
assert_equal 1, @calculator.counter
|
457
|
+
assert_equal 2, @calculator.counter(:reload)
|
458
|
+
assert_equal 1, Calculator.new.counter
|
459
|
+
end
|
460
|
+
|
461
|
+
def test_memoization_class_variables
|
462
|
+
@book.memoize_all
|
463
|
+
assert_equal "My Life by Brian 'Fudge' Turmuck", @book.instance_variable_get(:@_memoized_full_title)
|
464
|
+
assert_equal "My Life by Brian 'Fudge' Turmuck", @book.full_title
|
465
|
+
|
466
|
+
Book.memoize_all
|
467
|
+
assert_instance_of Array, Book.instance_variable_get(:@_memoized_all_types)
|
468
|
+
assert_equal 24, Book.all_types.count
|
469
|
+
end
|
470
|
+
|
471
|
+
def test_memoized_is_not_affected_by_freeze
|
472
|
+
@person.freeze
|
473
|
+
assert_equal 'Josh', @person.name
|
474
|
+
assert_equal 'Joshua', @person.update('Joshua')
|
475
|
+
end
|
476
|
+
|
477
|
+
def test_memoization_with_args
|
478
|
+
assert_equal 55, @calculator.fib(10)
|
479
|
+
assert_equal 11, @calculator.fib_calls
|
480
|
+
end
|
481
|
+
|
482
|
+
def test_reloadable_with_args
|
483
|
+
assert_equal 55, @calculator.fib(10)
|
484
|
+
assert_equal 11, @calculator.fib_calls
|
485
|
+
assert_equal 55, @calculator.fib(10, :reload)
|
486
|
+
assert_equal 12, @calculator.fib_calls
|
487
|
+
assert_equal 55, @calculator.fib(10, true)
|
488
|
+
assert_equal 13, @calculator.fib_calls
|
489
|
+
end
|
490
|
+
|
491
|
+
def test_memoization_with_boolean_arg
|
492
|
+
assert_equal 4, @calculator.add_or_subtract(2, 2, true)
|
493
|
+
assert_equal 2, @calculator.add_or_subtract(4, 2, false)
|
494
|
+
end
|
495
|
+
|
496
|
+
def test_object_memoization
|
497
|
+
[Company.new, Company.new, Company.new].each do |company|
|
498
|
+
company.extend Memoist
|
499
|
+
company.memoize :name
|
500
|
+
|
501
|
+
assert_equal '37signals', company.name
|
502
|
+
assert_equal 1, company.name_calls
|
503
|
+
assert_equal '37signals', company.name
|
504
|
+
assert_equal 1, company.name_calls
|
505
|
+
end
|
506
|
+
end
|
507
|
+
|
508
|
+
def test_memoized_module_methods
|
509
|
+
assert_equal 1.025, @calculator.sales_tax(10)
|
510
|
+
assert_equal 1, @calculator.sales_tax_calls
|
511
|
+
assert_equal 1.025, @calculator.sales_tax(10)
|
512
|
+
assert_equal 1, @calculator.sales_tax_calls
|
513
|
+
assert_equal 2.5625, @calculator.sales_tax(25)
|
514
|
+
assert_equal 2, @calculator.sales_tax_calls
|
515
|
+
end
|
516
|
+
|
517
|
+
def test_object_memoized_module_methods
|
518
|
+
company = Company.new
|
519
|
+
company.extend(Rates)
|
520
|
+
|
521
|
+
assert_equal 1.025, company.sales_tax(10)
|
522
|
+
assert_equal 1, company.sales_tax_calls
|
523
|
+
assert_equal 1.025, company.sales_tax(10)
|
524
|
+
assert_equal 1, company.sales_tax_calls
|
525
|
+
assert_equal 2.5625, company.sales_tax(25)
|
526
|
+
assert_equal 2, company.sales_tax_calls
|
527
|
+
end
|
528
|
+
|
529
|
+
def test_double_memoization_with_identifier
|
530
|
+
# Person memoize :age, :is_developer?, :memoize_protected_test, :name, :name?, :sleep, :update, :update_attributes
|
531
|
+
# Student < Person memoize :name, :identifier => :student
|
532
|
+
# Teacher < Person memoize :seniority
|
533
|
+
|
534
|
+
Person.memoize :name, identifier: :again
|
535
|
+
p = Person.new
|
536
|
+
assert_equal 'Josh', p.name
|
537
|
+
assert p.instance_variable_get(:@_memoized_again_name)
|
538
|
+
|
539
|
+
# HACK: tl;dr: Don't memoize classes in test that are used elsewhere.
|
540
|
+
# Calling Person.memoize :name, :identifier => :again pollutes Person
|
541
|
+
# and descendents since we cache the memoized method structures.
|
542
|
+
# This populates those structs, verifies Person is polluted, resets the
|
543
|
+
# structs, cleans up cached memoized_methods
|
544
|
+
Student.all_memoized_structs
|
545
|
+
Person.all_memoized_structs
|
546
|
+
Teacher.all_memoized_structs
|
547
|
+
assert Person.memoized_methods.any? { |m| m.ivar == '@_memoized_again_name' }
|
548
|
+
|
549
|
+
[Student, Teacher, Person].each(&:clear_structs)
|
550
|
+
assert Person.memoized_methods.reject! { |m| m.ivar == '@_memoized_again_name' }
|
551
|
+
assert_nil Student.memoized_methods.reject! { |m| m.ivar == '@_memoized_again_name' }
|
552
|
+
assert_nil Teacher.memoized_methods.reject! { |m| m.ivar == '@_memoized_again_name' }
|
553
|
+
end
|
554
|
+
|
555
|
+
def test_memoization_with_a_subclass
|
556
|
+
student = Student.new
|
557
|
+
student.name
|
558
|
+
student.name
|
559
|
+
assert_equal 1, student.student_name_calls
|
560
|
+
assert_equal 1, student.name_calls
|
561
|
+
end
|
562
|
+
|
563
|
+
def test_memoization_is_chainable
|
564
|
+
klass = Class.new do
|
565
|
+
def foo
|
566
|
+
'bar'
|
567
|
+
end
|
568
|
+
end
|
569
|
+
klass.extend Memoist
|
570
|
+
chainable = klass.memoize :foo
|
571
|
+
assert_equal :foo, chainable
|
572
|
+
end
|
573
|
+
|
574
|
+
def test_protected_method_memoization
|
575
|
+
person = Person.new
|
576
|
+
|
577
|
+
assert_raises(NoMethodError) { person.memoize_protected_test }
|
578
|
+
assert_equal 'protected', person.send(:memoize_protected_test)
|
579
|
+
end
|
580
|
+
|
581
|
+
def test_private_method_memoization
|
582
|
+
person = Person.new
|
583
|
+
|
584
|
+
assert_raises(NoMethodError) { person.is_developer? }
|
585
|
+
assert_equal 'Yes', person.send(:is_developer?)
|
586
|
+
assert_equal 1, person.is_developer_calls
|
587
|
+
assert_equal 'Yes', person.send(:is_developer?)
|
588
|
+
assert_equal 1, person.is_developer_calls
|
589
|
+
end
|
590
|
+
end
|
data/test/test_helper.rb
ADDED