memoizer 1.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,4 @@
1
+ == 1.0.1
2
+ * Bump version to avoid conflict with old memoizer gem
3
+ == 1.0.0
4
+ * Initial release
@@ -0,0 +1,22 @@
1
+ The MIT LICENSE
2
+
3
+ Copyright (c) 2011 Wegowise Inc.
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,63 @@
1
+ Description
2
+ ===========
3
+
4
+ Memoizer will memoize the results of your methods. It acts much like
5
+ ActiveSupport::Memoizable without all of that freezing business. The API for unmemoizing
6
+ is also a bit more expicit.
7
+
8
+ Install
9
+ =======
10
+
11
+ $ gem install memoizer
12
+
13
+ Usage
14
+ =====
15
+
16
+ To memoize an instance method:
17
+
18
+ class A
19
+ include Memoizer
20
+ def hello() 'hello!'; end
21
+ memoize :hello
22
+ end
23
+
24
+ Or you can memoize many methods at once:
25
+
26
+ class B
27
+ extend Memoizer
28
+ def hello() 'hello!'; end
29
+ def goodbye() 'goodbye :('; end
30
+ memoize :hello, :goodbye
31
+ end
32
+
33
+ Memoizing class methods works the same way:
34
+
35
+ class C
36
+ class << self
37
+ include Memoizer
38
+ def hello() 'hello!'; end
39
+ memoize :hello
40
+ end
41
+ end
42
+
43
+
44
+ To unmemoize a specific method:
45
+
46
+ instance = A.new
47
+ instance.hello # the hello method is now memoized
48
+ instance.unmemoize(:hello) # the hello method is no longer memoized
49
+ instance.hello # the hello method is run again and re-memoized
50
+
51
+
52
+ To unmemoize all methods for an instance:
53
+
54
+ instance = B.new
55
+ instance.hello # the hello method is now memoized
56
+ instance.goodbye # the goodbye method is now memoized
57
+ instance.unmemoize_all # neither hello nor goodbye are memoized anymore
58
+
59
+
60
+ License
61
+ =======
62
+
63
+ See LICENSE.txt
@@ -0,0 +1,39 @@
1
+ require 'rake'
2
+ require 'fileutils'
3
+
4
+ def gemspec_name
5
+ @gemspec_name ||= Dir['*.gemspec'][0]
6
+ end
7
+
8
+ def gemspec
9
+ @gemspec ||= eval(File.read(gemspec_name), binding, gemspec_name)
10
+ end
11
+
12
+ desc "Build the gem"
13
+ task :gem=>:gemspec do
14
+ sh "gem build #{gemspec_name}"
15
+ FileUtils.mkdir_p 'pkg'
16
+ FileUtils.mv "#{gemspec.name}-#{gemspec.version}.gem", 'pkg'
17
+ end
18
+
19
+ desc "Install the gem locally"
20
+ task :install => :gem do
21
+ sh %{gem install pkg/#{gemspec.name}-#{gemspec.version}}
22
+ end
23
+
24
+ desc "Generate the gemspec"
25
+ task :generate do
26
+ puts gemspec.to_ruby
27
+ end
28
+
29
+ desc "Validate the gemspec"
30
+ task :gemspec do
31
+ gemspec.validate
32
+ end
33
+
34
+ desc 'Run tests'
35
+ task :test do |t|
36
+ sh 'rspec spec'
37
+ end
38
+
39
+ task :default => :test
@@ -0,0 +1,74 @@
1
+ module Memoizer
2
+ def self.included(base)
3
+ base.extend ClassMethods
4
+ base.send :include, InstanceMethods
5
+ end
6
+
7
+ def self.safe_name(method_name)
8
+ method_name.to_s.sub(/\?\Z/, '_query').sub(/!\Z/, '_bang').to_sym
9
+ end
10
+
11
+ def self.ivar_name(method_name)
12
+ "_memoized_#{self.safe_name(method_name)}"
13
+ end
14
+
15
+ module ClassMethods
16
+ def memoize(*method_names)
17
+ method_names.each do |method_name|
18
+ safe_method_name = Memoizer.safe_name(method_name)
19
+ memoized_ivar_name = Memoizer.ivar_name(method_name)
20
+ unmemoized_method = "_unmemoized_#{method_name}"
21
+
22
+ attr_accessor memoized_ivar_name
23
+ alias_method unmemoized_method, method_name
24
+
25
+ no_args = self.instance_method(unmemoized_method).arity == 0
26
+
27
+ define_method method_name do |*args|
28
+ memoized_value = self.instance_variable_get("@#{memoized_ivar_name}")
29
+
30
+ # if the method takes no inputs, store the value in an array
31
+ if no_args
32
+ if !memoized_value.is_a?(Array)
33
+ memoized_value = [self.send(unmemoized_method)]
34
+ self.instance_variable_set("@#{memoized_ivar_name}", memoized_value)
35
+ end
36
+ memoized_value.first
37
+
38
+ #otherwise store in a hash indexed by the arguments
39
+ else
40
+ if !memoized_value.is_a?(Hash)
41
+ memoized_value = {args => self.send(unmemoized_method, *args)}
42
+ self.instance_variable_set("@#{memoized_ivar_name}", memoized_value)
43
+ elsif !memoized_value.has_key?(args)
44
+ memoized_value[args] = self.send(unmemoized_method, *args)
45
+ end
46
+ memoized_value[args]
47
+ end
48
+ end
49
+
50
+ if self.private_method_defined?(unmemoized_method)
51
+ private method_name
52
+ elsif self.protected_method_defined?(unmemoized_method)
53
+ protected method_name
54
+ end
55
+
56
+ end
57
+ end
58
+ end
59
+
60
+ module InstanceMethods
61
+ def unmemoize(method_name)
62
+ self.instance_variable_set("@#{Memoizer.ivar_name(method_name)}", nil)
63
+ end
64
+
65
+ def unmemoize_all
66
+ (methods + private_methods + protected_methods).each do |method|
67
+ if method.to_s =~ /^_unmemoized_(.*)/
68
+ unmemoize($1)
69
+ end
70
+ end
71
+ end
72
+ end
73
+
74
+ end
@@ -0,0 +1,3 @@
1
+ module Memoizer
2
+ VERSION = '1.0.1'
3
+ end
@@ -0,0 +1,17 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require 'rubygems' unless defined? Gem
3
+ require File.dirname(__FILE__) + "/lib/memoizer/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "memoizer"
7
+ s.version = Memoizer::VERSION
8
+ s.authors = ["Barun Singh"]
9
+ s.email = "bsingh@wegowise.com"
10
+ s.homepage = "http://github.com/wegowise/memoizer"
11
+ s.summary = "Memoizes (caches the results of) your methods"
12
+ s.description = "Memoizer caches the results of your method calls, works well with methods that accept arguments or return nil. It's a simpler and more expicit alternative to ActiveSupport::Memoizable"
13
+ s.required_rubygems_version = ">= 1.3.6"
14
+ s.files = Dir.glob(%w[{lib,spec}/**/*.rb [A-Z]*.{txt,rdoc,md} *.gemspec]) + %w{Rakefile}
15
+ s.extra_rdoc_files = ["README.md", "LICENSE.txt"]
16
+ s.license = 'MIT'
17
+ end
@@ -0,0 +1,209 @@
1
+ require 'memoizer'
2
+ require 'spec_helper'
3
+
4
+ class MemoizerSpecClass
5
+ include Memoizer
6
+ def no_params() Date.today; end
7
+ def with_params?(ndays, an_array) Date.today + ndays + an_array.length; end
8
+ def returning_nil!() Date.today; nil; end
9
+ memoize :no_params, :with_params?, :returning_nil!
10
+ end
11
+ class Beepbop < MemoizerSpecClass; end
12
+
13
+
14
+ describe Memoizer do
15
+ let(:today) { Date.today }
16
+
17
+ describe '.memoize' do
18
+ let(:object) { MemoizerSpecClass.new }
19
+ let(:tomorrow) { Date.today + 1 }
20
+
21
+ context "for a method with no params" do
22
+ it "stores memoized value" do
23
+ Timecop.freeze(today)
24
+ object.no_params.should == today
25
+ Timecop.freeze(tomorrow)
26
+ object.no_params.should == today
27
+ end
28
+ end
29
+
30
+ context "for a method with params (and ending in ?)" do
31
+ it "stores memoized value" do
32
+ Timecop.freeze(today)
33
+ object.with_params?(1, [1,2]).should == (today + 3)
34
+ Timecop.freeze(tomorrow)
35
+ object.with_params?(1, [1,2]).should == (today + 3)
36
+ end
37
+ it "does not confuse one set of inputs for another" do
38
+ Timecop.freeze(today)
39
+ object.with_params?(1, [1,2]).should == (today + 3)
40
+ object.with_params?(2, [1,2]).should == (today + 4)
41
+ Timecop.freeze(tomorrow)
42
+ object.with_params?(1, [1,2]).should == (today + 3)
43
+ object.with_params?(1, [2,2]).should == (today + 4)
44
+ end
45
+ end
46
+
47
+ context "for a method that returns nil (and ends in !)" do
48
+ it "stores the memoized value" do
49
+ object.returning_nil!
50
+ Date.stub!(:today).and_raise(ArgumentError)
51
+ object.returning_nil!.should be_nil
52
+ end
53
+ end
54
+
55
+ context "for subclasses" do
56
+ let(:object) { Beepbop.new }
57
+ it "still memoizes things" do
58
+ Timecop.freeze(today)
59
+ object.no_params.should == today
60
+ Timecop.freeze(tomorrow)
61
+ object.no_params.should == today
62
+ end
63
+ end
64
+
65
+ context 'for private methods' do
66
+ class Beirut < MemoizerSpecClass
67
+ def foo() bar; end
68
+ private
69
+ def bar() Date.today; end
70
+ memoize :bar
71
+ end
72
+ let(:object) { Beirut.new }
73
+
74
+ it "respects the privacy of the memoized method" do
75
+ Beirut.private_method_defined?(:bar).should be_true
76
+ Beirut.private_method_defined?(:_unmemoized_bar).should be_true
77
+ end
78
+
79
+ it "memoizes things" do
80
+ Timecop.freeze(today)
81
+ object.foo.should == today
82
+ Timecop.freeze(today + 1)
83
+ object.foo.should == today
84
+ end
85
+ end
86
+
87
+ context 'for protected methods' do
88
+ class Wonka < MemoizerSpecClass
89
+ def foo() bar; end
90
+ protected
91
+ def bar() Date.today; end
92
+ memoize :bar
93
+ end
94
+ let(:object) { Wonka.new }
95
+
96
+ it "respects the privacy of the memoized method" do
97
+ Wonka.protected_method_defined?(:bar).should be_true
98
+ Wonka.protected_method_defined?(:_unmemoized_bar).should be_true
99
+ end
100
+
101
+ it "memoizes things" do
102
+ Timecop.freeze(today)
103
+ object.foo.should == today
104
+ Timecop.freeze(today + 1)
105
+ object.foo.should == today
106
+ end
107
+ end
108
+
109
+ end
110
+
111
+
112
+ describe 'instance methods' do
113
+ class MemoizerSpecClass
114
+ def today() Date.today; end
115
+ def plus_ndays(ndays) Date.today + ndays; end
116
+ memoize :today, :plus_ndays
117
+ end
118
+
119
+ let(:object) { MemoizerSpecClass.new }
120
+ before do
121
+ Timecop.freeze(today)
122
+ object.today.should == today
123
+ object.plus_ndays(1).should == today + 1
124
+ object.plus_ndays(3).should == today + 3
125
+ end
126
+
127
+ describe '#unmemoize' do
128
+ context "for a method with no arguments" do
129
+ it "clears the memoized value so it can be rememoized" do
130
+ Timecop.freeze(today + 1)
131
+ object.today.should == today
132
+
133
+ object.unmemoize(:today)
134
+ object.today.should == today + 1
135
+
136
+ Timecop.freeze(today + 2)
137
+ object.today.should == today + 1
138
+ end
139
+ end
140
+
141
+ context "for a method with arguments" do
142
+ it "unmemoizes for all inupts" do
143
+ Timecop.freeze(today + 1)
144
+ object.plus_ndays(1).should == today + 1
145
+ object.plus_ndays(3).should == today + 3
146
+
147
+ object.unmemoize(:plus_ndays)
148
+ object.plus_ndays(1).should == today + 2
149
+ object.plus_ndays(3).should == today + 4
150
+
151
+ Timecop.freeze(today + 2)
152
+ object.plus_ndays(1).should == today + 2
153
+ object.plus_ndays(3).should == today + 4
154
+ end
155
+ end
156
+
157
+ it "only affects the method specified" do
158
+ Timecop.freeze(today + 1)
159
+ object.today.should == today
160
+
161
+ object.unmemoize(:plus_ndays)
162
+ object.today.should == today
163
+
164
+ object.unmemoize(:today)
165
+ object.today.should == today + 1
166
+ end
167
+
168
+ context "for subclasses" do
169
+ let(:object) { Beepbop.new }
170
+ it "clears the memoized value" do
171
+ Timecop.freeze(today + 1)
172
+ object.today.should == today
173
+
174
+ object.unmemoize(:today)
175
+ object.today.should == today + 1
176
+
177
+ Timecop.freeze(today + 2)
178
+ object.today.should == today + 1
179
+ end
180
+ end
181
+ end
182
+
183
+ describe '#unmemoize_all' do
184
+ shared_examples_for "unmemoizing methods" do
185
+ it "clears all memoized values" do
186
+ Timecop.freeze(today + 1)
187
+ object.today.should == today
188
+ object.plus_ndays(1).should == today + 1
189
+ object.plus_ndays(3).should == today + 3
190
+
191
+ object.unmemoize_all
192
+
193
+ object.today.should == today + 1
194
+ object.plus_ndays(1).should == today + 2
195
+ object.plus_ndays(3).should == today + 4
196
+ end
197
+ end
198
+
199
+ it_should_behave_like "unmemoizing methods"
200
+
201
+ context "for subclasses" do
202
+ let(:object) { Beepbop.new }
203
+ it_should_behave_like "unmemoizing methods"
204
+ end
205
+ end
206
+
207
+ end
208
+
209
+ end
@@ -0,0 +1,3 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'timecop'
metadata ADDED
@@ -0,0 +1,58 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: memoizer
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Barun Singh
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-09-02 00:00:00.000000000Z
13
+ dependencies: []
14
+ description: Memoizer caches the results of your method calls, works well with methods
15
+ that accept arguments or return nil. It's a simpler and more expicit alternative
16
+ to ActiveSupport::Memoizable
17
+ email: bsingh@wegowise.com
18
+ executables: []
19
+ extensions: []
20
+ extra_rdoc_files:
21
+ - README.md
22
+ - LICENSE.txt
23
+ files:
24
+ - lib/memoizer/version.rb
25
+ - lib/memoizer.rb
26
+ - spec/memoizer_spec.rb
27
+ - spec/spec_helper.rb
28
+ - CHANGELOG.txt
29
+ - LICENSE.txt
30
+ - README.md
31
+ - memoizer.gemspec
32
+ - Rakefile
33
+ homepage: http://github.com/wegowise/memoizer
34
+ licenses:
35
+ - MIT
36
+ post_install_message:
37
+ rdoc_options: []
38
+ require_paths:
39
+ - lib
40
+ required_ruby_version: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ required_rubygems_version: !ruby/object:Gem::Requirement
47
+ none: false
48
+ requirements:
49
+ - - ! '>='
50
+ - !ruby/object:Gem::Version
51
+ version: 1.3.6
52
+ requirements: []
53
+ rubyforge_project:
54
+ rubygems_version: 1.8.6
55
+ signing_key:
56
+ specification_version: 3
57
+ summary: Memoizes (caches the results of) your methods
58
+ test_files: []