method_cacher 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ InstalledFiles
7
+ _yardoc
8
+ coverage
9
+ doc/
10
+ lib/bundler/man
11
+ pkg
12
+ rdoc
13
+ spec/reports
14
+ test/tmp
15
+ test/version_tmp
16
+ tmp
17
+ /.idea/
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/.rvmrc ADDED
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # This is an RVM Project .rvmrc file, used to automatically load the ruby
4
+ # development environment upon cd'ing into the directory
5
+
6
+ # First we specify our desired <ruby>[@<gemset>], the @gemset name is optional.
7
+ environment_id="ruby-1.9.2-p180@method_cacher"
8
+
9
+ #
10
+ # Uncomment following line if you want options to be set only for given project.
11
+ #
12
+ # PROJECT_JRUBY_OPTS=( --1.9 )
13
+ #
14
+ # The variable PROJECT_JRUBY_OPTS requires the following to be run in shell:
15
+ #
16
+ # chmod +x ${rvm_path}/hooks/after_use_jruby_opts
17
+ #
18
+
19
+ #
20
+ # First we attempt to load the desired environment directly from the environment
21
+ # file. This is very fast and efficient compared to running through the entire
22
+ # CLI and selector. If you want feedback on which environment was used then
23
+ # insert the word 'use' after --create as this triggers verbose mode.
24
+ #
25
+ if [[ -d "${rvm_path:-$HOME/.rvm}/environments" \
26
+ && -s "${rvm_path:-$HOME/.rvm}/environments/$environment_id" ]]
27
+ then
28
+ \. "${rvm_path:-$HOME/.rvm}/environments/$environment_id"
29
+
30
+ if [[ -s "${rvm_path:-$HOME/.rvm}/hooks/after_use" ]]
31
+ then
32
+ . "${rvm_path:-$HOME/.rvm}/hooks/after_use"
33
+ fi
34
+ else
35
+ # If the environment file has not yet been created, use the RVM CLI to select.
36
+ if ! rvm --create use "$environment_id"
37
+ then
38
+ echo "Failed to create RVM environment '${environment_id}'."
39
+ return 1
40
+ fi
41
+ fi
42
+
43
+ #
44
+ # If you use an RVM gemset file to install a list of gems (*.gems), you can have
45
+ # it be automatically loaded. Uncomment the following and adjust the filename if
46
+ # necessary.
47
+ #
48
+ # filename=".gems"
49
+ # if [[ -s "$filename" ]]
50
+ # then
51
+ # rvm gemset import "$filename" | grep -v already | grep -v listed | grep -v complete | sed '/^$/d'
52
+ # fi
53
+
54
+ # If you use bundler, this might be useful to you:
55
+ # if [[ -s Gemfile ]] && ! command -v bundle >/dev/null
56
+ # then
57
+ # printf "The rubygem 'bundler' is not installed. Installing it now.\n"
58
+ # gem install bundler
59
+ # fi
60
+ # if [[ -s Gemfile ]] && command -v bundle
61
+ # then
62
+ # bundle install
63
+ # fi
64
+
65
+ if [[ $- == *i* ]] # check for interactive shells
66
+ then
67
+ echo "Using: $(tput setaf 2)$GEM_HOME$(tput sgr0)" # show the user the ruby and gemset they are using in green
68
+ else
69
+ echo "Using: $GEM_HOME" # don't use colors in interactive shells
70
+ fi
71
+
data/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ ## v0.0.1
2
+
3
+ + initial release
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'http://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in method_cacher.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,50 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ method_cacher (0.0.1.alpha)
5
+
6
+ GEM
7
+ remote: http://rubygems.org/
8
+ specs:
9
+ activemodel (3.0.11)
10
+ activesupport (= 3.0.11)
11
+ builder (~> 2.1.2)
12
+ i18n (~> 0.5.0)
13
+ activesupport (3.0.11)
14
+ archive-tar-minitar (0.5.2)
15
+ builder (2.1.2)
16
+ columnize (0.3.6)
17
+ diff-lcs (1.1.3)
18
+ i18n (0.5.0)
19
+ linecache19 (0.5.12)
20
+ ruby_core_source (>= 0.1.4)
21
+ rspec (2.7.0)
22
+ rspec-core (~> 2.7.0)
23
+ rspec-expectations (~> 2.7.0)
24
+ rspec-mocks (~> 2.7.0)
25
+ rspec-core (2.7.1)
26
+ rspec-expectations (2.7.0)
27
+ diff-lcs (~> 1.1.2)
28
+ rspec-mocks (2.7.0)
29
+ ruby-debug-base19 (0.11.25)
30
+ columnize (>= 0.3.1)
31
+ linecache19 (>= 0.5.11)
32
+ ruby_core_source (>= 0.1.4)
33
+ ruby-debug19 (0.11.6)
34
+ columnize (>= 0.3.1)
35
+ linecache19 (>= 0.5.11)
36
+ ruby-debug-base19 (>= 0.11.19)
37
+ ruby_core_source (0.1.5)
38
+ archive-tar-minitar (>= 0.5.2)
39
+ supermodel (0.1.6)
40
+ activemodel (~> 3.0.0)
41
+
42
+ PLATFORMS
43
+ ruby
44
+
45
+ DEPENDENCIES
46
+ activesupport
47
+ method_cacher!
48
+ rspec
49
+ ruby-debug19
50
+ supermodel
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (C) 2011 Yaron Walfish
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ this software and associated documentation files (the "Software"), to deal in
5
+ the Software without restriction, including without limitation the rights to
6
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7
+ of the Software, and to permit persons to whom the Software is furnished to do
8
+ so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,85 @@
1
+ # Method Cacher
2
+
3
+ Wraps specified methods with a mechanism that caches the return values.
4
+
5
+ __Features:__
6
+
7
+ + Can cache both instance and singleton (i.e. class) methods.
8
+ + Differentiates among calls to a method with different arguments.
9
+ + Generates methods to provide access the original uncached methods.
10
+ + Generates methods to clear the cache of each cached method.
11
+ + To specify methods to be cached, just call `caches_method :foo, :bar ...` before or after the actual definition of `foo` and `bar`.
12
+ + When used with Rails, it automatically uses the cache store configured in Rails.
13
+
14
+ ## Installation
15
+
16
+ Add the `method_cacher` gem your Gemfile and run the `bundle` command to install it.
17
+
18
+ ```ruby
19
+ gem 'method_cacher'
20
+ ```
21
+
22
+ __Requires Ruby 1.9.2 or later.__
23
+
24
+ ## Configuration
25
+
26
+ ...
27
+
28
+ ## Usage
29
+
30
+ Include the method cacher module in the class whose methods you wish to cache.
31
+
32
+ ```ruby
33
+ include MethodCacher::Base
34
+ ```
35
+
36
+ The module is included automatically for ActiveRecord when used in Rails.
37
+
38
+ Call `cache_method` from within the class definition, listing the names of the methods that are to be cached.
39
+
40
+ ```ruby
41
+ cache_method :instance_foo, :instance_bar, singleton: [:singleton_foo, :singleton_bar], obj_key: proc { |obj| obj.obj_key }
42
+ ```
43
+
44
+ Instance methods are specified as symbol arguments.
45
+
46
+ __Options:__
47
+
48
+ + :singleton - Singleton methods to be cached are specified in an array of symbols passed through this option.
49
+ + :obj\_key - A _proc_ that accepts the cached object as a single parameter. This _proc_ should return a value identifying this object.
50
+ If this option is not specified, the object key defaults to the value returned by a method named _id_, which is convenient for usage
51
+ with ActiveRecord objects.
52
+
53
+ `cache_method` can take any number of instance or singleton methods at once.
54
+
55
+ Subsequent calls to `cache_method` are possible in order to specify additional methods to be cached.
56
+ Specifying :obj\_id multiple times results in the last one being used for the class.
57
+
58
+ `cache_method` can be called _before_ or _after_ the specified methods are defined.
59
+
60
+ ```ruby
61
+ cache_method :foo # this works
62
+ def foo
63
+ ...
64
+ end
65
+
66
+ def bar
67
+ ...
68
+ end
69
+ cache_method :bar # this also works
70
+ ```
71
+
72
+ `cache_method` replaces each specified method with a cached version.
73
+
74
+ The cached versions take the same arguments as the originals, with different arguments caching separately.
75
+
76
+ Each of the original methods is made accessible through the alias `uncached_` followed by the method's name.
77
+ E.g. if the method is `foo`, the original is aliased as `uncached_foo`.
78
+
79
+ `cache_method` also adds methods to clear the cache of each cached method.
80
+ These methods are named `clear_cache_for_` followed by the cached method's name.
81
+ The clear cache methods take identical arguments as their respective original and cached methods, and only
82
+ clear the cache for the given set of arguments.
83
+
84
+ So for example, issuing `clear_cache_for_foo('a')`, would clear the cache for a call to `foo('a')`
85
+ but not to `foo('b')`.
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ require 'rspec/core/rake_task'
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ task default: :spec
@@ -0,0 +1,4 @@
1
+ require "rubygems"
2
+ require "method_cacher/version"
3
+ require "method_cacher/base"
4
+ require "method_cacher/railtie" if defined? Rails
@@ -0,0 +1,159 @@
1
+ require 'active_support/concern'
2
+ require 'active_support/core_ext'
3
+ require 'set'
4
+
5
+ module MethodCacher
6
+ # An object that holds configuration vars for MethodCacher
7
+ class Configuration
8
+ def caching_strategy= strategy
9
+ @caching_strategy = strategy
10
+ end
11
+
12
+ def caching_strategy
13
+ if @caching_strategy
14
+ @caching_strategy
15
+ elsif defined? Rails
16
+ Rails.cache # default to the rails caching strategy if it's a Rails app
17
+ end
18
+ end
19
+ end
20
+
21
+ @@config = MethodCacher::Configuration.new
22
+ mattr_reader :config
23
+
24
+ # Gives a block configuration style like that of Rails.
25
+ def self.configure(&block)
26
+ if block_given?
27
+ if block.arity == 1
28
+ # allows this style config: MethodCacher.configure do |config| config.variable = ... end
29
+ yield config
30
+ else
31
+ # allows this style config: MethodCacher.configure do config.variable = ... end
32
+ # While this option is more elegant because it doesn't require a block variable, it loses access to
33
+ # methods defined in the calling context because the context switches to that of the self object.
34
+ instance_eval &block
35
+ end
36
+ end
37
+ end
38
+
39
+ module Base
40
+ extend ActiveSupport::Concern
41
+
42
+ # default object key proc
43
+ OBJECT_KEY_PROC_DEFAULT = lambda { |obj| obj.id }
44
+
45
+ module ClassMethods
46
+
47
+ attr_accessor :methods_to_be_cached, :cached_methods, :obj_key
48
+ attr_accessor :singleton_methods_to_be_cached, :singleton_cached_methods
49
+
50
+ def caches_method(*method_names)
51
+ initialize_variables
52
+
53
+ # process parameters
54
+ options = method_names.extract_options!
55
+
56
+ self.obj_key = options[:obj_key] || self.obj_key # if obj_key is given in the option, replace it with what's given
57
+ self.methods_to_be_cached += method_names
58
+ self.singleton_methods_to_be_cached += [options[:singleton]].flatten
59
+
60
+ # Cache all currently defined instance methods that are given in the parameters.
61
+ self.methods_to_be_cached.clone.each do |method_name|
62
+ add_cached_method(method_name) if (private_instance_methods + protected_instance_methods + public_instance_methods).include?(method_name)
63
+ end
64
+
65
+ # Cache all currently defined singleton methods given in the parameters.
66
+ self.singleton_methods_to_be_cached.clone.each do |method_name|
67
+ singleton_add_cached_method(method_name) if singleton_methods.include?(method_name)
68
+ end
69
+ end
70
+
71
+ # This callback adds caching to the methods that were not defined yet when caches_method was called.
72
+ def method_added(method_name)
73
+ super
74
+ initialize_variables
75
+ add_cached_method(method_name) if self.methods_to_be_cached.include?(method_name) #and public_instance_methods.include?(method_name) # commented section is for testing the related spec
76
+ end
77
+
78
+ # Same as method_added but for singleton methods.
79
+ def singleton_method_added(method_name)
80
+ super
81
+ initialize_variables
82
+ singleton_add_cached_method(method_name) if self.singleton_methods_to_be_cached.include?(method_name)
83
+ end
84
+
85
+ private
86
+
87
+ # Initialize class instance variables for instance method caching.
88
+ def initialize_variables
89
+ self.obj_key ||= OBJECT_KEY_PROC_DEFAULT # identifies the object of this class, defaults to the object's id if defined
90
+
91
+ self.methods_to_be_cached ||= Set.new # stores the names of the methods designated to be cached
92
+ self.cached_methods ||= Set.new # stores the methods that are cached
93
+
94
+ # initialize class instance variables for singleton method caching
95
+ self.singleton_methods_to_be_cached ||= Set.new # stores the names of the methods designated to be cached
96
+ self.singleton_cached_methods ||= Set.new # stores the methods that are cached
97
+ end
98
+
99
+ # Creates the key used to cache a singleton method of the object.
100
+ def singleton_cached_method_key(method_name, *args)
101
+ [self.name, method_name, *args]
102
+ end
103
+
104
+ # Adds code to the class to cache the given instance method.
105
+ def add_cached_method(method_name)
106
+ self.methods_to_be_cached.delete(method_name)
107
+ self.cached_methods <<= method_name
108
+
109
+ # TODO: Make cached methods have the same accessibility (i.e. public/private/protected) as the original method.
110
+ class_eval <<-END_EVAL, __FILE__, __LINE__ + 1
111
+ alias :uncached_#{method_name} :#{method_name}
112
+ def #{method_name}(*args)
113
+ key = cached_method_key(:#{method_name}, *args)
114
+ key.nil? ? uncached_#{method_name}(*args) : MethodCacher.config.caching_strategy.fetch(key) { uncached_#{method_name}(*args) } # cache only for non-nil keys
115
+ end
116
+
117
+ def clear_cache_for_#{method_name}(*args)
118
+ MethodCacher.config.caching_strategy.delete(cached_method_key(:#{method_name}, *args))
119
+ end
120
+ END_EVAL
121
+ end
122
+
123
+ # Adds code to the class to cache the given singleton method.
124
+ def singleton_add_cached_method(method_name)
125
+ self.singleton_methods_to_be_cached.delete(method_name)
126
+ self.singleton_cached_methods <<= method_name
127
+ class_eval <<-END_EVAL, __FILE__, __LINE__ + 1
128
+ class <<self
129
+ alias :uncached_#{method_name} :#{method_name}
130
+ def #{method_name}(*args)
131
+ MethodCacher.config.caching_strategy.fetch(singleton_cached_method_key(:#{method_name}, *args)) { uncached_#{method_name}(*args) }
132
+ end
133
+
134
+ def clear_cache_for_#{method_name}(*args)
135
+ MethodCacher.config.caching_strategy.delete(singleton_cached_method_key(:#{method_name}, *args))
136
+ end
137
+ end
138
+ END_EVAL
139
+ end
140
+
141
+ end
142
+
143
+ module InstanceMethods
144
+
145
+ # A helper that clears the cache for all the cached methods of this object that are argument-less.
146
+ def clear_method_cache
147
+ self.class.cached_methods.each { |method_name| MethodCacher.config.caching_strategy.delete(cached_method_key(method_name)) }
148
+ end
149
+
150
+ private
151
+
152
+ # Creates the key used to cache a method of the object.
153
+ def cached_method_key(method_name, *args)
154
+ obj_key = self.class.obj_key.call(self)
155
+ [self.class.name, obj_key, method_name, *args]
156
+ end
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,10 @@
1
+ module MethodCacher
2
+ class Railtie < Rails::Railtie
3
+ initializer 'methodcacher.model_additions' do
4
+ ActiveSupport.on_load :active_record do
5
+ include Base
6
+ end
7
+ end
8
+ end
9
+ end
10
+
@@ -0,0 +1,3 @@
1
+ module MethodCacher
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__) # $: is the load path
3
+ require 'method_cacher/version'
4
+
5
+ Gem::Specification.new do |gem|
6
+ gem.authors = ["Yaron Walfish"]
7
+ gem.email = ["yaronw@yaronw.com"]
8
+ gem.description = %q{Caches Instance and Singleton Methods}
9
+ gem.summary = %q{Wraps specified instance and singleton methods with the Rails caching mechanism.}
10
+ gem.homepage = ""
11
+
12
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
13
+ gem.files = `git ls-files`.split("\n")
14
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
15
+ gem.name = "method_cacher"
16
+ gem.require_paths = ["lib"]
17
+ gem.version = MethodCacher::VERSION
18
+
19
+ gem.add_development_dependency 'rspec'
20
+ gem.add_development_dependency 'supermodel'
21
+ gem.add_development_dependency 'activesupport'
22
+ gem.add_development_dependency 'ruby-debug19'
23
+ end
@@ -0,0 +1,276 @@
1
+ require 'spec_helper'
2
+ require 'active_support/cache'
3
+ require 'method_cacher/base_test_helper'
4
+
5
+ describe MethodCacher::Configuration do
6
+ describe "yield style configuration" do
7
+ it "should assign configuration variables" do
8
+ MethodCacher.configure do |c|
9
+ c.caching_strategy = "test1"
10
+ end
11
+
12
+ MethodCacher.config.caching_strategy.should == "test1"
13
+ end
14
+ end
15
+
16
+ describe "eval style configuration" do
17
+ it "should assign configuration variables" do
18
+ MethodCacher.configure do
19
+ config.caching_strategy = "test2"
20
+ end
21
+
22
+ MethodCacher.config.caching_strategy.should == "test2"
23
+ end
24
+ end
25
+ end
26
+
27
+ describe MethodCacher::Base do
28
+ before (:all) do
29
+ MethodCacher.configure do
30
+ config.caching_strategy = ActiveSupport::Cache.lookup_store(:memory_store)
31
+ end
32
+ end
33
+
34
+ describe "#caches_method" do
35
+ context "when specified BEFORE the declaration of the methods to be cached" do
36
+ it "should cache the INSTANCE method associated with the names given in the arguments" do
37
+ obj = FirstClass.new
38
+ [:foo1, :foo2].each do |name|
39
+ lambda { obj.send(name) }.should be_twice_the_same # verify that original function is not called twice
40
+ obj.send(name).should == Dummy.current_value # verify that the right value is actually cached
41
+ end
42
+ end
43
+
44
+ it "should not cache INSTANCE methods whose names are not given in the arguments" do
45
+ obj = FirstClass.new
46
+ lambda { obj.foo3 }.should_not be_twice_the_same
47
+ end
48
+
49
+ it "should cache the SINGLETON methods associated with the names given in the :singleton option array" do
50
+ [:bar1, :bar2].each do |name|
51
+ lambda { FirstClass.send(name) }.should be_twice_the_same
52
+ FirstClass.send(name).should == Dummy.current_value
53
+ end
54
+ end
55
+
56
+ it "should not cache SINGLETON methods whose names are not given in the :singleton option" do
57
+ lambda { FirstClass.bar3 }.should_not be_twice_the_same
58
+ end
59
+
60
+ it "should cache INSTANCE methods given in separate calls" do
61
+ (1..4).to_a.collect { |x| :"foo#{x}" }.each do |name|
62
+ obj = SeventhClass.new
63
+ lambda { obj.send(name) }.should be_twice_the_same
64
+ end
65
+ end
66
+
67
+ it "should cache SINGLETON methods given in separate calls" do
68
+ (1..4).to_a.collect { |x| :"bar#{x}" }.each do |name|
69
+ lambda { SeventhClass.send(name) }.should be_twice_the_same
70
+ end
71
+ end
72
+
73
+ it "should cache protected INSTANCE methods" do
74
+ obj = EighthClass.new
75
+ debugger
76
+ lambda { obj.send(:foo1) }.should be_twice_the_same
77
+ end
78
+
79
+ it "should cache private INSTANCE methods" do
80
+ obj = EighthClass.new
81
+ lambda { obj.send(:foo3) }.should be_twice_the_same
82
+ end
83
+ end
84
+
85
+ context "when specified AFTER the declaration of the methods to be cached" do
86
+ it "should cache the INSTANCE method associated with the names given in the arguments" do
87
+ obj = SecondClass.new
88
+ [:foo1, :foo2].each do |name|
89
+ lambda { obj.send(name) }.should be_twice_the_same
90
+ obj.send(name).should == Dummy.current_value
91
+ end
92
+ end
93
+
94
+ it "should not cache INSTANCE methods whose names are not given in the arguments" do
95
+ obj = SecondClass.new
96
+ lambda { obj.foo3 }.should_not be_twice_the_same
97
+ end
98
+
99
+ it "should cache the SINGLETON methods associated with the names given in the :singleton option array" do
100
+ [:bar1, :bar2].each do |name|
101
+ lambda { SecondClass.send(name) }.should be_twice_the_same
102
+ SecondClass.send(name).should == Dummy.current_value
103
+ end
104
+ end
105
+
106
+ it "should not cache SINGLETON methods whose names are not given in the :singleton option" do
107
+ lambda { SecondClass.bar3 }.should_not be_twice_the_same
108
+ end
109
+
110
+ it "should cache INSTANCE methods given in separate calls" do
111
+ (5..8).to_a.collect { |x| :"foo#{x}" }.each do |name|
112
+ obj = SeventhClass.new
113
+ lambda { obj.send(name) }.should be_twice_the_same
114
+ end
115
+ end
116
+
117
+ it "should cache SINGLETON methods given in separate calls" do
118
+ (5..8).to_a.collect { |x| :"bar#{x}" }.each do |name|
119
+ lambda { SeventhClass.send(name) }.should be_twice_the_same
120
+ end
121
+ end
122
+
123
+ it "should cache protected INSTANCE methods" do
124
+ obj = EighthClass.new
125
+ lambda { obj.send(:foo2) }.should be_twice_the_same
126
+ end
127
+
128
+ it "should cache private INSTANCE methods" do
129
+ obj = EighthClass.new
130
+ lambda { obj.send(:foo4) }.should be_twice_the_same
131
+ end
132
+ end
133
+
134
+ describe "cache key mechanism for INSTANCE methods" do
135
+ context "when caching an INSTANCE method of one object" do
136
+ it "should have different sets of arguments cache separately" do
137
+ obj = FirstClass.new
138
+ lambda { obj.foo1(1,2) }.should be_twice_the_same
139
+ lambda { obj.foo1(1,3) }.should be_twice_the_same
140
+ obj.foo1(1,3).should_not == obj.foo1(1,2)
141
+ end
142
+ end
143
+
144
+ context "when caching the same INSTANCE method with identical parameter sets and of the same class" do
145
+ it "should have objects whose :obj_key proc evaluates differently cache separately" do
146
+ obj1 = ThirdClass.new
147
+ obj1.obj_key = 1
148
+ obj2 = ThirdClass.new
149
+ obj2.obj_key = 2
150
+
151
+ obj1.foo # cache methods
152
+ obj2.foo
153
+
154
+ obj2.foo.should_not == obj1.foo
155
+ end
156
+
157
+ it "should have objects whose :obj_key proc evaluates the same cache one value" do
158
+ obj1 = ThirdClass.new
159
+ obj1.obj_key = 1
160
+ obj2 = ThirdClass.new
161
+ obj2.obj_key = 1
162
+
163
+ obj1.foo # cache methods
164
+ obj2.foo
165
+
166
+ obj2.foo.should == obj1.foo
167
+ end
168
+ end
169
+
170
+ context "when caching INSTANCE methods of identical names with identical parameter sets, and identical :obj_key proc values" do
171
+ it "should have different classes cache separately" do
172
+ obj1 = ThirdClass.new
173
+ obj1.obj_key = 1
174
+ obj2 = FourthClass.new
175
+ obj2.obj_key = 1
176
+
177
+ obj1.foo # cache methods
178
+ obj2.foo
179
+
180
+ obj2.foo.should_not == obj1.foo
181
+ end
182
+ end
183
+
184
+ it "should default the :obj_key option to the instance method named 'id'" do
185
+ # test that the :obj_key mechanism is working for the default 'id' class
186
+ obj1 = FifthClass.new
187
+ obj1.id = 1
188
+ obj2 = FifthClass.new
189
+ obj2.id = 2
190
+
191
+ obj1.foo # cache methods
192
+ obj2.foo
193
+
194
+ obj2.foo.should_not == obj1.foo
195
+ end
196
+
197
+ it "should raise an exception if neither an :obj_key option is given nor the default fallback :id method is defined" do
198
+ obj = SixthClass.new
199
+ lambda { obj.foo }.should raise_error(NoMethodError)
200
+ end
201
+ end
202
+
203
+ describe "cache key mechanism for SINGLETON methods" do
204
+ context "when caching a SINGLETON method of one class" do
205
+ it "should have different sets of arguments cache separately" do
206
+ lambda { FirstClass.bar1(1,2) }.should be_twice_the_same
207
+ lambda { FirstClass.bar1(1,3) }.should be_twice_the_same
208
+ FirstClass.bar1(1,3).should_not == FirstClass.bar1(1,2)
209
+ end
210
+ end
211
+
212
+ context "when caching SINGLETON methods of identical names with identical parameter sets" do
213
+ it "should have different classes cache separately" do
214
+ ThirdClass.bar # cache methods
215
+ FourthClass.bar
216
+
217
+ FourthClass.bar.should_not == ThirdClass.bar
218
+ end
219
+ end
220
+ end
221
+
222
+ describe "dynamic methods creation" do
223
+ context "when caching an INSTANCE method" do
224
+ it "should include a method named uncached_[name of original method] that calls the original uncached method" do
225
+ obj = FirstClass.new
226
+
227
+ # works for methods without arguments
228
+ lambda { obj.uncached_foo1 }.should_not be_twice_the_same # uncached function should not cache # for testing this assertion: def uncached_#{method_name} \n rand 1000000000 \n end
229
+ obj.uncached_foo1.should == Dummy.current_value # uncached function should call original method # for testing this assertion: def uncached_#{method_name} \n 1 \n end
230
+
231
+ # works for methods with arguments
232
+ lambda { obj.uncached_foo1(1,2) }.should_not be_twice_the_same # uncached function should not cache
233
+ obj.uncached_foo1(1,2).should == Dummy.current_value(1,2) # uncached function should call original method
234
+ end
235
+
236
+ it "should include a method named clear_cache_for_[name of original method] that clears the cache for a specific method with specific arguments" do
237
+ obj = FirstClass.new
238
+
239
+ # works for methods without arguments
240
+ val = obj.foo1
241
+ obj.clear_cache_for_foo1
242
+ obj.foo1.should_not == val
243
+
244
+ # works for methods with arguments
245
+ val = obj.foo1(1,2)
246
+ obj.clear_cache_for_foo1(1,2)
247
+ obj.foo1(1,2).should_not == val
248
+ end
249
+ end
250
+
251
+ context "when caching a SINGLETON method" do
252
+ it "should include a method that calls the original uncached method" do
253
+ # works for methods without arguments
254
+ lambda { FirstClass.uncached_bar1 }.should_not be_twice_the_same # should not cache
255
+ FirstClass.uncached_bar1.should == Dummy.current_value # uncached function should call original method
256
+
257
+ # works for methods with arguments
258
+ lambda { FirstClass.uncached_bar1(1,2) }.should_not be_twice_the_same # should not cache
259
+ FirstClass.uncached_bar1(1,2).should == Dummy.current_value(1,2) # uncached function should call original method
260
+ end
261
+
262
+ it "should include a method named clear_cache_for_[name of original method] that clears the cache for a specific method with specific arguments" do
263
+ # works for methods without arguments
264
+ val = FirstClass.bar1
265
+ FirstClass.clear_cache_for_bar1
266
+ FirstClass.bar1.should_not == val
267
+
268
+ # works for methods with arguments
269
+ val = FirstClass.bar1(1,2)
270
+ FirstClass.clear_cache_for_bar1(1,2)
271
+ FirstClass.bar1(1,2).should_not == val
272
+ end
273
+ end
274
+ end
275
+ end
276
+ end
@@ -0,0 +1,264 @@
1
+ # Provides a function that returns a different value with each call and also bases the return value on the arguments given.
2
+ module Dummy
3
+ @@counter = 0
4
+ def self.function(*args)
5
+ @@counter += 1
6
+ current_value(*args)
7
+ end
8
+
9
+ # returns the current value of function without changin it
10
+ def self.current_value(*args)
11
+ "#{@@counter} #{args}"
12
+ end
13
+ end
14
+
15
+ # for testing method caching when caches_method is called before the methods are defined
16
+ class FirstClass
17
+ include MethodCacher::Base
18
+
19
+ def id
20
+ object_id
21
+ end
22
+
23
+ caches_method :foo1, :foo2, singleton: [:bar1, :bar2] #, :obj_key => proc { |s| s.object_id }
24
+ # test instance methods.
25
+ def foo1(*args)
26
+ Dummy.function(*args)
27
+ end
28
+
29
+ def foo2(*args)
30
+ Dummy.function(*args)
31
+ end
32
+
33
+ def foo3(*args)
34
+ Dummy.function(*args)
35
+ end
36
+
37
+ class <<self
38
+ # test singleton (i.e. class) methods.
39
+ def bar1(*args)
40
+ Dummy.function(*args)
41
+ end
42
+
43
+ def bar2(*args)
44
+ Dummy.function(*args)
45
+ end
46
+
47
+ def bar3(*args)
48
+ Dummy.function(*args)
49
+ end
50
+ end
51
+ end
52
+
53
+ # for testing method caching when caches_method is called after the methods are defined
54
+ class SecondClass
55
+ include MethodCacher::Base
56
+
57
+ def id
58
+ object_id
59
+ end
60
+
61
+ # test instance methods.
62
+ def foo1
63
+ Dummy.function
64
+ end
65
+
66
+ def foo2
67
+ Dummy.function
68
+ end
69
+
70
+ def foo3
71
+ Dummy.function
72
+ end
73
+
74
+ class <<self
75
+ # test singleton (i.e. class) methods.
76
+ def bar1
77
+ Dummy.function
78
+ end
79
+
80
+ def bar2
81
+ Dummy.function
82
+ end
83
+
84
+ def bar3
85
+ Dummy.function
86
+ end
87
+ end
88
+
89
+ caches_method :foo1, :foo2, singleton: [:bar1, :bar2]
90
+ end
91
+
92
+ # for testing :obj_key option and cache differentiation
93
+ class ThirdClass
94
+ include MethodCacher::Base
95
+
96
+ attr_accessor :obj_key
97
+
98
+ caches_method :foo, singleton: :bar, obj_key: proc { |obj| obj.obj_key }
99
+
100
+ def foo
101
+ Dummy.function
102
+ end
103
+
104
+ def self.bar
105
+ Dummy.function
106
+ end
107
+ end
108
+
109
+ # same as previous class
110
+ class FourthClass
111
+ include MethodCacher::Base
112
+
113
+ attr_accessor :obj_key
114
+
115
+ caches_method :foo, singleton: :bar, obj_key: proc { |obj| obj.obj_key }
116
+
117
+ def foo
118
+ Dummy.function
119
+ end
120
+
121
+ def self.bar
122
+ Dummy.function
123
+ end
124
+ end
125
+
126
+ # for testing :obj_key option default
127
+ class FifthClass
128
+ include MethodCacher::Base
129
+
130
+ attr_accessor :id
131
+
132
+ caches_method :foo
133
+
134
+ def foo
135
+ Dummy.function
136
+ end
137
+ end
138
+
139
+ # for testing when the :obj_key option is undefined and the default id method is missing
140
+ class SixthClass
141
+ include MethodCacher::Base
142
+
143
+ caches_method :foo
144
+
145
+ def foo
146
+ Dummy.function
147
+ end
148
+ end
149
+
150
+ # for testing adding methods to be cached using separate caches_method calls
151
+ class SeventhClass
152
+ include MethodCacher::Base
153
+
154
+ def id
155
+ object_id
156
+ end
157
+
158
+ caches_method :foo1, singleton: :bar1
159
+ caches_method :foo2, singleton: :bar2
160
+ caches_method :foo3, :foo4, singleton: [:bar3, :bar4]
161
+
162
+ # test instance methods.
163
+ def foo1
164
+ Dummy.function
165
+ end
166
+
167
+ def foo2
168
+ Dummy.function
169
+ end
170
+
171
+ def foo3
172
+ Dummy.function
173
+ end
174
+
175
+ def foo4
176
+ Dummy.function
177
+ end
178
+
179
+ def foo5
180
+ Dummy.function
181
+ end
182
+
183
+ def foo6
184
+ Dummy.function
185
+ end
186
+
187
+ def foo7
188
+ Dummy.function
189
+ end
190
+
191
+ def foo8
192
+ Dummy.function
193
+ end
194
+
195
+ class <<self
196
+ # test singleton (i.e. class) methods.
197
+ def bar1
198
+ Dummy.function
199
+ end
200
+
201
+ def bar2
202
+ Dummy.function
203
+ end
204
+
205
+ def bar3
206
+ Dummy.function
207
+ end
208
+
209
+ def bar4
210
+ Dummy.function
211
+ end
212
+
213
+ def bar5
214
+ Dummy.function
215
+ end
216
+
217
+ def bar6
218
+ Dummy.function
219
+ end
220
+
221
+ def bar7
222
+ Dummy.function
223
+ end
224
+
225
+ def bar8
226
+ Dummy.function
227
+ end
228
+ end
229
+
230
+ caches_method :foo5, singleton: :bar5
231
+ caches_method :foo6, singleton: :bar6
232
+ caches_method :foo7, :foo8, singleton: [:bar7, :bar8]
233
+ end
234
+
235
+ # for testing caching of private and protected instance methods
236
+ class EighthClass
237
+ include MethodCacher::Base
238
+
239
+ def id
240
+ object_id
241
+ end
242
+
243
+ caches_method :foo1, :foo3
244
+
245
+ protected
246
+ def foo1
247
+ Dummy.function
248
+ end
249
+
250
+ def foo2
251
+ Dummy.function
252
+ end
253
+
254
+ private
255
+ def foo3
256
+ Dummy.function
257
+ end
258
+
259
+ def foo4
260
+ Dummy.function
261
+ end
262
+
263
+ #caches_method :foo2, :foo4
264
+ end
File without changes
@@ -0,0 +1,14 @@
1
+ RSpec::Matchers.define :be_twice_the_same do |options|
2
+ match do |proc|
3
+ cached = proc.call
4
+ proc.call == cached
5
+ end
6
+
7
+ failure_message_for_should do |proc|
8
+ "expected #{proc} to return the same value when called twice"
9
+ end
10
+
11
+ failure_message_for_should_not do |proc|
12
+ "expected #{proc} to return two different values when called twice"
13
+ end
14
+ end
@@ -0,0 +1,4 @@
1
+ $:.push File.expand_path(".", __FILE__) # $: is the load path
2
+ require 'method_cacher'
3
+ require 'rspec_matchers'
4
+ require 'ruby-debug'
metadata ADDED
@@ -0,0 +1,113 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: method_cacher
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Yaron Walfish
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-12-22 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: &2156410720 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *2156410720
25
+ - !ruby/object:Gem::Dependency
26
+ name: supermodel
27
+ requirement: &2156410000 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *2156410000
36
+ - !ruby/object:Gem::Dependency
37
+ name: activesupport
38
+ requirement: &2156408920 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *2156408920
47
+ - !ruby/object:Gem::Dependency
48
+ name: ruby-debug19
49
+ requirement: &2156408100 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *2156408100
58
+ description: Caches Instance and Singleton Methods
59
+ email:
60
+ - yaronw@yaronw.com
61
+ executables: []
62
+ extensions: []
63
+ extra_rdoc_files: []
64
+ files:
65
+ - .gitignore
66
+ - .rspec
67
+ - .rvmrc
68
+ - CHANGELOG.md
69
+ - Gemfile
70
+ - Gemfile.lock
71
+ - LICENSE
72
+ - README.md
73
+ - Rakefile
74
+ - lib/method_cacher.rb
75
+ - lib/method_cacher/base.rb
76
+ - lib/method_cacher/railtie.rb
77
+ - lib/method_cacher/version.rb
78
+ - method_cacher.gemspec
79
+ - spec/method_cacher/base_spec.rb
80
+ - spec/method_cacher/base_test_helper.rb
81
+ - spec/method_cacher_spec.rb
82
+ - spec/rspec_matchers.rb
83
+ - spec/spec_helper.rb
84
+ homepage: ''
85
+ licenses: []
86
+ post_install_message:
87
+ rdoc_options: []
88
+ require_paths:
89
+ - lib
90
+ required_ruby_version: !ruby/object:Gem::Requirement
91
+ none: false
92
+ requirements:
93
+ - - ! '>='
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ required_rubygems_version: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ requirements: []
103
+ rubyforge_project:
104
+ rubygems_version: 1.8.10
105
+ signing_key:
106
+ specification_version: 3
107
+ summary: Wraps specified instance and singleton methods with the Rails caching mechanism.
108
+ test_files:
109
+ - spec/method_cacher/base_spec.rb
110
+ - spec/method_cacher/base_test_helper.rb
111
+ - spec/method_cacher_spec.rb
112
+ - spec/rspec_matchers.rb
113
+ - spec/spec_helper.rb