cashier 0.4.0 → 0.4.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/README.md +53 -41
- data/lib/cashier.rb +139 -113
- data/lib/cashier/adapters/cache_store.rb +1 -1
- data/lib/cashier/railtie.rb +6 -6
- data/lib/cashier/version.rb +1 -1
- data/spec/integration/rails_cache_integration_spec.rb +38 -0
- data/spec/integration/rails_configuration_spec.rb +9 -0
- data/spec/lib/{adapters → cashier/adapters}/cache_store_spec.rb +2 -3
- data/spec/lib/{adapters → cashier/adapters}/redis_store_spec.rb +0 -0
- data/spec/lib/cashier_spec.rb +99 -77
- data/spec/spec_helper.rb +4 -0
- metadata +55 -24
- data/lib/cashier/application_controller.rb +0 -28
- data/spec/controllers/application_controller_spec.rb +0 -23
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -29,10 +29,22 @@ caches_action :tag => proc {|c|
|
|
29
29
|
"users/#{c.current_user.id}/dashboard"
|
30
30
|
}
|
31
31
|
|
32
|
-
# in your sweeper, in your observers, in your
|
32
|
+
# in your sweeper, in your observers, in your Resque jobs...wherever
|
33
33
|
Cashier.expire 'complicated-action'
|
34
34
|
Cashier.expire 'tag1', 'tag2', 'tag3', 'tag4'
|
35
35
|
|
36
|
+
# It integrates smoothly with Rails.cache as well, not just the views
|
37
|
+
Rails.cache.fetch("user_1", :tag => ["users"]) { User.find(1) }
|
38
|
+
Rails.cache.fetch("user_2", :tag => ["users"]) { User.find(2) }
|
39
|
+
Rails.cache.fetch("user_3", :tag => ["users"]) { User.find(3) }
|
40
|
+
Rails.cache.fetch("admins", :tag => ["users"]) { User.where(role: "Admin").all }
|
41
|
+
|
42
|
+
# You can then expire all your users
|
43
|
+
Cashier.expire "users"
|
44
|
+
|
45
|
+
# You can also use Rails.cache.write
|
46
|
+
Rails.cache.write("foo", "bar", :tag => ["some_tag"])
|
47
|
+
|
36
48
|
# what's cached
|
37
49
|
Cashier.tags
|
38
50
|
|
@@ -77,19 +89,19 @@ Cashier has 2 adapters for the tags storing, `:cache_store` or `:redis_store`.
|
|
77
89
|
|
78
90
|
#### Setting an adapter for working with the cache as the tags storage
|
79
91
|
|
80
|
-
`config/initializers/cashier.rb`
|
81
|
-
|
82
92
|
```ruby
|
83
|
-
|
93
|
+
# config/environment/production.rb
|
94
|
+
|
95
|
+
config.cashier.adapter = :cache_store
|
96
|
+
# or config.cashier.adapter = :redis_store
|
84
97
|
```
|
85
98
|
|
86
99
|
#### Setting an adapter for working with Redis as the tags storage
|
87
100
|
|
88
|
-
`config/initializers/cashier.rb`
|
89
101
|
|
90
102
|
```ruby
|
91
|
-
|
92
|
-
|
103
|
+
# config/environment/production.rb
|
104
|
+
config.cashier.adapter.redis = Redis.new(:host => '127.0.0.1', :port => '3697') # or Resque.redis or any existing redis connection
|
93
105
|
```
|
94
106
|
|
95
107
|
### Why Redis?
|
@@ -118,54 +130,54 @@ Benchmark.measure do
|
|
118
130
|
end
|
119
131
|
end
|
120
132
|
```
|
121
|
-
|
122
133
|
Using the Redis adapter, the same piece of code takes 0.8 seconds, quite the difference :)
|
123
134
|
|
124
|
-
## Testing
|
125
135
|
|
126
|
-
|
136
|
+
### Notifications
|
127
137
|
|
128
|
-
|
129
|
-
|
138
|
+
Cashier will send out events when things happen inside the library.
|
139
|
+
The events are sent out through `ActiveSupport::Notifications` so you can pretty much subscribe to the events from anywhere you want.
|
130
140
|
|
131
|
-
|
132
|
-
```
|
133
|
-
|
134
|
-
I've also included some Rspec Matchers and a cucumber helper for testing
|
135
|
-
caching. The rspec matchers can be used like this:
|
141
|
+
Here are the way you can subscribe to the events and use the data from them.
|
136
142
|
|
137
143
|
```ruby
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
144
|
+
# Subscribe to the store fragment event, this is fired every time cashier will call the "store_fragment" method
|
145
|
+
# payload[:data] will be something like this: ["key", ["tag1", "tag2", "tag3"]]
|
146
|
+
ActiveSupport::Notifications.subscribe("store_fragment.cashier") do |name, start, finish, id, payload|
|
147
|
+
|
148
|
+
end
|
149
|
+
|
150
|
+
# Subscribe to the clear event. (no data)
|
151
|
+
ActiveSupport::Notifications.subscribe("clear.cashier") do |name, start, finish, id, payload|
|
152
|
+
|
153
|
+
end
|
154
|
+
|
155
|
+
# Subscribe to the delete_cache_key event
|
156
|
+
# this event will fire every time there's a Rails.cache.delete with the key
|
157
|
+
# payload[:data] will be the key name that's been deleted from the cache
|
158
|
+
ActiveSupport::Notifications.subscribe("delete_cache_key.cashier") do |name, start, finish, id, payload|
|
159
|
+
|
160
|
+
end
|
161
|
+
|
162
|
+
# Subscribe to the o_write_cache_key event
|
163
|
+
# this event will fire every time there's a Rails.cache.write with the key
|
164
|
+
# payload[:data] will be the key name that's been written to the cache
|
165
|
+
ActiveSupport::Notifications.subscribe("write_cache_key.cashier") do |name, start, finish, id, payload|
|
166
|
+
|
167
|
+
end
|
146
168
|
```
|
147
169
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
# features/support/cashier.rb
|
152
|
-
require 'cashier/cucumber'
|
153
|
-
```
|
170
|
+
### Notifications use case
|
171
|
+
At [Gogobot](http://www.gogobot.com) we have a plugin to invalidate the external CDN cache on full pages for logged out users.
|
172
|
+
The usage is pretty unlimited.
|
154
173
|
|
155
|
-
|
156
|
-
|
157
|
-
```ruby
|
158
|
-
Then /the dashboard should be cached/ do
|
159
|
-
"dashboard".should be_cached
|
160
|
-
end
|
161
|
-
```
|
162
|
-
Including `cashier/cucumber` will also wipe the cache before every
|
163
|
-
scenario.
|
174
|
+
If you think we're missing a notification, please do open an issue or be awesome and do it yourself and open a pull request.
|
164
175
|
|
165
176
|
## Contributors
|
166
177
|
|
167
|
-
* [
|
178
|
+
* [twinturbo](http://twitter.com/adman65) - Initial Implementation
|
168
179
|
* [KensoDev](http://twitter.com/kensodev) - Adding Redis support (Again \o/)
|
180
|
+
* [KensoDev](http://twitter.com/kensodev) - Adding plugins support for callback methods
|
169
181
|
|
170
182
|
## Contributing to Cashier
|
171
183
|
|
data/lib/cashier.rb
CHANGED
@@ -1,132 +1,152 @@
|
|
1
1
|
module Cashier
|
2
|
-
extend self
|
3
2
|
|
4
3
|
CACHE_KEY = 'cashier-tags'
|
5
4
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
5
|
+
class << self
|
6
|
+
|
7
|
+
# Public: whether the module will perform caching or not. this is being set in the application layer .perform_caching configuration
|
8
|
+
#
|
9
|
+
# Examples
|
10
|
+
#
|
11
|
+
# Cashier.perform_caching?
|
12
|
+
# # => true
|
13
|
+
#
|
14
|
+
def perform_caching?
|
15
|
+
::ApplicationController.perform_caching
|
11
16
|
end
|
12
|
-
end
|
13
|
-
|
14
|
-
# Public: set the adapter the Cashier module will use to store the keys
|
15
|
-
#
|
16
|
-
# cache_adapter - :cache_store / :redis_store
|
17
|
-
#
|
18
|
-
# Examples
|
19
|
-
#
|
20
|
-
# Cashier.adapter = :redis_store
|
21
|
-
#
|
22
|
-
def adapter=(cache_adapter)
|
23
|
-
@@adapter = cache_adapter
|
24
|
-
end
|
25
17
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
tags.each do |tag|
|
50
|
-
# store the fragment
|
51
|
-
adapter.store_fragment_in_tag(fragment, tag)
|
18
|
+
# Public: store a fragment with an array of tags for this fragment.
|
19
|
+
#
|
20
|
+
# fragment - cached fragment.
|
21
|
+
# tags - array of tags you want to assign this fragments.
|
22
|
+
#
|
23
|
+
# Examples
|
24
|
+
#
|
25
|
+
# Cachier.store_fragment('foo', 'tag1', 'tag2', 'tag3')
|
26
|
+
#
|
27
|
+
def store_fragment(fragment, *tags)
|
28
|
+
return unless perform_caching?
|
29
|
+
|
30
|
+
tags = tags.flatten
|
31
|
+
|
32
|
+
ActiveSupport::Notifications.instrument("store_fragment.cashier", :data => [fragment, tags]) do
|
33
|
+
tags.each do |tag|
|
34
|
+
# store the fragment
|
35
|
+
adapter.store_fragment_in_tag(fragment, tag)
|
36
|
+
end
|
37
|
+
|
38
|
+
# now store the tag for book keeping
|
39
|
+
adapter.store_tags(tags)
|
40
|
+
end
|
52
41
|
end
|
53
42
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
43
|
+
# Public: expire tags. expiring the keys 'assigned' to the tags you expire and removes the tags from the tags list
|
44
|
+
#
|
45
|
+
# tags - array of tags to expire.
|
46
|
+
#
|
47
|
+
# Examples
|
48
|
+
#
|
49
|
+
# Cashier.expire('tag1', 'tag2')
|
50
|
+
#
|
51
|
+
def expire(*tags)
|
52
|
+
return unless perform_caching?
|
53
|
+
|
54
|
+
ActiveSupport::Notifications.instrument("expire.cashier", :data => tags) do
|
55
|
+
# delete them from the cache
|
56
|
+
tags.each do |tag|
|
57
|
+
fragment_keys = adapter.get_fragments_for_tag(tag)
|
58
|
+
|
59
|
+
fragment_keys.each do |fragment_key|
|
60
|
+
Rails.cache.delete(fragment_key)
|
61
|
+
end
|
62
|
+
|
63
|
+
adapter.delete_tag(tag)
|
64
|
+
end
|
65
|
+
|
66
|
+
# now remove them from the list
|
67
|
+
# of stored tags
|
68
|
+
adapter.remove_tags(tags)
|
75
69
|
end
|
70
|
+
end
|
76
71
|
|
77
|
-
|
72
|
+
# Public: returns the array of tags stored in the tags store.
|
73
|
+
#
|
74
|
+
#
|
75
|
+
# Examples
|
76
|
+
#
|
77
|
+
# Cashier.tags
|
78
|
+
# # => ['tag1', 'tag2']
|
79
|
+
#
|
80
|
+
def tags
|
81
|
+
adapter.tags
|
78
82
|
end
|
79
83
|
|
80
|
-
#
|
81
|
-
#
|
82
|
-
|
83
|
-
|
84
|
+
# Public: clears the tags.
|
85
|
+
#
|
86
|
+
#
|
87
|
+
# Examples
|
88
|
+
#
|
89
|
+
# Cashier.clear
|
90
|
+
#
|
91
|
+
def clear
|
92
|
+
ActiveSupport::Notifications.instrument("clear.cashier") do
|
93
|
+
adapter.clear
|
94
|
+
end
|
95
|
+
end
|
84
96
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
97
|
+
# Public: get all the keys names as an array.
|
98
|
+
#
|
99
|
+
#
|
100
|
+
# Examples
|
101
|
+
#
|
102
|
+
# Cachier.keys
|
103
|
+
# # => ['key1', 'key2', 'key3']
|
104
|
+
#
|
105
|
+
def keys
|
106
|
+
adapter.keys
|
107
|
+
end
|
96
108
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
109
|
+
# Public: get all the keys for a specific tag as an array.
|
110
|
+
#
|
111
|
+
#
|
112
|
+
# Examples
|
113
|
+
#
|
114
|
+
# Cashier.tags_for('tag1')
|
115
|
+
# # => ['key1', 'key2', 'key3']
|
116
|
+
#
|
117
|
+
def keys_for(tag)
|
118
|
+
adapter.get_fragments_for_tag(tag)
|
119
|
+
end
|
107
120
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
121
|
+
# Public: adapter which is used by cashier.
|
122
|
+
#
|
123
|
+
# Examples
|
124
|
+
#
|
125
|
+
# Cashier.adapter
|
126
|
+
# # => Cashier::Adapters::CacheStore
|
127
|
+
#
|
128
|
+
# Cashier.adapter
|
129
|
+
# # => Cashier::Adapters::RedisStore
|
130
|
+
#
|
131
|
+
def adapter
|
132
|
+
if @@adapter == :cache_store
|
133
|
+
Cashier::Adapters::CacheStore
|
134
|
+
else
|
135
|
+
Cashier::Adapters::RedisStore
|
136
|
+
end
|
137
|
+
end
|
119
138
|
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
139
|
+
# Public: set the adapter the Cashier module will use to store the keys
|
140
|
+
#
|
141
|
+
# cache_adapter - :cache_store / :redis_store
|
142
|
+
#
|
143
|
+
# Examples
|
144
|
+
#
|
145
|
+
# Cashier.adapter = :redis_store
|
146
|
+
#
|
147
|
+
def adapter=(cache_adapter)
|
148
|
+
@@adapter = cache_adapter
|
149
|
+
end
|
130
150
|
end
|
131
151
|
end
|
132
152
|
|
@@ -134,3 +154,9 @@ require 'rails'
|
|
134
154
|
require 'cashier/railtie'
|
135
155
|
require 'cashier/adapters/cache_store'
|
136
156
|
require 'cashier/adapters/redis_store'
|
157
|
+
|
158
|
+
# Connect cashier up to the low level Rails cache.
|
159
|
+
ActiveSupport::Notifications.subscribe("cache_write.active_support") do |*args|
|
160
|
+
payload = ActiveSupport::Notifications::Event.new(*args).payload
|
161
|
+
Cashier.store_fragment payload[:key], payload[:tag] if payload[:tag]
|
162
|
+
end
|
data/lib/cashier/railtie.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
module Cashier
|
2
|
-
class Railtie < Rails::Railtie
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
2
|
+
class Railtie < ::Rails::Railtie
|
3
|
+
config.cashier = Cashier
|
4
|
+
|
5
|
+
initializer "cashier.active_support.cache.instrumentation" do |app|
|
6
|
+
ActiveSupport::Cache::Store.instrument = true
|
7
7
|
end
|
8
8
|
end
|
9
|
-
end
|
9
|
+
end
|
data/lib/cashier/version.rb
CHANGED
@@ -0,0 +1,38 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe "Rails cache integration" do
|
4
|
+
subject { Rails.cache }
|
5
|
+
let(:cashier) { Cashier }
|
6
|
+
|
7
|
+
it "should ensure that cache operations are instrumented" do
|
8
|
+
ActiveSupport::Cache::Store.instrument.should be_true
|
9
|
+
end
|
10
|
+
|
11
|
+
context "write" do
|
12
|
+
it "should write to cashier when I call Rails.cache.write with tags" do
|
13
|
+
cashier.should_receive(:store_fragment).with("foo", ["some_tag"])
|
14
|
+
subject.write("foo", "bar", :tag => ["some_tag"])
|
15
|
+
end
|
16
|
+
|
17
|
+
it "shuld not write to cashier when I call Rails.cache.write without tags" do
|
18
|
+
cashier.should_not_receive(:store_fragment)
|
19
|
+
subject.write("foo", "bar")
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should not fail when I don't pass in any options" do
|
23
|
+
expect { subject.write("foo", "bar", nil) }.to_not raise_error
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context "fetch" do
|
28
|
+
it "should write to cashier when I call Rails.cache.fetch with tags" do
|
29
|
+
cashier.should_receive(:store_fragment).with("foo", ["some_tag"])
|
30
|
+
subject.fetch("foo", :tag => ["some_tag"]) { "bar" }
|
31
|
+
end
|
32
|
+
|
33
|
+
it "shuld not write to cashier when I call Rails.cache.fetch without tags" do
|
34
|
+
cashier.should_not_receive(:store_fragment)
|
35
|
+
subject.fetch("foo") { "bar" }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -3,7 +3,7 @@ require 'spec_helper'
|
|
3
3
|
describe Cashier::Adapters::CacheStore do
|
4
4
|
subject { Cashier::Adapters::CacheStore }
|
5
5
|
let(:cache) { Rails.cache }
|
6
|
-
|
6
|
+
|
7
7
|
it "should store the fragment in a tag" do
|
8
8
|
subject.store_fragment_in_tag('fragment-key', 'dashboard')
|
9
9
|
cache.fetch('dashboard').should eql(['fragment-key'])
|
@@ -62,7 +62,6 @@ describe Cashier::Adapters::CacheStore do
|
|
62
62
|
|
63
63
|
context "keys" do
|
64
64
|
it "should return the list of keys" do
|
65
|
-
|
66
65
|
subject.store_tags(['dashboard', 'settings', 'email'])
|
67
66
|
|
68
67
|
subject.store_fragment_in_tag('key1', 'dashboard')
|
@@ -72,4 +71,4 @@ describe Cashier::Adapters::CacheStore do
|
|
72
71
|
subject.keys.should eql(%w(key1 key2 key3))
|
73
72
|
end
|
74
73
|
end
|
75
|
-
end
|
74
|
+
end
|
File without changes
|
data/spec/lib/cashier_spec.rb
CHANGED
@@ -1,112 +1,134 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe "Cashier" do
|
4
|
-
|
5
|
-
|
4
|
+
before(:each) do
|
5
|
+
Cashier.adapter = :cache_store
|
6
|
+
end
|
6
7
|
|
7
|
-
|
8
|
-
|
9
|
-
|
8
|
+
subject { Cashier }
|
9
|
+
|
10
|
+
let(:adapter) { Cashier.adapter }
|
11
|
+
|
12
|
+
describe "#store_fragment" do
|
13
|
+
it "should write the tag to the cache" do
|
14
|
+
adapter.should_receive(:store_fragment_in_tag).with('fragment-key', 'dashboard')
|
15
|
+
adapter.should_receive(:store_tags).with(["dashboard"])
|
10
16
|
|
11
|
-
|
12
|
-
subject.respond_to?(:adapter).should be_true
|
17
|
+
subject.store_fragment('fragment-key', 'dashboard')
|
13
18
|
end
|
14
|
-
end
|
15
19
|
|
16
|
-
|
17
|
-
|
18
|
-
|
20
|
+
it "should flatten tags" do
|
21
|
+
adapter.should_receive(:store_fragment_in_tag).with('fragment-key', 'dashboard')
|
22
|
+
adapter.should_receive(:store_tags).with(["dashboard"])
|
23
|
+
|
24
|
+
subject.store_fragment('fragment-key', ['dashboard'])
|
19
25
|
end
|
20
|
-
subject { Cashier }
|
21
|
-
let(:adapter) { Cashier.adapter }
|
22
26
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
adapter.should_receive(:store_tags).with(["dashboard"])
|
27
|
+
it "should store the tag for book keeping" do
|
28
|
+
adapter.should_receive(:store_fragment_in_tag).with('fragment-key', 'dashboard')
|
29
|
+
adapter.should_receive(:store_fragment_in_tag).with('fragment-key', 'settings')
|
27
30
|
|
28
|
-
|
29
|
-
end
|
31
|
+
adapter.should_receive(:store_tags).with(["dashboard", "settings"])
|
30
32
|
|
31
|
-
|
32
|
-
|
33
|
-
|
33
|
+
subject.store_fragment('fragment-key', 'dashboard', 'settings')
|
34
|
+
end
|
35
|
+
end
|
34
36
|
|
35
|
-
|
37
|
+
describe "Cashier notifications" do
|
38
|
+
let(:notification_system) { ActiveSupport::Notifications }
|
39
|
+
|
40
|
+
it "should raise a callback when I call store_fragment" do
|
41
|
+
notification_system.should_receive(:instrument).with("store_fragment.cashier", :data => ["foo", ["bar"]])
|
42
|
+
subject.store_fragment("foo", "bar")
|
43
|
+
end
|
36
44
|
|
37
|
-
|
38
|
-
|
45
|
+
it "should raise a callback method when I call clear" do
|
46
|
+
notification_system.should_receive(:instrument).with("clear.cashier")
|
47
|
+
subject.clear
|
39
48
|
end
|
40
49
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
50
|
+
it "should raise a callback method when I call expire" do
|
51
|
+
notification_system.should_receive(:instrument).with("expire.cashier", :data => ["some_tag"])
|
52
|
+
subject.expire("some_tag")
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe "#expire" do
|
57
|
+
before do
|
58
|
+
subject.store_fragment('fragment-key', 'dashboard')
|
59
|
+
end
|
45
60
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
61
|
+
it "should remove delete the fragment key" do
|
62
|
+
adapter.should_receive(:get_fragments_for_tag).with('dashboard').and_return(["fragment-key"])
|
63
|
+
adapter.should_receive(:delete_tag).with('dashboard')
|
64
|
+
adapter.should_receive(:remove_tags).with(['dashboard'])
|
50
65
|
|
51
|
-
|
52
|
-
|
66
|
+
subject.expire('dashboard')
|
67
|
+
end
|
53
68
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
69
|
+
it "should remove the tag" do
|
70
|
+
adapter.should_receive(:get_fragments_for_tag).with('dashboard').and_return([])
|
71
|
+
adapter.should_receive(:delete_tag).with('dashboard')
|
72
|
+
adapter.should_receive(:remove_tags).with(['dashboard'])
|
58
73
|
|
59
|
-
|
60
|
-
|
74
|
+
subject.expire('dashboard')
|
75
|
+
end
|
61
76
|
|
62
|
-
|
63
|
-
|
64
|
-
|
77
|
+
it "should remove the tag from the list of tracked tags" do
|
78
|
+
adapter.should_receive(:get_fragments_for_tag).with('dashboard').and_return(['fragment-key'])
|
79
|
+
adapter.should_receive(:delete_tag).with('dashboard')
|
65
80
|
|
66
|
-
|
67
|
-
end
|
81
|
+
subject.expire('dashboard')
|
68
82
|
end
|
83
|
+
end
|
69
84
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
85
|
+
describe "#tags" do
|
86
|
+
it "should return a list of active tags" do
|
87
|
+
subject.store_fragment('key1', 'dashboard')
|
88
|
+
subject.store_fragment('key2', 'settings')
|
89
|
+
subject.store_fragment('key3', 'email')
|
75
90
|
|
76
|
-
|
77
|
-
end
|
91
|
+
subject.tags.should eql(%w(dashboard settings email))
|
78
92
|
end
|
93
|
+
end
|
79
94
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
95
|
+
describe '#clear' do
|
96
|
+
before(:each) do
|
97
|
+
subject.store_fragment('key1', 'dashboard')
|
98
|
+
subject.store_fragment('key2', 'settings')
|
99
|
+
subject.store_fragment('key3', 'email')
|
100
|
+
end
|
86
101
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
102
|
+
it "should expire all tags" do
|
103
|
+
adapter.should_receive(:clear)
|
104
|
+
subject.clear
|
105
|
+
end
|
91
106
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
end
|
107
|
+
it "should clear the list of tracked tags" do
|
108
|
+
subject.clear
|
109
|
+
adapter.tags.should == []
|
96
110
|
end
|
111
|
+
end
|
97
112
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
end
|
113
|
+
describe '#keys' do
|
114
|
+
it "should return an array of all the tracked keys" do
|
115
|
+
adapter.should_receive(:keys).and_return(%w(key1 key2 key3))
|
116
|
+
subject.keys.should eql(%w(key1 key2 key3))
|
103
117
|
end
|
118
|
+
end
|
104
119
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
end
|
120
|
+
describe '#keys_for' do
|
121
|
+
it "should return an array of all the keys for the tag" do
|
122
|
+
adapter.should_receive(:get_fragments_for_tag).with('dashboard').and_return(%w(key1 key2 key3))
|
123
|
+
subject.keys_for('dashboard').should eql(%w(key1 key2 key3))
|
110
124
|
end
|
111
125
|
end
|
126
|
+
|
127
|
+
it "should allow me to set the adapter" do
|
128
|
+
subject.respond_to?(:adapter=).should be_true
|
129
|
+
end
|
130
|
+
|
131
|
+
it "shold allow to get the adapter" do
|
132
|
+
subject.respond_to?(:adapter).should be_true
|
133
|
+
end
|
112
134
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'simplecov'
|
2
2
|
require 'redis'
|
3
|
+
require 'dalli'
|
3
4
|
|
4
5
|
SimpleCov.start
|
5
6
|
|
@@ -28,8 +29,11 @@ RSpec.configure do |config|
|
|
28
29
|
"port" => 6397,
|
29
30
|
"dir" => Rails.root.join('tmp', 'cache'),
|
30
31
|
}.map { |k, v| "#{k} #{v}" }.join('\n')
|
32
|
+
|
31
33
|
`echo '#{redis_options}' | redis-server -`
|
32
34
|
|
35
|
+
sleep 0.25
|
36
|
+
|
33
37
|
Cashier::Adapters::RedisStore.redis = Redis.new(:host => '127.0.0.1', :port => 6397)
|
34
38
|
end
|
35
39
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cashier
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-12-14 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rails
|
16
|
-
requirement:
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,10 +21,15 @@ dependencies:
|
|
21
21
|
version: '3.0'
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements:
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '3.0'
|
25
30
|
- !ruby/object:Gem::Dependency
|
26
31
|
name: rspec
|
27
|
-
requirement:
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
28
33
|
none: false
|
29
34
|
requirements:
|
30
35
|
- - ! '>='
|
@@ -32,10 +37,15 @@ dependencies:
|
|
32
37
|
version: '0'
|
33
38
|
type: :development
|
34
39
|
prerelease: false
|
35
|
-
version_requirements:
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
36
46
|
- !ruby/object:Gem::Dependency
|
37
47
|
name: rspec-rails
|
38
|
-
requirement:
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
39
49
|
none: false
|
40
50
|
requirements:
|
41
51
|
- - ! '>='
|
@@ -43,10 +53,15 @@ dependencies:
|
|
43
53
|
version: '0'
|
44
54
|
type: :development
|
45
55
|
prerelease: false
|
46
|
-
version_requirements:
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
47
62
|
- !ruby/object:Gem::Dependency
|
48
63
|
name: dalli
|
49
|
-
requirement:
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
50
65
|
none: false
|
51
66
|
requirements:
|
52
67
|
- - ! '>='
|
@@ -54,10 +69,15 @@ dependencies:
|
|
54
69
|
version: '0'
|
55
70
|
type: :development
|
56
71
|
prerelease: false
|
57
|
-
version_requirements:
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
58
78
|
- !ruby/object:Gem::Dependency
|
59
79
|
name: simplecov
|
60
|
-
requirement:
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
61
81
|
none: false
|
62
82
|
requirements:
|
63
83
|
- - ! '>='
|
@@ -65,10 +85,15 @@ dependencies:
|
|
65
85
|
version: '0'
|
66
86
|
type: :development
|
67
87
|
prerelease: false
|
68
|
-
version_requirements:
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
69
94
|
- !ruby/object:Gem::Dependency
|
70
95
|
name: redis
|
71
|
-
requirement:
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
72
97
|
none: false
|
73
98
|
requirements:
|
74
99
|
- - ~>
|
@@ -76,7 +101,12 @@ dependencies:
|
|
76
101
|
version: 2.2.0
|
77
102
|
type: :development
|
78
103
|
prerelease: false
|
79
|
-
version_requirements:
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ~>
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: 2.2.0
|
80
110
|
description: Associate different cached content with a tag, then expire by tag instead
|
81
111
|
of key
|
82
112
|
email:
|
@@ -94,12 +124,10 @@ files:
|
|
94
124
|
- lib/cashier.rb
|
95
125
|
- lib/cashier/adapters/cache_store.rb
|
96
126
|
- lib/cashier/adapters/redis_store.rb
|
97
|
-
- lib/cashier/application_controller.rb
|
98
127
|
- lib/cashier/cucumber.rb
|
99
128
|
- lib/cashier/matchers.rb
|
100
129
|
- lib/cashier/railtie.rb
|
101
130
|
- lib/cashier/version.rb
|
102
|
-
- spec/controllers/application_controller_spec.rb
|
103
131
|
- spec/dummy/.gitignore
|
104
132
|
- spec/dummy/Gemfile
|
105
133
|
- spec/dummy/Gemfile.lock
|
@@ -144,8 +172,10 @@ files:
|
|
144
172
|
- spec/dummy/test/performance/browsing_test.rb
|
145
173
|
- spec/dummy/test/test_helper.rb
|
146
174
|
- spec/dummy/vendor/plugins/.gitkeep
|
147
|
-
- spec/
|
148
|
-
- spec/
|
175
|
+
- spec/integration/rails_cache_integration_spec.rb
|
176
|
+
- spec/integration/rails_configuration_spec.rb
|
177
|
+
- spec/lib/cashier/adapters/cache_store_spec.rb
|
178
|
+
- spec/lib/cashier/adapters/redis_store_spec.rb
|
149
179
|
- spec/lib/cashier_spec.rb
|
150
180
|
- spec/spec_helper.rb
|
151
181
|
homepage: https://github.com/threadedlabs/cashier
|
@@ -162,7 +192,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
162
192
|
version: '0'
|
163
193
|
segments:
|
164
194
|
- 0
|
165
|
-
hash:
|
195
|
+
hash: 2649723664706912496
|
166
196
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
167
197
|
none: false
|
168
198
|
requirements:
|
@@ -171,15 +201,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
171
201
|
version: '0'
|
172
202
|
segments:
|
173
203
|
- 0
|
174
|
-
hash:
|
204
|
+
hash: 2649723664706912496
|
175
205
|
requirements: []
|
176
206
|
rubyforge_project:
|
177
|
-
rubygems_version: 1.8.
|
207
|
+
rubygems_version: 1.8.24
|
178
208
|
signing_key:
|
179
209
|
specification_version: 3
|
180
210
|
summary: Tag based caching for Rails using Redis or Memcached
|
181
211
|
test_files:
|
182
|
-
- spec/controllers/application_controller_spec.rb
|
183
212
|
- spec/dummy/.gitignore
|
184
213
|
- spec/dummy/Gemfile
|
185
214
|
- spec/dummy/Gemfile.lock
|
@@ -224,7 +253,9 @@ test_files:
|
|
224
253
|
- spec/dummy/test/performance/browsing_test.rb
|
225
254
|
- spec/dummy/test/test_helper.rb
|
226
255
|
- spec/dummy/vendor/plugins/.gitkeep
|
227
|
-
- spec/
|
228
|
-
- spec/
|
256
|
+
- spec/integration/rails_cache_integration_spec.rb
|
257
|
+
- spec/integration/rails_configuration_spec.rb
|
258
|
+
- spec/lib/cashier/adapters/cache_store_spec.rb
|
259
|
+
- spec/lib/cashier/adapters/redis_store_spec.rb
|
229
260
|
- spec/lib/cashier_spec.rb
|
230
261
|
- spec/spec_helper.rb
|
@@ -1,28 +0,0 @@
|
|
1
|
-
# Hooks into ApplicationController's write_fragment method.
|
2
|
-
# write_fragment is used for action and fragment caching.
|
3
|
-
# Create an alias method chain to call our customer method
|
4
|
-
# which stores the associated key with the tag in a
|
5
|
-
# Redis Set. Then we can expire all those keys from anywhere
|
6
|
-
# in the code using Rails.cache.delete
|
7
|
-
#
|
8
|
-
# I use alias_method_chain instead of calling 'super'
|
9
|
-
# because there is a very rare case where someone
|
10
|
-
# may have redfined 'write_fragment' in their own
|
11
|
-
# controllers. Using an alias method chain
|
12
|
-
# keeps those methods intact.
|
13
|
-
|
14
|
-
class ApplicationController < ActionController::Base
|
15
|
-
def write_fragment_with_tagged_key(key, content, options = nil)
|
16
|
-
if options && options[:tag] && Cashier.perform_caching?
|
17
|
-
tags = case options[:tag].class.to_s
|
18
|
-
when 'Proc', 'Lambda'
|
19
|
-
options[:tag].call(self)
|
20
|
-
else
|
21
|
-
options[:tag]
|
22
|
-
end
|
23
|
-
Cashier.store_fragment fragment_cache_key(key), *tags
|
24
|
-
end
|
25
|
-
write_fragment_without_tagged_key(key, content, options)
|
26
|
-
end
|
27
|
-
alias_method_chain :write_fragment, :tagged_key
|
28
|
-
end
|
@@ -1,23 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe ApplicationController do
|
4
|
-
it "should be able to tag framgents" do
|
5
|
-
Cashier.should_receive(:store_fragment).with('views/key', 'tag')
|
6
|
-
controller.write_fragment('key', 'content', :tag => 'tag')
|
7
|
-
end
|
8
|
-
|
9
|
-
it "should be able write a fragment with multiple tags" do
|
10
|
-
Cashier.should_receive(:store_fragment).with('views/key', 'tag1', 'tag2')
|
11
|
-
controller.write_fragment('key', 'content', :tag => %w(tag1 tag2))
|
12
|
-
end
|
13
|
-
|
14
|
-
it "should able to create a tag with a proc" do
|
15
|
-
Cashier.should_receive(:store_fragment).with('views/key', 'tag')
|
16
|
-
controller.write_fragment('key', 'content', :tag => proc {|c| 'tag' })
|
17
|
-
end
|
18
|
-
|
19
|
-
it "should able to create a tag with a lambda" do
|
20
|
-
Cashier.should_receive(:store_fragment).with('views/key', 'tag')
|
21
|
-
controller.write_fragment('key', 'content', :tag => lambda {|c| 'tag' })
|
22
|
-
end
|
23
|
-
end
|