kasket 0.6.4 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/kasket.rb +6 -34
- data/lib/kasket/read_mixin.rb +6 -6
- data/lib/kasket/reload_association_mixin.rb +1 -1
- data/lib/kasket/write_mixin.rb +4 -10
- data/test/cache_expiry_test.rb +12 -12
- data/test/find_one_test.rb +4 -4
- data/test/helper.rb +5 -0
- data/test/read_mixin_test.rb +6 -5
- metadata +2 -6
- data/lib/kasket/cache.rb +0 -77
- data/lib/kasket/rack_middleware.rb +0 -13
- data/test/cache_test.rb +0 -102
data/lib/kasket.rb
CHANGED
@@ -5,7 +5,6 @@ require 'active_support'
|
|
5
5
|
require 'kasket/active_record_patches'
|
6
6
|
|
7
7
|
module Kasket
|
8
|
-
autoload :Cache, 'kasket/cache'
|
9
8
|
autoload :ConfigurationMixin, 'kasket/configuration_mixin'
|
10
9
|
autoload :ReloadAssociationMixin, 'kasket/reload_association_mixin'
|
11
10
|
autoload :RackMiddleware, 'kasket/rack_middleware'
|
@@ -15,17 +14,13 @@ module Kasket
|
|
15
14
|
|
16
15
|
class Version
|
17
16
|
MAJOR = 0
|
18
|
-
MINOR =
|
19
|
-
PATCH =
|
17
|
+
MINOR = 7
|
18
|
+
PATCH = 0
|
20
19
|
STRING = "#{MAJOR}.#{MINOR}.#{PATCH}"
|
21
20
|
end
|
22
21
|
|
23
22
|
module_function
|
24
23
|
|
25
|
-
def cache
|
26
|
-
Thread.current["kasket_cache"] ||= Cache.new
|
27
|
-
end
|
28
|
-
|
29
24
|
def setup(options = {})
|
30
25
|
CONFIGURATION[:max_collection_size] = options[:max_collection_size] if options[:max_collection_size]
|
31
26
|
|
@@ -33,34 +28,11 @@ module Kasket
|
|
33
28
|
ActiveRecord::Associations::BelongsToAssociation.send(:include, Kasket::ReloadAssociationMixin)
|
34
29
|
ActiveRecord::Associations::BelongsToPolymorphicAssociation.send(:include, Kasket::ReloadAssociationMixin)
|
35
30
|
ActiveRecord::Associations::HasOneThroughAssociation.send(:include, Kasket::ReloadAssociationMixin)
|
31
|
+
end
|
36
32
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
ApplicationController.before_filter do
|
41
|
-
Kasket.cache.clear_local
|
42
|
-
end
|
43
|
-
rescue NameError => e
|
44
|
-
puts('WARNING: The kasket before filter did not register (this is OK in the test environment)')
|
45
|
-
end
|
46
|
-
|
47
|
-
#sets up local cache clearing on rack
|
48
|
-
begin
|
49
|
-
ActionController::Dispatcher.middleware.use(Kasket::RackMiddleware)
|
50
|
-
rescue NameError => e
|
51
|
-
puts('WARNING: The kasket rack middleware is not in your rack stack (this is OK in the test environment)')
|
52
|
-
end
|
53
|
-
|
54
|
-
#sets up local cache clearing after each test case
|
55
|
-
begin
|
56
|
-
ActiveSupport::TestCase.class_eval do
|
57
|
-
setup :clear_cache
|
58
|
-
def clear_cache
|
59
|
-
Kasket.cache.clear_local
|
60
|
-
Rails.cache.clear if Rails.cache.respond_to?(:clear)
|
61
|
-
end
|
62
|
-
end
|
63
|
-
rescue NameError => e
|
33
|
+
def clear_local
|
34
|
+
if Rails.cache.respond_to?(:with_local_cache)
|
35
|
+
Rails.cache.send(:local_cache).try(:clear)
|
64
36
|
end
|
65
37
|
end
|
66
38
|
end
|
data/lib/kasket/read_mixin.rb
CHANGED
@@ -11,8 +11,8 @@ module Kasket
|
|
11
11
|
sql = sanitize_sql(sql)
|
12
12
|
query = kasket_parser.parse(sql) if use_kasket?
|
13
13
|
if query && has_kasket_index_on?(query[:index])
|
14
|
-
if value =
|
15
|
-
Array.wrap(value).collect! { |record| instantiate(record.
|
14
|
+
if value = Rails.cache.read(query[:key])
|
15
|
+
Array.wrap(value).collect! { |record| instantiate(record.dup) }
|
16
16
|
else
|
17
17
|
store_in_kasket(query[:key], find_by_sql_without_kasket(sql))
|
18
18
|
end
|
@@ -25,14 +25,14 @@ module Kasket
|
|
25
25
|
|
26
26
|
def store_in_kasket(key, records)
|
27
27
|
if records.size == 1
|
28
|
-
|
29
|
-
|
28
|
+
Rails.cache.write(key, records.first.instance_variable_get(:@attributes).dup)
|
29
|
+
elsif records.size <= Kasket::CONFIGURATION[:max_collection_size]
|
30
30
|
keys = records.map do |record|
|
31
31
|
key = kasket_key_for_id(record.id)
|
32
|
-
|
32
|
+
Rails.cache.write(key, record.instance_variable_get(:@attributes).dup)
|
33
33
|
key
|
34
34
|
end
|
35
|
-
|
35
|
+
Rails.cache.write(key, keys)
|
36
36
|
end
|
37
37
|
records
|
38
38
|
end
|
@@ -2,7 +2,7 @@ module Kasket
|
|
2
2
|
module ReloadAssociationMixin
|
3
3
|
def reload_with_kasket_clearing(*args)
|
4
4
|
if loaded?
|
5
|
-
|
5
|
+
Kasket.clear_local if include?(WriteMixin)
|
6
6
|
else
|
7
7
|
target_class = proxy_reflection.options[:polymorphic] ? association_class : proxy_reflection.klass
|
8
8
|
Kasket.cache.delete_matched_local(/^#{target_class.kasket_key_prefix}/) if target_class.respond_to?(:kasket_key_prefix)
|
data/lib/kasket/write_mixin.rb
CHANGED
@@ -4,7 +4,7 @@ module Kasket
|
|
4
4
|
module ClassMethods
|
5
5
|
def remove_from_kasket(ids)
|
6
6
|
Array(ids).each do |id|
|
7
|
-
|
7
|
+
Rails.cache.delete(kasket_key_for_id(id))
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
@@ -21,7 +21,7 @@ module Kasket
|
|
21
21
|
|
22
22
|
def store_in_kasket
|
23
23
|
if !readonly? && kasket_key
|
24
|
-
|
24
|
+
Rails.cache.write(kasket_key, @attributes.dup)
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
@@ -48,18 +48,12 @@ module Kasket
|
|
48
48
|
|
49
49
|
def clear_kasket_indices
|
50
50
|
kasket_keys.each do |key|
|
51
|
-
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
def clear_local_kasket_indices
|
56
|
-
kasket_keys.each do |key|
|
57
|
-
Kasket.cache.delete_local(key)
|
51
|
+
Rails.cache.delete(key)
|
58
52
|
end
|
59
53
|
end
|
60
54
|
|
61
55
|
def reload_with_kasket_clearing(*args)
|
62
|
-
|
56
|
+
Kasket.clear_local
|
63
57
|
reload_without_kasket_clearing(*args)
|
64
58
|
end
|
65
59
|
end
|
data/test/cache_expiry_test.rb
CHANGED
@@ -17,11 +17,11 @@ class CacheExpiryTest < ActiveSupport::TestCase
|
|
17
17
|
end
|
18
18
|
|
19
19
|
should "clear all indices for instance when deleted" do
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
20
|
+
Rails.cache.expects(:delete).with(Post.kasket_key_prefix + "id=#{@post.id}")
|
21
|
+
Rails.cache.expects(:delete).with(Post.kasket_key_prefix + "title='#{@post.title}'")
|
22
|
+
Rails.cache.expects(:delete).with(Post.kasket_key_prefix + "title='#{@post.title}'/first")
|
23
|
+
Rails.cache.expects(:delete).with(Post.kasket_key_prefix + "blog_id=#{@post.blog_id}/id=#{@post.id}")
|
24
|
+
Rails.cache.expects(:delete).never
|
25
25
|
|
26
26
|
@post.destroy
|
27
27
|
end
|
@@ -33,13 +33,13 @@ class CacheExpiryTest < ActiveSupport::TestCase
|
|
33
33
|
end
|
34
34
|
|
35
35
|
should "clear all indices for instance when updated" do
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
36
|
+
Rails.cache.expects(:delete).with(Post.kasket_key_prefix + "id=#{@post.id}")
|
37
|
+
Rails.cache.expects(:delete).with(Post.kasket_key_prefix + "title='#{@post.title}'")
|
38
|
+
Rails.cache.expects(:delete).with(Post.kasket_key_prefix + "title='#{@post.title}'/first")
|
39
|
+
Rails.cache.expects(:delete).with(Post.kasket_key_prefix + "title='new title'")
|
40
|
+
Rails.cache.expects(:delete).with(Post.kasket_key_prefix + "title='new title'/first")
|
41
|
+
Rails.cache.expects(:delete).with(Post.kasket_key_prefix + "blog_id=#{@post.blog_id}/id=#{@post.id}")
|
42
|
+
Rails.cache.expects(:delete).never
|
43
43
|
|
44
44
|
@post.title = "new title"
|
45
45
|
@post.save
|
data/test/find_one_test.rb
CHANGED
@@ -13,11 +13,11 @@ class FindOneTest < ActiveSupport::TestCase
|
|
13
13
|
end
|
14
14
|
|
15
15
|
should "only cache on indexed attributes" do
|
16
|
-
|
16
|
+
Rails.cache.expects(:read).twice
|
17
17
|
Post.find_by_id(1)
|
18
18
|
Post.find_by_id(1, :conditions => {:blog_id => 2})
|
19
19
|
|
20
|
-
|
20
|
+
Rails.cache.expects(:read).never
|
21
21
|
Post.first :conditions => {:blog_id => 2}
|
22
22
|
end
|
23
23
|
|
@@ -31,10 +31,10 @@ class FindOneTest < ActiveSupport::TestCase
|
|
31
31
|
Post.find(post.id)
|
32
32
|
assert(Rails.cache.read(post.kasket_key))
|
33
33
|
|
34
|
-
|
34
|
+
Rails.cache.expects(:read)
|
35
35
|
Post.find(post.id, :select => nil)
|
36
36
|
|
37
|
-
|
37
|
+
Rails.cache.expects(:read).never
|
38
38
|
Post.find(post.id, :select => 'title')
|
39
39
|
end
|
40
40
|
|
data/test/helper.rb
CHANGED
@@ -34,6 +34,11 @@ class ActiveSupport::TestCase
|
|
34
34
|
self.use_transactional_fixtures = true
|
35
35
|
|
36
36
|
self.use_instantiated_fixtures = false
|
37
|
+
|
38
|
+
setup :clear_cache
|
39
|
+
def clear_cache
|
40
|
+
Rails.cache.clear
|
41
|
+
end
|
37
42
|
end
|
38
43
|
|
39
44
|
ActiveSupport::TestCase.fixture_path = File.dirname(__FILE__) + "/fixtures/"
|
data/test/read_mixin_test.rb
CHANGED
@@ -10,24 +10,25 @@ class ReadMixinTest < ActiveSupport::TestCase
|
|
10
10
|
end
|
11
11
|
|
12
12
|
should "handle unsupported sql" do
|
13
|
+
Rails.cache.expects(:read).never
|
14
|
+
Rails.cache.expects(:write).never
|
13
15
|
assert_equal @records, Post.find_by_sql_with_kasket('select unsupported sql statement')
|
14
|
-
assert Kasket.cache.local.empty?
|
15
16
|
end
|
16
17
|
|
17
18
|
should "read results" do
|
18
|
-
|
19
|
-
assert_equal [ @records.first ], Post.find_by_sql('SELECT * FROM `posts` WHERE (id = 1)')
|
19
|
+
Rails.cache.write("kasket-#{Kasket::Version::STRING}/posts/version=3558/id=1", @database_results.first)
|
20
|
+
assert_equal [ @records.first ], Post.find_by_sql('SELECT * FROM `posts` WHERE (id = 1)')
|
20
21
|
end
|
21
22
|
|
22
23
|
should "store results in kasket" do
|
23
24
|
Post.find_by_sql('SELECT * FROM `posts` WHERE (id = 1)')
|
24
25
|
|
25
|
-
assert_equal @database_results.first,
|
26
|
+
assert_equal @database_results.first, Rails.cache.read("kasket-#{Kasket::Version::STRING}/posts/version=3558/id=1")
|
26
27
|
end
|
27
28
|
|
28
29
|
context "modifying results" do
|
29
30
|
setup do
|
30
|
-
|
31
|
+
Rails.cache.write("kasket-#{Kasket::Version::STRING}/posts/version=3558/id=1", @database_results.first)
|
31
32
|
@record = Post.find_by_sql('SELECT * FROM `posts` WHERE (id = 1)').first
|
32
33
|
@record.instance_variable_get(:@attributes)['id'] = 3
|
33
34
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kasket
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mick Staugaard
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date:
|
13
|
+
date: 2010-01-04 00:00:00 +01:00
|
14
14
|
default_executable:
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
@@ -70,16 +70,13 @@ files:
|
|
70
70
|
- Rakefile
|
71
71
|
- lib/kasket.rb
|
72
72
|
- lib/kasket/active_record_patches.rb
|
73
|
-
- lib/kasket/cache.rb
|
74
73
|
- lib/kasket/configuration_mixin.rb
|
75
74
|
- lib/kasket/dirty_mixin.rb
|
76
75
|
- lib/kasket/query_parser.rb
|
77
|
-
- lib/kasket/rack_middleware.rb
|
78
76
|
- lib/kasket/read_mixin.rb
|
79
77
|
- lib/kasket/reload_association_mixin.rb
|
80
78
|
- lib/kasket/write_mixin.rb
|
81
79
|
- test/cache_expiry_test.rb
|
82
|
-
- test/cache_test.rb
|
83
80
|
- test/database.yml
|
84
81
|
- test/dirty_test.rb
|
85
82
|
- test/find_one_test.rb
|
@@ -121,7 +118,6 @@ specification_version: 3
|
|
121
118
|
summary: A write back caching layer on active record
|
122
119
|
test_files:
|
123
120
|
- test/cache_expiry_test.rb
|
124
|
-
- test/cache_test.rb
|
125
121
|
- test/dirty_test.rb
|
126
122
|
- test/find_one_test.rb
|
127
123
|
- test/find_some_test.rb
|
data/lib/kasket/cache.rb
DELETED
@@ -1,77 +0,0 @@
|
|
1
|
-
module Kasket
|
2
|
-
class Cache
|
3
|
-
def initialize
|
4
|
-
clear_local
|
5
|
-
end
|
6
|
-
|
7
|
-
def read(*args)
|
8
|
-
result = @local_cache[args[0]] || Rails.cache.read(*args)
|
9
|
-
if result.is_a?(Array) && result.first.is_a?(String)
|
10
|
-
models = get_multi(result)
|
11
|
-
result = result.map { |key| models[key] }
|
12
|
-
end
|
13
|
-
|
14
|
-
@local_cache[args[0]] = result if result
|
15
|
-
result
|
16
|
-
end
|
17
|
-
|
18
|
-
def get_multi(keys)
|
19
|
-
map = Hash[*keys.zip(keys.map { |key| @local_cache[key] }).flatten]
|
20
|
-
missing_keys = map.select { |key, value| value.nil? }.map(&:first)
|
21
|
-
|
22
|
-
unless missing_keys.empty?
|
23
|
-
if Rails.cache.respond_to?(:read_multi)
|
24
|
-
missing_map = Rails.cache.read_multi(missing_keys)
|
25
|
-
missing_map.each do |key, value|
|
26
|
-
missing_map[key] = @local_cache[key] = value
|
27
|
-
end
|
28
|
-
map.merge!(missing_map)
|
29
|
-
else
|
30
|
-
missing_keys.each do |key|
|
31
|
-
map[key] = read(key)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
map
|
37
|
-
end
|
38
|
-
|
39
|
-
def write(key, value)
|
40
|
-
if storable?(value)
|
41
|
-
@local_cache[key] = value.duplicable? ? value.dup : value
|
42
|
-
Rails.cache.write(key, value.duplicable? ? value.dup : value) # Fix due to Rails.cache freezing values in 2.3.4
|
43
|
-
end
|
44
|
-
value
|
45
|
-
end
|
46
|
-
|
47
|
-
def delete(*args)
|
48
|
-
@local_cache.delete(args[0])
|
49
|
-
Rails.cache.delete(*args)
|
50
|
-
end
|
51
|
-
|
52
|
-
def delete_local(*keys)
|
53
|
-
keys.each do |key|
|
54
|
-
@local_cache.delete(key)
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
def delete_matched_local(matcher)
|
59
|
-
@local_cache.delete_if { |k,v| k =~ matcher }
|
60
|
-
end
|
61
|
-
|
62
|
-
def clear_local
|
63
|
-
@local_cache = {}
|
64
|
-
end
|
65
|
-
|
66
|
-
def local
|
67
|
-
@local_cache
|
68
|
-
end
|
69
|
-
|
70
|
-
protected
|
71
|
-
|
72
|
-
def storable?(value)
|
73
|
-
!value.is_a?(Array) || value.size <= Kasket::CONFIGURATION[:max_collection_size]
|
74
|
-
end
|
75
|
-
|
76
|
-
end
|
77
|
-
end
|
data/test/cache_test.rb
DELETED
@@ -1,102 +0,0 @@
|
|
1
|
-
require File.dirname(__FILE__) + '/helper'
|
2
|
-
|
3
|
-
class CacheTest < ActiveSupport::TestCase
|
4
|
-
|
5
|
-
context "Cache" do
|
6
|
-
setup do
|
7
|
-
@cache = Kasket::Cache.new
|
8
|
-
end
|
9
|
-
|
10
|
-
context "reading" do
|
11
|
-
|
12
|
-
should "work with non collection values" do
|
13
|
-
@cache.write('key', 'value')
|
14
|
-
assert_equal 'value', @cache.read('key')
|
15
|
-
end
|
16
|
-
|
17
|
-
should "fetch original results of stored collections" do
|
18
|
-
@cache.write('key1', 'value1')
|
19
|
-
@cache.write('key2', 'value2')
|
20
|
-
@cache.write('key3', 'value3')
|
21
|
-
@cache.write('collection_key', [ 'key1', 'key2', 'key3'])
|
22
|
-
|
23
|
-
assert_equal [ 'value1', 'value2', 'value3'], @cache.read('collection_key')
|
24
|
-
end
|
25
|
-
|
26
|
-
should "not impact original object" do
|
27
|
-
record = { 'id' => 1, 'color' => 'red' }
|
28
|
-
@cache.write('key', record)
|
29
|
-
record['id'] = 2
|
30
|
-
|
31
|
-
assert_not_equal record, @cache.read('key')
|
32
|
-
end
|
33
|
-
|
34
|
-
end
|
35
|
-
|
36
|
-
context "writing" do
|
37
|
-
setup do
|
38
|
-
@cache.write('key', 'value')
|
39
|
-
end
|
40
|
-
|
41
|
-
should "store the object locally" do
|
42
|
-
assert_equal 'value', @cache.local['key']
|
43
|
-
end
|
44
|
-
|
45
|
-
should "persist the object" do
|
46
|
-
@cache.clear_local
|
47
|
-
assert_equal 'value', @cache.read('key')
|
48
|
-
end
|
49
|
-
|
50
|
-
should "respect max collection size" do
|
51
|
-
original_max = Kasket::CONFIGURATION[:max_collection_size]
|
52
|
-
Kasket::CONFIGURATION[:max_collection_size] = 2
|
53
|
-
|
54
|
-
@cache.write('key', [ 'a', 'b'])
|
55
|
-
assert_equal 2, @cache.read('key').size
|
56
|
-
|
57
|
-
@cache.write('key2', ['a', 'b', 'c'])
|
58
|
-
assert_equal nil, @cache.read('key2')
|
59
|
-
|
60
|
-
Kasket::CONFIGURATION[:max_collection_size] = original_max
|
61
|
-
end
|
62
|
-
|
63
|
-
end
|
64
|
-
|
65
|
-
should "delete" do
|
66
|
-
@cache.write('key', 'value')
|
67
|
-
@cache.delete('key')
|
68
|
-
|
69
|
-
assert_equal nil, @cache.local['key']
|
70
|
-
assert_equal nil, @cache.read('key')
|
71
|
-
end
|
72
|
-
|
73
|
-
should "delete matched local" do
|
74
|
-
@cache.write('key1', 'value1')
|
75
|
-
@cache.write('key2', 'value2')
|
76
|
-
@cache.delete_matched_local(/2/)
|
77
|
-
|
78
|
-
assert_equal nil, @cache.local['key2']
|
79
|
-
assert_equal 'value1', @cache.local['key1']
|
80
|
-
assert_equal 'value2', @cache.read('key2')
|
81
|
-
end
|
82
|
-
|
83
|
-
should "delete local" do
|
84
|
-
@cache.write('key1', 'value1')
|
85
|
-
@cache.write('key2', 'value2')
|
86
|
-
@cache.delete_local('key1', 'key2')
|
87
|
-
|
88
|
-
assert_equal nil, @cache.local['key1']
|
89
|
-
assert_equal nil, @cache.local['key2']
|
90
|
-
assert_equal 'value1', @cache.read('key1')
|
91
|
-
assert_equal 'value2', @cache.read('key2')
|
92
|
-
end
|
93
|
-
|
94
|
-
should "clear local" do
|
95
|
-
@cache.write('key1', 'value1')
|
96
|
-
@cache.clear_local
|
97
|
-
|
98
|
-
assert @cache.local.blank?
|
99
|
-
end
|
100
|
-
|
101
|
-
end
|
102
|
-
end
|