cache_cow 0.0.1
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/.gitignore +6 -0
- data/.gitignore.bak +4 -0
- data/.rspec +2 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +16 -0
- data/Gemfile.lock +176 -0
- data/MIT-LICENSE +20 -0
- data/README.md +48 -0
- data/Rakefile +54 -0
- data/cache_cow.gemspec +32 -0
- data/lib/cache_cow.rb +8 -0
- data/lib/cache_cow/acts_as_cached.rb +172 -0
- data/lib/cache_cow/cacheable.rb +76 -0
- data/lib/cache_cow/cached_id_list.rb +46 -0
- data/lib/cache_cow/version.rb +3 -0
- data/lib/tasks/cache_cow_tasks.rake +4 -0
- data/spec/cache_cow/acts_as_cached_spec.rb +8 -0
- data/spec/cache_cow/cacheable_spec.rb +161 -0
- data/spec/cache_cow/cached_id_list_spec.rb +54 -0
- data/spec/cache_cow_spec.rb +8 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/assets/javascripts/application.js +9 -0
- data/spec/dummy/app/assets/stylesheets/application.css +7 -0
- data/spec/dummy/app/controllers/application_controller.rb +3 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/mailers/.gitkeep +0 -0
- data/spec/dummy/app/models/.gitkeep +0 -0
- data/spec/dummy/app/models/blog_comment.rb +2 -0
- data/spec/dummy/app/models/blog_post.rb +2 -0
- data/spec/dummy/app/models/comment.rb +2 -0
- data/spec/dummy/app/models/post.rb +6 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +49 -0
- data/spec/dummy/config/boot.rb +10 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +30 -0
- data/spec/dummy/config/environments/production.rb +60 -0
- data/spec/dummy/config/environments/test.rb +39 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/inflections.rb +10 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +5 -0
- data/spec/dummy/config/routes.rb +58 -0
- data/spec/dummy/db/migrate/20111026035600_create_posts.rb +11 -0
- data/spec/dummy/db/migrate/20111026035754_create_comments.rb +10 -0
- data/spec/dummy/db/migrate/20111026222959_add_sti_to_posts_and_comments.rb +6 -0
- data/spec/dummy/db/schema.rb +32 -0
- data/spec/dummy/lib/assets/.gitkeep +0 -0
- data/spec/dummy/log/.gitkeep +0 -0
- data/spec/dummy/public/404.html +26 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +26 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/dummy/spec/factories.rb +11 -0
- data/spec/spec_helper.rb +26 -0
- metadata +310 -0
@@ -0,0 +1,76 @@
|
|
1
|
+
module CacheCow
|
2
|
+
module Cacheable
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
module ClassMethods
|
6
|
+
|
7
|
+
def fetch_cache(*args, &block)
|
8
|
+
options = args.extract_options!
|
9
|
+
keys = args.flatten
|
10
|
+
|
11
|
+
# raise "Doesn't support multiget" unless keys.size == 1
|
12
|
+
Rails.cache.fetch cache_key(keys.first), &block
|
13
|
+
end
|
14
|
+
|
15
|
+
def read_cache(cache_id = nil, options = {})
|
16
|
+
Rails.cache.read cache_key(cache_id), options
|
17
|
+
end
|
18
|
+
|
19
|
+
def write_cache(cache_id, value, options = {})
|
20
|
+
Rails.cache.write cache_key(cache_id), value, { :expires_in => 1500 }.merge(options)
|
21
|
+
end
|
22
|
+
|
23
|
+
def expire_cache(cache_id = nil, options = {})
|
24
|
+
Rails.cache.delete cache_key(cache_id), options
|
25
|
+
end
|
26
|
+
|
27
|
+
def cached?(cache_id = nil)
|
28
|
+
Rails.cache.exist?(cache_key(cache_id))
|
29
|
+
end
|
30
|
+
|
31
|
+
def cache_key(cache_id)
|
32
|
+
[cache_name, acts_as_cached_version, cache_id].compact.join(':').gsub(' ', '_')[0..(max_key_length - 1)]
|
33
|
+
end
|
34
|
+
|
35
|
+
def cache_name
|
36
|
+
@cache_name ||= respond_to?(:base_class) ? base_class.name : name
|
37
|
+
end
|
38
|
+
|
39
|
+
def max_key_length
|
40
|
+
200
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
module InstanceMethods
|
46
|
+
|
47
|
+
def fetch_cache(key = nil, options = {}, &block)
|
48
|
+
self.class.fetch_cache cache_id(key), options, &block
|
49
|
+
end
|
50
|
+
|
51
|
+
def write_cache(key = nil, options = {})
|
52
|
+
self.class.write_cache cache_id(key), options
|
53
|
+
end
|
54
|
+
|
55
|
+
def read_cache(key = nil, options = {})
|
56
|
+
self.class.read_cache cache_id(key), options
|
57
|
+
end
|
58
|
+
|
59
|
+
def expire_cache(key = nil, options = {})
|
60
|
+
self.class.expire_cache cache_id(key), options
|
61
|
+
end
|
62
|
+
|
63
|
+
def cache_key(key = nil)
|
64
|
+
self.class.cache_key cache_id(key)
|
65
|
+
end
|
66
|
+
|
67
|
+
protected
|
68
|
+
|
69
|
+
def cache_id(key = nil)
|
70
|
+
key.nil? ? id.to_s : "#{id}:#{key}"
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module CacheCow
|
2
|
+
module CachedIdList
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
module ClassMethods
|
6
|
+
# For example:
|
7
|
+
#
|
8
|
+
# cached_id_list :groups, :accessor => :member_of?
|
9
|
+
def cached_id_list(association_name, options = {})
|
10
|
+
accessor_name = options[:accessor]
|
11
|
+
cache_key = cached_id_list_cache_key(association_name)
|
12
|
+
|
13
|
+
define_method(cache_key) do
|
14
|
+
fetch_cache cache_key, :expires_in => 1.month do
|
15
|
+
send(association_name).select("id").map(&:id)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# if accessor_name
|
20
|
+
# self.class_eval <<-RUBY
|
21
|
+
# def #{accessor_name}(model_or_id)
|
22
|
+
# return false if model_or_id.nil?
|
23
|
+
# model_id = model_or_id.is_a?(Fixnum) ? model_or_id : model_or_id.id
|
24
|
+
# #{cache_key}.include?(model_id)
|
25
|
+
# end
|
26
|
+
# RUBY
|
27
|
+
# end
|
28
|
+
end
|
29
|
+
|
30
|
+
def cached_id_list_cache_key(association_name)
|
31
|
+
"cached_" + association_name.to_s.singularize + "_ids"
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
module InstanceMethods
|
37
|
+
def expire_cached_id_list(association_name)
|
38
|
+
cache_key = self.class.cached_id_list_cache_key(association_name)
|
39
|
+
expire_cache cache_key
|
40
|
+
# instance_variable_set("@#{cache_key}", nil)
|
41
|
+
# instance_variable_set("@cached_#{association_name}", nil)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe CacheCow::Cacheable do
|
4
|
+
|
5
|
+
let(:cache_key) { "post:1" }
|
6
|
+
|
7
|
+
describe ".fetch_cache" do
|
8
|
+
|
9
|
+
it "returns nil if no block given and cache miss" do
|
10
|
+
Post.fetch_cache(cache_key).should be_nil
|
11
|
+
end
|
12
|
+
|
13
|
+
it "writes through value from block on read" do
|
14
|
+
Post.fetch_cache(cache_key) { "great post" }
|
15
|
+
Post.fetch_cache(cache_key).should == "great post"
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should be accessible from Rails.cache via class cache key" do
|
19
|
+
Rails.cache.read(Post.cache_key(cache_key)).should be_nil
|
20
|
+
Post.fetch_cache(cache_key) { "great post" }
|
21
|
+
Rails.cache.read(Post.cache_key(cache_key)).should == "great post"
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
describe ".read_cache" do
|
27
|
+
it "should read value if cache hit" do
|
28
|
+
Post.fetch_cache(cache_key) { "great post" }
|
29
|
+
Post.read_cache(cache_key).should == "great post"
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should return nil if cache miss" do
|
33
|
+
Post.read_cache(cache_key).should be_nil
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe ".write_cache" do
|
38
|
+
it "should write value" do
|
39
|
+
Post.write_cache(cache_key, "great post")
|
40
|
+
Post.read_cache(cache_key).should == "great post"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe ".expire_cache" do
|
45
|
+
it "should delete cached value" do
|
46
|
+
Post.write_cache(cache_key, "great post")
|
47
|
+
Post.expire_cache(cache_key)
|
48
|
+
Post.read_cache(cache_key).should be_nil
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe ".cached?" do
|
53
|
+
it "should return true if cache hit" do
|
54
|
+
Post.write_cache(cache_key, "great post")
|
55
|
+
Post.cached?(cache_key).should be_true
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should return false if cache miss" do
|
59
|
+
Post.cached?(cache_key).should be_false
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe ".cache_key" do
|
64
|
+
|
65
|
+
it "should namespace cache_key to class:version:cache_key" do
|
66
|
+
Post.cache_key(cache_key).should == "Post:1:post:1"
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should namespace subclass to base class name" do
|
70
|
+
BlogPost.cache_key(cache_key).should == "Post:1:post:1"
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
describe "instance_methods" do
|
76
|
+
let(:post) { stub_model(Post) }
|
77
|
+
|
78
|
+
describe "#fetch_cache" do
|
79
|
+
|
80
|
+
it "should write through value on read" do
|
81
|
+
post.fetch_cache(cache_key).should be_nil
|
82
|
+
post.fetch_cache(cache_key) { "great post" }
|
83
|
+
post.fetch_cache(cache_key).should == "great post"
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should use id as default cache_key if none given" do
|
87
|
+
post.fetch_cache { "another great post" }
|
88
|
+
Rails.cache.read(Post.cache_key(post.id)).should == "another great post"
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should store value under key namespaced by class and id" do
|
92
|
+
post.fetch_cache(cache_key) { "a third great post" }
|
93
|
+
Rails.cache.read(post.cache_key(cache_key)).should == "a third great post"
|
94
|
+
end
|
95
|
+
|
96
|
+
describe "options" do
|
97
|
+
it ":force => true forces cache miss" do
|
98
|
+
post.fetch_cache { "another great post" }
|
99
|
+
post.fetch_cache(cache_key, :force => true).should be_nil
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# spec for :compress
|
104
|
+
# spec for :expires_in
|
105
|
+
end
|
106
|
+
|
107
|
+
describe "#write_cache" do
|
108
|
+
it "should write to key for instance" do
|
109
|
+
post.write_cache("foo", "bar")
|
110
|
+
post.read_cache("foo").should == "bar"
|
111
|
+
end
|
112
|
+
|
113
|
+
it "should not write to another instance key" do
|
114
|
+
post.write_cache("foo", "bar")
|
115
|
+
stub_model(Post).read_cache("foo").should be_nil
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
describe "#read_cache" do
|
120
|
+
it "should read key for instance" do
|
121
|
+
post.write_cache("foo", "bar")
|
122
|
+
post.read_cache("foo").should == "bar"
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should not read key for another instance" do
|
126
|
+
stub_model(Post).write_cache("foo", "bar")
|
127
|
+
post.read_cache("foo").should be_nil
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
describe "#expire_cache" do
|
132
|
+
it "should expire key for instance" do
|
133
|
+
post.write_cache("foo", "bar")
|
134
|
+
post.read_cache("foo").should == "bar"
|
135
|
+
post.expire_cache("foo")
|
136
|
+
post.read_cache("foo").should be_nil
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
describe "#cache_key" do
|
141
|
+
it "should namespace key by its id" do
|
142
|
+
post.cache_key("cache_key").should == "Post:1:#{post.id}:cache_key"
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# describe "get_multi" do
|
148
|
+
# it "should do a rails multiget with all the keys" do
|
149
|
+
# Rails.cache.should_receive(:read_multi).with(*[User.cache_key("a"), User.cache_key("b")])
|
150
|
+
# User.get_multi(["a", "b"]) {}
|
151
|
+
# end
|
152
|
+
#
|
153
|
+
# it "should yield to a multiget object that passes along the appropriate options" do
|
154
|
+
# Rails.cache.should_receive(:read).with(User.cache_key("a"), :expires_in => '500').and_return("")
|
155
|
+
# User.get_multi(["a"]) do |multi|
|
156
|
+
# multi.fetch_cache("a", :expires_in => '500')
|
157
|
+
# end
|
158
|
+
# end
|
159
|
+
# end
|
160
|
+
|
161
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe CacheCow::CachedIdList do
|
4
|
+
|
5
|
+
let(:post) { Factory(:post) }
|
6
|
+
let(:comment) { Factory(:comment) }
|
7
|
+
|
8
|
+
describe ".cached_id_list" do
|
9
|
+
|
10
|
+
describe "comments" do
|
11
|
+
before(:each) do
|
12
|
+
post.comments << comment
|
13
|
+
post.save!
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should return ids by association" do
|
17
|
+
post.cached_comment_ids.should == post.comment_ids
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should write data to rails cache" do
|
21
|
+
post.cached_comment_ids # set cache
|
22
|
+
Rails.cache.read(post.cache_key("cached_comment_ids")).should == post.comment_ids
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should return [] if no comments" do
|
26
|
+
Factory(:post).cached_comment_ids.should == []
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
describe "blog_comments" do
|
32
|
+
it "should return ids by subclass association" do
|
33
|
+
comment_1 = Factory(:blog_comment)
|
34
|
+
comment_2 = Factory(:blog_comment)
|
35
|
+
post = Factory(:blog_post)
|
36
|
+
post.comments << comment_1
|
37
|
+
post.comments << comment_2
|
38
|
+
post.save!
|
39
|
+
post.cached_comment_ids.should == post.comment_ids
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "#expire_cached_id_list" do
|
45
|
+
it "should expire list of cached ids from given association name" do
|
46
|
+
post.comments << comment
|
47
|
+
post.cached_comment_ids # set cache
|
48
|
+
|
49
|
+
post.expire_cached_id_list(:comments)
|
50
|
+
|
51
|
+
Rails.cache.read(post.cache_key("cached_comment_ids")).should be_nil
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
data/spec/dummy/Rakefile
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
# Add your own tasks in files placed in lib/tasks ending in .rake,
|
3
|
+
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
|
4
|
+
|
5
|
+
require File.expand_path('../config/application', __FILE__)
|
6
|
+
|
7
|
+
Dummy::Application.load_tasks
|
@@ -0,0 +1,9 @@
|
|
1
|
+
// This is a manifest file that'll be compiled into including all the files listed below.
|
2
|
+
// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
|
3
|
+
// be included in the compiled file accessible from http://example.com/assets/application.js
|
4
|
+
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
5
|
+
// the compiled file.
|
6
|
+
//
|
7
|
+
//= require jquery
|
8
|
+
//= require jquery_ujs
|
9
|
+
//= require_tree .
|
@@ -0,0 +1,7 @@
|
|
1
|
+
/*
|
2
|
+
* This is a manifest file that'll automatically include all the stylesheets available in this directory
|
3
|
+
* and any sub-directories. You're free to add application-wide styles to this file and they'll appear at
|
4
|
+
* the top of the compiled file, but it's generally better to create a new file per style scope.
|
5
|
+
*= require_self
|
6
|
+
*= require_tree .
|
7
|
+
*/
|
File without changes
|
File without changes
|