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
data/Rakefile
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
#!/usr/bin/env rake
|
2
|
+
# frozen_string_literal: true
|
2
3
|
require 'bundler/gem_tasks'
|
3
4
|
|
4
5
|
require 'rake/testtask'
|
5
6
|
require 'rdoc/task'
|
6
7
|
|
7
|
-
desc
|
8
|
-
task :
|
8
|
+
desc('Default: run tests and style checks.')
|
9
|
+
task(default: [:test, :rubocop])
|
9
10
|
|
10
|
-
desc
|
11
|
+
desc('Test the identity_cache plugin.')
|
11
12
|
Rake::TestTask.new(:test) do |t|
|
12
13
|
t.libs << 'lib'
|
13
14
|
t.libs << 'test'
|
@@ -15,9 +16,17 @@ Rake::TestTask.new(:test) do |t|
|
|
15
16
|
t.verbose = true
|
16
17
|
end
|
17
18
|
|
18
|
-
|
19
|
+
task :rubocop do
|
20
|
+
require 'rubocop/rake_task'
|
21
|
+
RuboCop::RakeTask.new
|
22
|
+
end
|
23
|
+
|
24
|
+
desc('Update serialization format test fixture.')
|
19
25
|
task :update_serialization_format do
|
20
|
-
|
26
|
+
%w(mysql2 postgresql).each do |db|
|
27
|
+
ENV["DB"] = db
|
28
|
+
ruby './test/helpers/update_serialization_format.rb'
|
29
|
+
end
|
21
30
|
end
|
22
31
|
|
23
32
|
namespace :benchmark do
|
data/dev.yml
CHANGED
@@ -3,7 +3,10 @@ name: identity-cache
|
|
3
3
|
up:
|
4
4
|
- homebrew:
|
5
5
|
- postgresql
|
6
|
-
|
6
|
+
- mysql-client@5.7:
|
7
|
+
or: [mysql@5.7]
|
8
|
+
conflicts: [mysql-connector-c, mysql, mysql-client]
|
9
|
+
- ruby: 2.6.5
|
7
10
|
- railgun
|
8
11
|
- bundler
|
9
12
|
|
@@ -27,6 +30,14 @@ commands:
|
|
27
30
|
bundle exec ruby -I test "$@"
|
28
31
|
fi
|
29
32
|
|
33
|
+
style:
|
34
|
+
desc: 'Run rubocop checks'
|
35
|
+
run: bundle exec rubocop "$@"
|
36
|
+
|
37
|
+
check:
|
38
|
+
desc: 'Run tests and style checks'
|
39
|
+
run: bundle exec rake test && bundle exec rubocop
|
40
|
+
|
30
41
|
benchmark-cpu:
|
31
42
|
desc: 'Run the identity cache CPU benchmark'
|
32
43
|
run: bundle exec rake benchmark:cpu
|
@@ -38,18 +49,3 @@ commands:
|
|
38
49
|
update-serialization-format:
|
39
50
|
desc: 'Update serialization format test fixture'
|
40
51
|
run: bundle exec rake update_serialization_format
|
41
|
-
|
42
|
-
railgun:
|
43
|
-
image: dev:railgun-common-services-0.2.x
|
44
|
-
ip_address: 192.168.64.98
|
45
|
-
memory: 1G
|
46
|
-
cores: 1
|
47
|
-
disk: 1G
|
48
|
-
services:
|
49
|
-
mysql: 3306
|
50
|
-
postgresql: 5432
|
51
|
-
memcached: 11211
|
52
|
-
|
53
|
-
packages:
|
54
|
-
- git@github.com:Shopify/dev-shopify.git
|
55
|
-
|
data/identity_cache.gemspec
CHANGED
@@ -1,27 +1,46 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
+
# frozen_string_literal: true
|
2
3
|
require File.expand_path('../lib/identity_cache/version', __FILE__)
|
3
4
|
|
4
5
|
Gem::Specification.new do |gem|
|
5
|
-
gem.authors
|
6
|
+
gem.authors = [
|
7
|
+
"Camilo Lopez",
|
8
|
+
"Tom Burns",
|
9
|
+
"Harry Brundage",
|
10
|
+
"Dylan Thacker-Smith",
|
11
|
+
"Tobias Lutke",
|
12
|
+
"Arthur Neves",
|
13
|
+
"Francis Bogsanyi",
|
14
|
+
]
|
6
15
|
gem.email = ["gems@shopify.com"]
|
7
|
-
gem.description =
|
8
|
-
gem.summary =
|
16
|
+
gem.description = "Opt-in read through Active Record caching."
|
17
|
+
gem.summary = "IdentityCache lets you specify how you want to cache your " \
|
18
|
+
"model objects, at the model level, and adds a number of " \
|
19
|
+
"convenience methods for accessing those objects through " \
|
20
|
+
"the cache. Memcached is used as the backend cache store, " \
|
21
|
+
"and the database is only hit when a copy of the object " \
|
22
|
+
"cannot be found in Memcached."
|
9
23
|
gem.homepage = "https://github.com/Shopify/identity_cache"
|
10
24
|
|
11
|
-
gem.files =
|
12
|
-
|
13
|
-
|
25
|
+
gem.files = Dir.chdir(File.expand_path(__dir__)) do
|
26
|
+
%x(git ls-files -z).split("\x0").reject { |f| f.match(%r{^test/}) }
|
27
|
+
end
|
28
|
+
gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
|
29
|
+
gem.test_files = gem.files.grep(%r{^test/})
|
14
30
|
gem.name = "identity_cache"
|
15
31
|
gem.require_paths = ["lib"]
|
16
32
|
gem.version = IdentityCache::VERSION
|
17
33
|
|
18
|
-
gem.required_ruby_version = '>= 2.
|
34
|
+
gem.required_ruby_version = '>= 2.5.0'
|
35
|
+
|
36
|
+
gem.metadata['allowed_push_host'] = 'https://rubygems.org'
|
19
37
|
|
20
|
-
gem.add_dependency('ar_transaction_changes', '~> 1.
|
21
|
-
gem.add_dependency('activerecord', '>=
|
38
|
+
gem.add_dependency('ar_transaction_changes', '~> 1.1')
|
39
|
+
gem.add_dependency('activerecord', '>= 5.2')
|
22
40
|
|
23
41
|
gem.add_development_dependency('memcached', '~> 1.8.0')
|
24
42
|
gem.add_development_dependency('memcached_store', '~> 1.0.0')
|
43
|
+
gem.add_development_dependency('dalli')
|
25
44
|
gem.add_development_dependency('rake')
|
26
45
|
gem.add_development_dependency('mocha', '0.14.0')
|
27
46
|
gem.add_development_dependency('spy')
|
@@ -33,6 +52,6 @@ Gem::Specification.new do |gem|
|
|
33
52
|
gem.add_development_dependency('cityhash', '0.6.0')
|
34
53
|
gem.add_development_dependency('mysql2')
|
35
54
|
gem.add_development_dependency('pg')
|
36
|
-
gem.add_development_dependency('stackprof')
|
55
|
+
gem.add_development_dependency('stackprof')
|
37
56
|
end
|
38
57
|
end
|
data/lib/identity_cache.rb
CHANGED
@@ -1,8 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'active_record'
|
2
3
|
require 'active_support/core_ext/module/attribute_accessors'
|
3
4
|
require 'ar_transaction_changes'
|
4
5
|
|
5
6
|
require "identity_cache/version"
|
7
|
+
require "identity_cache/record_not_found"
|
8
|
+
require "identity_cache/encoder"
|
9
|
+
require "identity_cache/cache_key_loader"
|
10
|
+
require "identity_cache/load_strategy/load_request"
|
11
|
+
require "identity_cache/load_strategy/multi_load_request"
|
12
|
+
require "identity_cache/load_strategy/eager"
|
13
|
+
require "identity_cache/load_strategy/lazy"
|
14
|
+
require "identity_cache/cached"
|
15
|
+
require "identity_cache/cached/prefetcher"
|
16
|
+
require "identity_cache/cached/embedded_fetching"
|
17
|
+
require "identity_cache/cached/association"
|
18
|
+
require "identity_cache/cached/attribute"
|
19
|
+
require "identity_cache/cached/attribute_by_one"
|
20
|
+
require "identity_cache/cached/attribute_by_multi"
|
21
|
+
require "identity_cache/cached/belongs_to"
|
22
|
+
require "identity_cache/cached/primary_index"
|
23
|
+
require "identity_cache/cached/recursive/association"
|
24
|
+
require "identity_cache/cached/recursive/has_one"
|
25
|
+
require "identity_cache/cached/recursive/has_many"
|
26
|
+
require "identity_cache/cached/reference/association"
|
27
|
+
require "identity_cache/cached/reference/has_one"
|
28
|
+
require "identity_cache/cached/reference/has_many"
|
29
|
+
require "identity_cache/expiry_hook"
|
6
30
|
require 'identity_cache/memoized_cache_proxy'
|
7
31
|
require 'identity_cache/belongs_to_caching'
|
8
32
|
require 'identity_cache/cache_key_generation'
|
@@ -15,18 +39,14 @@ require "identity_cache/cache_invalidation"
|
|
15
39
|
require "identity_cache/cache_fetcher"
|
16
40
|
require "identity_cache/fallback_fetcher"
|
17
41
|
require 'identity_cache/without_primary_index'
|
42
|
+
require 'identity_cache/with_primary_index'
|
18
43
|
|
19
44
|
module IdentityCache
|
20
45
|
extend ActiveSupport::Concern
|
21
46
|
|
22
|
-
|
23
|
-
|
24
|
-
include
|
25
|
-
include IdentityCache::ConfigurationDSL
|
26
|
-
include IdentityCache::QueryAPI
|
27
|
-
include IdentityCache::CacheInvalidation
|
28
|
-
include IdentityCache::ShouldUseCache
|
29
|
-
include IdentityCache::ParentModelExpiration
|
47
|
+
autoload :MemCacheStoreCAS, 'identity_cache/mem_cache_store_cas'
|
48
|
+
|
49
|
+
include WithPrimaryIndex
|
30
50
|
|
31
51
|
CACHED_NIL = :idc_cached_nil
|
32
52
|
BATCH_SIZE = 1000
|
@@ -34,43 +54,34 @@ module IdentityCache
|
|
34
54
|
DELETED_TTL = 1000
|
35
55
|
|
36
56
|
class AlreadyIncludedError < StandardError; end
|
57
|
+
|
37
58
|
class AssociationError < StandardError; end
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
end
|
42
|
-
end
|
59
|
+
|
60
|
+
class InverseAssociationError < StandardError; end
|
61
|
+
|
43
62
|
class UnsupportedScopeError < StandardError; end
|
63
|
+
|
44
64
|
class UnsupportedAssociationError < StandardError; end
|
65
|
+
|
45
66
|
class DerivedModelError < StandardError; end
|
46
67
|
|
68
|
+
mattr_accessor :cache_namespace
|
69
|
+
self.cache_namespace = "IDC:#{CACHE_VERSION}:"
|
70
|
+
|
71
|
+
# Fetched records are not read-only and this could sometimes prevent IDC from
|
72
|
+
# reflecting what's truly in the database when fetch_read_only_records is false.
|
73
|
+
# When set to true, it will only return read-only records when cache is used.
|
74
|
+
mattr_accessor :fetch_read_only_records
|
75
|
+
self.fetch_read_only_records = true
|
76
|
+
|
47
77
|
class << self
|
48
78
|
include IdentityCache::CacheHash
|
49
79
|
|
50
80
|
attr_accessor :readonly
|
51
81
|
attr_writer :logger
|
52
82
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
version = Gem::Version.new(IdentityCache::VERSION)
|
57
|
-
|
58
|
-
# Inverse active record associations are set when loading embedded
|
59
|
-
# cache_has_many associations from the cache when never_set_inverse_association
|
60
|
-
# is false. When set to true, it will only set the inverse cached association.
|
61
|
-
mattr_accessor :never_set_inverse_association
|
62
|
-
self.never_set_inverse_association = version >= Gem::Version.new("0.5")
|
63
|
-
|
64
|
-
# Fetched records are not read-only and this could sometimes prevent IDC from
|
65
|
-
# reflecting what's truly in the database when fetch_read_only_records is false.
|
66
|
-
# When set to true, it will only return read-only records when cache is used.
|
67
|
-
mattr_accessor :fetch_read_only_records
|
68
|
-
self.fetch_read_only_records = version >= Gem::Version.new("0.5")
|
69
|
-
|
70
|
-
def included(base) #:nodoc:
|
71
|
-
raise AlreadyIncludedError if base.respond_to?(:cached_model)
|
72
|
-
base.class_attribute :cached_model
|
73
|
-
base.cached_model = base
|
83
|
+
def append_features(base) #:nodoc:
|
84
|
+
raise AlreadyIncludedError if base.include?(IdentityCache)
|
74
85
|
super
|
75
86
|
end
|
76
87
|
|
@@ -101,8 +112,27 @@ module IdentityCache
|
|
101
112
|
end
|
102
113
|
|
103
114
|
def should_use_cache? # :nodoc:
|
104
|
-
|
105
|
-
|
115
|
+
ActiveRecord::Base.connection_handler.connection_pool_list.none? do |pool|
|
116
|
+
pool.active_connection? &&
|
117
|
+
# Rails wraps each of your tests in a transaction, so that any changes
|
118
|
+
# made to the database during the test can be rolled back afterwards.
|
119
|
+
# These transactions are flagged as "unjoinable", which tries to make
|
120
|
+
# your application behave as if they weren't there. In particular:
|
121
|
+
#
|
122
|
+
# - Opening another transaction during the test creates a savepoint,
|
123
|
+
# which can be rolled back independently of the main transaction.
|
124
|
+
# - When those nested transactions complete, any `after_commit`
|
125
|
+
# callbacks for records modified during the transaction will run,
|
126
|
+
# even though the changes haven't actually been committed yet.
|
127
|
+
#
|
128
|
+
# By ignoring unjoinable transactions, IdentityCache's behaviour
|
129
|
+
# during your test suite will more closely match production.
|
130
|
+
#
|
131
|
+
# When there are no open transactions, `current_transaction` returns a
|
132
|
+
# special `NullTransaction` object that is unjoinable, meaning we will
|
133
|
+
# use the cache.
|
134
|
+
pool.connection.current_transaction.joinable?
|
135
|
+
end
|
106
136
|
end
|
107
137
|
|
108
138
|
# Cache retrieval and miss resolver primitive; given a key it will try to
|
@@ -114,7 +144,7 @@ module IdentityCache
|
|
114
144
|
#
|
115
145
|
def fetch(key)
|
116
146
|
if should_use_cache?
|
117
|
-
unmap_cached_nil_for(cache.fetch(key) { map_cached_nil_for
|
147
|
+
unmap_cached_nil_for(cache.fetch(key) { map_cached_nil_for(yield) })
|
118
148
|
else
|
119
149
|
yield
|
120
150
|
end
|
@@ -135,12 +165,12 @@ module IdentityCache
|
|
135
165
|
# +keys+ A collection or array of key strings
|
136
166
|
def fetch_multi(*keys)
|
137
167
|
keys.flatten!(1)
|
138
|
-
return {} if keys.
|
168
|
+
return {} if keys.empty?
|
139
169
|
|
140
170
|
result = if should_use_cache?
|
141
171
|
fetch_in_batches(keys.uniq) do |missed_keys|
|
142
172
|
results = yield missed_keys
|
143
|
-
results.map {|e| map_cached_nil_for
|
173
|
+
results.map { |e| map_cached_nil_for(e) }
|
144
174
|
end
|
145
175
|
else
|
146
176
|
results = yield keys
|
@@ -154,29 +184,26 @@ module IdentityCache
|
|
154
184
|
result
|
155
185
|
end
|
156
186
|
|
157
|
-
def with_never_set_inverse_association(value = true)
|
158
|
-
old_value = self.never_set_inverse_association
|
159
|
-
self.never_set_inverse_association = value
|
160
|
-
yield
|
161
|
-
ensure
|
162
|
-
self.never_set_inverse_association = old_value
|
163
|
-
end
|
164
|
-
|
165
|
-
|
166
187
|
def with_fetch_read_only_records(value = true)
|
167
|
-
old_value =
|
188
|
+
old_value = fetch_read_only_records
|
168
189
|
self.fetch_read_only_records = value
|
169
190
|
yield
|
170
191
|
ensure
|
171
192
|
self.fetch_read_only_records = old_value
|
172
193
|
end
|
173
194
|
|
195
|
+
def eager_load!
|
196
|
+
ParentModelExpiration.install_all_pending_parent_expiry_hooks
|
197
|
+
end
|
198
|
+
|
174
199
|
private
|
175
200
|
|
176
201
|
def fetch_in_batches(keys)
|
177
|
-
keys.each_slice(BATCH_SIZE).each_with_object
|
178
|
-
result.merge!
|
202
|
+
keys.each_slice(BATCH_SIZE).each_with_object({}) do |slice, result|
|
203
|
+
result.merge!(cache.fetch_multi(*slice) { |missed_keys| yield missed_keys })
|
179
204
|
end
|
180
205
|
end
|
181
206
|
end
|
182
207
|
end
|
208
|
+
|
209
|
+
require 'identity_cache/railtie' if defined?(Rails)
|
@@ -1,59 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module IdentityCache
|
2
3
|
module BelongsToCaching
|
3
4
|
extend ActiveSupport::Concern
|
4
5
|
|
5
6
|
included do |base|
|
6
|
-
base.class_attribute
|
7
|
+
base.class_attribute(:cached_belongs_tos)
|
7
8
|
base.cached_belongs_tos = {}
|
8
9
|
end
|
9
10
|
|
10
11
|
module ClassMethods
|
11
|
-
def cache_belongs_to(association
|
12
|
+
def cache_belongs_to(association)
|
12
13
|
ensure_base_model
|
13
|
-
raise NotImplementedError if options[:embed]
|
14
14
|
|
15
|
-
unless
|
15
|
+
unless (reflection = reflect_on_association(association))
|
16
16
|
raise AssociationError, "Association named '#{association}' was not found on #{self.class}"
|
17
17
|
end
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
options[:association_reflection] = association_reflection
|
26
|
-
options[:prepopulate_method_name] = "prepopulate_fetched_#{association}"
|
27
|
-
|
28
|
-
build_normalized_belongs_to_cache(association, options)
|
29
|
-
end
|
30
|
-
|
31
|
-
private
|
19
|
+
if reflection.scope
|
20
|
+
raise(
|
21
|
+
UnsupportedAssociationError,
|
22
|
+
"caching association #{self}.#{association} is scoped which isn't supported"
|
23
|
+
)
|
24
|
+
end
|
32
25
|
|
33
|
-
|
34
|
-
foreign_key = options[:association_reflection].foreign_key
|
35
|
-
self.class_eval(<<-CODE, __FILE__, __LINE__ + 1)
|
36
|
-
def #{options[:cached_accessor_name]}
|
37
|
-
association_klass = association(:#{association}).klass
|
38
|
-
if association_klass.should_use_cache? && #{foreign_key}.present? && !association(:#{association}).loaded?
|
39
|
-
if instance_variable_defined?(:@#{options[:records_variable_name]})
|
40
|
-
@#{options[:records_variable_name]}
|
41
|
-
else
|
42
|
-
@#{options[:records_variable_name]} = association_klass.fetch_by_id(#{foreign_key})
|
43
|
-
end
|
44
|
-
else
|
45
|
-
if IdentityCache.fetch_read_only_records && association_klass.should_use_cache?
|
46
|
-
readonly_copy(association(:#{association}).load_target)
|
47
|
-
else
|
48
|
-
#{association}
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
26
|
+
cached_belongs_to = Cached::BelongsTo.new(association, reflection: reflection)
|
52
27
|
|
53
|
-
|
54
|
-
@#{options[:records_variable_name]} = record
|
55
|
-
end
|
56
|
-
CODE
|
28
|
+
cached_belongs_tos[association] = cached_belongs_to.tap(&:build)
|
57
29
|
end
|
58
30
|
end
|
59
31
|
end
|