cache_back 0.4.1 → 0.5.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/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
|