identity_cache 0.5.1 → 1.0.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.
- checksums.yaml +5 -5
- data/.github/probots.yml +2 -0
- data/.github/workflows/ci.yml +26 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +5 -0
- data/.travis.yml +24 -9
- data/CHANGELOG.md +21 -0
- data/Gemfile +5 -1
- data/README.md +28 -26
- data/Rakefile +14 -5
- data/dev.yml +9 -16
- data/gemfiles/Gemfile.latest-release +6 -0
- data/gemfiles/Gemfile.rails-edge +6 -0
- data/gemfiles/Gemfile.rails52 +6 -0
- data/identity_cache.gemspec +26 -10
- data/lib/identity_cache.rb +49 -46
- data/lib/identity_cache/belongs_to_caching.rb +12 -40
- data/lib/identity_cache/cache_fetcher.rb +6 -5
- data/lib/identity_cache/cache_hash.rb +2 -2
- data/lib/identity_cache/cache_invalidation.rb +4 -11
- data/lib/identity_cache/cache_key_generation.rb +17 -65
- data/lib/identity_cache/cache_key_loader.rb +128 -0
- data/lib/identity_cache/cached.rb +7 -0
- data/lib/identity_cache/cached/association.rb +87 -0
- data/lib/identity_cache/cached/attribute.rb +123 -0
- data/lib/identity_cache/cached/attribute_by_multi.rb +37 -0
- data/lib/identity_cache/cached/attribute_by_one.rb +88 -0
- data/lib/identity_cache/cached/belongs_to.rb +93 -0
- data/lib/identity_cache/cached/embedded_fetching.rb +41 -0
- data/lib/identity_cache/cached/prefetcher.rb +51 -0
- data/lib/identity_cache/cached/primary_index.rb +97 -0
- data/lib/identity_cache/cached/recursive/association.rb +68 -0
- data/lib/identity_cache/cached/recursive/has_many.rb +9 -0
- data/lib/identity_cache/cached/recursive/has_one.rb +9 -0
- data/lib/identity_cache/cached/reference/association.rb +16 -0
- data/lib/identity_cache/cached/reference/has_many.rb +105 -0
- data/lib/identity_cache/cached/reference/has_one.rb +100 -0
- data/lib/identity_cache/configuration_dsl.rb +53 -215
- data/lib/identity_cache/encoder.rb +95 -0
- data/lib/identity_cache/expiry_hook.rb +36 -0
- data/lib/identity_cache/fallback_fetcher.rb +2 -1
- data/lib/identity_cache/load_strategy/eager.rb +28 -0
- data/lib/identity_cache/load_strategy/lazy.rb +71 -0
- data/lib/identity_cache/load_strategy/load_request.rb +20 -0
- data/lib/identity_cache/load_strategy/multi_load_request.rb +27 -0
- data/lib/identity_cache/memoized_cache_proxy.rb +127 -58
- data/lib/identity_cache/parent_model_expiration.rb +45 -11
- data/lib/identity_cache/query_api.rb +128 -394
- data/lib/identity_cache/railtie.rb +8 -0
- data/lib/identity_cache/record_not_found.rb +6 -0
- data/lib/identity_cache/should_use_cache.rb +1 -0
- data/lib/identity_cache/version.rb +3 -2
- data/lib/identity_cache/with_primary_index.rb +136 -0
- data/lib/identity_cache/without_primary_index.rb +24 -3
- data/performance/cache_runner.rb +28 -34
- data/performance/cpu.rb +3 -2
- data/performance/externals.rb +4 -3
- data/performance/profile.rb +6 -5
- data/railgun.yml +16 -0
- metadata +44 -73
- data/Gemfile.rails42 +0 -6
- data/Gemfile.rails50 +0 -6
- data/test/attribute_cache_test.rb +0 -110
- data/test/cache_fetch_includes_test.rb +0 -46
- data/test/cache_hash_test.rb +0 -14
- data/test/cache_invalidation_test.rb +0 -139
- data/test/deeply_nested_associated_record_test.rb +0 -19
- data/test/denormalized_has_many_test.rb +0 -214
- data/test/denormalized_has_one_test.rb +0 -160
- data/test/fetch_multi_test.rb +0 -308
- data/test/fetch_test.rb +0 -258
- data/test/fixtures/serialized_record.mysql2 +0 -0
- data/test/fixtures/serialized_record.postgresql +0 -0
- data/test/helpers/active_record_objects.rb +0 -106
- data/test/helpers/database_connection.rb +0 -72
- data/test/helpers/serialization_format.rb +0 -51
- data/test/helpers/update_serialization_format.rb +0 -27
- data/test/identity_cache_test.rb +0 -29
- data/test/index_cache_test.rb +0 -161
- data/test/memoized_attributes_test.rb +0 -59
- data/test/memoized_cache_proxy_test.rb +0 -107
- data/test/normalized_belongs_to_test.rb +0 -107
- data/test/normalized_has_many_test.rb +0 -231
- data/test/normalized_has_one_test.rb +0 -9
- data/test/prefetch_associations_test.rb +0 -379
- data/test/readonly_test.rb +0 -109
- data/test/recursive_denormalized_has_many_test.rb +0 -131
- data/test/save_test.rb +0 -82
- data/test/schema_change_test.rb +0 -112
- data/test/serialization_format_change_test.rb +0 -16
- data/test/test_helper.rb +0 -140
data/Gemfile.rails42
DELETED
data/Gemfile.rails50
DELETED
@@ -1,110 +0,0 @@
|
|
1
|
-
require "test_helper"
|
2
|
-
|
3
|
-
class AttributeCacheTest < IdentityCache::TestCase
|
4
|
-
NAMESPACE = IdentityCache::CacheKeyGeneration::DEFAULT_NAMESPACE
|
5
|
-
|
6
|
-
def setup
|
7
|
-
super
|
8
|
-
AssociatedRecord.cache_attribute :name
|
9
|
-
|
10
|
-
@parent = Item.create!(:title => 'bob')
|
11
|
-
@record = @parent.associated_records.create!(:name => 'foo')
|
12
|
-
@name_attribute_key = "#{NAMESPACE}attr:AssociatedRecord:name:id:#{cache_hash(@record.id.to_s)}"
|
13
|
-
IdentityCache.cache.clear
|
14
|
-
end
|
15
|
-
|
16
|
-
def test_attribute_values_are_fetched_and_returned_on_cache_misses
|
17
|
-
fetch = Spy.on(IdentityCache.cache, :fetch).and_call_through
|
18
|
-
|
19
|
-
assert_queries(1) do
|
20
|
-
assert_equal 'foo', AssociatedRecord.fetch_name_by_id(1)
|
21
|
-
end
|
22
|
-
assert fetch.has_been_called_with?(@name_attribute_key)
|
23
|
-
end
|
24
|
-
|
25
|
-
def test_attribute_values_are_returned_on_cache_hits
|
26
|
-
assert_equal 'foo', AssociatedRecord.fetch_name_by_id(1)
|
27
|
-
|
28
|
-
assert_queries(0) do
|
29
|
-
assert_equal 'foo', AssociatedRecord.fetch_name_by_id(1)
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
def test_nil_is_stored_in_the_cache_on_cache_misses
|
34
|
-
assert_nil AssociatedRecord.fetch_name_by_id(2)
|
35
|
-
|
36
|
-
assert_queries(0) do
|
37
|
-
assert_nil AssociatedRecord.fetch_name_by_id(2)
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
def test_cached_attribute_values_are_expired_from_the_cache_when_an_existing_record_is_saved
|
42
|
-
assert_queries(1) { assert_equal 'foo', AssociatedRecord.fetch_name_by_id(1) }
|
43
|
-
assert_queries(0) { assert_equal 'foo', AssociatedRecord.fetch_name_by_id(1) }
|
44
|
-
|
45
|
-
@record.save!
|
46
|
-
|
47
|
-
assert_queries(1) { assert_equal 'foo', AssociatedRecord.fetch_name_by_id(1) }
|
48
|
-
end
|
49
|
-
|
50
|
-
def test_cached_attribute_values_are_expired_from_the_cache_when_an_existing_record_with_changed_attributes_is_saved
|
51
|
-
assert_queries(1) { assert_equal 'foo', AssociatedRecord.fetch_name_by_id(1) }
|
52
|
-
assert_queries(0) { assert_equal 'foo', AssociatedRecord.fetch_name_by_id(1) }
|
53
|
-
|
54
|
-
@record.name = 'bar'
|
55
|
-
@record.save!
|
56
|
-
|
57
|
-
assert_queries(1) { assert_equal 'bar', AssociatedRecord.fetch_name_by_id(1) }
|
58
|
-
end
|
59
|
-
|
60
|
-
def test_cached_attribute_values_are_expired_from_the_cache_when_an_existing_record_is_destroyed
|
61
|
-
assert_queries(1) { assert_equal 'foo', AssociatedRecord.fetch_name_by_id(1) }
|
62
|
-
assert_queries(0) { assert_equal 'foo', AssociatedRecord.fetch_name_by_id(1) }
|
63
|
-
|
64
|
-
@record.destroy
|
65
|
-
|
66
|
-
assert_queries(1) { assert_nil AssociatedRecord.fetch_name_by_id(1) }
|
67
|
-
end
|
68
|
-
|
69
|
-
def test_cached_attribute_values_are_expired_from_the_cache_when_a_new_record_is_saved
|
70
|
-
new_id = 2
|
71
|
-
assert_queries(1) { assert_nil AssociatedRecord.fetch_name_by_id(new_id) }
|
72
|
-
assert_queries(0) { assert_nil AssociatedRecord.fetch_name_by_id(new_id) }
|
73
|
-
|
74
|
-
@parent.associated_records.create(:name => 'bar')
|
75
|
-
|
76
|
-
assert_queries(1) { assert_equal 'bar', AssociatedRecord.fetch_name_by_id(new_id) }
|
77
|
-
end
|
78
|
-
|
79
|
-
def test_fetching_by_attribute_delegates_to_block_if_transactions_are_open
|
80
|
-
IdentityCache.cache.expects(:read).never
|
81
|
-
|
82
|
-
@record.transaction do
|
83
|
-
assert_queries(1) do
|
84
|
-
assert_equal 'foo', AssociatedRecord.fetch_name_by_id(1)
|
85
|
-
end
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
def test_previously_stored_cached_nils_are_busted_by_new_record_saves
|
90
|
-
assert_nil AssociatedRecord.fetch_name_by_id(2)
|
91
|
-
AssociatedRecord.create(:name => "Jim")
|
92
|
-
assert_equal "Jim", AssociatedRecord.fetch_name_by_id(2)
|
93
|
-
end
|
94
|
-
|
95
|
-
def test_cache_attribute_on_derived_model_raises
|
96
|
-
assert_raises(IdentityCache::DerivedModelError) do
|
97
|
-
StiRecordTypeA.cache_attribute :name
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
def test_cache_attribute_respects_should_use_cache
|
102
|
-
AssociatedRecord.stubs(:should_use_cache?).returns(false)
|
103
|
-
|
104
|
-
assert_queries(1) do
|
105
|
-
assert_memcache_operations(0) do
|
106
|
-
AssociatedRecord.fetch_name_by_id(@record.id)
|
107
|
-
end
|
108
|
-
end
|
109
|
-
end
|
110
|
-
end
|
@@ -1,46 +0,0 @@
|
|
1
|
-
require "test_helper"
|
2
|
-
|
3
|
-
class CacheFetchIncludesTest < IdentityCache::TestCase
|
4
|
-
def setup
|
5
|
-
super
|
6
|
-
end
|
7
|
-
|
8
|
-
def test_cached_embedded_has_manys_are_included_in_includes
|
9
|
-
Item.send(:cache_has_many, :associated_records, :embed => true)
|
10
|
-
assert_equal [:associated_records], Item.send(:cache_fetch_includes)
|
11
|
-
end
|
12
|
-
|
13
|
-
def test_cached_nonembedded_has_manys_are_included_in_includes
|
14
|
-
Item.send(:cache_has_many, :associated_records, :embed => :ids)
|
15
|
-
assert_equal [], Item.send(:cache_fetch_includes)
|
16
|
-
end
|
17
|
-
|
18
|
-
def test_cached_has_ones_are_included_in_includes
|
19
|
-
Item.send(:cache_has_one, :associated)
|
20
|
-
assert_equal [:associated], Item.send(:cache_fetch_includes)
|
21
|
-
end
|
22
|
-
|
23
|
-
def test_cached_nonembedded_belongs_tos_are_not_included_in_includes
|
24
|
-
Item.send(:cache_belongs_to, :item)
|
25
|
-
assert_equal [], Item.send(:cache_fetch_includes)
|
26
|
-
end
|
27
|
-
|
28
|
-
def test_cached_child_associations_are_included_in_includes
|
29
|
-
Item.send(:cache_has_many, :associated_records, :embed => true)
|
30
|
-
AssociatedRecord.send(:cache_has_many, :deeply_associated_records, :embed => true)
|
31
|
-
assert_equal [{:associated_records => [:deeply_associated_records]}], Item.send(:cache_fetch_includes)
|
32
|
-
end
|
33
|
-
|
34
|
-
def test_multiple_cached_associations_and_child_associations_are_included_in_includes
|
35
|
-
Item.send(:cache_has_many, :associated_records, :embed => true)
|
36
|
-
PolymorphicRecord.send(:include, IdentityCache::WithoutPrimaryIndex)
|
37
|
-
Item.send(:cache_has_many, :polymorphic_records, {:inverse_name => :owner, :embed => true})
|
38
|
-
Item.send(:cache_has_one, :associated, :embed => true)
|
39
|
-
AssociatedRecord.send(:cache_has_many, :deeply_associated_records, :embed => true)
|
40
|
-
assert_equal [
|
41
|
-
{:associated_records => [:deeply_associated_records]},
|
42
|
-
:polymorphic_records,
|
43
|
-
{:associated => [:deeply_associated_records]}
|
44
|
-
], Item.send(:cache_fetch_includes)
|
45
|
-
end
|
46
|
-
end
|
data/test/cache_hash_test.rb
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
require 'test_helper'
|
2
|
-
|
3
|
-
class CacheHashTest < IdentityCache::TestCase
|
4
|
-
|
5
|
-
def test_memcache_hash
|
6
|
-
3.times do
|
7
|
-
random_str = Array.new(200){rand(36).to_s(36)}.join
|
8
|
-
hash_val = IdentityCache.memcache_hash(random_str)
|
9
|
-
assert hash_val
|
10
|
-
assert_kind_of Numeric, hash_val
|
11
|
-
assert_equal 0, (hash_val >> 64)
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
@@ -1,139 +0,0 @@
|
|
1
|
-
require "test_helper"
|
2
|
-
|
3
|
-
class CacheInvalidationTest < IdentityCache::TestCase
|
4
|
-
def setup
|
5
|
-
super
|
6
|
-
|
7
|
-
@record = Item.new(:title => 'foo')
|
8
|
-
@record.associated_records << AssociatedRecord.new(:name => 'bar')
|
9
|
-
@record.associated_records << AssociatedRecord.new(:name => 'baz')
|
10
|
-
@record.save
|
11
|
-
@record.reload
|
12
|
-
@baz, @bar = @record.associated_records[0], @record.associated_records[1]
|
13
|
-
@record.reload
|
14
|
-
end
|
15
|
-
|
16
|
-
def test_reload_invalidate_cached_ids
|
17
|
-
Item.cache_has_many :associated_records, :embed => :ids
|
18
|
-
|
19
|
-
variable_name = "@#{@record.class.send(:embedded_associations)[:associated_records][:ids_variable_name]}"
|
20
|
-
|
21
|
-
@record.fetch_associated_record_ids
|
22
|
-
assert_equal [@baz.id, @bar.id], @record.instance_variable_get(variable_name)
|
23
|
-
|
24
|
-
@record.reload
|
25
|
-
assert_equal false, @record.instance_variable_defined?(variable_name)
|
26
|
-
|
27
|
-
@record.fetch_associated_record_ids
|
28
|
-
assert_equal [@baz.id, @bar.id], @record.instance_variable_get(variable_name)
|
29
|
-
end
|
30
|
-
|
31
|
-
def test_reload_invalidate_cached_objects
|
32
|
-
Item.cache_has_many :associated_records, :embed => :ids
|
33
|
-
|
34
|
-
variable_name = "@#{@record.class.send(:embedded_associations)[:associated_records][:records_variable_name]}"
|
35
|
-
|
36
|
-
@record.fetch_associated_records
|
37
|
-
assert_equal [@baz, @bar], @record.instance_variable_get(variable_name)
|
38
|
-
|
39
|
-
@record.reload
|
40
|
-
assert_equal false, @record.instance_variable_defined?(variable_name)
|
41
|
-
|
42
|
-
@record.fetch_associated_records
|
43
|
-
assert_equal [@baz, @bar], @record.instance_variable_get(variable_name)
|
44
|
-
end
|
45
|
-
|
46
|
-
def test_after_a_reload_the_cache_perform_as_expected
|
47
|
-
Item.cache_has_many :associated_records, :embed => :ids
|
48
|
-
|
49
|
-
assert_equal [@baz, @bar], @record.fetch_associated_records
|
50
|
-
assert_equal [@baz, @bar], @record.associated_records
|
51
|
-
|
52
|
-
@baz.destroy
|
53
|
-
@record.reload
|
54
|
-
|
55
|
-
assert_equal [@bar], @record.fetch_associated_records
|
56
|
-
assert_equal [@bar], @record.associated_records
|
57
|
-
end
|
58
|
-
|
59
|
-
def test_cache_invalidation_expire_properly_if_child_is_embed_in_multiple_parents
|
60
|
-
Item.cache_has_many :associated_records, :embed => true
|
61
|
-
ItemTwo.cache_has_many :associated_records, :embed => true
|
62
|
-
|
63
|
-
baz = AssociatedRecord.new(:name => 'baz')
|
64
|
-
|
65
|
-
record1 = Item.new(:title => 'foo')
|
66
|
-
record1.associated_records << baz
|
67
|
-
record1.save!
|
68
|
-
|
69
|
-
record2 = ItemTwo.new(:title => 'bar')
|
70
|
-
record2.associated_records << baz
|
71
|
-
record2.save!
|
72
|
-
|
73
|
-
record1.class.fetch(record1.id)
|
74
|
-
record2.class.fetch(record2.id)
|
75
|
-
|
76
|
-
expected_keys = [
|
77
|
-
record1.primary_cache_index_key,
|
78
|
-
record2.primary_cache_index_key,
|
79
|
-
]
|
80
|
-
|
81
|
-
expected_keys.each do |expected_key|
|
82
|
-
assert IdentityCache.cache.fetch(expected_key) { nil }
|
83
|
-
end
|
84
|
-
|
85
|
-
baz.save!
|
86
|
-
|
87
|
-
expected_keys.each do |expected_key|
|
88
|
-
refute IdentityCache.cache.fetch(expected_key) { nil }
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
def test_cache_invalidation_expire_properly_if_child_is_embed_in_multiple_parents_with_ids
|
93
|
-
Item.cache_has_many :associated_records, :embed => :ids
|
94
|
-
ItemTwo.cache_has_many :associated_records, :embed => :ids
|
95
|
-
|
96
|
-
baz = AssociatedRecord.new(:name => 'baz')
|
97
|
-
|
98
|
-
record1 = Item.new(:title => 'foo')
|
99
|
-
record1.save
|
100
|
-
|
101
|
-
record2 = ItemTwo.new(:title => 'bar')
|
102
|
-
record2.save
|
103
|
-
|
104
|
-
record1.class.fetch(record1.id)
|
105
|
-
record2.class.fetch(record2.id)
|
106
|
-
|
107
|
-
expected_keys = [
|
108
|
-
record1.primary_cache_index_key,
|
109
|
-
record2.primary_cache_index_key,
|
110
|
-
]
|
111
|
-
|
112
|
-
expected_keys.each do |expected_key|
|
113
|
-
assert IdentityCache.cache.fetch(expected_key) { nil }
|
114
|
-
end
|
115
|
-
|
116
|
-
baz.item = record1
|
117
|
-
baz.item_two = record2
|
118
|
-
baz.save!
|
119
|
-
|
120
|
-
expected_keys.each do |expected_key|
|
121
|
-
refute IdentityCache.cache.fetch(expected_key) { nil }
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
|
-
def test_dedup_cache_invalidation_of_records_embedded_twice_through_different_associations
|
126
|
-
Item.cache_has_many :associated_records, embed: true
|
127
|
-
AssociatedRecord.cache_has_many :deeply_associated_records, embed: true
|
128
|
-
Item.cache_has_many :deeply_associated_records, embed: true
|
129
|
-
|
130
|
-
deeply_associated_record = DeeplyAssociatedRecord.new(name: 'deep', item_id: @record.id)
|
131
|
-
@record.associated_records[0].deeply_associated_records << deeply_associated_record
|
132
|
-
deeply_associated_record.reload
|
133
|
-
|
134
|
-
Item.any_instance.expects(:expire_primary_index).once
|
135
|
-
|
136
|
-
deeply_associated_record.name = "deep2"
|
137
|
-
deeply_associated_record.save!
|
138
|
-
end
|
139
|
-
end
|
@@ -1,19 +0,0 @@
|
|
1
|
-
require "test_helper"
|
2
|
-
|
3
|
-
class DeeplyNestedAssociatedRecordHasOneTest < IdentityCache::TestCase
|
4
|
-
def test_deeply_nested_models_can_cache_has_one_associations
|
5
|
-
assert_nothing_raised do
|
6
|
-
PolymorphicRecord.include(IdentityCache::WithoutPrimaryIndex)
|
7
|
-
Deeply::Nested::AssociatedRecord.has_one :polymorphic_record, as: 'owner'
|
8
|
-
Deeply::Nested::AssociatedRecord.cache_has_one :polymorphic_record, inverse_name: :owner
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
def test_deeply_nested_models_can_cache_has_many_associations
|
13
|
-
assert_nothing_raised do
|
14
|
-
PolymorphicRecord.include(IdentityCache)
|
15
|
-
Deeply::Nested::AssociatedRecord.has_many :polymorphic_records, as: 'owner'
|
16
|
-
Deeply::Nested::AssociatedRecord.cache_has_many :polymorphic_records, inverse_name: :owner
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
@@ -1,214 +0,0 @@
|
|
1
|
-
require "test_helper"
|
2
|
-
|
3
|
-
class DenormalizedHasManyTest < IdentityCache::TestCase
|
4
|
-
def setup
|
5
|
-
super
|
6
|
-
PolymorphicRecord.include(IdentityCache::WithoutPrimaryIndex)
|
7
|
-
Item.cache_has_many :associated_records, :embed => true
|
8
|
-
|
9
|
-
@record = Item.new(:title => 'foo')
|
10
|
-
@record.associated_records << AssociatedRecord.new(:name => 'bar')
|
11
|
-
@record.associated_records << AssociatedRecord.new(:name => 'baz')
|
12
|
-
@record.save
|
13
|
-
@record.reload
|
14
|
-
end
|
15
|
-
|
16
|
-
def test_uncached_record_from_the_db_should_come_back_with_association_array
|
17
|
-
record_from_db = Item.find(@record.id)
|
18
|
-
assert_equal Array, record_from_db.fetch_associated_records.class
|
19
|
-
end
|
20
|
-
|
21
|
-
def test_uncached_record_from_the_db_will_use_normal_association
|
22
|
-
expected = @record.associated_records
|
23
|
-
record_from_db = Item.find(@record.id)
|
24
|
-
|
25
|
-
Item.any_instance.expects(:association).with(:associated_records).returns(expected)
|
26
|
-
|
27
|
-
assert_equal @record, record_from_db
|
28
|
-
assert_equal expected, record_from_db.fetch_associated_records
|
29
|
-
end
|
30
|
-
|
31
|
-
def test_on_cache_hit_record_should_come_back_with_cached_association_array
|
32
|
-
Item.fetch(@record.id) # warm cache
|
33
|
-
|
34
|
-
record_from_cache_hit = Item.fetch(@record.id)
|
35
|
-
assert_equal @record, record_from_cache_hit
|
36
|
-
assert_equal Array, record_from_cache_hit.fetch_associated_records.class
|
37
|
-
end
|
38
|
-
|
39
|
-
def test_on_cache_hit_record_should_come_back_with_cached_association
|
40
|
-
Item.fetch(@record.id) # warm cache
|
41
|
-
|
42
|
-
record_from_cache_hit = Item.fetch(@record.id)
|
43
|
-
assert_equal @record, record_from_cache_hit
|
44
|
-
|
45
|
-
expected = @record.associated_records
|
46
|
-
|
47
|
-
assoc = mock()
|
48
|
-
assoc.expects(:klass).returns(Item)
|
49
|
-
Item.any_instance.expects(:association).with(:associated_records).returns(assoc).once
|
50
|
-
|
51
|
-
assert_equal expected, record_from_cache_hit.fetch_associated_records
|
52
|
-
end
|
53
|
-
|
54
|
-
def test_on_cache_miss_record_should_embed_associated_objects_and_return
|
55
|
-
record_from_cache_miss = Item.fetch(@record.id)
|
56
|
-
expected = @record.associated_records
|
57
|
-
|
58
|
-
assert_equal @record, record_from_cache_miss
|
59
|
-
assert_equal expected, record_from_cache_miss.fetch_associated_records
|
60
|
-
end
|
61
|
-
|
62
|
-
def test_changes_in_associated_records_should_expire_the_parents_cache
|
63
|
-
Item.fetch(@record.id)
|
64
|
-
key = @record.primary_cache_index_key
|
65
|
-
assert_not_nil IdentityCache.cache.fetch(key)
|
66
|
-
|
67
|
-
IdentityCache.cache.expects(:delete).with(@record.associated_records.first.primary_cache_index_key)
|
68
|
-
IdentityCache.cache.expects(:delete).with(key)
|
69
|
-
@record.associated_records.first.save
|
70
|
-
end
|
71
|
-
|
72
|
-
def test_changes_in_associated_records_foreign_keys_should_expire_new_parent_and_old_parents_cache
|
73
|
-
@associatated_record = @record.associated_records.first
|
74
|
-
old_key = @record.primary_cache_index_key
|
75
|
-
@new_record = Item.create!
|
76
|
-
new_key = @new_record.primary_cache_index_key
|
77
|
-
|
78
|
-
IdentityCache.cache.expects(:delete).with(@associatated_record.primary_cache_index_key)
|
79
|
-
IdentityCache.cache.expects(:delete).with(old_key)
|
80
|
-
IdentityCache.cache.expects(:delete).with(new_key)
|
81
|
-
@associatated_record.item = @new_record
|
82
|
-
@associatated_record.save!
|
83
|
-
end
|
84
|
-
|
85
|
-
def test_cached_associations_after_commit_hook_will_not_fail_on_undefined_parent_association
|
86
|
-
ar = AssociatedRecord.new
|
87
|
-
ar.save
|
88
|
-
assert_nothing_raised { ar.expire_parent_caches }
|
89
|
-
end
|
90
|
-
|
91
|
-
def test_cache_without_guessable_inverse_name_raises
|
92
|
-
assert_raises IdentityCache::InverseAssociationError do
|
93
|
-
Item.cache_has_many :polymorphic_records, :embed => true
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
def test_cache_without_guessable_inverse_name_does_not_raise_when_inverse_name_specified
|
98
|
-
assert_nothing_raised do
|
99
|
-
Item.cache_has_many :polymorphic_records, :inverse_name => :owner, :embed => true
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
def test_cache_uses_inverse_of_on_association
|
104
|
-
Item.has_many :invertable_association, :inverse_of => :owner, :class_name => 'PolymorphicRecord', :as => "owner"
|
105
|
-
Item.cache_has_many :invertable_association, :embed => true
|
106
|
-
end
|
107
|
-
|
108
|
-
def test_saving_associated_records_should_expire_itself_and_the_parents_cache
|
109
|
-
child = @record.associated_records.first
|
110
|
-
IdentityCache.cache.expects(:delete).with(child.primary_cache_index_key).once
|
111
|
-
IdentityCache.cache.expects(:delete).with(@record.primary_cache_index_key)
|
112
|
-
child.save!
|
113
|
-
end
|
114
|
-
|
115
|
-
def test_fetch_association_does_not_allow_chaining
|
116
|
-
check = proc { assert_equal false, Item.fetch(@record.id).fetch_associated_records.respond_to?(:where) }
|
117
|
-
2.times { check.call } # for miss and hit
|
118
|
-
Item.transaction { check.call }
|
119
|
-
end
|
120
|
-
|
121
|
-
def test_never_set_inverse_association_on_cache_hit
|
122
|
-
Item.fetch(@record.id) # warm cache
|
123
|
-
|
124
|
-
item = IdentityCache.with_never_set_inverse_association do
|
125
|
-
Item.fetch(@record.id)
|
126
|
-
end
|
127
|
-
|
128
|
-
associated_record = item.fetch_associated_records.to_a.first
|
129
|
-
assert item.object_id != associated_record.item.object_id
|
130
|
-
end
|
131
|
-
|
132
|
-
def test_deprecated_set_inverse_association_on_cache_hit
|
133
|
-
IdentityCache.never_set_inverse_association = false
|
134
|
-
Item.fetch(@record.id) # warm cache
|
135
|
-
|
136
|
-
item = Item.fetch(@record.id)
|
137
|
-
|
138
|
-
associated_record = item.fetch_associated_records.to_a.first
|
139
|
-
assert_equal item.object_id, associated_record.item.object_id
|
140
|
-
ensure
|
141
|
-
IdentityCache.never_set_inverse_association = true
|
142
|
-
end
|
143
|
-
|
144
|
-
def test_returned_records_should_be_readonly_on_cache_hit
|
145
|
-
IdentityCache.with_fetch_read_only_records do
|
146
|
-
Item.fetch(@record.id) # warm cache
|
147
|
-
record_from_cache_hit = Item.fetch(@record.id)
|
148
|
-
assert record_from_cache_hit.fetch_associated_records.all?(&:readonly?)
|
149
|
-
end
|
150
|
-
end
|
151
|
-
|
152
|
-
def test_returned_records_should_be_readonly_on_cache_miss
|
153
|
-
IdentityCache.with_fetch_read_only_records do
|
154
|
-
record_from_cache_miss = Item.fetch(@record.id)
|
155
|
-
assert record_from_cache_miss.fetch_associated_records.all?(&:readonly?)
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
|
-
def test_db_returned_records_should_never_be_readonly
|
160
|
-
IdentityCache.with_fetch_read_only_records do
|
161
|
-
record_from_db = Item.find(@record.id)
|
162
|
-
uncached_records = record_from_db.associated_records
|
163
|
-
assert uncached_records.none?(&:readonly?)
|
164
|
-
assert record_from_db.fetch_associated_records.all?(&:readonly?)
|
165
|
-
assert record_from_db.associated_records.none?(&:readonly?)
|
166
|
-
end
|
167
|
-
end
|
168
|
-
|
169
|
-
def test_returned_records_with_open_transactions_should_not_be_readonly
|
170
|
-
IdentityCache.with_fetch_read_only_records do
|
171
|
-
Item.transaction do
|
172
|
-
assert_equal IdentityCache.should_use_cache?, false
|
173
|
-
assert Item.fetch(@record.id).fetch_associated_records.none?(&:readonly?)
|
174
|
-
end
|
175
|
-
end
|
176
|
-
end
|
177
|
-
|
178
|
-
def test_respect_should_use_cache_from_embedded_records
|
179
|
-
Item.fetch(@record.id)
|
180
|
-
AssociatedRecord.stubs(:should_use_cache?).returns(false)
|
181
|
-
|
182
|
-
assert_memcache_operations(1) do
|
183
|
-
assert_queries(1) do
|
184
|
-
Item.fetch(@record.id).fetch_associated_records
|
185
|
-
end
|
186
|
-
end
|
187
|
-
end
|
188
|
-
|
189
|
-
class CheckAssociationTest < IdentityCache::TestCase
|
190
|
-
def test_unsupported_through_assocation
|
191
|
-
assert_raises IdentityCache::UnsupportedAssociationError, "caching through associations isn't supported" do
|
192
|
-
Item.has_many :deeply_through_associated_records, :through => :associated_records, foreign_key: 'associated_record_id', inverse_of: :item, :class_name => 'DeeplyAssociatedRecord'
|
193
|
-
Item.cache_has_many :deeply_through_associated_records, :embed => true
|
194
|
-
end
|
195
|
-
end
|
196
|
-
|
197
|
-
def test_unsupported_joins_in_assocation_scope
|
198
|
-
scope = -> { joins(:associated_record).where(associated_records: { name: 'contrived example' }) }
|
199
|
-
Item.has_many :deeply_joined_associated_records, scope, inverse_of: :item, class_name: 'DeeplyAssociatedRecord'
|
200
|
-
Item.cache_has_many :deeply_joined_associated_records, :embed => true
|
201
|
-
|
202
|
-
message = "caching association Item.deeply_joined_associated_records scoped with a join isn't supported"
|
203
|
-
assert_raises IdentityCache::UnsupportedAssociationError, message do
|
204
|
-
Item.fetch(1)
|
205
|
-
end
|
206
|
-
end
|
207
|
-
|
208
|
-
def test_cache_has_many_on_derived_model_raises
|
209
|
-
assert_raises(IdentityCache::DerivedModelError) do
|
210
|
-
StiRecordTypeA.cache_has_many :polymorphic_records, :inverse_name => :owner, :embed => true
|
211
|
-
end
|
212
|
-
end
|
213
|
-
end
|
214
|
-
end
|