memoizer 1.0.1

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