identity_cache 0.4.1 → 1.1.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.
Files changed (94) hide show
  1. checksums.yaml +5 -5
  2. data/.github/probots.yml +2 -0
  3. data/.github/workflows/ci.yml +92 -0
  4. data/.gitignore +2 -0
  5. data/.rubocop.yml +5 -0
  6. data/CAVEATS.md +25 -0
  7. data/CHANGELOG.md +73 -19
  8. data/Gemfile +5 -1
  9. data/LICENSE +1 -1
  10. data/README.md +49 -27
  11. data/Rakefile +14 -5
  12. data/dev.yml +12 -16
  13. data/gemfiles/Gemfile.latest-release +8 -0
  14. data/gemfiles/Gemfile.min-supported +7 -0
  15. data/gemfiles/Gemfile.rails-edge +7 -0
  16. data/identity_cache.gemspec +29 -10
  17. data/lib/identity_cache.rb +78 -51
  18. data/lib/identity_cache/belongs_to_caching.rb +12 -40
  19. data/lib/identity_cache/cache_fetcher.rb +6 -5
  20. data/lib/identity_cache/cache_hash.rb +2 -2
  21. data/lib/identity_cache/cache_invalidation.rb +4 -11
  22. data/lib/identity_cache/cache_key_generation.rb +17 -65
  23. data/lib/identity_cache/cache_key_loader.rb +128 -0
  24. data/lib/identity_cache/cached.rb +7 -0
  25. data/lib/identity_cache/cached/association.rb +87 -0
  26. data/lib/identity_cache/cached/attribute.rb +123 -0
  27. data/lib/identity_cache/cached/attribute_by_multi.rb +37 -0
  28. data/lib/identity_cache/cached/attribute_by_one.rb +88 -0
  29. data/lib/identity_cache/cached/belongs_to.rb +100 -0
  30. data/lib/identity_cache/cached/embedded_fetching.rb +41 -0
  31. data/lib/identity_cache/cached/prefetcher.rb +61 -0
  32. data/lib/identity_cache/cached/primary_index.rb +96 -0
  33. data/lib/identity_cache/cached/recursive/association.rb +109 -0
  34. data/lib/identity_cache/cached/recursive/has_many.rb +9 -0
  35. data/lib/identity_cache/cached/recursive/has_one.rb +9 -0
  36. data/lib/identity_cache/cached/reference/association.rb +16 -0
  37. data/lib/identity_cache/cached/reference/has_many.rb +105 -0
  38. data/lib/identity_cache/cached/reference/has_one.rb +100 -0
  39. data/lib/identity_cache/configuration_dsl.rb +53 -215
  40. data/lib/identity_cache/encoder.rb +95 -0
  41. data/lib/identity_cache/expiry_hook.rb +36 -0
  42. data/lib/identity_cache/fallback_fetcher.rb +2 -1
  43. data/lib/identity_cache/load_strategy/eager.rb +28 -0
  44. data/lib/identity_cache/load_strategy/lazy.rb +71 -0
  45. data/lib/identity_cache/load_strategy/load_request.rb +20 -0
  46. data/lib/identity_cache/load_strategy/multi_load_request.rb +27 -0
  47. data/lib/identity_cache/mem_cache_store_cas.rb +53 -0
  48. data/lib/identity_cache/memoized_cache_proxy.rb +137 -58
  49. data/lib/identity_cache/parent_model_expiration.rb +46 -11
  50. data/lib/identity_cache/query_api.rb +102 -408
  51. data/lib/identity_cache/railtie.rb +8 -0
  52. data/lib/identity_cache/record_not_found.rb +6 -0
  53. data/lib/identity_cache/should_use_cache.rb +1 -0
  54. data/lib/identity_cache/version.rb +3 -2
  55. data/lib/identity_cache/with_primary_index.rb +136 -0
  56. data/lib/identity_cache/without_primary_index.rb +24 -3
  57. data/performance/cache_runner.rb +25 -73
  58. data/performance/cpu.rb +4 -3
  59. data/performance/externals.rb +4 -3
  60. data/performance/profile.rb +6 -5
  61. data/railgun.yml +16 -0
  62. metadata +60 -73
  63. data/.travis.yml +0 -30
  64. data/Gemfile.rails42 +0 -6
  65. data/Gemfile.rails50 +0 -6
  66. data/test/attribute_cache_test.rb +0 -110
  67. data/test/cache_fetch_includes_test.rb +0 -46
  68. data/test/cache_hash_test.rb +0 -14
  69. data/test/cache_invalidation_test.rb +0 -139
  70. data/test/deeply_nested_associated_record_test.rb +0 -19
  71. data/test/denormalized_has_many_test.rb +0 -211
  72. data/test/denormalized_has_one_test.rb +0 -160
  73. data/test/fetch_multi_test.rb +0 -308
  74. data/test/fetch_test.rb +0 -258
  75. data/test/fixtures/serialized_record.mysql2 +0 -0
  76. data/test/fixtures/serialized_record.postgresql +0 -0
  77. data/test/helpers/active_record_objects.rb +0 -106
  78. data/test/helpers/database_connection.rb +0 -72
  79. data/test/helpers/serialization_format.rb +0 -42
  80. data/test/helpers/update_serialization_format.rb +0 -24
  81. data/test/identity_cache_test.rb +0 -29
  82. data/test/index_cache_test.rb +0 -161
  83. data/test/memoized_attributes_test.rb +0 -49
  84. data/test/memoized_cache_proxy_test.rb +0 -107
  85. data/test/normalized_belongs_to_test.rb +0 -107
  86. data/test/normalized_has_many_test.rb +0 -231
  87. data/test/normalized_has_one_test.rb +0 -9
  88. data/test/prefetch_associations_test.rb +0 -364
  89. data/test/readonly_test.rb +0 -109
  90. data/test/recursive_denormalized_has_many_test.rb +0 -131
  91. data/test/save_test.rb +0 -82
  92. data/test/schema_change_test.rb +0 -112
  93. data/test/serialization_format_change_test.rb +0 -16
  94. data/test/test_helper.rb +0 -140
@@ -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,211 +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
- Item.fetch(@record.id) # warm cache
134
-
135
- item = Item.fetch(@record.id)
136
-
137
- associated_record = item.fetch_associated_records.to_a.first
138
- assert_equal item.object_id, associated_record.item.object_id
139
- end
140
-
141
- def test_returned_records_should_be_readonly_on_cache_hit
142
- IdentityCache.with_fetch_read_only_records do
143
- Item.fetch(@record.id) # warm cache
144
- record_from_cache_hit = Item.fetch(@record.id)
145
- assert record_from_cache_hit.fetch_associated_records.all?(&:readonly?)
146
- end
147
- end
148
-
149
- def test_returned_records_should_be_readonly_on_cache_miss
150
- IdentityCache.with_fetch_read_only_records do
151
- record_from_cache_miss = Item.fetch(@record.id)
152
- assert record_from_cache_miss.fetch_associated_records.all?(&:readonly?)
153
- end
154
- end
155
-
156
- def test_db_returned_records_should_never_be_readonly
157
- IdentityCache.with_fetch_read_only_records do
158
- record_from_db = Item.find(@record.id)
159
- uncached_records = record_from_db.associated_records
160
- assert uncached_records.none?(&:readonly?)
161
- assert record_from_db.fetch_associated_records.all?(&:readonly?)
162
- assert record_from_db.associated_records.none?(&:readonly?)
163
- end
164
- end
165
-
166
- def test_returned_records_with_open_transactions_should_not_be_readonly
167
- IdentityCache.with_fetch_read_only_records do
168
- Item.transaction do
169
- assert_equal IdentityCache.should_use_cache?, false
170
- assert Item.fetch(@record.id).fetch_associated_records.none?(&:readonly?)
171
- end
172
- end
173
- end
174
-
175
- def test_respect_should_use_cache_from_embedded_records
176
- Item.fetch(@record.id)
177
- AssociatedRecord.stubs(:should_use_cache?).returns(false)
178
-
179
- assert_memcache_operations(1) do
180
- assert_queries(1) do
181
- Item.fetch(@record.id).fetch_associated_records
182
- end
183
- end
184
- end
185
-
186
- class CheckAssociationTest < IdentityCache::TestCase
187
- def test_unsupported_through_assocation
188
- assert_raises IdentityCache::UnsupportedAssociationError, "caching through associations isn't supported" do
189
- Item.has_many :deeply_through_associated_records, :through => :associated_records, foreign_key: 'associated_record_id', inverse_of: :item, :class_name => 'DeeplyAssociatedRecord'
190
- Item.cache_has_many :deeply_through_associated_records, :embed => true
191
- end
192
- end
193
-
194
- def test_unsupported_joins_in_assocation_scope
195
- scope = -> { joins(:associated_record).where(associated_records: { name: 'contrived example' }) }
196
- Item.has_many :deeply_joined_associated_records, scope, inverse_of: :item, class_name: 'DeeplyAssociatedRecord'
197
- Item.cache_has_many :deeply_joined_associated_records, :embed => true
198
-
199
- message = "caching association Item.deeply_joined_associated_records scoped with a join isn't supported"
200
- assert_raises IdentityCache::UnsupportedAssociationError, message do
201
- Item.fetch(1)
202
- end
203
- end
204
-
205
- def test_cache_has_many_on_derived_model_raises
206
- assert_raises(IdentityCache::DerivedModelError) do
207
- StiRecordTypeA.cache_has_many :polymorphic_records, :inverse_name => :owner, :embed => true
208
- end
209
- end
210
- end
211
- end
@@ -1,160 +0,0 @@
1
- require "test_helper"
2
-
3
- class DenormalizedHasOneTest < IdentityCache::TestCase
4
- def setup
5
- super
6
- PolymorphicRecord.include(IdentityCache::WithoutPrimaryIndex)
7
- Item.cache_has_one :associated
8
- Item.cache_index :title, :unique => true
9
- @record = Item.new(:title => 'foo')
10
- @record.associated = AssociatedRecord.new(:name => 'bar')
11
- @record.save
12
-
13
- @record.reload
14
- end
15
-
16
- def test_on_cache_miss_record_should_embed_associated_object
17
- fetch = Spy.on(IdentityCache.cache, :fetch).and_call_through
18
-
19
- record_from_cache_miss = Item.fetch_by_title('foo')
20
-
21
- assert_equal @record, record_from_cache_miss
22
- assert_not_nil @record.fetch_associated
23
- assert_equal @record.associated, record_from_cache_miss.fetch_associated
24
- assert fetch.has_been_called_with?(@record.attribute_cache_key_for_attribute_and_current_values(:id, [:title], true))
25
- assert fetch.has_been_called_with?(@record.primary_cache_index_key)
26
- end
27
-
28
- def test_on_cache_miss_record_should_embed_nil_object
29
-
30
- @record.associated = nil
31
- @record.save!
32
- @record.reload
33
- Item.expects(:resolve_cache_miss).with(@record.id).once.returns(@record)
34
-
35
- fetch = Spy.on(IdentityCache.cache, :fetch).and_call_through
36
-
37
- record_from_cache_miss = Item.fetch_by_title('foo')
38
- record_from_cache_miss.expects(:associated).never
39
-
40
- assert_equal @record, record_from_cache_miss
41
- 5.times do
42
- assert_nil record_from_cache_miss.fetch_associated
43
- end
44
- assert fetch.has_been_called_with?(@record.attribute_cache_key_for_attribute_and_current_values(:id, [:title], true))
45
- assert fetch.has_been_called_with?(@record.primary_cache_index_key)
46
- end
47
-
48
- def test_on_record_from_the_db_will_use_normal_association
49
- record_from_db = Item.find_by_title('foo')
50
-
51
- assert_equal @record, record_from_db
52
- assert_not_nil record_from_db.fetch_associated
53
- end
54
-
55
- def test_on_cache_hit_record_should_come_back_with_cached_association
56
- Item.expects(:resolve_cache_miss).with(1).once.returns(@record)
57
- Item.fetch_by_title('foo')
58
-
59
- record_from_cache_hit = Item.fetch_by_title('foo')
60
- expected = @record.associated
61
-
62
- assert_equal @record, record_from_cache_hit
63
- assert_equal expected, record_from_cache_hit.fetch_associated
64
- end
65
-
66
- def test_on_cache_hit_record_should_come_back_with_cached_nil_association
67
- @record.associated = nil
68
- @record.save!
69
- @record.reload
70
-
71
- Item.expects(:resolve_cache_miss).with(1).once.returns(@record)
72
- Item.fetch_by_title('foo')
73
-
74
- record_from_cache_hit = Item.fetch_by_title('foo')
75
- record_from_cache_hit.expects(:associated).never
76
-
77
- assert_equal @record, record_from_cache_hit
78
- 5.times do
79
- assert_nil record_from_cache_hit.fetch_associated
80
- end
81
- end
82
-
83
- def test_changes_in_associated_record_should_expire_the_parents_cache
84
- Item.fetch_by_title('foo')
85
- key = @record.primary_cache_index_key
86
- assert_not_nil IdentityCache.cache.fetch(key)
87
-
88
- IdentityCache.cache.expects(:delete).at_least(1).with(key)
89
- IdentityCache.cache.expects(:delete).with(@record.associated.primary_cache_index_key)
90
-
91
- @record.associated.save
92
- end
93
-
94
- def test_cached_associations_after_commit_hook_will_not_fail_on_undefined_parent_association
95
- ar = AssociatedRecord.new
96
- ar.save
97
- assert_nothing_raised { ar.expire_parent_caches }
98
- end
99
-
100
- def test_cache_without_guessable_inverse_name_raises
101
- assert_raises IdentityCache::InverseAssociationError do
102
- Item.cache_has_one :polymorphic_record, :embed => true
103
- end
104
- end
105
-
106
- def test_cache_without_guessable_inverse_name_does_not_raise_when_inverse_name_specified
107
- assert_nothing_raised do
108
- Item.cache_has_one :polymorphic_record, :inverse_name => :owner, :embed => true
109
- end
110
- end
111
-
112
- def test_unsupported_through_assocation
113
- assert_raises IdentityCache::UnsupportedAssociationError, "caching through associations isn't supported" do
114
- Item.has_one :deeply_associated, :through => :associated, :class_name => 'DeeplyAssociatedRecord'
115
- Item.cache_has_one :deeply_associated, :embed => true
116
- end
117
- end
118
-
119
- def test_cache_has_one_on_derived_model_raises
120
- assert_raises(IdentityCache::DerivedModelError) do
121
- StiRecordTypeA.cache_has_one :polymorphic_record, :inverse_name => :owner, :embed => true
122
- end
123
- end
124
-
125
- def test_returned_record_should_be_readonly_on_cache_hit
126
- IdentityCache.with_fetch_read_only_records do
127
- Item.fetch_by_title('foo')
128
- record_from_cache_hit = Item.fetch_by_title('foo')
129
- assert record_from_cache_hit.fetch_associated.readonly?
130
- refute record_from_cache_hit.associated.readonly?
131
- end
132
- end
133
-
134
- def test_returned_record_should_be_readonly_on_cache_miss
135
- IdentityCache.with_fetch_read_only_records do
136
- assert IdentityCache.should_use_cache?
137
- record_from_cache_miss = Item.fetch_by_title('foo')
138
- assert record_from_cache_miss.fetch_associated.readonly?
139
- end
140
- end
141
-
142
- def test_db_returned_record_should_never_be_readonly
143
- IdentityCache.with_fetch_read_only_records do
144
- record_from_db = Item.find_by_title('foo')
145
- uncached_record = record_from_db.associated
146
- refute uncached_record.readonly?
147
- record_from_db.fetch_associated
148
- refute uncached_record.readonly?
149
- end
150
- end
151
-
152
- def test_returned_record_with_open_transactions_should_not_be_readonly
153
- IdentityCache.with_fetch_read_only_records do
154
- Item.transaction do
155
- refute IdentityCache.should_use_cache?
156
- refute Item.fetch_by_title('foo').fetch_associated.readonly?
157
- end
158
- end
159
- end
160
- end