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.
Files changed (91) hide show
  1. checksums.yaml +5 -5
  2. data/.github/probots.yml +2 -0
  3. data/.github/workflows/ci.yml +26 -0
  4. data/.gitignore +1 -0
  5. data/.rubocop.yml +5 -0
  6. data/.travis.yml +24 -9
  7. data/CHANGELOG.md +21 -0
  8. data/Gemfile +5 -1
  9. data/README.md +28 -26
  10. data/Rakefile +14 -5
  11. data/dev.yml +9 -16
  12. data/gemfiles/Gemfile.latest-release +6 -0
  13. data/gemfiles/Gemfile.rails-edge +6 -0
  14. data/gemfiles/Gemfile.rails52 +6 -0
  15. data/identity_cache.gemspec +26 -10
  16. data/lib/identity_cache.rb +49 -46
  17. data/lib/identity_cache/belongs_to_caching.rb +12 -40
  18. data/lib/identity_cache/cache_fetcher.rb +6 -5
  19. data/lib/identity_cache/cache_hash.rb +2 -2
  20. data/lib/identity_cache/cache_invalidation.rb +4 -11
  21. data/lib/identity_cache/cache_key_generation.rb +17 -65
  22. data/lib/identity_cache/cache_key_loader.rb +128 -0
  23. data/lib/identity_cache/cached.rb +7 -0
  24. data/lib/identity_cache/cached/association.rb +87 -0
  25. data/lib/identity_cache/cached/attribute.rb +123 -0
  26. data/lib/identity_cache/cached/attribute_by_multi.rb +37 -0
  27. data/lib/identity_cache/cached/attribute_by_one.rb +88 -0
  28. data/lib/identity_cache/cached/belongs_to.rb +93 -0
  29. data/lib/identity_cache/cached/embedded_fetching.rb +41 -0
  30. data/lib/identity_cache/cached/prefetcher.rb +51 -0
  31. data/lib/identity_cache/cached/primary_index.rb +97 -0
  32. data/lib/identity_cache/cached/recursive/association.rb +68 -0
  33. data/lib/identity_cache/cached/recursive/has_many.rb +9 -0
  34. data/lib/identity_cache/cached/recursive/has_one.rb +9 -0
  35. data/lib/identity_cache/cached/reference/association.rb +16 -0
  36. data/lib/identity_cache/cached/reference/has_many.rb +105 -0
  37. data/lib/identity_cache/cached/reference/has_one.rb +100 -0
  38. data/lib/identity_cache/configuration_dsl.rb +53 -215
  39. data/lib/identity_cache/encoder.rb +95 -0
  40. data/lib/identity_cache/expiry_hook.rb +36 -0
  41. data/lib/identity_cache/fallback_fetcher.rb +2 -1
  42. data/lib/identity_cache/load_strategy/eager.rb +28 -0
  43. data/lib/identity_cache/load_strategy/lazy.rb +71 -0
  44. data/lib/identity_cache/load_strategy/load_request.rb +20 -0
  45. data/lib/identity_cache/load_strategy/multi_load_request.rb +27 -0
  46. data/lib/identity_cache/memoized_cache_proxy.rb +127 -58
  47. data/lib/identity_cache/parent_model_expiration.rb +45 -11
  48. data/lib/identity_cache/query_api.rb +128 -394
  49. data/lib/identity_cache/railtie.rb +8 -0
  50. data/lib/identity_cache/record_not_found.rb +6 -0
  51. data/lib/identity_cache/should_use_cache.rb +1 -0
  52. data/lib/identity_cache/version.rb +3 -2
  53. data/lib/identity_cache/with_primary_index.rb +136 -0
  54. data/lib/identity_cache/without_primary_index.rb +24 -3
  55. data/performance/cache_runner.rb +28 -34
  56. data/performance/cpu.rb +3 -2
  57. data/performance/externals.rb +4 -3
  58. data/performance/profile.rb +6 -5
  59. data/railgun.yml +16 -0
  60. metadata +44 -73
  61. data/Gemfile.rails42 +0 -6
  62. data/Gemfile.rails50 +0 -6
  63. data/test/attribute_cache_test.rb +0 -110
  64. data/test/cache_fetch_includes_test.rb +0 -46
  65. data/test/cache_hash_test.rb +0 -14
  66. data/test/cache_invalidation_test.rb +0 -139
  67. data/test/deeply_nested_associated_record_test.rb +0 -19
  68. data/test/denormalized_has_many_test.rb +0 -214
  69. data/test/denormalized_has_one_test.rb +0 -160
  70. data/test/fetch_multi_test.rb +0 -308
  71. data/test/fetch_test.rb +0 -258
  72. data/test/fixtures/serialized_record.mysql2 +0 -0
  73. data/test/fixtures/serialized_record.postgresql +0 -0
  74. data/test/helpers/active_record_objects.rb +0 -106
  75. data/test/helpers/database_connection.rb +0 -72
  76. data/test/helpers/serialization_format.rb +0 -51
  77. data/test/helpers/update_serialization_format.rb +0 -27
  78. data/test/identity_cache_test.rb +0 -29
  79. data/test/index_cache_test.rb +0 -161
  80. data/test/memoized_attributes_test.rb +0 -59
  81. data/test/memoized_cache_proxy_test.rb +0 -107
  82. data/test/normalized_belongs_to_test.rb +0 -107
  83. data/test/normalized_has_many_test.rb +0 -231
  84. data/test/normalized_has_one_test.rb +0 -9
  85. data/test/prefetch_associations_test.rb +0 -379
  86. data/test/readonly_test.rb +0 -109
  87. data/test/recursive_denormalized_has_many_test.rb +0 -131
  88. data/test/save_test.rb +0 -82
  89. data/test/schema_change_test.rb +0 -112
  90. data/test/serialization_format_change_test.rb +0 -16
  91. data/test/test_helper.rb +0 -140
@@ -1,6 +0,0 @@
1
- source 'https://rubygems.org'
2
- gemspec
3
-
4
- gem 'activerecord', '~> 4.2.0'
5
- gem 'activesupport', '~> 4.2.0'
6
- gem 'mysql2', '~> 0.3.13'
@@ -1,6 +0,0 @@
1
- source 'https://rubygems.org'
2
- gemspec
3
-
4
- gem 'activerecord', '~> 5.0.0'
5
- gem 'activesupport', '~> 5.0.0'
6
- gem 'mysql2', '>= 0.3.18', '< 0.5'
@@ -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
@@ -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