method_cache 0.6.3

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.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Justin Balthrop
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,42 @@
1
+ = MethodCache
2
+
3
+ MethodCache lets you easily cache the results of any instance method or class method in
4
+ Ruby.
5
+
6
+ == Usage:
7
+
8
+ class Foo
9
+ extend MethodCache
10
+
11
+ cache_method :bar
12
+ def bar
13
+ # do expensive calculation
14
+ end
15
+
16
+ cache_class_method :baz, :clone => true, :expiry => 1.day
17
+ def self.baz
18
+ # do some expensive calculation that will be invalid tomorrow
19
+ end
20
+ end
21
+
22
+ foo = Foo.new
23
+ foo.bar # does calculation
24
+ foo.bar # cached
25
+
26
+ Foo.baz # does calculation
27
+ Foo.baz # cached
28
+
29
+ Foo.invalidate_cached_method(:baz)
30
+
31
+ Foo.baz # does calculation
32
+ Foo.baz # cached
33
+
34
+ == Install:
35
+
36
+ sudo gem install ninjudd-memcache -s http://gems.github.com
37
+ sudo gem install ninjudd-cache_version -s http://gems.github.com
38
+ sudo gem install ninjudd-method_cache -s http://gems.github.com
39
+
40
+ == License:
41
+
42
+ Copyright (c) 2009 Justin Balthrop, Geni.com; Published under The MIT License, see LICENSE
data/Rakefile ADDED
@@ -0,0 +1,45 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+
5
+ begin
6
+ require 'jeweler'
7
+ Jeweler::Tasks.new do |s|
8
+ s.name = "method_cache"
9
+ s.summary = %Q{Simple memcache-based memoization library for Ruby}
10
+ s.email = "code@justinbalthrop.com"
11
+ s.homepage = "http://github.com/ninjudd/method_cache"
12
+ s.description = "Simple memcache-based memoization library for Ruby"
13
+ s.authors = ["Justin Balthrop"]
14
+ s.add_dependency('memcache', '>= 1.0.0')
15
+ end
16
+ Jeweler::GemcutterTasks.new
17
+ rescue LoadError
18
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
19
+ end
20
+
21
+ Rake::TestTask.new do |t|
22
+ t.libs << 'lib'
23
+ t.pattern = 'test/**/*_test.rb'
24
+ t.verbose = false
25
+ end
26
+
27
+ Rake::RDocTask.new do |rdoc|
28
+ rdoc.rdoc_dir = 'rdoc'
29
+ rdoc.title = 'method_cache'
30
+ rdoc.options << '--line-numbers' << '--inline-source'
31
+ rdoc.rdoc_files.include('README*')
32
+ rdoc.rdoc_files.include('lib/**/*.rb')
33
+ end
34
+
35
+ begin
36
+ require 'rcov/rcovtask'
37
+ Rcov::RcovTask.new do |t|
38
+ t.libs << 'test'
39
+ t.test_files = FileList['test/**/*_test.rb']
40
+ t.verbose = true
41
+ end
42
+ rescue LoadError
43
+ end
44
+
45
+ task :default => :test
data/VERSION.yml ADDED
@@ -0,0 +1,5 @@
1
+ ---
2
+ :build:
3
+ :patch: 3
4
+ :major: 0
5
+ :minor: 6
@@ -0,0 +1,172 @@
1
+ module MethodCache
2
+ class Proxy
3
+ attr_reader :method_name, :opts, :args, :target
4
+
5
+ def initialize(method_name, opts)
6
+ @method_name = method_name
7
+ @opts = opts
8
+ end
9
+
10
+ def bind(target, args)
11
+ self.clone.bind!(target, args)
12
+ end
13
+
14
+ def bind!(target, args)
15
+ @target = target
16
+ @args = args
17
+ @key = nil
18
+ self
19
+ end
20
+
21
+ def invalidate
22
+ if block_given?
23
+ # Only invalidate if the block returns true.
24
+ value = cache[key]
25
+ return if value and not yield(value)
26
+ end
27
+ cache.delete(key)
28
+ end
29
+
30
+ def context
31
+ opts[:context]
32
+ end
33
+
34
+ def version
35
+ dynamic_opt(:version)
36
+ end
37
+
38
+ def cached?
39
+ not cache[key].nil?
40
+ end
41
+
42
+ def update
43
+ value = block_given? ? yield(cache[key]) : target.send(method_name_without_caching, *args)
44
+ write_to_cache(key, value)
45
+ value
46
+ end
47
+
48
+ def value
49
+ value = cache[key] unless MethodCache.disabled?
50
+ value = nil unless valid?(:load, value)
51
+
52
+ if value.nil?
53
+ value = target.send(method_name_without_caching, *args)
54
+ write_to_cache(key, value) if valid?(:save, value)
55
+ end
56
+
57
+ value = nil if value == NULL
58
+ if clone? and value
59
+ value.clone
60
+ else
61
+ value
62
+ end
63
+ end
64
+
65
+ NULL = 'NULL'
66
+ def method_with_caching
67
+ proxy = self # Need access to the proxy in the closure.
68
+
69
+ lambda do |*args|
70
+ proxy.bind(self, args).value
71
+ end
72
+ end
73
+
74
+ def method_name_without_caching
75
+ @method_name_without_caching ||= begin
76
+ base_name, punctuation = method_name.to_s.sub(/([?!=])$/, ''), $1
77
+ "#{base_name}_without_caching#{punctuation}"
78
+ end
79
+ end
80
+
81
+ def cache
82
+ if @cache.nil?
83
+ @cache = opts[:cache] || MethodCache.default_cache
84
+ @cache = Memcache.pool[@cache] if @cache.kind_of?(Symbol)
85
+ end
86
+ @cache
87
+ end
88
+
89
+ def local?
90
+ cache.kind_of?(Hash)
91
+ end
92
+
93
+ def clone?
94
+ !!opts[:clone]
95
+ end
96
+
97
+ def key
98
+ if @key.nil?
99
+ arg_string = ([method_name, target] + args).collect do |arg|
100
+ object_key(arg)
101
+ end.join('|')
102
+ @key = ['m', version, arg_string].compact.join('|')
103
+ end
104
+ @key
105
+ end
106
+
107
+ private
108
+
109
+ def expiry(value)
110
+ dynamic_opt(:expiry, value).to_i
111
+ end
112
+
113
+ def valid?(type, value)
114
+ name = "#{type}_validation".to_sym
115
+ return true unless opts[name]
116
+ return unless value
117
+
118
+ dynamic_opt(name, value)
119
+ end
120
+
121
+ def dynamic_opt(name, value = nil)
122
+ if opts[name].kind_of?(Proc)
123
+ proc = opts[name].bind(target)
124
+ case proc.arity
125
+ when 0: proc.call()
126
+ when 1: proc.call(value)
127
+ else
128
+ proc.call(value, *args)
129
+ end
130
+ else
131
+ opts[name]
132
+ end
133
+ end
134
+
135
+ def write_to_cache(key, value)
136
+ if cache.kind_of?(Hash)
137
+ raise 'expiry not permitted when cache is a Hash' if opts[:expiry]
138
+ cache[key] = value
139
+ else
140
+ value = value.nil? ? NULL : value
141
+ cache.set(key, value, :expiry => expiry(value))
142
+ end
143
+ end
144
+
145
+ def object_key(arg)
146
+ return "#{class_key(arg.class)}-#{arg.string_hash}" if arg.respond_to?(:string_hash)
147
+
148
+ case arg
149
+ when NilClass : 'nil'
150
+ when TrueClass : 'true'
151
+ when FalseClass : 'false'
152
+ when Numeric : arg.to_s
153
+ when Symbol : ":#{arg}"
154
+ when String : "'#{arg}'"
155
+ when Class, Module : class_key(arg)
156
+ when Hash
157
+ '{' + arg.collect {|key, value| "#{object_key(key)}=#{object_key(value)}"}.sort.join(',') + '}'
158
+ when Array
159
+ '[' + arg.collect {|item| object_key(item)}.join(',') + ']'
160
+ when defined?(ActiveRecord::Base) && ActiveRecord::Base
161
+ "#{class_key(arg.class)}-#{arg.id}"
162
+ else
163
+ hash = local? ? arg.hash : Marshal.dump(arg).hash
164
+ "#{class_key(arg.class)}-#{hash}"
165
+ end
166
+ end
167
+
168
+ def class_key(klass)
169
+ klass.respond_to?(:version) ? "#{klass.name}_#{klass.version(context)}" : klass.name
170
+ end
171
+ end
172
+ end
@@ -0,0 +1,177 @@
1
+ require 'memcache'
2
+
3
+ $:.unshift(File.dirname(__FILE__))
4
+ require 'method_cache/proxy'
5
+
6
+ module MethodCache
7
+ def cache_method(method_name, opts = {})
8
+ method_name = method_name.to_sym
9
+ proxy = opts.kind_of?(Proxy) ? opts : Proxy.new(method_name, opts)
10
+
11
+ if self.class == Class
12
+ return if instance_methods.include?(proxy.method_name_without_caching)
13
+
14
+ if cached_instance_methods.empty?
15
+ include(InvalidationMethods)
16
+ extend(MethodAdded)
17
+ end
18
+
19
+ cached_instance_methods[method_name] = nil
20
+ begin
21
+ # Replace instance method.
22
+ alias_method proxy.method_name_without_caching, method_name
23
+ define_method method_name, proxy.method_with_caching
24
+ rescue NameError => e
25
+ # The method has not been defined yet. We will alias it in method_added.
26
+ # pp e, e.backtrace
27
+ end
28
+ cached_instance_methods[method_name] = proxy
29
+
30
+ elsif self.class == Module
31
+ # We will alias all methods when the module is mixed-in.
32
+ extend(ModuleAdded) if cached_module_methods.empty?
33
+ cached_module_methods[method_name.to_sym] = proxy
34
+ end
35
+ end
36
+
37
+ def cache_class_method(method_name, opts = {})
38
+ method_name = method_name.to_sym
39
+ proxy = opts.kind_of?(Proxy) ? opts : Proxy.new(method_name, opts)
40
+
41
+ return if methods.include?(proxy.method_name_without_caching)
42
+
43
+ if cached_class_methods.empty?
44
+ extend(InvalidationMethods)
45
+ extend(SingletonMethodAdded)
46
+ end
47
+
48
+ method_name = method_name.to_sym
49
+ cached_class_methods[method_name] = nil
50
+ begin
51
+ # Replace class method.
52
+ (class << self; self; end).module_eval do
53
+ alias_method proxy.method_name_without_caching, method_name
54
+ define_method method_name, proxy.method_with_caching
55
+ end
56
+ rescue NameError => e
57
+ # The method has not been defined yet. We will alias it in singleton_method_added.
58
+ # pp e, e.backtrace
59
+ end
60
+ cached_class_methods[method_name] = proxy
61
+ end
62
+
63
+ def self.default_cache
64
+ @default_cache ||= {}
65
+ end
66
+
67
+ def cached_instance_methods(method_name = nil)
68
+ if method_name
69
+ method_name = method_name.to_sym
70
+ ancestors.each do |klass|
71
+ next unless klass.kind_of?(MethodCache)
72
+ proxy = klass.cached_instance_methods[method_name]
73
+ return proxy if proxy
74
+ end
75
+ nil
76
+ else
77
+ @cached_instance_methods ||= {}
78
+ end
79
+ end
80
+
81
+ def cached_class_methods(method_name = nil)
82
+ if method_name
83
+ method_name = method_name.to_sym
84
+ ancestors.each do |klass|
85
+ next unless klass.kind_of?(MethodCache)
86
+ proxy = klass.cached_class_methods[method_name]
87
+ return proxy if proxy
88
+ end
89
+ nil
90
+ else
91
+ @cached_class_methods ||= {}
92
+ end
93
+ end
94
+
95
+ def cached_module_methods(method_name = nil)
96
+ if method_name
97
+ cached_module_methods[method_name.to_sym]
98
+ else
99
+ @cached_module_methods ||= {}
100
+ end
101
+ end
102
+
103
+ def self.disable(&block)
104
+ @disabled, old = true, @disabled
105
+ yield
106
+ ensure
107
+ @disabled = old
108
+ end
109
+
110
+ def self.disabled?
111
+ @disabled
112
+ end
113
+
114
+ module InvalidationMethods
115
+ def invalidate_cached_method(method_name, *args, &block)
116
+ cached_method(method_name, args).invalidate(&block)
117
+ end
118
+
119
+ def method_value_cached?(method_name, *args)
120
+ cached_method(method_name, args).cached?
121
+ end
122
+
123
+ def update_cached_method(method_name, *args, &block)
124
+ cached_method(method_name, args).update(&block)
125
+ end
126
+
127
+ def without_method_cache(&block)
128
+ MethodCache.disable(&block)
129
+ end
130
+
131
+ private
132
+
133
+ def cached_method(method_name, args)
134
+ if self.kind_of?(Class) or self.kind_of?(Module)
135
+ proxy = cached_class_methods(method_name)
136
+ else
137
+ proxy = self.class.send(:cached_instance_methods, method_name)
138
+ end
139
+ raise "method '#{method_name}' not cached" unless proxy
140
+ proxy.bind(self, args)
141
+ end
142
+ end
143
+
144
+ module MethodAdded
145
+ def method_added(method_name)
146
+ if proxy = cached_instance_methods(method_name)
147
+ cache_method(method_name, proxy)
148
+ end
149
+ super
150
+ end
151
+ end
152
+
153
+ module SingletonMethodAdded
154
+ def singleton_method_added(method_name)
155
+ if proxy = cached_class_methods(method_name)
156
+ cache_class_method(method_name, proxy)
157
+ end
158
+ super
159
+ end
160
+ end
161
+
162
+ module ModuleAdded
163
+ def extended(mod)
164
+ mod.extend(MethodCache)
165
+ cached_module_methods.each do |method_name, proxy|
166
+ mod.cache_class_method(method_name, proxy)
167
+ end
168
+ end
169
+
170
+ def included(mod)
171
+ mod.extend(MethodCache)
172
+ cached_module_methods.each do |method_name, proxy|
173
+ mod.cache_method(method_name, proxy)
174
+ end
175
+ end
176
+ end
177
+ end
@@ -0,0 +1,53 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{method_cache}
8
+ s.version = "0.6.3"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Justin Balthrop"]
12
+ s.date = %q{2010-04-13}
13
+ s.description = %q{Simple memcache-based memoization library for Ruby}
14
+ s.email = %q{code@justinbalthrop.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ "LICENSE",
21
+ "README.rdoc",
22
+ "Rakefile",
23
+ "VERSION.yml",
24
+ "lib/method_cache.rb",
25
+ "lib/method_cache/proxy.rb",
26
+ "method_cache.gemspec",
27
+ "test/method_cache_test.rb",
28
+ "test/test_helper.rb"
29
+ ]
30
+ s.homepage = %q{http://github.com/ninjudd/method_cache}
31
+ s.rdoc_options = ["--charset=UTF-8"]
32
+ s.require_paths = ["lib"]
33
+ s.rubygems_version = %q{1.3.5}
34
+ s.summary = %q{Simple memcache-based memoization library for Ruby}
35
+ s.test_files = [
36
+ "test/method_cache_test.rb",
37
+ "test/test_helper.rb"
38
+ ]
39
+
40
+ if s.respond_to? :specification_version then
41
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
42
+ s.specification_version = 3
43
+
44
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
45
+ s.add_runtime_dependency(%q<memcache>, [">= 1.0.0"])
46
+ else
47
+ s.add_dependency(%q<memcache>, [">= 1.0.0"])
48
+ end
49
+ else
50
+ s.add_dependency(%q<memcache>, [">= 1.0.0"])
51
+ end
52
+ end
53
+
@@ -0,0 +1,131 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ class Foo
4
+ extend MethodCache
5
+
6
+ def foo(i)
7
+ @i ||= 0
8
+ @i += i
9
+ end
10
+ cache_method :foo
11
+
12
+ cache_method :bar
13
+ def bar
14
+ @i ||= 0
15
+ @i += 1
16
+ end
17
+
18
+ @@i = 0
19
+ def baz(i)
20
+ @@i += i
21
+ end
22
+ cache_method :baz, :cache => :remote
23
+ end
24
+
25
+ module Bar
26
+ extend MethodCache
27
+
28
+ cache_method :foo
29
+ def foo(i)
30
+ @i ||= 0
31
+ @i += i
32
+ end
33
+ end
34
+
35
+ class Baz
36
+ include Bar
37
+ end
38
+
39
+ class TestMethodCache < Test::Unit::TestCase
40
+ should 'cache methods locally' do
41
+ a = Foo.new
42
+ f1 = a.foo(1)
43
+ f2 = a.foo(2)
44
+
45
+ assert_equal 1, f1
46
+ assert_equal 3, f2
47
+
48
+ assert f1 == a.foo(1)
49
+ assert f1 != f2
50
+ assert f2 == a.foo(2)
51
+
52
+ b = a.bar
53
+ assert b == a.bar
54
+ assert b == a.bar
55
+ end
56
+
57
+ should 'disable method_cache' do
58
+ a = Foo.new
59
+ f1 = a.foo(1)
60
+
61
+ f2 = a.without_method_cache do
62
+ a.foo(1)
63
+ end
64
+
65
+ f3 = MethodCache.disable do
66
+ a.foo(1)
67
+ end
68
+
69
+ assert f1 != f2
70
+ assert f1 != f3
71
+ assert f2 != f3
72
+ end
73
+
74
+ should 'cache methods remotely' do
75
+ a = Foo.new
76
+ b1 = a.baz(1)
77
+ b2 = a.baz(2)
78
+
79
+ assert_equal 1, b1
80
+ assert_equal 3, b2
81
+
82
+ assert b1 == a.baz(1)
83
+ assert b1 != b2
84
+ assert b2 == a.baz(2)
85
+ end
86
+
87
+ should 'cache methods for mixins' do
88
+ a = Baz.new
89
+
90
+ assert_equal 1, a.foo(1)
91
+ assert_equal 1, a.foo(1)
92
+ assert_equal 3, a.foo(2)
93
+ assert_equal 3, a.foo(2)
94
+ end
95
+
96
+ should 'invalidate cached method' do
97
+ a = Foo.new
98
+
99
+ assert_equal 1, a.foo(1)
100
+ assert_equal 3, a.foo(2)
101
+
102
+ a.invalidate_cached_method(:foo, 1)
103
+
104
+ assert_equal 4, a.foo(1)
105
+ assert_equal 3, a.foo(2)
106
+ end
107
+
108
+ should 'use consistent local keys' do
109
+ a = Foo.new
110
+ o = Object.new
111
+ a_hash = a.hash
112
+ o_hash = o.hash
113
+
114
+ 5.times do
115
+ key = a.send(:cached_method, :bar, [{'a' => 3, 'b' => [5,6], 'c' => o}, [1,nil,{:o => o}]]).key
116
+ assert_equal "m|:bar|Foo-#{a_hash}|{'a'=3,'b'=[5,6],'c'=Object-#{o_hash}}|[1,nil,{:o=Object-#{o_hash}}]", key
117
+ end
118
+ end
119
+
120
+ should 'use consistent remote keys' do
121
+ a = Foo.new
122
+ o = Object.new
123
+ a_hash = Marshal.dump(a).hash
124
+ o_hash = Marshal.dump(o).hash
125
+
126
+ 5.times do
127
+ key = a.send(:cached_method, :baz, [{:a => 3, :b => [5,6], :c => o}, [false,true,{:o => o}]]).key
128
+ assert_equal "m|:baz|Foo-#{a_hash}|{:a=3,:b=[5,6],:c=Object-#{o_hash}}|[false,true,{:o=Object-#{o_hash}}]", key
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,13 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+ require 'mocha'
5
+ require 'pp'
6
+
7
+ $LOAD_PATH.unshift File.dirname(__FILE__) + "/../lib"
8
+ $LOAD_PATH.unshift File.dirname(__FILE__) + "/../../memcache/lib"
9
+ $LOAD_PATH.unshift File.dirname(__FILE__) + "/../../cache_version/lib"
10
+ require 'method_cache'
11
+
12
+ class Test::Unit::TestCase
13
+ end
metadata ADDED
@@ -0,0 +1,74 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: method_cache
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.6.3
5
+ platform: ruby
6
+ authors:
7
+ - Justin Balthrop
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-04-13 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: memcache
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.0.0
24
+ version:
25
+ description: Simple memcache-based memoization library for Ruby
26
+ email: code@justinbalthrop.com
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - LICENSE
33
+ - README.rdoc
34
+ files:
35
+ - LICENSE
36
+ - README.rdoc
37
+ - Rakefile
38
+ - VERSION.yml
39
+ - lib/method_cache.rb
40
+ - lib/method_cache/proxy.rb
41
+ - method_cache.gemspec
42
+ - test/method_cache_test.rb
43
+ - test/test_helper.rb
44
+ has_rdoc: true
45
+ homepage: http://github.com/ninjudd/method_cache
46
+ licenses: []
47
+
48
+ post_install_message:
49
+ rdoc_options:
50
+ - --charset=UTF-8
51
+ require_paths:
52
+ - lib
53
+ required_ruby_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: "0"
58
+ version:
59
+ required_rubygems_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: "0"
64
+ version:
65
+ requirements: []
66
+
67
+ rubyforge_project:
68
+ rubygems_version: 1.3.5
69
+ signing_key:
70
+ specification_version: 3
71
+ summary: Simple memcache-based memoization library for Ruby
72
+ test_files:
73
+ - test/method_cache_test.rb
74
+ - test/test_helper.rb