cache_back 0.4.1 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION +1 -1
- data/lib/cache_back/cache.rb +28 -2
- data/lib/cache_back/conditions_parser.rb +72 -0
- data/lib/cache_back/configuration_mixin.rb +43 -7
- data/lib/cache_back/dirty_mixin.rb +1 -1
- data/lib/cache_back/read_mixin.rb +56 -14
- data/lib/cache_back/reload_association_mixin.rb +15 -0
- data/lib/cache_back/write_mixin.rb +22 -16
- data/lib/cache_back.rb +33 -8
- data/test/cache_expiry_test.rb +55 -0
- data/test/dirty_test.rb +3 -2
- data/test/find_one_test.rb +11 -0
- data/test/find_some_test.rb +10 -0
- data/test/fixtures/blogs.yml +4 -1
- data/test/fixtures/posts.yml +4 -0
- data/test/helper.rb +3 -1
- data/test/serialization_test.rb +21 -0
- metadata +8 -2
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.5.0
|
data/lib/cache_back/cache.rb
CHANGED
@@ -5,7 +5,16 @@ module CacheBack
|
|
5
5
|
end
|
6
6
|
|
7
7
|
def read(*args)
|
8
|
-
@local_cache[args[0]]
|
8
|
+
result = @local_cache[args[0]] || Rails.cache.read(*args)
|
9
|
+
if result.is_a?(CachedModel)
|
10
|
+
result = result.instanciate_model
|
11
|
+
elsif result.is_a?(Array)
|
12
|
+
models = get_multi(result)
|
13
|
+
result = result.map { |key| models[key]}
|
14
|
+
end
|
15
|
+
|
16
|
+
@local_cache[args[0]] = result if result
|
17
|
+
result
|
9
18
|
end
|
10
19
|
|
11
20
|
def get_multi(keys)
|
@@ -16,7 +25,8 @@ module CacheBack
|
|
16
25
|
if Rails.cache.respond_to?(:read_multi)
|
17
26
|
missing_map = Rails.cache.read_multi(missing_keys)
|
18
27
|
missing_map.each do |key, value|
|
19
|
-
|
28
|
+
value = value.instanciate_model if value.is_a?(CachedModel)
|
29
|
+
missing_map[key] = @local_cache[key] = value
|
20
30
|
end
|
21
31
|
map.merge!(missing_map)
|
22
32
|
else
|
@@ -31,6 +41,11 @@ module CacheBack
|
|
31
41
|
|
32
42
|
def write(*args)
|
33
43
|
@local_cache[args[0]] = args[1]
|
44
|
+
|
45
|
+
if args[1].is_a?(ActiveRecord::Base)
|
46
|
+
args[1] = CachedModel.new(args[1])
|
47
|
+
end
|
48
|
+
|
34
49
|
Rails.cache.write(*args)
|
35
50
|
end
|
36
51
|
|
@@ -42,5 +57,16 @@ module CacheBack
|
|
42
57
|
def reset!
|
43
58
|
@local_cache = {}
|
44
59
|
end
|
60
|
+
|
61
|
+
class CachedModel
|
62
|
+
def initialize(model)
|
63
|
+
@name = model.class.name
|
64
|
+
@attributes = model.instance_variable_get(:@attributes)
|
65
|
+
end
|
66
|
+
|
67
|
+
def instanciate_model
|
68
|
+
@name.constantize.send(:instantiate, @attributes)
|
69
|
+
end
|
70
|
+
end
|
45
71
|
end
|
46
72
|
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module CacheBack
|
2
|
+
class ConditionsParser
|
3
|
+
def initialize(model_class)
|
4
|
+
@model_class = model_class
|
5
|
+
end
|
6
|
+
|
7
|
+
def attribute_value_pairs(options, scope)
|
8
|
+
from_scope = attribute_value_pairs_for_conditions((scope || {})[:conditions])
|
9
|
+
return nil unless from_scope
|
10
|
+
|
11
|
+
from_options = attribute_value_pairs_for_conditions(options[:conditions])
|
12
|
+
return nil unless from_options
|
13
|
+
|
14
|
+
pairs = from_scope.inject(from_options) do |memo, pair|
|
15
|
+
attribute = pair[0]
|
16
|
+
if memo_pair = memo.find{ |p| p[0] == attribute}
|
17
|
+
memo_pair[0] = Array(memo_pair[0])
|
18
|
+
memo_pair[0] << pair[1]
|
19
|
+
else
|
20
|
+
memo << pair
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
pairs.sort! { |pair1, pair2| pair1[0] <=> pair2[0] }
|
25
|
+
|
26
|
+
pairs.map do |attribute, value|
|
27
|
+
if value.is_a?(Array)
|
28
|
+
value.flatten!
|
29
|
+
if value.size == 1
|
30
|
+
[attribute.to_sym, value[0]]
|
31
|
+
else
|
32
|
+
[attribute.to_sym, value]
|
33
|
+
end
|
34
|
+
else
|
35
|
+
[attribute.to_sym, value]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def attribute_value_pairs_for_conditions(conditions)
|
41
|
+
case conditions
|
42
|
+
when Hash
|
43
|
+
conditions.to_a.collect { |key, value| [key.to_s, value] }
|
44
|
+
when String
|
45
|
+
parse_indices_from_condition(conditions)
|
46
|
+
when Array
|
47
|
+
parse_indices_from_condition(*conditions)
|
48
|
+
when NilClass
|
49
|
+
[]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
AND = /\s+AND\s+/i
|
54
|
+
TABLE_AND_COLUMN = /(?:(?:`|")?(\w+)(?:`|")?\.)?(?:`|")?(\w+)(?:`|")?/ # Matches: `users`.id, `users`.`id`, users.id, id
|
55
|
+
VALUE = /'?(\d+|\?|(?:(?:[^']|'')*))'?/ # Matches: 123, ?, '123', '12''3'
|
56
|
+
KEY_EQ_VALUE = /^\(?#{TABLE_AND_COLUMN}\s+=\s+#{VALUE}\)?$/ # Matches: KEY = VALUE, (KEY = VALUE)
|
57
|
+
ORDER = /^#{TABLE_AND_COLUMN}\s*(ASC|DESC)?$/i # Matches: COLUMN ASC, COLUMN DESC, COLUMN
|
58
|
+
|
59
|
+
def parse_indices_from_condition(conditions = '', *values)
|
60
|
+
values = values.dup
|
61
|
+
conditions.split(AND).inject([]) do |indices, condition|
|
62
|
+
matched, table_name, column_name, sql_value = *(KEY_EQ_VALUE.match(condition))
|
63
|
+
if matched
|
64
|
+
value = sql_value == '?' ? values.shift : @model_class.columns_hash[column_name].type_cast(sql_value)
|
65
|
+
indices << [column_name, value]
|
66
|
+
else
|
67
|
+
return nil
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require '
|
1
|
+
require 'active_support'
|
2
2
|
|
3
3
|
module CacheBack
|
4
4
|
autoload :ReadMixin, 'cache_back/read_mixin'
|
@@ -26,18 +26,54 @@ module CacheBack
|
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
|
-
def cache_back_key_for(
|
30
|
-
"cache_back/#{table_name}/
|
29
|
+
def cache_back_key_for(attribute_value_pairs)
|
30
|
+
key = "cache_back/#{table_name}/version=#{inherited_cache_back_version}"
|
31
|
+
attribute_value_pairs.each do |attribute, value|
|
32
|
+
key << "/#{attribute}="
|
33
|
+
if value.is_a?(Array)
|
34
|
+
key << value.join(',')
|
35
|
+
else
|
36
|
+
key << value.to_s
|
37
|
+
end
|
38
|
+
end
|
39
|
+
key
|
40
|
+
end
|
41
|
+
|
42
|
+
def cache_back_key_for_id(id)
|
43
|
+
cache_back_key_for([['id', id]])
|
44
|
+
end
|
45
|
+
|
46
|
+
def cache_back_indices
|
47
|
+
result = @cache_back_indices || []
|
48
|
+
result += superclass.cache_back_indices unless self == ActiveRecord::Base
|
49
|
+
result.uniq
|
50
|
+
end
|
51
|
+
|
52
|
+
def has_cache_back_index_on?(sorted_attributes)
|
53
|
+
cache_back_indices.include?(sorted_attributes)
|
31
54
|
end
|
32
55
|
|
33
56
|
def has_cache_back(options = {})
|
34
|
-
|
35
|
-
|
57
|
+
has_cache_back_on :id
|
58
|
+
end
|
59
|
+
|
60
|
+
def has_cache_back_on(*args)
|
61
|
+
options = args.extract_options!
|
62
|
+
attributes = args.sort! { |x, y| x.to_s <=> y.to_s }
|
63
|
+
|
64
|
+
@cache_back_version ||= options.delete(:version) || '1'
|
65
|
+
@cache_back_option ||= options
|
66
|
+
|
67
|
+
if attributes != [:id] && !cache_back_indices.include?([:id])
|
68
|
+
has_cache_back_on(:id)
|
69
|
+
end
|
70
|
+
|
71
|
+
@cache_back_indices ||= []
|
72
|
+
@cache_back_indices << attributes unless @cache_back_indices.include?(attributes)
|
36
73
|
|
37
74
|
include WriteMixin unless instance_methods.include?('store_in_cache_back')
|
38
|
-
extend ReadMixin unless methods.include?('
|
75
|
+
extend ReadMixin unless methods.include?('without_cache_back')
|
39
76
|
extend DirtyMixin unless methods.include?('cache_back_dirty_methods')
|
40
77
|
end
|
41
|
-
|
42
78
|
end
|
43
79
|
end
|
@@ -1,29 +1,60 @@
|
|
1
|
+
require 'cache_back/conditions_parser'
|
2
|
+
|
1
3
|
module CacheBack
|
2
4
|
module ReadMixin
|
3
5
|
|
4
6
|
def self.extended(model_class)
|
5
7
|
class << model_class
|
6
|
-
alias_method_chain :
|
7
|
-
alias_method_chain :
|
8
|
+
alias_method_chain :find_some, :cache_back unless methods.include?('find_some_without_cache_back')
|
9
|
+
alias_method_chain :find_every, :cache_back unless methods.include?('find_every_without_cache_back')
|
8
10
|
end
|
9
11
|
end
|
10
12
|
|
11
|
-
def
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
def without_cache_back(&block)
|
14
|
+
old_value = @use_cache_back || true
|
15
|
+
@use_cache_back = false
|
16
|
+
yield
|
17
|
+
ensure
|
18
|
+
@use_cache_back = old_value
|
19
|
+
end
|
20
|
+
|
21
|
+
def find_every_with_cache_back(options)
|
22
|
+
attribute_value_pairs = cache_back_conditions_parser.attribute_value_pairs(options, scope(:find)) if cache_safe?(options)
|
23
|
+
|
24
|
+
limit = (options[:limit] || (scope(:find) || {})[:limit])
|
25
|
+
|
26
|
+
if (limit.nil? || limit == 1) && attribute_value_pairs && has_cache_back_index_on?(attribute_value_pairs.map(&:first))
|
27
|
+
collection_key = cache_back_key_for(attribute_value_pairs)
|
28
|
+
collection_key << '/first' if limit == 1
|
29
|
+
unless records = CacheBack.cache.read(collection_key)
|
30
|
+
records = without_cache_back do
|
31
|
+
find_every_without_cache_back(options)
|
32
|
+
end
|
33
|
+
|
34
|
+
if records.size == 1
|
35
|
+
CacheBack.cache.write(collection_key, records[0])
|
36
|
+
elsif records.size <= CacheBack::CONFIGURATION[:max_collection_size]
|
37
|
+
records.each { |record| record.store_in_cache_back if record }
|
38
|
+
CacheBack.cache.write(collection_key, records.map(&:cache_back_key))
|
39
|
+
end
|
16
40
|
end
|
17
41
|
|
18
|
-
|
42
|
+
Array(records)
|
19
43
|
else
|
20
|
-
|
44
|
+
without_cache_back do
|
45
|
+
find_every_without_cache_back(options)
|
46
|
+
end
|
21
47
|
end
|
22
48
|
end
|
23
49
|
|
24
50
|
def find_some_with_cache_back(ids, options)
|
25
|
-
if cache_safe?(options)
|
26
|
-
|
51
|
+
attribute_value_pairs = cache_back_conditions_parser.attribute_value_pairs(options, scope(:find)) if cache_safe?(options)
|
52
|
+
attribute_value_pairs << [:id, ids] if attribute_value_pairs
|
53
|
+
|
54
|
+
limit = (options[:limit] || (scope(:find) || {})[:limit])
|
55
|
+
|
56
|
+
if limit.nil? && attribute_value_pairs && has_cache_back_index_on?(attribute_value_pairs.map(&:first))
|
57
|
+
id_to_key_map = Hash[ids.uniq.map { |id| [id, cache_back_key_for_id(id)] }]
|
27
58
|
cached_record_map = CacheBack.cache.get_multi(id_to_key_map.values)
|
28
59
|
|
29
60
|
missing_keys = Hash[cached_record_map.select { |key, record| record.nil? }].keys
|
@@ -32,19 +63,30 @@ module CacheBack
|
|
32
63
|
|
33
64
|
missing_ids = Hash[id_to_key_map.invert.select { |key, id| missing_keys.include?(key) }].values
|
34
65
|
|
35
|
-
db_records =
|
66
|
+
db_records = without_cache_back do
|
67
|
+
find_some_without_cache_back(missing_ids, options)
|
68
|
+
end
|
36
69
|
db_records.each { |record| record.store_in_cache_back if record }
|
37
70
|
|
38
71
|
(cached_record_map.values + db_records).compact.uniq.sort { |x, y| x.id <=> y.id}
|
39
72
|
else
|
40
|
-
|
73
|
+
without_cache_back do
|
74
|
+
find_some_without_cache_back(ids, options)
|
75
|
+
end
|
41
76
|
end
|
42
77
|
end
|
43
78
|
|
44
79
|
private
|
45
80
|
|
46
81
|
def cache_safe?(options)
|
47
|
-
options
|
82
|
+
@use_cache_back != false && [options, scope(:find) || {}].all? do |hash|
|
83
|
+
result = hash[:select].nil? && hash[:joins].nil? && hash[:order].nil? && hash[:offset].nil?
|
84
|
+
result && (options[:limit].nil? || options[:limit] == 1)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def cache_back_conditions_parser
|
89
|
+
@cache_back_conditions_parser ||= CacheBack::ConditionsParser.new(self)
|
48
90
|
end
|
49
91
|
|
50
92
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module CacheBack
|
2
|
+
module ReloadAssociationMixin
|
3
|
+
def reload_with_cache_back_clearing(*args)
|
4
|
+
# TODO we could calculate the right key to clear by parsing the association conditions.
|
5
|
+
# this would clear less keys, and we would not have to load the target
|
6
|
+
load_target
|
7
|
+
target.clear_cache_back_indices if target.respond_to?(:clear_cache_back_indices)
|
8
|
+
reload_without_cache_back_clearing(*args)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.included(base)
|
12
|
+
base.alias_method_chain :reload, :cache_back_clearing
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -3,7 +3,7 @@ module CacheBack
|
|
3
3
|
module ClassMethods
|
4
4
|
def remove_from_cache_back(ids)
|
5
5
|
Array(ids).each do |id|
|
6
|
-
CacheBack.cache.delete(
|
6
|
+
CacheBack.cache.delete(cache_back_key_for_id(id))
|
7
7
|
end
|
8
8
|
end
|
9
9
|
|
@@ -14,29 +14,35 @@ module CacheBack
|
|
14
14
|
end
|
15
15
|
|
16
16
|
module InstanceMethods
|
17
|
-
def shallow_clone
|
18
|
-
clone = self.class.new
|
19
|
-
clone.instance_variable_set("@attributes", instance_variable_get(:@attributes))
|
20
|
-
clone.instance_variable_set("@new_record", new_record?)
|
21
|
-
clone
|
22
|
-
end
|
23
|
-
|
24
17
|
def cache_back_key
|
25
|
-
@cache_back_key ||= new_record? ? nil : self.class.
|
18
|
+
@cache_back_key ||= new_record? ? nil : self.class.cache_back_key_for_id(id)
|
26
19
|
end
|
27
20
|
|
28
21
|
def store_in_cache_back
|
29
22
|
if !readonly? && cache_back_key
|
30
|
-
CacheBack.cache.write(cache_back_key,
|
23
|
+
CacheBack.cache.write(cache_back_key, self, self.class.inherited_cache_back_options)
|
31
24
|
end
|
32
25
|
end
|
33
26
|
|
34
|
-
def
|
35
|
-
|
27
|
+
def clear_cache_back_indices
|
28
|
+
new_attributes = attributes.symbolize_keys
|
29
|
+
|
30
|
+
old_attributes = Hash[changes.map {|attribute, values| [attribute, values[0]]}].symbolize_keys
|
31
|
+
old_attributes.reverse_merge!(new_attributes)
|
32
|
+
|
33
|
+
self.class.cache_back_indices.each do |index|
|
34
|
+
old_key = self.class.cache_back_key_for(index.map { |attribute| [attribute, old_attributes[attribute]]})
|
35
|
+
new_key = self.class.cache_back_key_for(index.map { |attribute| [attribute, new_attributes[attribute]]})
|
36
|
+
|
37
|
+
[old_key, new_key].uniq.each do |key|
|
38
|
+
CacheBack.cache.delete(key)
|
39
|
+
CacheBack.cache.delete("#{key}/first")
|
40
|
+
end
|
41
|
+
end
|
36
42
|
end
|
37
43
|
|
38
44
|
def reload_with_cache_back_clearing(*args)
|
39
|
-
|
45
|
+
clear_cache_back_indices
|
40
46
|
reload_without_cache_back_clearing(*args)
|
41
47
|
end
|
42
48
|
end
|
@@ -45,9 +51,9 @@ module CacheBack
|
|
45
51
|
model_class.extend ClassMethods
|
46
52
|
model_class.send :include, InstanceMethods
|
47
53
|
|
48
|
-
|
49
|
-
model_class.
|
50
|
-
|
54
|
+
model_class.after_save :clear_cache_back_indices
|
55
|
+
model_class.after_destroy :clear_cache_back_indices
|
56
|
+
|
51
57
|
model_class.alias_method_chain :reload, :cache_back_clearing
|
52
58
|
|
53
59
|
class << model_class
|
data/lib/cache_back.rb
CHANGED
@@ -1,23 +1,48 @@
|
|
1
1
|
require 'rubygems'
|
2
|
-
require '
|
2
|
+
require 'active_record'
|
3
|
+
require 'active_support'
|
3
4
|
|
4
5
|
require 'cache_back/configuration_mixin'
|
6
|
+
require 'cache_back/reload_association_mixin'
|
5
7
|
require 'cache_back/cache'
|
6
8
|
require 'cache_back/rack_middleware'
|
7
9
|
|
8
10
|
module CacheBack
|
11
|
+
CONFIGURATION = {:max_collection_size => 100}
|
12
|
+
|
9
13
|
module_function
|
10
14
|
|
11
15
|
def cache
|
12
16
|
Thread.current["cache_back_cache"] ||= Cache.new
|
13
17
|
end
|
14
|
-
end
|
15
18
|
|
16
|
-
|
19
|
+
def setup(options = {})
|
20
|
+
CONFIGURATION[:max_collection_size] = options[:max_collection_size] if options[:max_collection_size]
|
17
21
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
22
|
+
ActiveRecord::Base.extend(CacheBack::ConfigurationMixin)
|
23
|
+
ActiveRecord::Associations::BelongsToAssociation.send(:include, CacheBack::ReloadAssociationMixin)
|
24
|
+
ActiveRecord::Associations::BelongsToPolymorphicAssociation.send(:include, CacheBack::ReloadAssociationMixin)
|
25
|
+
ActiveRecord::Associations::HasOneThroughAssociation.send(:include, CacheBack::ReloadAssociationMixin)
|
26
|
+
|
27
|
+
#sets up local cache clearing after each request
|
28
|
+
begin
|
29
|
+
ApplicationController.after_filter do
|
30
|
+
CacheBack.cache.reset!
|
31
|
+
end
|
32
|
+
rescue NameError => e
|
23
33
|
|
34
|
+
end
|
35
|
+
|
36
|
+
#sets up local cache clearing after each test case
|
37
|
+
begin
|
38
|
+
ActiveSupport::TestCase.class_eval do
|
39
|
+
setup :clear_cache
|
40
|
+
def clear_cache
|
41
|
+
CacheBack.cache.reset!
|
42
|
+
Rails.cache.clear if Rails.cache.respond_to?(:clear)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
rescue NameError => e
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/helper'
|
2
|
+
|
3
|
+
class CacheExpiryTest < ActiveSupport::TestCase
|
4
|
+
fixtures :blogs, :posts
|
5
|
+
|
6
|
+
Post.has_cache_back
|
7
|
+
Post.has_cache_back_on :title
|
8
|
+
Post.has_cache_back_on :blog_id
|
9
|
+
|
10
|
+
context "a cached object" do
|
11
|
+
setup do
|
12
|
+
post = Post.first
|
13
|
+
@post = Post.find(post.id)
|
14
|
+
assert(Rails.cache.read(@post.cache_back_key))
|
15
|
+
end
|
16
|
+
|
17
|
+
should "be removed from cache when deleted" do
|
18
|
+
@post.destroy
|
19
|
+
assert_nil(Rails.cache.read(@post.cache_back_key))
|
20
|
+
end
|
21
|
+
|
22
|
+
should "clear all indices for instance when deleted" do
|
23
|
+
CacheBack.cache.expects(:delete).with("cache_back/posts/version=1/id=#{@post.id}")
|
24
|
+
CacheBack.cache.expects(:delete).with("cache_back/posts/version=1/id=#{@post.id}/first")
|
25
|
+
CacheBack.cache.expects(:delete).with("cache_back/posts/version=1/title=#{@post.title}")
|
26
|
+
CacheBack.cache.expects(:delete).with("cache_back/posts/version=1/title=#{@post.title}/first")
|
27
|
+
CacheBack.cache.expects(:delete).with("cache_back/posts/version=1/blog_id=#{@post.blog_id}")
|
28
|
+
CacheBack.cache.expects(:delete).with("cache_back/posts/version=1/blog_id=#{@post.blog_id}/first")
|
29
|
+
CacheBack.cache.expects(:delete).never
|
30
|
+
|
31
|
+
@post.destroy
|
32
|
+
end
|
33
|
+
|
34
|
+
should "be removed from cache when updated" do
|
35
|
+
@post.title = "new title"
|
36
|
+
@post.save
|
37
|
+
assert_nil(Rails.cache.read(@post.cache_back_key))
|
38
|
+
end
|
39
|
+
|
40
|
+
should "clear all indices for instance when updated" do
|
41
|
+
CacheBack.cache.expects(:delete).with("cache_back/posts/version=1/id=#{@post.id}")
|
42
|
+
CacheBack.cache.expects(:delete).with("cache_back/posts/version=1/id=#{@post.id}/first")
|
43
|
+
CacheBack.cache.expects(:delete).with("cache_back/posts/version=1/title=#{@post.title}")
|
44
|
+
CacheBack.cache.expects(:delete).with("cache_back/posts/version=1/title=#{@post.title}/first")
|
45
|
+
CacheBack.cache.expects(:delete).with("cache_back/posts/version=1/title=new title")
|
46
|
+
CacheBack.cache.expects(:delete).with("cache_back/posts/version=1/title=new title/first")
|
47
|
+
CacheBack.cache.expects(:delete).with("cache_back/posts/version=1/blog_id=#{@post.blog_id}")
|
48
|
+
CacheBack.cache.expects(:delete).with("cache_back/posts/version=1/blog_id=#{@post.blog_id}/first")
|
49
|
+
CacheBack.cache.expects(:delete).never
|
50
|
+
|
51
|
+
@post.title = "new title"
|
52
|
+
@post.save
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/test/dirty_test.rb
CHANGED
@@ -6,13 +6,14 @@ class DirtyTest < ActiveSupport::TestCase
|
|
6
6
|
Post.has_cache_back
|
7
7
|
Post.cache_back_dirty_methods :make_dirty!
|
8
8
|
|
9
|
-
should "
|
9
|
+
should "clear the indices when a dirty method is called" do
|
10
10
|
post = Post.first
|
11
11
|
|
12
12
|
pots = Post.find(post.id)
|
13
13
|
assert(Rails.cache.read(post.cache_back_key))
|
14
14
|
|
15
|
-
CacheBack.cache.expects(:write)
|
16
15
|
post.make_dirty!
|
16
|
+
|
17
|
+
assert_nil(Rails.cache.read(post.cache_back_key))
|
17
18
|
end
|
18
19
|
end
|
data/test/find_one_test.rb
CHANGED
@@ -30,4 +30,15 @@ class FindOneTest < ActiveSupport::TestCase
|
|
30
30
|
CacheBack.cache.expects(:read).never
|
31
31
|
Post.find(post.id, :select => 'title')
|
32
32
|
end
|
33
|
+
|
34
|
+
should "respect scope" do
|
35
|
+
post = Post.find(Post.first.id)
|
36
|
+
other_blog = Blog.first(:conditions => "id != #{post.blog_id}")
|
37
|
+
|
38
|
+
assert(Rails.cache.read(post.cache_back_key))
|
39
|
+
|
40
|
+
assert_raise(ActiveRecord::RecordNotFound) do
|
41
|
+
other_blog.posts.find(post.id)
|
42
|
+
end
|
43
|
+
end
|
33
44
|
end
|
data/test/find_some_test.rb
CHANGED
@@ -4,6 +4,7 @@ class FindSomeTest < ActiveSupport::TestCase
|
|
4
4
|
fixtures :blogs, :posts
|
5
5
|
|
6
6
|
Post.has_cache_back
|
7
|
+
Post.has_cache_back_on :blog_id
|
7
8
|
|
8
9
|
should "cache find(id, id) calls" do
|
9
10
|
post1 = Post.first
|
@@ -36,4 +37,13 @@ class FindSomeTest < ActiveSupport::TestCase
|
|
36
37
|
found_posts = Post.find(post1.id, post2.id)
|
37
38
|
assert_equal([post1, post2].map(&:id).sort, found_posts.map(&:id).sort)
|
38
39
|
end
|
40
|
+
|
41
|
+
should "cache on index other than primary key" do
|
42
|
+
blog = blogs(:a_blog)
|
43
|
+
posts = Post.find_all_by_blog_id(blog.id)
|
44
|
+
|
45
|
+
Post.expects(:find_every_without_cache_back).never
|
46
|
+
|
47
|
+
assert_equal(posts, Post.find_all_by_blog_id(blog.id))
|
48
|
+
end
|
39
49
|
end
|
data/test/fixtures/blogs.yml
CHANGED
data/test/fixtures/posts.yml
CHANGED
data/test/helper.rb
CHANGED
@@ -16,12 +16,14 @@ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
|
16
16
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
17
17
|
require 'cache_back'
|
18
18
|
|
19
|
+
CacheBack.setup
|
20
|
+
|
19
21
|
class ActiveSupport::TestCase
|
20
22
|
include ActiveRecord::TestFixtures
|
21
23
|
|
22
24
|
fixtures :all
|
23
25
|
|
24
|
-
setup :clear_cache
|
26
|
+
#setup :clear_cache
|
25
27
|
|
26
28
|
def create_fixtures(*table_names)
|
27
29
|
if block_given?
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/helper'
|
2
|
+
|
3
|
+
class SerializationTest < ActiveSupport::TestCase
|
4
|
+
fixtures :blogs, :posts
|
5
|
+
|
6
|
+
Post.has_cache_back
|
7
|
+
|
8
|
+
should "store a CachedModel" do
|
9
|
+
post = Post.first
|
10
|
+
post.store_in_cache_back
|
11
|
+
assert_instance_of(CacheBack::Cache::CachedModel, Rails.cache.read(post.cache_back_key))
|
12
|
+
end
|
13
|
+
|
14
|
+
should "bring convert CachedModel to model instances" do
|
15
|
+
post = Post.first
|
16
|
+
post.store_in_cache_back
|
17
|
+
|
18
|
+
post = CacheBack.cache.read(post.cache_back_key)
|
19
|
+
assert_instance_of(Post, post)
|
20
|
+
end
|
21
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cache_back
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mick Staugaard
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-11-
|
12
|
+
date: 2009-11-29 00:00:00 -08:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -50,11 +50,14 @@ files:
|
|
50
50
|
- VERSION
|
51
51
|
- lib/cache_back.rb
|
52
52
|
- lib/cache_back/cache.rb
|
53
|
+
- lib/cache_back/conditions_parser.rb
|
53
54
|
- lib/cache_back/configuration_mixin.rb
|
54
55
|
- lib/cache_back/dirty_mixin.rb
|
55
56
|
- lib/cache_back/rack_middleware.rb
|
56
57
|
- lib/cache_back/read_mixin.rb
|
58
|
+
- lib/cache_back/reload_association_mixin.rb
|
57
59
|
- lib/cache_back/write_mixin.rb
|
60
|
+
- test/cache_expiry_test.rb
|
58
61
|
- test/database.yml
|
59
62
|
- test/dirty_test.rb
|
60
63
|
- test/find_one_test.rb
|
@@ -64,6 +67,7 @@ files:
|
|
64
67
|
- test/fixtures/posts.yml
|
65
68
|
- test/helper.rb
|
66
69
|
- test/schema.rb
|
70
|
+
- test/serialization_test.rb
|
67
71
|
has_rdoc: true
|
68
72
|
homepage: http://github.com/staugaard/cache_back
|
69
73
|
licenses: []
|
@@ -93,8 +97,10 @@ signing_key:
|
|
93
97
|
specification_version: 3
|
94
98
|
summary: A write back caching layer on active record
|
95
99
|
test_files:
|
100
|
+
- test/cache_expiry_test.rb
|
96
101
|
- test/dirty_test.rb
|
97
102
|
- test/find_one_test.rb
|
98
103
|
- test/find_some_test.rb
|
99
104
|
- test/helper.rb
|
100
105
|
- test/schema.rb
|
106
|
+
- test/serialization_test.rb
|