kasket 0.6.4 → 0.7.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.
- 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
|