identity_cache 0.4.1 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
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