ar_cache 1.0.0 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8eab4ff7873091b901a45bfa48e948679d7feda3e0e547201d3f5ab931bbf77e
4
- data.tar.gz: 642654c010e8ced742980a0657480305be9904835a23715d884df3a90d42eef8
3
+ metadata.gz: '09eeda930c7263476f3b09e53747f9ec87ac070e65f18031856cdb05ad57a083'
4
+ data.tar.gz: 425c321debdcc05d81d4c0815abecb0d9e19fa417bb251265ba6dab420dd03e1
5
5
  SHA512:
6
- metadata.gz: a41629683860591506020f8ee516d6d3948ac0daf5381dff1c1b3410fc659be978a4f27d31c0ba49be3fbdf9fd608e7c424a215f5efd75842f5fbe47b31a2d75
7
- data.tar.gz: 71dc11fbccc0816af45df3a1e3e3b3460c595f67fed0376afb9202237367970593441937131e80514d7dfd6bd601d4d2bdc3e3329267c9f6370efb36708291ec
6
+ metadata.gz: 827f0ecdf9e583870a454c16cfaa44c95b9d7b0ba4f3942097daf1c76fed218adfdafcd5733b68fa6476297c8b6d35c728e1a1b6f66db615d9fde834183ea4e8
7
+ data.tar.gz: 273bef3daea68391d681045c384488bf9266a1f75cd5ea7057b6aaf70928ebe97934cb65daadf3f6a3442177d948923116867be503f479c6ac8ab06ac23bb286
data/CHANGELOG.md ADDED
@@ -0,0 +1,13 @@
1
+ # Change log
2
+
3
+ ## main
4
+
5
+ ## 1.0.0 (2021-03-02)
6
+
7
+ - Initial version.
8
+
9
+ ## 1.1.0 (2021-03-11)
10
+
11
+ - Fully automatic delete cache when call delete_all/update_all method.
12
+ - Optimize has_one(through:) cache implementation.
13
+ - ActiveRecord::Relation#reload and ActiveRecord::Associations::Association#reload should skip read cache if associated target is already loaded.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- ar_cache (1.0.0)
4
+ ar_cache (1.1.0)
5
5
  activerecord (>= 6.1, < 7)
6
6
 
7
7
  GEM
@@ -129,9 +129,9 @@ GEM
129
129
  thor (~> 1.0)
130
130
  rainbow (3.0.0)
131
131
  rake (13.0.3)
132
- regexp_parser (2.0.3)
132
+ regexp_parser (2.1.1)
133
133
  rexml (3.2.4)
134
- rubocop (1.9.0)
134
+ rubocop (1.11.0)
135
135
  parallel (~> 1.10)
136
136
  parser (>= 3.0.0.0)
137
137
  rainbow (>= 2.2.2, < 4.0)
data/README.md CHANGED
@@ -1,7 +1,10 @@
1
1
  # ArCache
2
2
 
3
+ ![Test Status](https://github.com/OuYangJinTing/ar_cache/workflows/CI/badge.svg)
4
+ [![Gem Version](https://badge.fury.io/rb/ar_cache.svg)](https://badge.fury.io/rb/ar_cache)
5
+
3
6
  `ArCache` is an modern cacheing library for `ActiveRecord` inspired by cache-money and second_level_cache.
4
- It works automatically by copied `ActiveRecord` related code.
7
+ It works automatically by overridden `ActiveRecord` related CURD code.
5
8
  When executing standard `ActiveRecord` query, it will first query the cache, and if there is none in the cache,
6
9
  then query the database and write the result to the cache.
7
10
 
@@ -82,41 +85,35 @@ For configuration information, please see [configuration](lib/generators/ar_cach
82
85
 
83
86
  If all the following conditions are met, ArCache will try to read the cache:
84
87
 
85
- - Use hash as `#where` parameter.
88
+ - **Use hash as `#where` parameter**.
86
89
  - Query condition contains unique index.
87
90
  - Condition of unique index is only one array or no array.
88
91
  - No call `#select` or select value is table column.
89
92
  - No call `#order` or order value is table column and only one.
90
93
  - No call `#limit` or value of the unique index isn't array.
91
- - No call `#joins`.
92
- - No call `#left_joins`.
93
- - No call `#skip_query_cache!`.
94
- - No call `#skip_ar_cache`.
95
- - No call `#explain`.
96
- - No call `#from`.
97
- - No call `#group`.
98
- - No call `value`.
94
+ - No call `#joins`, `#left_joins`, `#skip_query_cache!`, `#skip_ar_cache`, `#explain`, `#from`, `#group`, `#offset`, `#lock`
99
95
  - ...
100
96
 
101
97
  **Cacheable example:**
102
98
 
103
99
  ```ruby
104
- User.find(1) # primary key cache
105
- User.where(id: [1, 2]) # array query cache
106
- User.where(email: 'foobar@gmail.com') # sigle-column unique index cache
107
- User.where(name: 'foobar', status: :active) # multi-column unique index cache
108
- User.includes(:account).where(id: [1, 2]) # association cache
109
- User.first.account # association model cache
100
+ User.find(1) # support primary key cache
101
+ User.where(id: [1, 2]) # support multi-value unique index cache
102
+ User.where(email: 'foobar@gmail.com') # support sigle-column unique index cache
103
+ User.where(name: 'foobar', status: :active) # support multi-column unique index cache
104
+ User.includes(:account).where(id: [1, 2]) # support association preload cache
105
+ User.first.account # support association reader cach
110
106
  ```
111
107
 
108
+ The association cache support belongs_to and has_one, small amount of complex has_one(scope, through:, as:) don't support, then has_many cache support, please watch to future version.
109
+
112
110
  ## Cache iteration
113
111
 
114
112
  The following cases will cause cache iteration:
115
113
 
116
114
  - Table field changes.
117
- - Open `ArCache` or close `ArCache`.
118
- - `ActiveRecord` update/delete condition does not hit the unique index.
119
- - `ActiveRecord` update/delete join other tables.
115
+ - Turn on `ArCache` or turn off `ArCache`.
116
+ - Call `#upsert_all` method.
120
117
 
121
118
  **Notice: After iteration, all existing caches of the table will be expired!**
122
119
 
@@ -130,6 +127,8 @@ The following cases will cause cache iteration:
130
127
  - Prohibit use `ActiveRecord` other underlying methods to directly update/delete data! (You is a fake activerecord user if this code appears)
131
128
  - Prohibit skip `ActiveRecord` directly update/delete data!
132
129
 
130
+ If you have to do this, please consider turning off ArCache.
131
+
133
132
  ## Alternatives
134
133
 
135
134
  There are some other gems implementations for `ActiveRecord` cache such as:
data/lib/ar_cache.rb CHANGED
@@ -18,5 +18,31 @@ require 'ar_cache/active_record'
18
18
  require_relative './generators/ar_cache/install_generator' if defined?(Rails)
19
19
 
20
20
  module ArCache
21
- singleton_class.delegate :configure, to: Configuration
21
+ PRELOADER = ::ActiveRecord::Associations::Preloader.new
22
+
23
+ class << self
24
+ delegate :configure, to: Configuration
25
+
26
+ def skip_cache?
27
+ Thread.current[:ar_cache_skip_cache]
28
+ end
29
+
30
+ def skip_cache
31
+ Thread.current[:ar_cache_skip_cache] = true
32
+ yield
33
+ ensure
34
+ Thread.current[:ar_cache_skip_cache] = false
35
+ end
36
+
37
+ def pre_expire?
38
+ Thread.current[:ar_cache_pre_expire]
39
+ end
40
+
41
+ def pre_expire
42
+ Thread.current[:ar_cache_pre_expire] = true
43
+ yield
44
+ ensure
45
+ Thread.current[:ar_cache_pre_expire] = false
46
+ end
47
+ end
22
48
  end
@@ -5,6 +5,7 @@ require 'ar_cache/active_record/relation'
5
5
  require 'ar_cache/active_record/core'
6
6
  require 'ar_cache/active_record/persistence'
7
7
  require 'ar_cache/active_record/insert_all'
8
+ require 'ar_cache/active_record/associations/association'
8
9
  require 'ar_cache/active_record/associations/singular_association'
9
10
  require 'ar_cache/active_record/associations/has_one_through_association'
10
11
  require 'ar_cache/active_record/connection_adapters/abstract/transaction'
@@ -18,11 +19,13 @@ ActiveSupport.on_load(:active_record, run_once: true) do
18
19
  ActiveRecord::ModelSchema::ClassMethods.prepend(ArCache::ActiveRecord::ModelSchema::ClassMethods)
19
20
 
20
21
  ActiveRecord::Persistence.prepend(ArCache::ActiveRecord::Persistence)
22
+ ActiveRecord::Persistence::ClassMethods.prepend(ArCache::ActiveRecord::Persistence::ClassMethods)
21
23
 
22
24
  ActiveRecord::InsertAll.prepend(ArCache::ActiveRecord::InsertAll)
23
25
 
24
26
  ActiveRecord::Relation.prepend(ArCache::ActiveRecord::Relation)
25
27
 
28
+ ActiveRecord::Associations::Association.prepend(ArCache::ActiveRecord::Associations::Association)
26
29
  ActiveRecord::Associations::SingularAssociation.prepend(ArCache::ActiveRecord::Associations::SingularAssociation)
27
30
  ActiveRecord::Associations::HasOneThroughAssociation.prepend(ArCache::ActiveRecord::Associations::HasOneThroughAssociation)
28
31
 
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ArCache
4
+ module ActiveRecord
5
+ module Associations
6
+ module Association
7
+ def reload(...)
8
+ loaded? ? ArCache.skip_cache { super } : super
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -4,33 +4,17 @@ module ArCache
4
4
  module ActiveRecord
5
5
  module Associations
6
6
  module HasOneThroughAssociation
7
- private def find_target # rubocop:disable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
7
+ private def find_target
8
8
  return super if reflection.klass.ar_cache_table.disabled?
9
9
  return super if reflection.through_reflection.klass.ar_cache_table.disabled?
10
10
 
11
- if owner.strict_loading? && owner.validation_context.nil?
12
- Base.strict_loading_violation!(owner: owner.class, association: reflection.klass)
11
+ if (owner.strict_loading? || reflection.strict_loading?) && owner.validation_context.nil?
12
+ Base.strict_loading_violation!(owner: owner.class, reflection: reflection)
13
13
  end
14
14
 
15
- if reflection.strict_loading? && owner.validation_context.nil?
16
- Base.strict_loading_violation!(owner: owner.class, association: reflection.name)
17
- end
18
-
19
- # TODO: Should not instantiate AR
20
- through_record = if reflection.scope
21
- owner.association(reflection.through_reflection.name).scope.merge(reflection.scope).first
22
- else
23
- owner.send(reflection.through_reflection.name)
24
- end
25
- return super if through_record.is_a?(::ActiveRecord::Associations::CollectionProxy)
26
- return nil if !through_record || through_record.destroyed?
27
-
28
- record = through_record.send(reflection.source_reflection.name)
29
- record = record.first if record.is_a?(::ActiveRecord::Associations::CollectionProxy)
30
- return nil unless record
31
-
32
- record.tap { |r| set_inverse_instance(r) }
33
- rescue StandardError # If scope depend on other table, will raise exception
15
+ ArCache::PRELOADER.preload(owner, reflection.name)
16
+ owner.send(reflection.name)
17
+ rescue StandardError
34
18
  super
35
19
  end
36
20
  end
@@ -4,71 +4,67 @@ module ArCache
4
4
  module ActiveRecord
5
5
  module ConnectionAdapters
6
6
  module DatabaseStatements
7
- def insert(arel, ...)
8
- super.tap do
9
- if arel.is_a?(String)
10
- sql = arel.downcase
11
- ArCache::Table.all.each do |table|
12
- current_transaction.add_changed_table(table.name) if sql.include?(table.name)
13
- end
14
- else # is Arel::InsertManager
15
- klass = arel.ast.relation.instance_variable_get(:@klass)
16
- current_transaction.add_changed_table(klass.table_name)
17
- end
18
- end
19
- end
20
- alias create insert
7
+ # def insert(arel, ...)
8
+ # super.tap do
9
+ # if arel.is_a?(String)
10
+ # sql = arel.downcase
11
+ # ArCache::Table.all.each do |table|
12
+ # current_transaction.add_changed_table(table.name) if sql.include?(table.name)
13
+ # end
14
+ # else # is Arel::InsertManager
15
+ # klass = arel.ast.relation.instance_variable_get(:@klass)
16
+ # current_transaction.add_changed_table(klass.table_name)
17
+ # end
18
+ # end
19
+ # end
20
+ # alias create insert
21
21
 
22
22
  def update(arel, ...)
23
- super.tap { |num| update_ar_cache_version(arel) unless num.zero? }
23
+ super.tap { |num| update_ar_cache(arel) unless num.zero? }
24
24
  end
25
25
 
26
26
  def delete(arel, ...)
27
- super.tap { |num| update_ar_cache_version(arel) unless num.zero? }
27
+ super.tap { |num| update_ar_cache(arel) unless num.zero? }
28
28
  end
29
29
 
30
30
  def truncate(table_name, ...)
31
- super.tap { update_ar_cache_version_by_table(table_name) }
31
+ super.tap { update_ar_cache_by_table(table_name) }
32
32
  end
33
33
 
34
34
  def truncate_tables(*table_names)
35
35
  super.tap do
36
- table_names.each { |table_name| update_ar_cache_version_by_table(table_name) }
36
+ table_names.each { |table_name| update_ar_cache_by_table(table_name) }
37
37
  end
38
38
  end
39
39
 
40
- private def update_ar_cache_version(arel_or_sql_string)
40
+ private def update_ar_cache(arel_or_sql_string)
41
41
  if arel_or_sql_string.is_a?(String)
42
- update_ar_cache_version_by_sql(arel_or_sql_string)
42
+ update_ar_cache_by_sql(arel_or_sql_string)
43
43
  else # is Arel::TreeManager
44
- update_ar_cache_version_by_arel(arel_or_sql_string)
44
+ update_ar_cache_by_arel(arel_or_sql_string)
45
45
  end
46
46
  end
47
47
 
48
- private def update_ar_cache_version_by_arel(arel)
49
- # arel.ast.relation may be of the following types:
50
- # - Arel::Nodes::JoinSource
51
- # - Arel::Table
48
+ private def update_ar_cache_by_arel(arel)
49
+ return if ArCache.pre_expire?
50
+
52
51
  arel_table = arel.ast.relation.is_a?(Arel::Table) ? arel.ast.relation : arel.ast.relation.left
53
52
  klass = arel_table.instance_variable_get(:@klass)
54
- return if klass.ar_cache_table.disabled?
55
-
56
- where_clause = ArCache::WhereClause.new(klass, arel.ast.wheres)
57
- if where_clause.cacheable?
58
- current_transaction.add_changed_table(klass.table_name)
59
- current_transaction.add_ar_cache_keys(where_clause.cache_keys)
60
- else
61
- current_transaction.add_ar_cache_table(klass.ar_cache_table)
62
- end
53
+ current_transaction.update_ar_cache_table(klass.ar_cache_table) if klass.ar_cache_table.enabled?
63
54
  end
64
55
 
65
- private def update_ar_cache_version_by_sql(sql)
56
+ private def update_ar_cache_by_sql(sql)
66
57
  sql = sql.downcase
67
- ArCache::Table.all.each { |table| current_transaction.add_ar_cache_table(table) if sql.include?(table.name) }
58
+
59
+ ArCache::Table.all.each do |table|
60
+ current_transaction.update_ar_cache_table(table) if table.enabled? && sql.include?(table.name)
61
+ end
68
62
  end
69
63
 
70
- private def update_ar_cache_version_by_table(table_name)
71
- ArCache::Table.all.each { |table| table.update_version if table_name.casecmp?(table.name) }
64
+ private def update_ar_cache_by_table(table_name)
65
+ ArCache::Table.all.each do |table|
66
+ current_transaction.update_ar_cache_table(table) if table.enabled? && table_name.casecmp?(table.name)
67
+ end
72
68
  end
73
69
  end
74
70
  end
@@ -4,15 +4,15 @@ module ArCache
4
4
  module ActiveRecord
5
5
  module ConnectionAdapters
6
6
  module NullTransaction
7
- def add_ar_cache_keys(keys, delay: false) # rubocop:disable Lint/UnusedMethodArgument
7
+ def delete_ar_cache_keys(keys, delay: false) # rubocop:disable Lint/UnusedMethodArgument
8
8
  ArCache::Store.delete_multi(keys)
9
9
  end
10
10
 
11
- def add_ar_cache_table(table, delay: false) # rubocop:disable Lint/UnusedMethodArgument
11
+ def update_ar_cache_table(table, delay: false) # rubocop:disable Lint/UnusedMethodArgument
12
12
  table.update_version
13
13
  end
14
14
 
15
- def add_changed_table(_); end
15
+ def add_changed_table(...); end
16
16
  end
17
17
 
18
18
  module Transaction
@@ -24,12 +24,12 @@ module ArCache
24
24
  @ar_cache_tables = []
25
25
  end
26
26
 
27
- def add_ar_cache_keys(keys, delay: false)
27
+ def delete_ar_cache_keys(keys, delay: false)
28
28
  super if !delay && read_uncommitted?
29
29
  @ar_cache_keys.push(*keys)
30
30
  end
31
31
 
32
- def add_ar_cache_table(table, delay: false)
32
+ def update_ar_cache_table(table, delay: false)
33
33
  add_changed_table(table.name) unless delay
34
34
 
35
35
  super if !delay && read_uncommitted?
@@ -49,8 +49,8 @@ module ArCache
49
49
  ArCache::Store.delete_multi(@ar_cache_keys.uniq) if @ar_cache_keys.any?
50
50
  else
51
51
  transaction = connection.current_transaction
52
- @ar_cache_tables.each { |table| transaction.add_ar_cache_table(table, delay: true) }
53
- transaction.add_ar_cache_keys(@ar_cache_keys, delay: true)
52
+ @ar_cache_tables.each { |table| transaction.update_ar_cache_table(table, delay: true) }
53
+ transaction.delete_ar_cache_keys(@ar_cache_keys, delay: true)
54
54
  end
55
55
  end
56
56
 
@@ -6,9 +6,8 @@ module ArCache
6
6
  def execute
7
7
  super.tap do
8
8
  if on_duplicate == :update
9
- connection.current_transaction.add_ar_cache_table(model.ar_cache_table)
10
- else
11
- connection.transaction_manager.add_changed_table(model.table_name)
9
+ connection.current_transaction.update_ar_cache_table(model.ar_cache_table)
10
+ connection.current_transaction.add_changed_table(model.table_name)
12
11
  end
13
12
  end
14
13
  end
@@ -3,20 +3,30 @@
3
3
  module ArCache
4
4
  module ActiveRecord
5
5
  module Persistence
6
- def reload(options = nil)
7
- self.class.connection.clear_query_cache
6
+ module ClassMethods
7
+ def _update_record(_, constraints)
8
+ ArCache.pre_expire do
9
+ delete_ar_cache_key(constraints[@primary_key])
10
+ super
11
+ end
12
+ end
8
13
 
9
- fresh_object =
10
- if options && options[:lock]
11
- self.class.unscoped { self.class.skip_ar_cache.lock(options[:lock]).find(id) }
12
- else
13
- self.class.unscoped { self.class.skip_ar_cache.find(id) }
14
+ def _delete_record(constraints)
15
+ ArCache.pre_expire do
16
+ delete_ar_cache_key(constraints[@primary_key])
17
+ super
14
18
  end
19
+ end
20
+
21
+ private def delete_ar_cache_key(id)
22
+ key = ar_cache_table.primary_cache_key(id)
23
+ connection.current_transaction.delete_ar_cache_keys([key])
24
+ connection.current_transaction.add_changed_table(table_name)
25
+ end
26
+ end
15
27
 
16
- @attributes = fresh_object.instance_variable_get(:@attributes)
17
- @new_record = false
18
- @previously_new_record = false
19
- self
28
+ def reload(...)
29
+ ArCache.skip_cache { super }
20
30
  end
21
31
  end
22
32
  end
@@ -3,6 +3,11 @@
3
3
  module ArCache
4
4
  module ActiveRecord
5
5
  module Relation
6
+ def reload
7
+ @skip_ar_cache = true if loaded?
8
+ super
9
+ end
10
+
6
11
  def skip_ar_cache
7
12
  tap { @skip_ar_cache = true }
8
13
  end
@@ -12,36 +17,33 @@ module ArCache
12
17
  super
13
18
  end
14
19
 
15
- private def exec_queries(&block) # rubocop:disable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity, Metrics/MethodLength
16
- skip_query_cache_if_necessary do
17
- records =
18
- if where_clause.contradiction?
19
- []
20
- elsif eager_loading?
21
- apply_join_dependency do |relation, join_dependency|
22
- if relation.null_relation?
23
- []
24
- else
25
- relation = join_dependency.apply_column_aliases(relation)
26
- rows = connection.select_all(relation.arel, 'SQL')
27
- join_dependency.instantiate(rows, strict_loading_value, &block)
28
- end.freeze
29
- end
30
- elsif @skip_ar_cache ||
31
- klass.ar_cache_table.disabled? ||
32
- connection.transaction_manager.changed_table?(table_name)
33
- klass.find_by_sql(arel, &block).freeze
34
- else
35
- ArCache::Query.new(self).exec_queries(&block).freeze
36
- end
37
-
38
- preload_associations(records) unless skip_preloading_value
39
-
40
- records.each(&:readonly!) if readonly_value
41
- records.each(&:strict_loading!) if strict_loading_value
42
-
43
- records
44
- end
20
+ def update_all(...)
21
+ ArCache.pre_expire { delete_ar_cache_keys ? super : 0 }
22
+ end
23
+
24
+ def delete_all
25
+ ArCache.pre_expire { delete_ar_cache_keys ? super : 0 }
26
+ end
27
+
28
+ private def delete_ar_cache_keys
29
+ return true if klass.ar_cache_table.disabled?
30
+
31
+ where_clause = ArCache::WhereClause.new(klass, arel.constraints)
32
+ keys = if where_clause.cacheable? && where_clause.primary_key_index?
33
+ where_clause.primary_cache_keys
34
+ else
35
+ pluck(primary_key).map { |item| klass.ar_cache_table.primary_cache_key(item) }
36
+ end
37
+
38
+ return false if keys.empty?
39
+
40
+ @klass.connection.current_transaction.delete_ar_cache_keys(keys)
41
+ @klass.connection.current_transaction.add_changed_table(@klass.table_name)
42
+ true
43
+ end
44
+
45
+ private def exec_queries(&block)
46
+ @skip_ar_cache ? super : ArCache::Query.new(self).exec_queries(&block).freeze
45
47
  end
46
48
  end
47
49
  end
@@ -10,7 +10,8 @@ module ArCache
10
10
  @where_clause = ArCache::WhereClause.new(@relation.klass, @relation.where_clause.send(:predicates))
11
11
  end
12
12
 
13
- def exec_queries(&block)
13
+ def exec_queries(&block) # rubocop:disable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
14
+ return [] if relation.where_clause.contradiction?
14
15
  return relation.skip_ar_cache.send(:exec_queries, &block) unless exec_queries_cacheable?
15
16
 
16
17
  records = table.read(where_clause, @select_values, &block)
@@ -29,15 +30,25 @@ module ArCache
29
30
  end
30
31
 
31
32
  records_order(records)
33
+
34
+ relation.preload_associations(records) unless relation.skip_preloading_value
35
+
36
+ records.each(&:readonly!) if relation.readonly_value
37
+ records.each(&:strict_loading!) if relation.strict_loading_value
38
+
39
+ records
32
40
  end
33
41
 
34
42
  private def exec_queries_cacheable? # rubocop:disable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
43
+ return false if relation.klass.ar_cache_table.disabled?
35
44
  return false if relation.skip_query_cache_value
36
45
  return false if relation.lock_value
37
46
  return false if relation.group_values.any?
38
47
  return false if relation.joins_values.any?
39
48
  return false if relation.left_outer_joins_values.any?
40
49
  return false if relation.offset_value
50
+ return false if relation.eager_loading?
51
+ return false if relation.connection.transaction_manager.changed_table?(table.name)
41
52
  return false unless relation.from_clause.empty?
42
53
  return false unless where_clause.cacheable?
43
54
  return false unless select_values_cacheable?
@@ -43,7 +43,7 @@ module ArCache
43
43
  end
44
44
 
45
45
  def disabled?
46
- @disabled
46
+ @disabled || ArCache.skip_cache?
47
47
  end
48
48
 
49
49
  def enabled?
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ArCache
4
- VERSION = '1.0.0'
4
+ VERSION = '1.1.0'
5
5
  end
@@ -66,11 +66,10 @@ module ArCache
66
66
  @cache_hash
67
67
  end
68
68
 
69
- def cache_keys
70
- keys = cache_hash.keys
71
- keys += cache_hash.values unless primary_key_index?
69
+ def primary_cache_keys
70
+ raise 'Does not detect primary key index' unless primary_key_index?
72
71
 
73
- keys
72
+ @primary_cache_keys ||= Array(where_values_hash[table.primary_key]).map { |v| table.primary_cache_key(v) }
74
73
  end
75
74
 
76
75
  def missed_hash
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ar_cache
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - OuYangJinTing
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-03-02 00:00:00.000000000 Z
11
+ date: 2021-03-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -41,6 +41,7 @@ files:
41
41
  - ".github/workflows/main.yml"
42
42
  - ".gitignore"
43
43
  - ".rubocop.yml"
44
+ - CHANGELOG.md
44
45
  - CODE_OF_CONDUCT.md
45
46
  - Gemfile
46
47
  - Gemfile.common
@@ -56,6 +57,7 @@ files:
56
57
  - gemfiles/rails-edge
57
58
  - lib/ar_cache.rb
58
59
  - lib/ar_cache/active_record.rb
60
+ - lib/ar_cache/active_record/associations/association.rb
59
61
  - lib/ar_cache/active_record/associations/has_one_through_association.rb
60
62
  - lib/ar_cache/active_record/associations/singular_association.rb
61
63
  - lib/ar_cache/active_record/connection_adapters/abstract/database_statements.rb