sequel-cacheable 0.0.7 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,223 +1,30 @@
1
1
  require 'sequel'
2
- require 'hashr'
3
2
  require 'msgpack'
4
3
 
4
+ require 'sequel-cacheable/version'
5
+ require 'sequel-cacheable/packer'
6
+ require 'sequel-cacheable/driver'
7
+ require 'sequel-cacheable/class_methods'
8
+ require 'sequel-cacheable/instance_methods'
9
+ require 'sequel-cacheable/dataset_methods'
10
+
5
11
  module Sequel::Plugins
6
12
  module Cacheable
7
13
  def self.configure(model, store, options = {})
8
14
  model.instance_eval do
9
- @cache_store = store
10
- @cache_store_type = Hashr.new({
11
- :set_with_ttl => store.respond_to?(:set) ? store.method(:set).arity != 2 : false,
12
- :delete_method => (
13
- (store.respond_to?(:del) && :del) ||
14
- (store.respond_to?(:delete) && :delete) ||
15
- (raise NoMethodError, "#{store.class} is not implemented delete method")
16
- )
17
- })
18
- @cache_options = Hashr.new(options, {
15
+ @cache_options = {
19
16
  :ttl => 3600,
20
- :ignore_exception => false,
21
17
  :pack_lib => MessagePack,
22
- :query_cache => store.respond_to?(:keys)
23
- })
24
- end
25
- end
26
-
27
- module ClassMethods
28
- attr_reader :cache_store
29
- attr_reader :cache_store_type
30
- attr_reader :cache_options
31
-
32
- def inherited(subclass)
33
- super
34
- cache = [@cache_store, @cache_store_type, @cache_options]
35
- subclass.instance_eval do
36
- @cache_store = cache[0]
37
- @cache_store_type = cache[1]
38
- @cache_options = cache[2]
39
- end
40
- end
41
-
42
- def cache_set(key, obj, ttl = nil)
43
- return obj if obj.nil?
44
-
45
- ttl = ttl || cache_options.ttl
46
- if cache_options.pack_lib?
47
- obj = obj.map{|o| o.id } if obj.kind_of?(Array)
48
- obj = cache_options.pack_lib.pack(obj)
49
- end
50
-
51
- args = [key, obj]
52
- args << ttl if cache_store_type.set_with_ttl?
53
- cache_store.set(*args)
54
- unless cache_store_type.set_with_ttl?
55
- cache_store.expire(key, ttl)
56
- end
57
- end
58
-
59
- def cache_get(key)
60
- if cache_options.ignore_exceptions?
61
- obj = cache_store.get(key) rescue nil
62
- else
63
- obj = cache_store.get(key)
64
- end
65
-
66
- if obj && cache_options.pack_lib?
67
- obj = restore_cache(cache_options.pack_lib.unpack(obj))
68
- end
69
-
70
- obj
71
- end
72
-
73
- def cache_mget(*keys)
74
- if cache_options.ignore_exceptions?
75
- objs = cache_store.mget(*keys) rescue nil
76
- else
77
- objs = cache_store.mget(*keys)
78
- end
79
-
80
- if objs && cache_options.pack_lib?
81
- objs.map!{|obj|
82
- key = keys.shift
83
- (obj && restore_cache(cache_options.pack_lib.unpack(obj))) ||
84
- model[key.sub(/^#{model}::/, '')]
85
- }
86
- end
87
-
88
- objs || []
89
- end
90
-
91
- def cache_del(key)
92
- cache_store.send(cache_store_type.delete_method, key)
93
- end
94
-
95
- def cache_set_get(key, ttl = nil)
96
- if (val = cache_get(key)).nil?
97
- val = yield
98
- cache_set(key, val, ttl)
99
- end
100
- val
101
- end
102
-
103
- def restore_cache(object)
104
- return object if object.nil?
105
-
106
- return cache_mget(*object.map{|id| "#{model}::#{id}" }) if object.kind_of?(Array)
107
-
108
- object.keys.each do | key |
109
- value = object.delete(key)
110
- key = key.to_sym rescue key
111
- case db_schema[key][:type]
112
- when :date
113
- value = Date.new(*value)
114
- when :time
115
- value = Sequel::SQLTime.at(value[0], value[1])
116
- when :datetime
117
- value = Time.at(value[0], value[1])
118
- when :decimal
119
- value = BigDecimal.new(value)
120
- when :integer
121
- value = value.to_i
122
- end
123
- object[key] = value
124
- end
125
- new(object, true)
126
- end
127
-
128
- def clear_query_cache
129
- return unless cache_options.query_cache?
130
- cache_store.keys("#{model.name}::Query::*").each do | key |
131
- cache_del(key)
132
- end
133
- end
134
-
135
- def primary_key_lookup(key)
136
- cache_set_get("#{model}::#{key}") do
137
- super(key)
138
- end
139
- end
140
- end
141
-
142
- module InstanceMethods
143
- def msgpack_hash
144
- hash = {}
145
- @values.each_pair do | key, value |
146
- case value
147
- when Date
148
- value = [value.year, value.mon, value.mday, value.start]
149
- when Sequel::SQLTime, Time
150
- value = [value.to_i, value.usec]
151
- when BigDecimal, Bignum
152
- value = value.to_s
153
- end
154
- hash[key] = value
155
- end
156
- hash
157
- end
158
-
159
- def to_msgpack(*args)
160
- msgpack_hash.to_msgpack
161
- end
162
-
163
- def after_initialize
164
- store_cache unless id.nil?
165
- super
166
- end
167
-
168
- def after_update
169
- restore_cache
170
- super
171
- end
172
-
173
- def delete
174
- delete_cache
175
- super
176
- end
177
-
178
- def destroy(*args)
179
- delete_cache
180
- super(*args)
181
- end
182
-
183
- def store_cache
184
- model.cache_set(cache_key, self)
185
- end
186
-
187
- def delete_cache
188
- model.cache_del(cache_key)
189
- model.clear_query_cache
190
- end
191
-
192
- def restore_cache
193
- delete_cache
194
- store_cache
195
- end
196
-
197
- def cache_key
198
- "#{self.class.name}::#{self.id.to_s}"
199
- end
200
- end
201
-
202
- module DatasetMethods
203
- def all(*args)
204
- if model.cache_options.query_cache? && @row_proc.kind_of?(Class) && @row_proc.included_modules.include?(Sequel::Model::InstanceMethods)
205
- @row_proc.cache_set_get(query_to_cache_key) { super(*args) }
206
- else
207
- super(*args)
208
- end
209
- end
210
-
211
- def first(*args)
212
- if model.cache_options.query_cache? && @row_proc.kind_of?(Class) && @row_proc.included_modules.include?(Sequel::Model::InstanceMethods)
213
- @row_proc.cache_set_get(query_to_cache_key) { super(*args) }
214
- else
215
- super(*args)
216
- end
217
- end
218
-
219
- def query_to_cache_key
220
- model.name + '::Query::' + select_sql.gsub(/ /, '_')
18
+ :query_cache => false
19
+ }.merge(options)
20
+ @cache_driver = Driver.factory(
21
+ store,
22
+ Packer.factory(@cache_options[:pack_lib])
23
+ )
24
+ @caches = {
25
+ :instance => [],
26
+ :query => []
27
+ }
221
28
  end
222
29
  end
223
30
  end
@@ -1,98 +1,22 @@
1
- # Generated by jeweler
2
- # DO NOT EDIT THIS FILE DIRECTLY
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
- # -*- encoding: utf-8 -*-
1
+ lib = File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'sequel-cacheable/version'
5
4
 
6
- Gem::Specification.new do |s|
7
- s.name = "sequel-cacheable"
8
- s.version = "0.0.7"
5
+ Gem::Specification.new do |gem|
6
+ gem.name = "sequel-cacheable"
7
+ gem.version = Sequel::Plugins::Cacheable::VERSION
8
+ gem.authors = ["Sho Kusano"]
9
+ gem.email = ["rosylilly@aduca.org"]
10
+ gem.description = %q{This plug-in caching mechanism to implement the Model of the Sequel}
11
+ gem.summary = %q{This plug-in caching mechanism to implement the Model of the Sequel}
12
+ gem.homepage = "https://github.com/rosylilly/sequel-cacheable"
13
+ gem.license = "MIT"
9
14
 
10
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = ["Sho Kusano <rosylilly>"]
12
- s.date = "2012-05-01"
13
- s.description = "This plug-in caching mechanism to implement the Model of the Sequel"
14
- s.email = "rosylilly@aduca.org"
15
- s.extra_rdoc_files = [
16
- "LICENSE.txt",
17
- "README.rdoc"
18
- ]
19
- s.files = [
20
- ".document",
21
- ".rspec",
22
- ".travis.yml",
23
- "Gemfile",
24
- "Gemfile.lock",
25
- "Guardfile",
26
- "LICENSE.txt",
27
- "README.rdoc",
28
- "Rakefile",
29
- "VERSION",
30
- "lib/sequel-cacheable.rb",
31
- "sequel-cacheable.gemspec",
32
- "spec/lib/sequel-cacheable_spec.rb",
33
- "spec/spec_helper.rb"
34
- ]
35
- s.homepage = "http://github.com/rosylilly/sequel-cacheable"
36
- s.licenses = ["MIT"]
37
- s.require_paths = ["lib"]
38
- s.rubygems_version = "1.8.22"
39
- s.summary = "This plug-in caching mechanism to implement the Model of the Sequel"
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
40
19
 
41
- if s.respond_to? :specification_version then
42
- s.specification_version = 3
43
-
44
- if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
45
- s.add_runtime_dependency(%q<sequel>, ["~> 3.34"])
46
- s.add_runtime_dependency(%q<msgpack>, ["~> 0.4.6"])
47
- s.add_runtime_dependency(%q<hashr>, ["~> 0.0.19"])
48
- s.add_development_dependency(%q<bundler>, [">= 1.0.0"])
49
- s.add_development_dependency(%q<rspec>, ["~> 2"])
50
- s.add_development_dependency(%q<yard>, ["~> 0.7"])
51
- s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
52
- s.add_development_dependency(%q<jeweler>, ["~> 1.8.3"])
53
- s.add_development_dependency(%q<simplecov>, ["~> 0.6.1"])
54
- s.add_development_dependency(%q<guard>, ["~> 1.0.0"])
55
- s.add_development_dependency(%q<guard-rspec>, ["~> 0.7"])
56
- s.add_development_dependency(%q<growl>, [">= 0"])
57
- s.add_development_dependency(%q<sqlite3>, [">= 0"])
58
- s.add_development_dependency(%q<memcache>, [">= 0"])
59
- s.add_development_dependency(%q<redis>, [">= 0"])
60
- s.add_development_dependency(%q<queencheck>, [">= 0"])
61
- else
62
- s.add_dependency(%q<sequel>, ["~> 3.34"])
63
- s.add_dependency(%q<msgpack>, ["~> 0.4.6"])
64
- s.add_dependency(%q<hashr>, ["~> 0.0.19"])
65
- s.add_dependency(%q<bundler>, [">= 1.0.0"])
66
- s.add_dependency(%q<rspec>, ["~> 2"])
67
- s.add_dependency(%q<yard>, ["~> 0.7"])
68
- s.add_dependency(%q<rdoc>, ["~> 3.12"])
69
- s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
70
- s.add_dependency(%q<simplecov>, ["~> 0.6.1"])
71
- s.add_dependency(%q<guard>, ["~> 1.0.0"])
72
- s.add_dependency(%q<guard-rspec>, ["~> 0.7"])
73
- s.add_dependency(%q<growl>, [">= 0"])
74
- s.add_dependency(%q<sqlite3>, [">= 0"])
75
- s.add_dependency(%q<memcache>, [">= 0"])
76
- s.add_dependency(%q<redis>, [">= 0"])
77
- s.add_dependency(%q<queencheck>, [">= 0"])
78
- end
79
- else
80
- s.add_dependency(%q<sequel>, ["~> 3.34"])
81
- s.add_dependency(%q<msgpack>, ["~> 0.4.6"])
82
- s.add_dependency(%q<hashr>, ["~> 0.0.19"])
83
- s.add_dependency(%q<bundler>, [">= 1.0.0"])
84
- s.add_dependency(%q<rspec>, ["~> 2"])
85
- s.add_dependency(%q<yard>, ["~> 0.7"])
86
- s.add_dependency(%q<rdoc>, ["~> 3.12"])
87
- s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
88
- s.add_dependency(%q<simplecov>, ["~> 0.6.1"])
89
- s.add_dependency(%q<guard>, ["~> 1.0.0"])
90
- s.add_dependency(%q<guard-rspec>, ["~> 0.7"])
91
- s.add_dependency(%q<growl>, [">= 0"])
92
- s.add_dependency(%q<sqlite3>, [">= 0"])
93
- s.add_dependency(%q<memcache>, [">= 0"])
94
- s.add_dependency(%q<redis>, [">= 0"])
95
- s.add_dependency(%q<queencheck>, [">= 0"])
96
- end
20
+ gem.add_dependency 'sequel', '~> 3.42'
21
+ gem.add_dependency 'msgpack', '~> 0.5.1'
97
22
  end
98
-
@@ -0,0 +1,8 @@
1
+ require 'spec_helper'
2
+
3
+ describe Sequel::Plugins::Cacheable::DalliDriver do
4
+ let(:store) { DalliCli }
5
+
6
+ include_examples :driver
7
+ end
8
+
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe Sequel::Plugins::Cacheable::MemcacheDriver do
4
+ let(:store) { MemcacheCli }
5
+
6
+ include_examples :driver
7
+ end
@@ -0,0 +1,8 @@
1
+ require 'spec_helper'
2
+
3
+ describe Sequel::Plugins::Cacheable::RedisDriver do
4
+ let(:store) { RedisCli }
5
+
6
+ include_examples :driver
7
+ end
8
+
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+
3
+ describe Sequel::Plugins::Cacheable::Driver do
4
+ let(:store) { RedisCli }
5
+
6
+ include_examples :driver
7
+
8
+ describe '.factory' do
9
+ subject { described_class.factory(store) }
10
+
11
+ context 'when Memcache' do
12
+ let(:store) { MemcacheCli }
13
+
14
+ it { should be_a(Sequel::Plugins::Cacheable::MemcacheDriver) }
15
+ end
16
+
17
+ context 'when Dalli' do
18
+ let(:store) { DalliCli }
19
+
20
+ it { should be_a(Sequel::Plugins::Cacheable::DalliDriver) }
21
+ end
22
+
23
+ context 'when Redis' do
24
+ let(:store) { RedisCli }
25
+
26
+ it { should be_a(Sequel::Plugins::Cacheable::RedisDriver) }
27
+ end
28
+
29
+ context 'when Unkown Store' do
30
+ let(:store) { mock }
31
+
32
+ it { should be_a(Sequel::Plugins::Cacheable::Driver) }
33
+ end
34
+ end
35
+ end
@@ -1,247 +1,5 @@
1
1
  require 'spec_helper'
2
2
 
3
- class RedisModel < Sequel::Model(:spec)
4
- end
5
-
6
- class MemcacheModel < Sequel::Model(:spec)
7
- plugin :cacheable, MemcacheCli
8
- end
9
-
10
- QueenCheck::Arbitrary(Float, Fixnum.arbitrary.gen)
11
- QueenCheck::Arbitrary(String, QueenCheck::Gen.quadratic(200).bind { | length |
12
- if length.zero?
13
- QueenCheck::Gen.unit("")
14
- else
15
- QueenCheck::Gen.rand.resize(1, length).fmap { | r |
16
- str = []
17
- r.times { str << QueenCheck::Alphabet.arbitrary.gen.value(0)[0] }
18
- str.join()
19
- }
20
- end
21
- })
22
-
23
3
  describe Sequel::Plugins::Cacheable do
24
- QueenCheck("Generate Test Datas",
25
- String, Fixnum, Float, Bignum, Boolean) do |string, fixnum, float, bignum, boolean|
26
- float = float * 1.0 / (10 ** (rand(4) + 1))
27
- RedisModel.create({
28
- :string => string,
29
- :integer => fixnum,
30
- :float => float,
31
- :bignum => bignum,
32
- :numeric => BigDecimal(float.to_s),
33
- :date => Date.today,
34
- :datetime => DateTime.now,
35
- :time => Time.now,
36
- :bool => boolean
37
- })
38
- true
39
- end
40
-
41
- context "NoNameClass" do
42
- before {
43
- @model = Class.new(Sequel::Model(:rspec))
44
- }
45
-
46
- it "should raise NoMethodError if the delete method is not found" do
47
- proc {
48
- @model.plugin :cacheable, 1
49
- }.should raise_error(NoMethodError)
50
- end
51
- end
52
-
53
- context MemcacheModel do
54
- subject { MemcacheModel }
55
-
56
- its("cache_store") { should == MemcacheCli }
57
- its("cache_store_type.set_with_ttl?") { should be_true }
58
- its("cache_store_type.delete_method") { should == :delete }
59
- its("cache_options.ttl") { should == 3600 }
60
- its("cache_options.ignore_exceptions") { should be_false }
61
- its("cache_options.pack_lib") { should == MessagePack }
62
-
63
- describe "cache control" do
64
- it "set" do
65
- MemcacheModel.cache_set('MemcacheModel::test', MemcacheModel[1])
66
- end
67
-
68
- it "get" do
69
- cache = MemcacheModel.cache_get('MemcacheModel::test')
70
- cache.should == MemcacheModel[1]
71
- end
72
-
73
- it "del" do
74
- MemcacheModel.cache_del('MemcacheModel::test')
75
- MemcacheModel.cache_get('MemcacheModel::test').should be_nil
76
- end
77
- end
78
-
79
- describe "act as cache" do
80
- context "Model[40]" do
81
- before do
82
- @obj = MemcacheModel[40]
83
- end
84
-
85
- it "stored cache" do
86
- MemcacheModel.cache_get(@obj.cache_key).should == @obj
87
- end
88
-
89
- it "restoreble cache data" do
90
- cached = MessagePack.unpack(MemcacheCli.get(@obj.cache_key))
91
- cached['string'].should == @obj.string
92
- Time.at(cached['time'][0], cached['time'][1]).should === @obj.time
93
- end
94
-
95
- it "update cache data" do
96
- @obj.string = 'modified++'
97
- cached = MessagePack.unpack(MemcacheCli.get(@obj.cache_key))
98
- cached['string'].should_not == @obj.string
99
- @obj.save
100
- cached = MessagePack.unpack(MemcacheCli.get(@obj.cache_key))
101
- cached['string'].should == @obj.string
102
- end
103
-
104
- it "delete cache data" do
105
- cache_key = @obj.cache_key; @obj.delete
106
- MemcacheCli.get(cache_key).should be_nil
107
- MemcacheModel[40].should be_nil
108
- end
109
-
110
- it "destroy cache data" do
111
- @obj = MemcacheModel[41]
112
- cache_key = @obj.cache_key; @obj.destroy
113
- MemcacheCli.get(cache_key).should be_nil
114
- MemcacheModel[41].should be_nil
115
- end
116
- end
117
- end
118
-
119
- describe "query cache" do
120
- it "get" do
121
- MemcacheModel.all.should == MemcacheModel.all
122
- end
123
-
124
- pending "not supported query cache on Memcache Client"
125
- end
126
- end
127
-
128
- context RedisModel do
129
- subject { RedisModel }
130
-
131
- it "should raise LoadError if the plugin is not found" do
132
- proc{ RedisModel.plugin :something_or_other}.should raise_error(LoadError)
133
- end
134
-
135
- its("columns") { should == [:id,:string,:integer,:float,:bignum,:numeric,:date,:datetime,:time,:bool] }
136
-
137
- its("plugins") {
138
- should_not include(Sequel::Plugins::Cacheable)
139
- RedisModel.plugin :cacheable, RedisCli
140
- should include(Sequel::Plugins::Cacheable)
141
- }
142
-
143
- its("cache_store") { should == RedisCli }
144
- its("cache_store_type.set_with_ttl?") { should be_false }
145
- its("cache_store_type.delete_method") { should == :del }
146
- its("cache_options.ttl") { should == 3600 }
147
- its("cache_options.ignore_exceptions") { should be_false }
148
- its("cache_options.pack_lib") { should == MessagePack }
149
-
150
- describe "cache control" do
151
- it "set" do
152
- RedisModel.cache_set('RedisModel::test', RedisModel[1])
153
- end
154
-
155
- it "get" do
156
- RedisModel.cache_get('RedisModel::test').should == RedisModel[1]
157
- end
158
-
159
- it "del" do
160
- RedisModel.cache_del('RedisModel::test')
161
- RedisModel.cache_get('RedisModel::test').should be_nil
162
- end
163
- end
164
-
165
- describe "act as cache" do
166
- context "Model[50]" do
167
- before do
168
- @obj = RedisModel[50]
169
- end
170
-
171
- it "unstored cache on new" do
172
- before_count = RedisCli.keys.length
173
- RedisModel.new({:string => 'test'})
174
- RedisCli.keys.length.should == before_count
175
- end
176
-
177
- it "stored cache" do
178
- RedisModel.cache_get(@obj.cache_key).should == @obj
179
- end
180
-
181
- it "restoreble cache data" do
182
- cached = MessagePack.unpack(RedisCli.get(@obj.cache_key))
183
- cached['string'].should == @obj.string
184
- Time.at(cached['time'][0], cached['time'][1]).should === @obj.time
185
- end
186
-
187
- it "update cache data" do
188
- @obj.string = 'modified++'
189
- cached = MessagePack.unpack(RedisCli.get(@obj.cache_key))
190
- cached['string'].should_not == @obj.string
191
- @obj.save
192
- cached = MessagePack.unpack(RedisCli.get(@obj.cache_key))
193
- cached['string'].should == @obj.string
194
- end
195
-
196
- it "delete cache data" do
197
- @obj.delete
198
- RedisCli.keys(@obj.cache_key).should == []
199
- RedisModel[50].should be_nil
200
- end
201
-
202
- it "destroy cache data" do
203
- @obj = RedisModel[51]
204
- @obj.delete
205
- RedisCli.keys(@obj.cache_key).should == []
206
- RedisModel[51].should be_nil
207
- end
208
- end
209
- end
210
-
211
- describe "query cache" do
212
- it "set and get" do
213
- RedisModel.clear_query_cache
214
- models = RedisModel.limit(3).all
215
- cache_key = RedisCli.keys('RedisModel::Query::*')[0]
216
- RedisModel.cache_get(cache_key).should == models
217
- end
218
-
219
- it "clear on update" do
220
- RedisModel.clear_query_cache
221
- RedisModel.all
222
- cache_key = RedisCli.keys('RedisModel::Query::*')
223
- cache_key.should_not be_empty
224
- RedisModel[2].save({:string => 'test++'})
225
- RedisCli.keys('RedisModel::Query::*').should be_empty
226
- end
227
-
228
- it "clear on delete" do
229
- RedisModel.clear_query_cache
230
- RedisModel.all
231
- cache_key = RedisCli.keys('RedisModel::Query::*')
232
- cache_key.should_not be_empty
233
- RedisModel[2].delete
234
- RedisCli.keys('RedisModel::Query::*').should be_empty
235
- end
236
-
237
- it "cacheout rescue" do
238
- RedisModel.clear_query_cache
239
- caches = RedisModel.limit(20).all
240
- cache_key = RedisCli.keys('RedisModel::Query::*')[0]
241
- cache = caches[rand(caches.length-1)]
242
- RedisModel.cache_del(cache.cache_key)
243
- RedisModel.cache_get(cache_key).should == caches
244
- end
245
- end
246
- end
4
+ it { should be_const_defined(:VERSION) }
247
5
  end
@@ -0,0 +1,5 @@
1
+ require 'spec_helper'
2
+
3
+ describe DalliModel do
4
+ it_should_behave_like :cacheable
5
+ end
@@ -0,0 +1,5 @@
1
+ require 'spec_helper'
2
+
3
+ describe MemcacheModel do
4
+ it_should_behave_like :cacheable
5
+ end
@@ -0,0 +1,5 @@
1
+ require 'spec_helper'
2
+
3
+ describe RedisModel do
4
+ it_should_behave_like :cacheable
5
+ end