cache_fu 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,250 @@
1
+ require File.join(File.dirname(__FILE__), 'helper')
2
+ require 'test/unit'
3
+ require 'action_controller/test_process'
4
+
5
+ ActionController::Routing::Routes.draw do |map|
6
+ map.connect ':controller/:action/:id'
7
+ end
8
+
9
+ class FooController < ActionController::Base
10
+ def url_for(*args)
11
+ "http://#{Time.now.to_i}.foo.com"
12
+ end
13
+ end
14
+
15
+ class BarController < ActionController::Base
16
+ def page
17
+ render :text => "give me my bongos"
18
+ end
19
+
20
+ def index
21
+ render :text => "doop!"
22
+ end
23
+
24
+ def edit
25
+ render :text => "rawk"
26
+ end
27
+
28
+ def trees_are_swell?
29
+ true
30
+ end
31
+
32
+ def rescue_action(e)
33
+ raise e
34
+ end
35
+ end
36
+
37
+ class FooTemplate
38
+ include ::ActionView::Helpers::CacheHelper
39
+
40
+ attr_reader :controller
41
+
42
+ def initialize
43
+ @controller = FooController.new
44
+ end
45
+ end
46
+
47
+ context "Fragment caching (when used with memcached)" do
48
+ include FragmentCacheSpecSetup
49
+
50
+ setup do
51
+ @view = FooTemplate.new
52
+ end
53
+
54
+ specify "should be able to cache with a normal, non-keyed Rails cache calls" do
55
+ _erbout = ""
56
+ content = "Caching is fun!"
57
+
58
+ ActsAsCached.config[:store].expects(:set).with(@view.controller.url_for.gsub('http://',''), content, ActsAsCached.config[:ttl])
59
+
60
+ @view.cache { _erbout << content }
61
+ end
62
+
63
+ specify "should be able to cache with a normal cache call when we don't have a default ttl" do
64
+ begin
65
+ _erbout = ""
66
+ content = "Caching is fun!"
67
+
68
+ original_ttl = ActsAsCached.config.delete(:ttl)
69
+ ActsAsCached.config[:store].expects(:set).with(@view.controller.url_for.gsub('http://',''), content, 25.minutes)
70
+
71
+ @view.cache { _erbout << content }
72
+ ensure
73
+ ActsAsCached.config[:ttl] = original_ttl
74
+ end
75
+ end
76
+
77
+ specify "should be able to cache with a normal, keyed Rails cache calls" do
78
+ _erbout = ""
79
+ content = "Wow, even a key?!"
80
+ key = "#{Time.now.to_i}_wow_key"
81
+
82
+ ActsAsCached.config[:store].expects(:set).with(key, content, ActsAsCached.config[:ttl])
83
+
84
+ @view.cache(key) { _erbout << content }
85
+ end
86
+
87
+ specify "should be able to cache with new time-to-live option" do
88
+ _erbout = ""
89
+ content = "Time to live? TIME TO DIE!!"
90
+ key = "#{Time.now.to_i}_death_key"
91
+
92
+ ActsAsCached.config[:store].expects(:set).with(key, content, 60)
93
+ @view.cache(key, { :ttl => 60 }) { _erbout << content }
94
+ end
95
+
96
+ specify "should ignore everything but time-to-live when options are present" do
97
+ _erbout = ""
98
+ content = "Don't mess around, here, sir."
99
+ key = "#{Time.now.to_i}_mess_key"
100
+
101
+ ActsAsCached.config[:store].expects(:set).with(key, content, 60)
102
+ @view.cache(key, { :other_options => "for the kids", :ttl => 60 }) { _erbout << content }
103
+ end
104
+
105
+ specify "should be able to skip cache gets" do
106
+ ActsAsCached.skip_cache_gets = true
107
+ ActsAsCached.config[:store].expects(:get).never
108
+ _erbout = ""
109
+ @view.cache { _erbout << "Caching is fun!" }
110
+ ActsAsCached.skip_cache_gets = false
111
+ end
112
+ end
113
+
114
+ context "Action caching (when used with memcached)" do
115
+ include FragmentCacheSpecSetup
116
+ page_content = "give me my bongos"
117
+ index_content = "doop!"
118
+ edit_content = "rawk"
119
+
120
+ setup do
121
+ @controller = BarController.new
122
+ @request = ActionController::TestRequest.new
123
+ @response = ActionController::TestResponse.new
124
+ end
125
+
126
+ teardown do # clear the filter chain between specs to avoid chaos
127
+ BarController.write_inheritable_attribute('filter_chain', [])
128
+ end
129
+
130
+ # little helper for prettier expections on the cache
131
+ def cache_expects(method, expected_times = 1)
132
+ ActsAsCached.config[:store].expects(method).times(expected_times)
133
+ end
134
+
135
+ specify "should cache using default ttl for a normal action cache without ttl" do
136
+ BarController.caches_action :page
137
+
138
+ key = 'test.host/bar/page'
139
+ cache_expects(:set).with(key, page_content, ActsAsCached.config[:ttl])
140
+ get :page
141
+ @response.body.should == page_content
142
+
143
+ cache_expects(:read).with(key, nil).returns(page_content)
144
+ get :page
145
+ @response.body.should == page_content
146
+ end
147
+
148
+ specify "should cache using defaul ttl for normal, multiple action caches" do
149
+ BarController.caches_action :page, :index
150
+
151
+ cache_expects(:set).with('test.host/bar/page', page_content, ActsAsCached.config[:ttl])
152
+ get :page
153
+ cache_expects(:set).with('test.host/bar', index_content, ActsAsCached.config[:ttl])
154
+ get :index
155
+ end
156
+
157
+ specify "should be able to action cache with ttl" do
158
+ BarController.caches_action :page => { :ttl => 2.minutes }
159
+
160
+ cache_expects(:set).with('test.host/bar/page', page_content, 2.minutes)
161
+ get :page
162
+ @response.body.should == page_content
163
+ end
164
+
165
+ specify "should be able to action cache multiple actions with ttls" do
166
+ BarController.caches_action :index, :page => { :ttl => 5.minutes }
167
+
168
+ cache_expects(:set).with('test.host/bar/page', page_content, 5.minutes)
169
+ cache_expects(:set).with('test.host/bar', index_content, ActsAsCached.config[:ttl])
170
+
171
+ get :page
172
+ @response.body.should == page_content
173
+
174
+ get :index
175
+ @response.body.should == index_content
176
+ cache_expects(:read).with('test.host/bar', nil).returns(index_content)
177
+
178
+ get :index
179
+ end
180
+
181
+ specify "should be able to action cache conditionally when passed something that returns true" do
182
+ BarController.caches_action :page => { :if => :trees_are_swell? }
183
+
184
+ cache_expects(:set).with('test.host/bar/page', page_content, ActsAsCached.config[:ttl])
185
+
186
+ get :page
187
+ @response.body.should == page_content
188
+
189
+ cache_expects(:read).with('test.host/bar/page', nil).returns(page_content)
190
+
191
+ get :page
192
+ end
193
+
194
+ #check for edginess
195
+ if [].respond_to?(:extract_options!)
196
+ specify "should not break cache_path overrides" do
197
+ BarController.caches_action :page, :cache_path => 'http://test.host/some/custom/path'
198
+ cache_expects(:set).with('test.host/some/custom/path', page_content, ActsAsCached.config[:ttl])
199
+ get :page
200
+ end
201
+
202
+ specify "should not break cache_path block overrides" do
203
+ BarController.caches_action :edit, :cache_path => Proc.new { |c| c.params[:id] ? "http://test.host/#{c.params[:id]}/edit" : "http://test.host/edit" }
204
+ cache_expects(:set).with('test.host/edit', edit_content, ActsAsCached.config[:ttl])
205
+ get :edit
206
+
207
+ get :index
208
+ cache_expects(:set).with('test.host/5/edit', edit_content, ActsAsCached.config[:ttl])
209
+ get :edit, :id => 5
210
+ end
211
+
212
+ specify "should play nice with custom ttls and cache_path overrides" do
213
+ BarController.caches_action :page => { :ttl => 5.days }, :cache_path => 'http://test.host/my/custom/path'
214
+ cache_expects(:set).with('test.host/my/custom/path', page_content, 5.days)
215
+ get :page
216
+ end
217
+
218
+ specify "should play nice with custom ttls and cache_path block overrides" do
219
+ BarController.caches_action :edit, :cache_path => Proc.new { |c| c.params[:id] ? "http://test.host/#{c.params[:id]}/edit" : "http://test.host/edit" }
220
+ cache_expects(:set).with('test.host/5/edit', edit_content, ActsAsCached.config[:ttl])
221
+ get :edit, :id => 5
222
+ end
223
+
224
+ specify "should play nice with the most complicated thing i can throw at it" do
225
+ BarController.caches_action :index => { :ttl => 24.hours }, :page => { :ttl => 5.seconds }, :edit => { :ttl => 5.days }, :cache_path => Proc.new { |c| c.params[:id] ? "http://test.host/#{c.params[:id]}/#{c.params[:action]}" : "http://test.host/#{c.params[:action]}" }
226
+ cache_expects(:set).with('test.host/index', index_content, 24.hours)
227
+ get :index
228
+ cache_expects(:set).with('test.host/5/edit', edit_content, 5.days)
229
+ get :edit, :id => 5
230
+ cache_expects(:set).with('test.host/5/page', page_content, 5.seconds)
231
+ get :page, :id => 5
232
+
233
+ cache_expects(:read).with('test.host/5/page', nil).returns(page_content)
234
+ get :page, :id => 5
235
+ cache_expects(:read).with('test.host/5/edit', nil).returns(edit_content)
236
+ get :edit, :id => 5
237
+ cache_expects(:read).with('test.host/index', nil).returns(index_content)
238
+ get :index
239
+ end
240
+ end
241
+
242
+ specify "should be able to skip action caching when passed something that returns false" do
243
+ BarController.caches_action :page => { :if => Proc.new {|c| !c.trees_are_swell?} }
244
+
245
+ cache_expects(:set, 0).with('test.host/bar/page', page_content, ActsAsCached.config[:ttl])
246
+
247
+ get :page
248
+ @response.body.should == page_content
249
+ end
250
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,215 @@
1
+ ##
2
+ # This file exists to fake out all the Railsisms we use so we can run the
3
+ # tests in isolation.
4
+ $LOAD_PATH.unshift 'lib/'
5
+ #
6
+
7
+ begin
8
+ require 'rubygems'
9
+ gem 'mocha', '>= 0.4.0'
10
+ require 'mocha'
11
+ gem 'test-spec', '= 0.3.0'
12
+ require 'test/spec'
13
+ require 'multi_rails_init'
14
+ rescue LoadError
15
+ puts '=> acts_as_cached tests depend on the following gems: mocha (0.4.0+), test-spec (0.3.0), multi_rails (0.0.2), and rails.'
16
+ end
17
+
18
+ begin
19
+ require 'redgreen'
20
+ rescue LoadError
21
+ nil
22
+ end
23
+
24
+ Test::Spec::Should.send :alias_method, :have, :be
25
+ Test::Spec::ShouldNot.send :alias_method, :have, :be
26
+
27
+ ##
28
+ # real men test without mocks
29
+ if $with_memcache = ARGV.include?('with-memcache')
30
+ require 'memcache'
31
+ end
32
+
33
+ ##
34
+ # init.rb hacks
35
+ RAILS_ROOT = '.' unless defined? RAILS_ROOT
36
+ RAILS_ENV = 'test' unless defined? RAILS_ENV
37
+
38
+ ##
39
+ # get the default config using absolute path, so tests all play nice when run in isolation
40
+ DEFAULT_CONFIG_FILE = File.expand_path(File.dirname(__FILE__) + '/../defaults/memcached.yml.default')
41
+
42
+ ##
43
+ # aac
44
+ require 'acts_as_cached'
45
+ Object.send :include, ActsAsCached::Mixin
46
+
47
+ ##
48
+ # i need you.
49
+ module Enumerable
50
+ def index_by
51
+ inject({}) do |accum, elem|
52
+ accum[yield(elem)] = elem
53
+ accum
54
+ end
55
+ end
56
+ end
57
+
58
+ ##
59
+ # mocky.
60
+ class HashStore < Hash
61
+ alias :get :[]
62
+
63
+ def get_multi(*values)
64
+ reject { |k,v| !values.include? k }
65
+ end
66
+
67
+ def set(key, value, *others)
68
+ self[key] = value
69
+ end
70
+
71
+ def namespace
72
+ nil
73
+ end
74
+ end
75
+
76
+ $cache = HashStore.new
77
+
78
+ class Story
79
+ acts_as_cached($with_memcache ? {} : { :store => $cache })
80
+
81
+ attr_accessor :id, :title
82
+
83
+ def initialize(attributes = {})
84
+ attributes.each { |key, value| instance_variable_set("@#{key}", value) }
85
+ end
86
+
87
+ def attributes
88
+ { :id => id, :title => title }
89
+ end
90
+
91
+ def ==(other)
92
+ return false unless other.respond_to? :attributes
93
+ attributes == other.attributes
94
+ end
95
+
96
+ def self.find(*args)
97
+ options = args.last.is_a?(Hash) ? args.pop : {}
98
+
99
+ if (ids = args.flatten).size > 1
100
+ ids.map { |id| $stories[id.to_i] }
101
+ elsif (id = args.flatten.first).to_i.to_s == id.to_s
102
+ $stories[id.to_i]
103
+ end
104
+ end
105
+
106
+ def self.find_by_title(*args)
107
+ title = args.shift
108
+ find(args).select { |s| s.title == title }
109
+ end
110
+
111
+ def self.base_class
112
+ Story
113
+ end
114
+
115
+ def self.something_cool; :redbull end
116
+ def something_flashy; :molassy end
117
+
118
+ def self.block_on(target = nil)
119
+ target || :something
120
+ end
121
+
122
+ def self.pass_through(target)
123
+ target
124
+ end
125
+
126
+ def self.two_params(first, second)
127
+ "first: #{first} | second: #{second}"
128
+ end
129
+
130
+ def self.find_live(*args) false end
131
+ end
132
+
133
+ class Feature < Story; end
134
+ class Interview < Story; end
135
+
136
+ module ActionController
137
+ class Base
138
+ def rendering_runtime(*args) '' end
139
+ def self.silence; yield end
140
+ end
141
+ end
142
+
143
+ class MemCache
144
+ attr_accessor :servers
145
+ def initialize(*args) end
146
+ class MemCacheError < StandardError; end unless defined? MemCacheError
147
+ end unless $with_memcache
148
+
149
+ module StoryCacheSpecSetup
150
+ def self.included(base)
151
+ base.setup do
152
+ setup_cache_spec
153
+ Story.instance_eval { @max_key_length = nil }
154
+ end
155
+ end
156
+
157
+ def setup_cache_spec
158
+ @story = Story.new(:id => 1, :title => "acts_as_cached 2 released!")
159
+ @story2 = Story.new(:id => 2, :title => "BDD is something you can use")
160
+ @story3 = Story.new(:id => 3, :title => "RailsConf is overrated.")
161
+ $stories = { 1 => @story, 2 => @story2, 3 => @story3 }
162
+
163
+ $with_memcache ? with_memcache : with_mock
164
+ end
165
+
166
+ def with_memcache
167
+ unless $mc_setup_for_story_cache_spec
168
+ ActsAsCached.config.clear
169
+ config = YAML.load_file(DEFAULT_CONFIG_FILE)
170
+ config['test'] = config['development'].merge('benchmarking' => false, 'disabled' => false)
171
+ ActsAsCached.config = config
172
+ $mc_setup_for_story_cache_spec = true
173
+ end
174
+
175
+ Story.send :acts_as_cached
176
+ Story.expire_cache(1)
177
+ Story.expire_cache(2)
178
+ Story.expire_cache(3)
179
+ Story.expire_cache(:block)
180
+ Story.set_cache(2, @story2)
181
+ end
182
+
183
+ def with_mock
184
+ $cache.clear
185
+
186
+ Story.send :acts_as_cached, :store => $cache
187
+ $cache['Story:2'] = @story2
188
+ end
189
+ end
190
+
191
+ module FragmentCacheSpecSetup
192
+ def self.included(base)
193
+ base.setup { setup_fragment_spec }
194
+ end
195
+
196
+ def setup_fragment_spec
197
+ unless $mc_setup_for_fragment_cache_spec
198
+ ActsAsCached.config.clear
199
+ config = YAML.load_file(DEFAULT_CONFIG_FILE)
200
+
201
+ if $with_memcache
202
+ other_options = { 'fragments' => true }
203
+ else
204
+ Object.const_set(:CACHE, $cache) unless defined? CACHE
205
+ other_options = { 'fragments' => true, 'store' => $cache }
206
+ end
207
+
208
+ config['test'] = config['development'].merge other_options
209
+
210
+ ActsAsCached.config = config
211
+ ActsAsCached::FragmentCache.setup!
212
+ $mc_setup_for_fragment_cache_spec = true
213
+ end
214
+ end
215
+ end
@@ -0,0 +1,43 @@
1
+ require File.join(File.dirname(__FILE__), 'helper')
2
+
3
+ context "When local_cache_for_request is called" do
4
+ include StoryCacheSpecSetup
5
+
6
+ setup do
7
+ ActionController::Base.new.local_cache_for_request
8
+ $cache = CACHE if $with_memcache
9
+ end
10
+
11
+ specify "get_cache should pull from the local cache on a second hit" do
12
+ $cache.expects(:get).with('Story:2').returns(@story2)
13
+ @story2.get_cache
14
+ $cache.expects(:get).never
15
+ @story2.get_cache
16
+ end
17
+
18
+ specify "set_cache should set to the local cache" do
19
+ $cache.expects(:set).at_least_once.returns(@story)
20
+ ActsAsCached::LocalCache.local_cache.expects(:[]=).with('Story:1', @story).returns(@story)
21
+ @story.set_cache
22
+ end
23
+
24
+ specify "expire_cache should clear from the local cache" do
25
+ @story2.get_cache
26
+ $cache.expects(:delete).at_least_once
27
+ ActsAsCached::LocalCache.local_cache.expects(:delete).with('Story:2')
28
+ @story2.expire_cache
29
+ end
30
+
31
+ specify "clear_cache should clear from the local cache" do
32
+ @story2.get_cache
33
+ $cache.expects(:delete).at_least_once
34
+ ActsAsCached::LocalCache.local_cache.expects(:delete).with('Story:2')
35
+ @story2.clear_cache
36
+ end
37
+
38
+ specify "cached? should check the local cache" do
39
+ @story2.get_cache
40
+ $cache.expects(:get).never
41
+ @story2.cached?
42
+ end
43
+ end
data/test/sti_test.rb ADDED
@@ -0,0 +1,36 @@
1
+ require File.join(File.dirname(__FILE__), 'helper')
2
+
3
+ context "An STI subclass acting as cached" do
4
+ include StoryCacheSpecSetup
5
+
6
+ setup do
7
+ @feature = Feature.new(:id => 3, :title => 'Behind the scenes of acts_as_cached')
8
+ @interview = Interview.new(:id => 4, :title => 'An interview with the Arcade Fire')
9
+ @feature.expire_cache
10
+ @interview.expire_cache
11
+ $stories.update 3 => @feature, 4 => @interview
12
+ end
13
+
14
+ specify "should be just as retrievable as any other cachable Ruby object" do
15
+ Feature.cached?(3).should.equal false
16
+ Feature.get_cache(3)
17
+ Feature.cached?(3).should.equal true
18
+ end
19
+
20
+ specify "should have a key corresponding to its parent class" do
21
+ @feature.cache_key.should.equal "Story:3"
22
+ @interview.cache_key.should.equal "Story:4"
23
+ end
24
+
25
+ specify "should be able to get itself from the cache via its parent class" do
26
+ Story.get_cache(3).should.equal @feature
27
+ Story.get_cache(4).should.equal @interview
28
+ end
29
+
30
+ specify "should take on its parents cache options but be able to set its own" do
31
+ @feature.cache_key.should.equal "Story:3"
32
+ Feature.cache_config[:version] = 1
33
+ @feature.cache_key.should.equal "Story:1:3"
34
+ @story.cache_key.should.equal "Story:1"
35
+ end
36
+ end
metadata ADDED
@@ -0,0 +1,103 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cache_fu
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.1.2
6
+ platform: ruby
7
+ authors:
8
+ - ""
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-05-23 00:00:00 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rails
17
+ prerelease: false
18
+ requirement: &id001 !ruby/object:Gem::Requirement
19
+ none: false
20
+ requirements:
21
+ - - ~>
22
+ - !ruby/object:Gem::Version
23
+ version: "3.0"
24
+ type: :runtime
25
+ version_requirements: *id001
26
+ - !ruby/object:Gem::Dependency
27
+ name: rails
28
+ prerelease: false
29
+ requirement: &id002 !ruby/object:Gem::Requirement
30
+ none: false
31
+ requirements:
32
+ - - ~>
33
+ - !ruby/object:Gem::Version
34
+ version: "3.0"
35
+ type: :development
36
+ version_requirements: *id002
37
+ description: This gem is kreetitech's fork (http://github.com/kreetitech/cache_fu).
38
+ email:
39
+ - ""
40
+ executables: []
41
+
42
+ extensions: []
43
+
44
+ extra_rdoc_files: []
45
+
46
+ files:
47
+ - rails/init.rb
48
+ - lib/acts_as_cached/benchmarking.rb
49
+ - lib/acts_as_cached/cache_methods.rb
50
+ - lib/acts_as_cached/config.rb
51
+ - lib/acts_as_cached/disabled.rb
52
+ - lib/acts_as_cached/fragment_cache.rb
53
+ - lib/acts_as_cached/local_cache.rb
54
+ - lib/acts_as_cached/railtie.rb
55
+ - lib/acts_as_cached/recipes.rb
56
+ - lib/cache_fu.rb
57
+ - lib/cache_fu.rb~
58
+ - lib/cache_fu.rb~5453d1247ce57bb1ea5ad70936b97707d5dbdab0
59
+ - lib/cache_fu.rb~5453d1247ce57bb1ea5ad70936b97707d5dbdab0_0
60
+ - lib/tasks/memcached.rake
61
+ - test/benchmarking_test.rb
62
+ - test/cache_test.rb
63
+ - test/config_test.rb
64
+ - test/disabled_test.rb
65
+ - test/extensions_test.rb
66
+ - test/fragment_cache_test.rb
67
+ - test/helper.rb
68
+ - test/local_cache_test.rb
69
+ - test/sti_test.rb
70
+ - defaults/extensions.rb.default
71
+ - defaults/memcached.yml.default
72
+ - defaults/memcached_ctl.default
73
+ - LICENSE
74
+ - README
75
+ homepage: http://github.com/kreetitech/cache_fu
76
+ licenses: []
77
+
78
+ post_install_message:
79
+ rdoc_options: []
80
+
81
+ require_paths:
82
+ - lib
83
+ required_ruby_version: !ruby/object:Gem::Requirement
84
+ none: false
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: "0"
89
+ required_rubygems_version: !ruby/object:Gem::Requirement
90
+ none: false
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: "0"
95
+ requirements: []
96
+
97
+ rubyforge_project:
98
+ rubygems_version: 1.8.3
99
+ signing_key:
100
+ specification_version: 3
101
+ summary: Makes caching easy for ActiveRecord models
102
+ test_files: []
103
+