revo-cache_fu 1.0.0

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,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,92 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: revo-cache_fu
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Chris Wanstrath
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-08-18 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: DESCRIPTION!
17
+ email: chris@ozmm.org
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - LICENSE
24
+ - README
25
+ files:
26
+ - CHANGELOG
27
+ - LICENSE
28
+ - README
29
+ - Rakefile
30
+ - VERSION
31
+ - cache_fu.gemspec
32
+ - defaults/extensions.rb.default
33
+ - defaults/memcached.yml.default
34
+ - defaults/memcached_ctl.default
35
+ - init.rb
36
+ - install.rb
37
+ - lib/acts_as_cached.rb
38
+ - lib/acts_as_cached/benchmarking.rb
39
+ - lib/acts_as_cached/cache_methods.rb
40
+ - lib/acts_as_cached/config.rb
41
+ - lib/acts_as_cached/disabled.rb
42
+ - lib/acts_as_cached/fragment_cache.rb
43
+ - lib/acts_as_cached/local_cache.rb
44
+ - lib/acts_as_cached/memcached_rails.rb
45
+ - lib/acts_as_cached/recipes.rb
46
+ - tasks/memcached.rake
47
+ - test/benchmarking_test.rb
48
+ - test/cache_test.rb
49
+ - test/config_test.rb
50
+ - test/disabled_test.rb
51
+ - test/extensions_test.rb
52
+ - test/fragment_cache_test.rb
53
+ - test/helper.rb
54
+ - test/local_cache_test.rb
55
+ - test/sti_test.rb
56
+ has_rdoc: false
57
+ homepage: http://github.com/defunkt/cache_fu
58
+ licenses:
59
+ post_install_message:
60
+ rdoc_options:
61
+ - --charset=UTF-8
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: "0"
69
+ version:
70
+ required_rubygems_version: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: "0"
75
+ version:
76
+ requirements: []
77
+
78
+ rubyforge_project:
79
+ rubygems_version: 1.3.5
80
+ signing_key:
81
+ specification_version: 3
82
+ summary: Summarize your gem
83
+ test_files:
84
+ - test/sti_test.rb
85
+ - test/benchmarking_test.rb
86
+ - test/local_cache_test.rb
87
+ - test/config_test.rb
88
+ - test/extensions_test.rb
89
+ - test/fragment_cache_test.rb
90
+ - test/disabled_test.rb
91
+ - test/cache_test.rb
92
+ - test/helper.rb