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.
- 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
|