memoization 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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