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.
- checksums.yaml +5 -5
- data/.github/probots.yml +2 -0
- data/.github/workflows/ci.yml +92 -0
- data/.gitignore +2 -0
- data/.rubocop.yml +5 -0
- data/CAVEATS.md +25 -0
- data/CHANGELOG.md +73 -19
- data/Gemfile +5 -1
- data/LICENSE +1 -1
- data/README.md +49 -27
- data/Rakefile +14 -5
- data/dev.yml +12 -16
- data/gemfiles/Gemfile.latest-release +8 -0
- data/gemfiles/Gemfile.min-supported +7 -0
- data/gemfiles/Gemfile.rails-edge +7 -0
- data/identity_cache.gemspec +29 -10
- data/lib/identity_cache.rb +78 -51
- 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 +100 -0
- data/lib/identity_cache/cached/embedded_fetching.rb +41 -0
- data/lib/identity_cache/cached/prefetcher.rb +61 -0
- data/lib/identity_cache/cached/primary_index.rb +96 -0
- data/lib/identity_cache/cached/recursive/association.rb +109 -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/mem_cache_store_cas.rb +53 -0
- data/lib/identity_cache/memoized_cache_proxy.rb +137 -58
- data/lib/identity_cache/parent_model_expiration.rb +46 -11
- data/lib/identity_cache/query_api.rb +102 -408
- 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 +25 -73
- data/performance/cpu.rb +4 -3
- data/performance/externals.rb +4 -3
- data/performance/profile.rb +6 -5
- data/railgun.yml +16 -0
- metadata +60 -73
- data/.travis.yml +0 -30
- 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 -211
- 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 -42
- data/test/helpers/update_serialization_format.rb +0 -24
- data/test/identity_cache_test.rb +0 -29
- data/test/index_cache_test.rb +0 -161
- data/test/memoized_attributes_test.rb +0 -49
- 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 -364
- 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
Binary file
|
Binary file
|
@@ -1,106 +0,0 @@
|
|
1
|
-
module SwitchNamespace
|
2
|
-
|
3
|
-
module ClassMethods
|
4
|
-
def rails_cache_key_namespace
|
5
|
-
"#{self.namespace}:#{super}"
|
6
|
-
end
|
7
|
-
end
|
8
|
-
|
9
|
-
def self.included(base)
|
10
|
-
base.extend ClassMethods
|
11
|
-
base.class_eval do
|
12
|
-
class_attribute :namespace
|
13
|
-
self.namespace = 'ns'
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
module ActiveRecordObjects
|
19
|
-
|
20
|
-
def setup_models(base = ActiveRecord::Base)
|
21
|
-
Object.send :const_set, 'DeeplyAssociatedRecord', Class.new(base) {
|
22
|
-
include IdentityCache
|
23
|
-
belongs_to :item
|
24
|
-
belongs_to :associated_record
|
25
|
-
default_scope { order('name DESC') }
|
26
|
-
}
|
27
|
-
|
28
|
-
Object.send :const_set, 'AssociatedRecord', Class.new(base) {
|
29
|
-
include IdentityCache
|
30
|
-
belongs_to :item, inverse_of: :associated_records
|
31
|
-
belongs_to :item_two, inverse_of: :associated_records
|
32
|
-
has_many :deeply_associated_records
|
33
|
-
default_scope { order('id DESC') }
|
34
|
-
}
|
35
|
-
|
36
|
-
Object.send :const_set, 'NormalizedAssociatedRecord', Class.new(base) {
|
37
|
-
include IdentityCache
|
38
|
-
belongs_to :item
|
39
|
-
default_scope { order('id DESC') }
|
40
|
-
}
|
41
|
-
|
42
|
-
Object.send :const_set, 'NotCachedRecord', Class.new(base) {
|
43
|
-
belongs_to :item, :touch => true
|
44
|
-
default_scope { order('id DESC') }
|
45
|
-
}
|
46
|
-
|
47
|
-
Object.send :const_set, 'PolymorphicRecord', Class.new(base) {
|
48
|
-
belongs_to :owner, :polymorphic => true
|
49
|
-
}
|
50
|
-
|
51
|
-
Object.send :const_set, 'Deeply', Module.new
|
52
|
-
Deeply.send :const_set, 'Nested', Module.new
|
53
|
-
Deeply::Nested.send :const_set, 'AssociatedRecord', Class.new(base) {
|
54
|
-
include IdentityCache
|
55
|
-
}
|
56
|
-
|
57
|
-
Object.send :const_set, 'Item', Class.new(base) {
|
58
|
-
include IdentityCache
|
59
|
-
belongs_to :item
|
60
|
-
has_many :associated_records, inverse_of: :item
|
61
|
-
has_many :deeply_associated_records, inverse_of: :item
|
62
|
-
has_many :normalized_associated_records
|
63
|
-
has_many :not_cached_records
|
64
|
-
has_many :polymorphic_records, :as => 'owner'
|
65
|
-
has_one :polymorphic_record, :as => 'owner'
|
66
|
-
has_one :associated, :class_name => 'AssociatedRecord'
|
67
|
-
}
|
68
|
-
|
69
|
-
Object.send :const_set, 'ItemTwo', Class.new(base) {
|
70
|
-
include IdentityCache
|
71
|
-
has_many :associated_records, inverse_of: :item_two, foreign_key: :item_two_id
|
72
|
-
self.table_name = 'items2'
|
73
|
-
}
|
74
|
-
|
75
|
-
Object.send :const_set, 'KeyedRecord', Class.new(base) {
|
76
|
-
include IdentityCache
|
77
|
-
self.primary_key = "hashed_key"
|
78
|
-
}
|
79
|
-
|
80
|
-
Object.send :const_set, 'StiRecord', Class.new(base) {
|
81
|
-
include IdentityCache
|
82
|
-
has_many :polymorphic_records, :as => 'owner'
|
83
|
-
}
|
84
|
-
|
85
|
-
Object.send :const_set, 'StiRecordTypeA', Class.new(StiRecord) {
|
86
|
-
}
|
87
|
-
end
|
88
|
-
|
89
|
-
def teardown_models
|
90
|
-
ActiveSupport::DescendantsTracker.clear
|
91
|
-
ActiveSupport::Dependencies.clear
|
92
|
-
Object.send :remove_const, 'DeeplyAssociatedRecord'
|
93
|
-
Object.send :remove_const, 'PolymorphicRecord'
|
94
|
-
Object.send :remove_const, 'NormalizedAssociatedRecord'
|
95
|
-
Object.send :remove_const, 'AssociatedRecord'
|
96
|
-
Object.send :remove_const, 'NotCachedRecord'
|
97
|
-
Object.send :remove_const, 'Item'
|
98
|
-
Object.send :remove_const, 'ItemTwo'
|
99
|
-
Object.send :remove_const, 'KeyedRecord'
|
100
|
-
Object.send :remove_const, 'StiRecord'
|
101
|
-
Object.send :remove_const, 'StiRecordTypeA'
|
102
|
-
Deeply::Nested.send :remove_const, 'AssociatedRecord'
|
103
|
-
Deeply.send :remove_const, 'Nested'
|
104
|
-
Object.send :remove_const, 'Deeply'
|
105
|
-
end
|
106
|
-
end
|
@@ -1,72 +0,0 @@
|
|
1
|
-
module DatabaseConnection
|
2
|
-
def self.db_name
|
3
|
-
ENV.fetch('DB', 'mysql2')
|
4
|
-
end
|
5
|
-
|
6
|
-
def self.setup
|
7
|
-
db_config = ENV['DATABASE_URL'] || DEFAULT_CONFIG[db_name]
|
8
|
-
begin
|
9
|
-
ActiveRecord::Base.establish_connection(db_config)
|
10
|
-
ActiveRecord::Base.connection
|
11
|
-
rescue
|
12
|
-
raise unless db_config.is_a?(Hash)
|
13
|
-
ActiveRecord::Base.establish_connection(db_config.merge('database' => nil))
|
14
|
-
ActiveRecord::Base.connection.create_database(db_config['database'])
|
15
|
-
ActiveRecord::Base.establish_connection(db_config)
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
def self.drop_tables
|
20
|
-
TABLES.keys.each do |table|
|
21
|
-
ActiveRecord::Base.connection.drop_table(table) if table_exists?(table)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
def self.table_exists?(table)
|
26
|
-
if ActiveRecord::Base.connection.respond_to?(:data_source_exists?)
|
27
|
-
ActiveRecord::Base.connection.data_source_exists?(table)
|
28
|
-
else
|
29
|
-
ActiveRecord::Base.connection.table_exists?(table)
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
def self.create_tables
|
34
|
-
TABLES.each do |table, fields|
|
35
|
-
fields = fields.dup
|
36
|
-
options = fields.last.is_a?(Hash) ? fields.pop : {}
|
37
|
-
ActiveRecord::Base.connection.create_table(table, options) do |t|
|
38
|
-
fields.each do |column_type, *args|
|
39
|
-
t.send(column_type, *args)
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
TABLES = {
|
46
|
-
:polymorphic_records => [[:string, :owner_type], [:integer, :owner_id], [:timestamps, null: true]],
|
47
|
-
:deeply_associated_records => [[:string, :name], [:integer, :associated_record_id], [:integer, :item_id], [:timestamps, null: true]],
|
48
|
-
:associated_records => [[:string, :name], [:integer, :item_id], [:integer, :item_two_id]],
|
49
|
-
:normalized_associated_records => [[:string, :name], [:integer, :item_id], [:timestamps, null: true]],
|
50
|
-
:not_cached_records => [[:string, :name], [:integer, :item_id], [:timestamps, null: true]],
|
51
|
-
:items => [[:integer, :item_id], [:string, :title], [:timestamps, null: true]],
|
52
|
-
:items2 => [[:integer, :item_id], [:string, :title], [:timestamps, null: true]],
|
53
|
-
:keyed_records => [[:string, :value], :primary_key => "hashed_key"],
|
54
|
-
:sti_records => [[:string, :type], [:string, :name]],
|
55
|
-
}
|
56
|
-
|
57
|
-
DEFAULT_CONFIG = {
|
58
|
-
'mysql2' => {
|
59
|
-
'adapter' => 'mysql2',
|
60
|
-
'database' => 'identity_cache_test',
|
61
|
-
'host' => ENV['MYSQL_HOST'] || '127.0.0.1',
|
62
|
-
'username' => 'root'
|
63
|
-
},
|
64
|
-
'postgresql' => {
|
65
|
-
'adapter' => 'postgresql',
|
66
|
-
'database' => 'identity_cache_test',
|
67
|
-
'host' => ENV['POSTGRES_HOST'] || '127.0.0.1',
|
68
|
-
'username' => 'postgres',
|
69
|
-
'prepared_statements' => false,
|
70
|
-
}
|
71
|
-
}
|
72
|
-
end
|
@@ -1,42 +0,0 @@
|
|
1
|
-
module SerializationFormat
|
2
|
-
def serialized_record
|
3
|
-
AssociatedRecord.cache_has_many :deeply_associated_records, :embed => true
|
4
|
-
AssociatedRecord.cache_belongs_to :item, :embed => false
|
5
|
-
Item.cache_has_many :associated_records, :embed => true
|
6
|
-
Item.cache_has_one :associated
|
7
|
-
time = Time.parse('1970-01-01T00:00:00 UTC')
|
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.associated = AssociatedRecord.new(:name => 'bork')
|
13
|
-
record.not_cached_records << NotCachedRecord.new(:name => 'NoCache', created_at: time)
|
14
|
-
record.associated.deeply_associated_records << DeeplyAssociatedRecord.new(:name => "corge", created_at: time)
|
15
|
-
record.associated.deeply_associated_records << DeeplyAssociatedRecord.new(:name => "qux", created_at: time)
|
16
|
-
record.created_at = time
|
17
|
-
record.save
|
18
|
-
[Item, NotCachedRecord, DeeplyAssociatedRecord].each do |model|
|
19
|
-
model.update_all(updated_at: time)
|
20
|
-
end
|
21
|
-
record.reload
|
22
|
-
Item.fetch(record.id)
|
23
|
-
IdentityCache.fetch(record.primary_cache_index_key)
|
24
|
-
end
|
25
|
-
|
26
|
-
def serialized_record_file
|
27
|
-
File.expand_path("../../fixtures/serialized_record.#{DatabaseConnection.db_name}", __FILE__)
|
28
|
-
end
|
29
|
-
|
30
|
-
def serialize(record, anIO = nil)
|
31
|
-
hash = {
|
32
|
-
:version => IdentityCache::CACHE_VERSION,
|
33
|
-
:record => record
|
34
|
-
}
|
35
|
-
|
36
|
-
if anIO
|
37
|
-
Marshal.dump(hash, anIO)
|
38
|
-
else
|
39
|
-
Marshal.dump(hash)
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
@@ -1,24 +0,0 @@
|
|
1
|
-
$LOAD_PATH.unshift File.expand_path("../../../lib", __FILE__)
|
2
|
-
|
3
|
-
require 'logger'
|
4
|
-
require 'active_record'
|
5
|
-
require 'memcached_store'
|
6
|
-
require 'active_support/cache/memcached_store'
|
7
|
-
require_relative 'serialization_format'
|
8
|
-
require_relative 'database_connection'
|
9
|
-
require_relative 'active_record_objects'
|
10
|
-
require 'identity_cache'
|
11
|
-
|
12
|
-
include SerializationFormat
|
13
|
-
include ActiveRecordObjects
|
14
|
-
|
15
|
-
DatabaseConnection.setup
|
16
|
-
DatabaseConnection.drop_tables
|
17
|
-
DatabaseConnection.create_tables
|
18
|
-
IdentityCache.logger = Logger.new(nil)
|
19
|
-
IdentityCache.cache_backend = ActiveSupport::Cache::MemcachedStore.new("localhost:11211", :support_cas => true)
|
20
|
-
setup_models
|
21
|
-
File.open(serialized_record_file, 'w') {|file| serialize(serialized_record, file) }
|
22
|
-
puts "Serialized record to #{serialized_record_file}"
|
23
|
-
IdentityCache.cache.clear
|
24
|
-
teardown_models
|
data/test/identity_cache_test.rb
DELETED
@@ -1,29 +0,0 @@
|
|
1
|
-
require "test_helper"
|
2
|
-
|
3
|
-
class IdentityCacheTest < IdentityCache::TestCase
|
4
|
-
|
5
|
-
class BadModelBase < ActiveRecord::Base
|
6
|
-
include IdentityCache
|
7
|
-
end
|
8
|
-
|
9
|
-
class BadModel < BadModelBase
|
10
|
-
end
|
11
|
-
|
12
|
-
def test_identity_cache_raises_if_loaded_twice
|
13
|
-
assert_raises(IdentityCache::AlreadyIncludedError) do
|
14
|
-
BadModel.class_eval do
|
15
|
-
include IdentityCache
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
def test_should_use_cache_outside_transaction
|
21
|
-
assert_equal true, IdentityCache.should_use_cache?
|
22
|
-
end
|
23
|
-
|
24
|
-
def test_should_use_cache_in_transaction
|
25
|
-
ActiveRecord::Base.transaction do
|
26
|
-
assert_equal false, IdentityCache.should_use_cache?
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
data/test/index_cache_test.rb
DELETED
@@ -1,161 +0,0 @@
|
|
1
|
-
require "test_helper"
|
2
|
-
|
3
|
-
class IndexCacheTest < IdentityCache::TestCase
|
4
|
-
NAMESPACE = IdentityCache::CacheKeyGeneration::DEFAULT_NAMESPACE
|
5
|
-
|
6
|
-
def setup
|
7
|
-
super
|
8
|
-
@record = Item.new
|
9
|
-
@record.id = 1
|
10
|
-
@record.title = 'bob'
|
11
|
-
end
|
12
|
-
|
13
|
-
def test_fetch_with_garbage_input
|
14
|
-
Item.cache_index :title, :id
|
15
|
-
|
16
|
-
assert_queries(1) do
|
17
|
-
assert_equal [], Item.fetch_by_title_and_id('garbage', 'garbage')
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
def test_fetch_with_unique_adds_limit_clause
|
22
|
-
Item.cache_index :title, :id, :unique => true
|
23
|
-
|
24
|
-
Item.connection.expects(:exec_query)
|
25
|
-
.with(regexp_matches(/ LIMIT 1\Z/i), any_parameters)
|
26
|
-
.returns(ActiveRecord::Result.new([], []))
|
27
|
-
|
28
|
-
assert_nil Item.fetch_by_title_and_id('title', '2')
|
29
|
-
end
|
30
|
-
|
31
|
-
def test_unique_index_caches_nil
|
32
|
-
Item.cache_index :title, :unique => true
|
33
|
-
assert_nil Item.fetch_by_title('bob')
|
34
|
-
assert_equal IdentityCache::CACHED_NIL, backend.read(cache_key(unique: true))
|
35
|
-
end
|
36
|
-
|
37
|
-
def test_unique_index_expired_by_new_record
|
38
|
-
Item.cache_index :title, :unique => true
|
39
|
-
IdentityCache.cache.write(cache_key(unique: true), IdentityCache::CACHED_NIL)
|
40
|
-
@record.save!
|
41
|
-
assert_equal IdentityCache::DELETED, backend.read(cache_key(unique: true))
|
42
|
-
end
|
43
|
-
|
44
|
-
def test_unique_index_filled_on_fetch_by
|
45
|
-
Item.cache_index :title, :unique => true
|
46
|
-
@record.save!
|
47
|
-
assert_equal @record, Item.fetch_by_title('bob')
|
48
|
-
assert_equal @record.id, backend.read(cache_key(unique: true))
|
49
|
-
end
|
50
|
-
|
51
|
-
def test_unique_index_expired_by_updated_record
|
52
|
-
Item.cache_index :title, :unique => true
|
53
|
-
@record.save!
|
54
|
-
old_cache_key = cache_key(unique: true)
|
55
|
-
IdentityCache.cache.write(old_cache_key, @record.id)
|
56
|
-
|
57
|
-
@record.title = 'robert'
|
58
|
-
new_cache_key = cache_key(unique: true)
|
59
|
-
IdentityCache.cache.write(new_cache_key, IdentityCache::CACHED_NIL)
|
60
|
-
@record.save!
|
61
|
-
assert_equal IdentityCache::DELETED, backend.read(old_cache_key)
|
62
|
-
assert_equal IdentityCache::DELETED, backend.read(new_cache_key)
|
63
|
-
end
|
64
|
-
|
65
|
-
def test_non_unique_index_caches_empty_result
|
66
|
-
Item.cache_index :title
|
67
|
-
assert_equal [], Item.fetch_by_title('bob')
|
68
|
-
assert_equal [], backend.read(cache_key)
|
69
|
-
end
|
70
|
-
|
71
|
-
def test_non_unique_index_expired_by_new_record
|
72
|
-
Item.cache_index :title
|
73
|
-
IdentityCache.cache.write(cache_key, [])
|
74
|
-
@record.save!
|
75
|
-
assert_equal IdentityCache::DELETED, backend.read(cache_key)
|
76
|
-
end
|
77
|
-
|
78
|
-
def test_non_unique_index_filled_on_fetch_by
|
79
|
-
Item.cache_index :title
|
80
|
-
@record.save!
|
81
|
-
assert_equal [@record], Item.fetch_by_title('bob')
|
82
|
-
assert_equal [@record.id], backend.read(cache_key)
|
83
|
-
end
|
84
|
-
|
85
|
-
def test_non_unique_index_fetches_multiple_records
|
86
|
-
Item.cache_index :title
|
87
|
-
@record.save!
|
88
|
-
record2 = Item.create(:title => 'bob') { |item| item.id = 2 }
|
89
|
-
|
90
|
-
assert_equal [@record, record2], Item.fetch_by_title('bob')
|
91
|
-
assert_equal [1, 2], backend.read(cache_key)
|
92
|
-
end
|
93
|
-
|
94
|
-
def test_non_unique_index_expired_by_updating_record
|
95
|
-
Item.cache_index :title
|
96
|
-
@record.save!
|
97
|
-
old_cache_key = cache_key
|
98
|
-
IdentityCache.cache.write(old_cache_key, [@record.id])
|
99
|
-
|
100
|
-
@record.title = 'robert'
|
101
|
-
new_cache_key = cache_key
|
102
|
-
IdentityCache.cache.write(new_cache_key, [])
|
103
|
-
@record.save!
|
104
|
-
assert_equal IdentityCache::DELETED, backend.read(old_cache_key)
|
105
|
-
assert_equal IdentityCache::DELETED, backend.read(new_cache_key)
|
106
|
-
end
|
107
|
-
|
108
|
-
def test_non_unique_index_expired_by_destroying_record
|
109
|
-
Item.cache_index :title
|
110
|
-
@record.save!
|
111
|
-
IdentityCache.cache.write(cache_key, [@record.id])
|
112
|
-
@record.destroy
|
113
|
-
assert_equal IdentityCache::DELETED, backend.read(cache_key)
|
114
|
-
end
|
115
|
-
|
116
|
-
def test_set_table_name_cache_fetch
|
117
|
-
Item.cache_index :title
|
118
|
-
Item.table_name = 'items2'
|
119
|
-
@record.save!
|
120
|
-
assert_equal [@record], Item.fetch_by_title('bob')
|
121
|
-
assert_equal [@record.id], backend.read(cache_key)
|
122
|
-
end
|
123
|
-
|
124
|
-
def test_fetch_by_index_raises_when_called_on_a_scope
|
125
|
-
Item.cache_index :title
|
126
|
-
assert_raises(IdentityCache::UnsupportedScopeError) do
|
127
|
-
Item.where(updated_at: nil).fetch_by_title('bob')
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
131
|
-
def test_fetch_by_unique_index_raises_when_called_on_a_scope
|
132
|
-
Item.cache_index :title, :id, :unique => true
|
133
|
-
assert_raises(IdentityCache::UnsupportedScopeError) do
|
134
|
-
Item.where(updated_at: nil).fetch_by_title_and_id('bob', 2)
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
|
-
def test_cache_index_on_derived_model_raises
|
139
|
-
assert_raises(IdentityCache::DerivedModelError) do
|
140
|
-
StiRecordTypeA.cache_index :name, :id
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
def test_cache_index_with_non_id_primary_key
|
145
|
-
KeyedRecord.cache_index :value
|
146
|
-
KeyedRecord.create!(value: "a") { |r| r.hashed_key = 123 }
|
147
|
-
assert_equal [123], KeyedRecord.fetch_by_value('a').map(&:id)
|
148
|
-
end
|
149
|
-
|
150
|
-
def test_unique_cache_index_with_non_id_primary_key
|
151
|
-
KeyedRecord.cache_index :value, unique: true
|
152
|
-
KeyedRecord.create!(value: "a") { |r| r.hashed_key = 123 }
|
153
|
-
assert_equal 123, KeyedRecord.fetch_by_value('a').id
|
154
|
-
end
|
155
|
-
|
156
|
-
private
|
157
|
-
|
158
|
-
def cache_key(unique: false)
|
159
|
-
"#{NAMESPACE}attr#{unique ? '' : 's'}:Item:id:title:#{cache_hash(@record.title)}"
|
160
|
-
end
|
161
|
-
end
|
@@ -1,49 +0,0 @@
|
|
1
|
-
require "test_helper"
|
2
|
-
|
3
|
-
class MemoizedAttributesTest < IdentityCache::TestCase
|
4
|
-
def test_memoization_should_not_break_dirty_tracking_with_empty_cache
|
5
|
-
item = Item.create!
|
6
|
-
|
7
|
-
IdentityCache.cache.with_memoization do
|
8
|
-
Item.fetch(item.id).title = "my title"
|
9
|
-
Item.fetch(item.id).update!(title: "my title")
|
10
|
-
end
|
11
|
-
|
12
|
-
assert_equal "my title", Item.find(item.id).title
|
13
|
-
end
|
14
|
-
|
15
|
-
def test_memoization_should_not_break_dirty_tracking_with_filled_cache
|
16
|
-
item = Item.create!
|
17
|
-
|
18
|
-
IdentityCache.cache.with_memoization do
|
19
|
-
Item.fetch(item.id)
|
20
|
-
Item.fetch(item.id).title = "my title"
|
21
|
-
Item.fetch(item.id).update!(title: "my title")
|
22
|
-
end
|
23
|
-
|
24
|
-
assert_equal "my title", Item.find(item.id).title
|
25
|
-
end
|
26
|
-
|
27
|
-
def test_memoization_with_fetch_multi_should_not_break_dirty_tracking_with_empty_cache
|
28
|
-
item = Item.create!
|
29
|
-
|
30
|
-
IdentityCache.cache.with_memoization do
|
31
|
-
Item.fetch_multi(item.id).first.title = "my title"
|
32
|
-
Item.fetch_multi(item.id).first.update!(title: "my title")
|
33
|
-
end
|
34
|
-
|
35
|
-
assert_equal "my title", Item.find(item.id).title
|
36
|
-
end
|
37
|
-
|
38
|
-
def test_memoization_with_fetch_multi_should_not_break_dirty_tracking_with_filled_cache
|
39
|
-
item = Item.create!
|
40
|
-
|
41
|
-
IdentityCache.cache.with_memoization do
|
42
|
-
Item.fetch_multi(item.id)
|
43
|
-
Item.fetch_multi(item.id).first.title = "my title"
|
44
|
-
Item.fetch_multi(item.id).first.update!(title: "my title")
|
45
|
-
end
|
46
|
-
|
47
|
-
assert_equal "my title", Item.find(item.id).title
|
48
|
-
end
|
49
|
-
end
|