memoization 0.0.1

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.
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in memoization.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Burke Libbey
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,29 @@
1
+ # Memoization
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'memoization'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install memoization
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,108 @@
1
+ require 'active_support/core_ext/kernel/singleton_class'
2
+ require 'active_support/core_ext/module/aliasing'
3
+ require 'active_support/deprecation'
4
+
5
+ module Memoization
6
+ def self.memoized_ivar_for(symbol)
7
+ "@_memoized_#{symbol.to_s.sub(/\?\Z/, '_query').sub(/!\Z/, '_bang')}".to_sym
8
+ end
9
+
10
+ module InstanceMethods
11
+ def self.included(base)
12
+ base.class_eval do
13
+ unless base.method_defined?(:freeze_without_memoizable)
14
+ alias_method_chain :freeze, :memoizable
15
+ end
16
+ end
17
+ end
18
+
19
+ def freeze_with_memoizable
20
+ memoize_all unless frozen?
21
+ freeze_without_memoizable
22
+ end
23
+
24
+ def memoize_all
25
+ prime_cache ".*"
26
+ end
27
+
28
+ def unmemoize_all
29
+ flush_cache ".*"
30
+ end
31
+
32
+ def prime_cache(*syms)
33
+ syms.each do |sym|
34
+ methods.each do |m|
35
+ if m.to_s =~ /^_unmemoized_(#{sym})/
36
+ if method(m).arity == 0
37
+ __send__($1)
38
+ else
39
+ ivar = Memoization.memoized_ivar_for($1)
40
+ instance_variable_set(ivar, {})
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ def flush_cache(*syms)
48
+ syms.each do |sym|
49
+ (methods + private_methods + protected_methods).each do |m|
50
+ if m.to_s =~ /^_unmemoized_(#{sym.to_s.gsub(/\?\Z/, '\?')})/
51
+ ivar = Memoization.memoized_ivar_for($1)
52
+ instance_variable_get(ivar).clear if instance_variable_defined?(ivar)
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+
59
+ def memoize(*symbols)
60
+ symbols.each do |symbol|
61
+ original_method = :"_unmemoized_#{symbol}"
62
+ memoized_ivar = Memoization.memoized_ivar_for(symbol)
63
+
64
+ class_eval <<-EOS, __FILE__, __LINE__ + 1
65
+ include InstanceMethods # include InstanceMethods
66
+ #
67
+ if method_defined?(:#{original_method}) # if method_defined?(:_unmemoized_mime_type)
68
+ raise "Already memoized #{symbol}" # raise "Already memoized mime_type"
69
+ end # end
70
+ alias #{original_method} #{symbol} # alias _unmemoized_mime_type mime_type
71
+ #
72
+ if instance_method(:#{symbol}).arity == 0 # if instance_method(:mime_type).arity == 0
73
+ def #{symbol}(reload = false) # def mime_type(reload = false)
74
+ if reload || !defined?(#{memoized_ivar}) || #{memoized_ivar}.empty? # if reload || !defined?(@_memoized_mime_type) || @_memoized_mime_type.empty?
75
+ #{memoized_ivar} = [#{original_method}] # @_memoized_mime_type = [_unmemoized_mime_type]
76
+ end # end
77
+ #{memoized_ivar}[0] # @_memoized_mime_type[0]
78
+ end # end
79
+ else # else
80
+ def #{symbol}(*args) # def mime_type(*args)
81
+ #{memoized_ivar} ||= {} unless frozen? # @_memoized_mime_type ||= {} unless frozen?
82
+ args_length = method(:#{original_method}).arity # args_length = method(:_unmemoized_mime_type).arity
83
+ if args.length == args_length + 1 && # if args.length == args_length + 1 &&
84
+ (args.last == true || args.last == :reload) # (args.last == true || args.last == :reload)
85
+ reload = args.pop # reload = args.pop
86
+ end # end
87
+ #
88
+ if defined?(#{memoized_ivar}) && #{memoized_ivar} # if defined?(@_memoized_mime_type) && @_memoized_mime_type
89
+ if !reload && #{memoized_ivar}.has_key?(args) # if !reload && @_memoized_mime_type.has_key?(args)
90
+ #{memoized_ivar}[args] # @_memoized_mime_type[args]
91
+ elsif #{memoized_ivar} # elsif @_memoized_mime_type
92
+ #{memoized_ivar}[args] = #{original_method}(*args) # @_memoized_mime_type[args] = _unmemoized_mime_type(*args)
93
+ end # end
94
+ else # else
95
+ #{original_method}(*args) # _unmemoized_mime_type(*args)
96
+ end # end
97
+ end # end
98
+ end # end
99
+ #
100
+ if private_method_defined?(#{original_method.inspect}) # if private_method_defined?(:_unmemoized_mime_type)
101
+ private #{symbol.inspect} # private :mime_type
102
+ elsif protected_method_defined?(#{original_method.inspect}) # elsif protected_method_defined?(:_unmemoized_mime_type)
103
+ protected #{symbol.inspect} # protected :mime_type
104
+ end # end
105
+ EOS
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,3 @@
1
+ module Memoization
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,17 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/memoization/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Burke Libbey"]
6
+ gem.email = ["burke@burkelibbey.org"]
7
+ gem.description = %q{Forward-port of ActiveSupport::Memoizable for 3.2 and beyond.}
8
+ gem.summary = %q{Forward-port of ActiveSupport::Memoizable for 3.2 and beyond. Because it's actually useful.}
9
+ gem.homepage = ""
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "memoization"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = Memoization::VERSION
17
+ end
@@ -0,0 +1,64 @@
1
+ ORIG_ARGV = ARGV.dup
2
+
3
+ begin
4
+ old, $VERBOSE = $VERBOSE, nil
5
+ # require File.expand_path('../../../load_paths', __FILE__)
6
+ ensure
7
+ $VERBOSE = old
8
+ end
9
+
10
+ lib = File.expand_path("#{File.dirname(__FILE__)}/../lib")
11
+ $:.unshift(lib) unless $:.include?('lib') || $:.include?(lib)
12
+
13
+ require 'active_support/core_ext/kernel/reporting'
14
+
15
+ require 'active_support/core_ext/string/encoding'
16
+ if "ruby".encoding_aware?
17
+ # These are the normal settings that will be set up by Railties
18
+ # TODO: Have these tests support other combinations of these values
19
+ silence_warnings do
20
+ Encoding.default_internal = "UTF-8"
21
+ Encoding.default_external = "UTF-8"
22
+ end
23
+ end
24
+
25
+ require 'test/unit'
26
+ # require 'empty_bool'
27
+
28
+ silence_warnings { require 'mocha' }
29
+
30
+ ENV['NO_RELOAD'] = '1'
31
+ require 'active_support'
32
+
33
+ # Include shims until we get off 1.8.6
34
+ require 'active_support/ruby/shim' if RUBY_VERSION < '1.8.7'
35
+
36
+ def uses_memcached(test_name)
37
+ require 'memcache'
38
+ begin
39
+ MemCache.new('localhost:11211').stats
40
+ yield
41
+ rescue MemCache::MemCacheError
42
+ $stderr.puts "Skipping #{test_name} tests. Start memcached and try again."
43
+ end
44
+ end
45
+
46
+ def with_kcode(code)
47
+ if RUBY_VERSION < '1.9'
48
+ begin
49
+ old_kcode, $KCODE = $KCODE, code
50
+ yield
51
+ ensure
52
+ $KCODE = old_kcode
53
+ end
54
+ else
55
+ yield
56
+ end
57
+ end
58
+
59
+ # Show backtraces for deprecated behavior for quicker cleanup.
60
+ ActiveSupport::Deprecation.debug = true
61
+
62
+ if RUBY_VERSION < '1.9'
63
+ $KCODE = 'UTF8'
64
+ end
@@ -0,0 +1,279 @@
1
+ require './lib/memoization'
2
+ require './test/abstract_unit'
3
+
4
+ class MemoizationTest < ActiveSupport::TestCase
5
+ class Person
6
+ extend Memoization
7
+
8
+ attr_reader :name_calls, :age_calls, :is_developer_calls, :name_query_calls
9
+
10
+ def initialize
11
+ @name_calls = 0
12
+ @age_calls = 0
13
+ @is_developer_calls = 0
14
+ @name_query_calls = 0
15
+ end
16
+
17
+ def name
18
+ @name_calls += 1
19
+ "Josh"
20
+ end
21
+
22
+ def name?
23
+ @name_query_calls += 1
24
+ true
25
+ end
26
+ memoize :name?
27
+
28
+ def update(name)
29
+ "Joshua"
30
+ end
31
+ memoize :update
32
+
33
+ def age
34
+ @age_calls += 1
35
+ nil
36
+ end
37
+
38
+ memoize :name, :age
39
+
40
+ protected
41
+
42
+ def memoize_protected_test
43
+ 'protected'
44
+ end
45
+ memoize :memoize_protected_test
46
+
47
+ private
48
+
49
+ def is_developer?
50
+ @is_developer_calls += 1
51
+ "Yes"
52
+ end
53
+ memoize :is_developer?
54
+ end
55
+
56
+ class Company
57
+ attr_reader :name_calls
58
+ def initialize
59
+ @name_calls = 0
60
+ end
61
+
62
+ def name
63
+ @name_calls += 1
64
+ "37signals"
65
+ end
66
+ end
67
+
68
+ module Rates
69
+ extend Memoization
70
+
71
+ attr_reader :sales_tax_calls
72
+ def sales_tax(price)
73
+ @sales_tax_calls ||= 0
74
+ @sales_tax_calls += 1
75
+ price * 0.1025
76
+ end
77
+ memoize :sales_tax
78
+ end
79
+
80
+ class Calculator
81
+ extend Memoization
82
+ include Rates
83
+
84
+ attr_reader :fib_calls
85
+ def initialize
86
+ @fib_calls = 0
87
+ end
88
+
89
+ def fib(n)
90
+ @fib_calls += 1
91
+
92
+ if n == 0 || n == 1
93
+ n
94
+ else
95
+ fib(n - 1) + fib(n - 2)
96
+ end
97
+ end
98
+ memoize :fib
99
+
100
+ def add_or_subtract(i, j, add)
101
+ if add
102
+ i + j
103
+ else
104
+ i - j
105
+ end
106
+ end
107
+ memoize :add_or_subtract
108
+
109
+ def counter
110
+ @count ||= 0
111
+ @count += 1
112
+ end
113
+ memoize :counter
114
+ end
115
+
116
+ def setup
117
+ @person = Person.new
118
+ @calculator = Calculator.new
119
+ end
120
+
121
+ def test_memoization
122
+ assert_equal "Josh", @person.name
123
+ assert_equal 1, @person.name_calls
124
+
125
+ 3.times { assert_equal "Josh", @person.name }
126
+ assert_equal 1, @person.name_calls
127
+ end
128
+
129
+ def test_memoization_with_punctuation
130
+ assert_equal true, @person.name?
131
+
132
+ assert_nothing_raised(NameError) do
133
+ @person.memoize_all
134
+ @person.unmemoize_all
135
+ end
136
+ end
137
+
138
+ def test_memoization_flush_with_punctuation
139
+ assert_equal true, @person.name?
140
+ @person.flush_cache(:name?)
141
+ 3.times { assert_equal true, @person.name? }
142
+ assert_equal 2, @person.name_query_calls
143
+ end
144
+
145
+ def test_memoization_with_nil_value
146
+ assert_equal nil, @person.age
147
+ assert_equal 1, @person.age_calls
148
+
149
+ 3.times { assert_equal nil, @person.age }
150
+ assert_equal 1, @person.age_calls
151
+ end
152
+
153
+ def test_reloadable
154
+ assert_equal 1, @calculator.counter
155
+ assert_equal 2, @calculator.counter(:reload)
156
+ assert_equal 2, @calculator.counter
157
+ assert_equal 3, @calculator.counter(true)
158
+ assert_equal 3, @calculator.counter
159
+ end
160
+
161
+ def test_flush_cache
162
+ assert_equal 1, @calculator.counter
163
+
164
+ assert @calculator.instance_variable_get(:@_memoized_counter).any?
165
+ @calculator.flush_cache(:counter)
166
+ assert @calculator.instance_variable_get(:@_memoized_counter).empty?
167
+
168
+ assert_equal 2, @calculator.counter
169
+ end
170
+
171
+ def test_unmemoize_all
172
+ assert_equal 1, @calculator.counter
173
+
174
+ assert @calculator.instance_variable_get(:@_memoized_counter).any?
175
+ @calculator.unmemoize_all
176
+ assert @calculator.instance_variable_get(:@_memoized_counter).empty?
177
+
178
+ assert_equal 2, @calculator.counter
179
+ end
180
+
181
+ def test_memoize_all
182
+ @calculator.memoize_all
183
+ assert @calculator.instance_variable_defined?(:@_memoized_counter)
184
+ end
185
+
186
+ def test_memoization_cache_is_different_for_each_instance
187
+ assert_equal 1, @calculator.counter
188
+ assert_equal 2, @calculator.counter(:reload)
189
+ assert_equal 1, Calculator.new.counter
190
+ end
191
+
192
+ def test_memoized_is_not_affected_by_freeze
193
+ @person.freeze
194
+ assert_equal "Josh", @person.name
195
+ assert_equal "Joshua", @person.update("Joshua")
196
+ end
197
+
198
+ def test_memoization_with_args
199
+ assert_equal 55, @calculator.fib(10)
200
+ assert_equal 11, @calculator.fib_calls
201
+ end
202
+
203
+ def test_reloadable_with_args
204
+ assert_equal 55, @calculator.fib(10)
205
+ assert_equal 11, @calculator.fib_calls
206
+ assert_equal 55, @calculator.fib(10, :reload)
207
+ assert_equal 12, @calculator.fib_calls
208
+ assert_equal 55, @calculator.fib(10, true)
209
+ assert_equal 13, @calculator.fib_calls
210
+ end
211
+
212
+ def test_memoization_with_boolean_arg
213
+ assert_equal 4, @calculator.add_or_subtract(2, 2, true)
214
+ assert_equal 2, @calculator.add_or_subtract(4, 2, false)
215
+ end
216
+
217
+ def test_object_memoization
218
+ [Company.new, Company.new, Company.new].each do |company|
219
+ company.extend Memoization
220
+ company.memoize :name
221
+
222
+ assert_equal "37signals", company.name
223
+ assert_equal 1, company.name_calls
224
+ assert_equal "37signals", company.name
225
+ assert_equal 1, company.name_calls
226
+ end
227
+ end
228
+
229
+ def test_memoized_module_methods
230
+ assert_equal 1.025, @calculator.sales_tax(10)
231
+ assert_equal 1, @calculator.sales_tax_calls
232
+ assert_equal 1.025, @calculator.sales_tax(10)
233
+ assert_equal 1, @calculator.sales_tax_calls
234
+ assert_equal 2.5625, @calculator.sales_tax(25)
235
+ assert_equal 2, @calculator.sales_tax_calls
236
+ end
237
+
238
+ def test_object_memoized_module_methods
239
+ company = Company.new
240
+ company.extend(Rates)
241
+
242
+ assert_equal 1.025, company.sales_tax(10)
243
+ assert_equal 1, company.sales_tax_calls
244
+ assert_equal 1.025, company.sales_tax(10)
245
+ assert_equal 1, company.sales_tax_calls
246
+ assert_equal 2.5625, company.sales_tax(25)
247
+ assert_equal 2, company.sales_tax_calls
248
+ end
249
+
250
+ def test_double_memoization
251
+ assert_raise(RuntimeError) { Person.memoize :name }
252
+ person = Person.new
253
+ person.extend Memoization
254
+ assert_raise(RuntimeError) { person.memoize :name }
255
+
256
+ company = Company.new
257
+ company.extend Memoization
258
+ company.memoize :name
259
+ assert_raise(RuntimeError) { company.memoize :name }
260
+ end
261
+
262
+ def test_protected_method_memoization
263
+ person = Person.new
264
+
265
+ assert_raise(NoMethodError) { person.memoize_protected_test }
266
+ assert_equal "protected", person.send(:memoize_protected_test)
267
+ end
268
+
269
+ def test_private_method_memoization
270
+ person = Person.new
271
+
272
+ assert_raise(NoMethodError) { person.is_developer? }
273
+ assert_equal "Yes", person.send(:is_developer?)
274
+ assert_equal 1, person.is_developer_calls
275
+ assert_equal "Yes", person.send(:is_developer?)
276
+ assert_equal 1, person.is_developer_calls
277
+ end
278
+
279
+ end
metadata ADDED
@@ -0,0 +1,58 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: memoization
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Burke Libbey
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-03-30 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: Forward-port of ActiveSupport::Memoizable for 3.2 and beyond.
15
+ email:
16
+ - burke@burkelibbey.org
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - .gitignore
22
+ - Gemfile
23
+ - LICENSE
24
+ - README.md
25
+ - Rakefile
26
+ - lib/memoization.rb
27
+ - lib/memoization/version.rb
28
+ - memoization.gemspec
29
+ - test/abstract_unit.rb
30
+ - test/memoization_test.rb
31
+ homepage: ''
32
+ licenses: []
33
+ post_install_message:
34
+ rdoc_options: []
35
+ require_paths:
36
+ - lib
37
+ required_ruby_version: !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ! '>='
41
+ - !ruby/object:Gem::Version
42
+ version: '0'
43
+ required_rubygems_version: !ruby/object:Gem::Requirement
44
+ none: false
45
+ requirements:
46
+ - - ! '>='
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ requirements: []
50
+ rubyforge_project:
51
+ rubygems_version: 1.8.11
52
+ signing_key:
53
+ specification_version: 3
54
+ summary: Forward-port of ActiveSupport::Memoizable for 3.2 and beyond. Because it's
55
+ actually useful.
56
+ test_files:
57
+ - test/abstract_unit.rb
58
+ - test/memoization_test.rb