ar_cache 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 (42) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/main.yml +68 -0
  3. data/.gitignore +8 -0
  4. data/.rubocop.yml +56 -0
  5. data/CODE_OF_CONDUCT.md +84 -0
  6. data/Gemfile +7 -0
  7. data/Gemfile.common +17 -0
  8. data/Gemfile.lock +192 -0
  9. data/LICENSE.txt +21 -0
  10. data/README.md +166 -0
  11. data/Rakefile +28 -0
  12. data/ar_cache.gemspec +34 -0
  13. data/bin/activerecord-test +20 -0
  14. data/bin/console +15 -0
  15. data/bin/setup +8 -0
  16. data/gemfiles/rails-6-1 +3 -0
  17. data/gemfiles/rails-edge +3 -0
  18. data/lib/ar_cache.rb +22 -0
  19. data/lib/ar_cache/active_record.rb +36 -0
  20. data/lib/ar_cache/active_record/associations/has_one_through_association.rb +39 -0
  21. data/lib/ar_cache/active_record/associations/singular_association.rb +18 -0
  22. data/lib/ar_cache/active_record/connection_adapters/abstract/database_statements.rb +76 -0
  23. data/lib/ar_cache/active_record/connection_adapters/abstract/transaction.rb +90 -0
  24. data/lib/ar_cache/active_record/core.rb +21 -0
  25. data/lib/ar_cache/active_record/insert_all.rb +17 -0
  26. data/lib/ar_cache/active_record/model_schema.rb +27 -0
  27. data/lib/ar_cache/active_record/persistence.rb +23 -0
  28. data/lib/ar_cache/active_record/relation.rb +48 -0
  29. data/lib/ar_cache/configuration.rb +59 -0
  30. data/lib/ar_cache/log_subscriber.rb +8 -0
  31. data/lib/ar_cache/marshal.rb +81 -0
  32. data/lib/ar_cache/mock_table.rb +55 -0
  33. data/lib/ar_cache/query.rb +95 -0
  34. data/lib/ar_cache/record.rb +54 -0
  35. data/lib/ar_cache/store.rb +38 -0
  36. data/lib/ar_cache/table.rb +126 -0
  37. data/lib/ar_cache/version.rb +5 -0
  38. data/lib/ar_cache/where_clause.rb +151 -0
  39. data/lib/generators/ar_cache/install_generator.rb +22 -0
  40. data/lib/generators/ar_cache/templates/configuration.rb +34 -0
  41. data/lib/generators/ar_cache/templates/migrate/create_ar_cache_records.rb.tt +16 -0
  42. metadata +107 -0
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2020 OuYangJinTing
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,166 @@
1
+ # ArCache
2
+
3
+ `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.
5
+ When executing standard `ActiveRecord` query, it will first query the cache, and if there is none in the cache,
6
+ then query the database and write the result to the cache.
7
+
8
+ > **! WARNING: Please read these [information](#Warning) before using `ArCache`.**
9
+
10
+ ## Features
11
+
12
+ - `Low impact`: If your code strictly comply with the activerecord style, you don’t need to modify any code.([see more details](#Warning))
13
+ - `Read cache`: Automatically intercept ActiveRecord queries, then try to fetch data from cache.
14
+ - `Write cache`: If the query is cacheable and the cached data is not exists, it will be automatically written to the cache after the query.
15
+ - `Expire cache`: Automatically expire cache after updated/modified data.
16
+ - `Iterative cache`: The cache version will be updated after table fields, `ArCache` switch or `ArCache` coder changed.
17
+ - `Shared cache`: The cache is not only used with ActiveRecord, you can easily use it in other places.([see examples](examples))
18
+
19
+ ## Installation
20
+
21
+ Add this line to your application's Gemfile:
22
+
23
+ ```ruby
24
+ gem 'ar_cache'
25
+ ```
26
+
27
+ And then execute:
28
+
29
+ ```shell
30
+ bundle install
31
+ ```
32
+
33
+ ## Post Installation
34
+
35
+ If is an `rails` application:
36
+
37
+ ```shell
38
+ rails generate ar_cache:install
39
+ ```
40
+
41
+ Otherwise copy [configuration](lib/generators/ar_cache/templates/configuration.rb) and [migration](lib/generators/ar_cache/templates/migrate/create_ar_cache_records.rb.tt) files to your application.
42
+
43
+ After that review the migrations then migrate:
44
+
45
+ ```shell
46
+ rake db:migrate
47
+ ```
48
+
49
+ ## Usage
50
+
51
+ `ArCache` works automatically, so you don’t need to care about how to use the cache, just need to know how to skip and delete the cache.
52
+
53
+ Skip cache:
54
+
55
+ - `ActiveRecord::Persistence#reload`, eg:
56
+
57
+ ```ruby
58
+ User.find(1).reload
59
+ ```
60
+
61
+ - `ActiveRecord::Relation#skip_ar_cache`, eg:
62
+
63
+ ```ruby
64
+ User.skip_ar_cache.find(1)
65
+ User.where(id: [1, 2]).skip_ar_cache.load
66
+ ```
67
+
68
+ Delete cache:
69
+
70
+ - `ArCache::Table`, eg:
71
+
72
+ ```ruby
73
+ User.ar_cache_table.delete(id...)
74
+ User.first.ar_cache_table.delete(id...)
75
+ ```
76
+
77
+ ## Configuration
78
+
79
+ For configuration information, please see [configuration](lib/generators/ar_cache/templates/configuration.rb) file.
80
+
81
+ ## Cacheable query
82
+
83
+ If all the following conditions are met, ArCache will try to read the cache:
84
+
85
+ - Use hash as `#where` parameter.
86
+ - Query condition contains unique index.
87
+ - Condition of unique index is only one array or no array.
88
+ - No call `#select` or select value is table column.
89
+ - No call `#order` or order value is table column and only one.
90
+ - 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`.
99
+ - ...
100
+
101
+ **Cacheable example:**
102
+
103
+ ```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
110
+ ```
111
+
112
+ ## Cache iteration
113
+
114
+ The following cases will cause cache iteration:
115
+
116
+ - 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.
120
+
121
+ **Notice: After iteration, all existing caches of the table will be expired!**
122
+
123
+ ## How it works(Work in progress)
124
+
125
+ `ArCache` works based on the unique index of the table.
126
+
127
+ ## Warning
128
+
129
+ - Prohibit the use of `#execute` update/delete operations!
130
+ - Prohibit use `ActiveRecord` other underlying methods to directly update/delete data! (You is a fake activerecord user if this code appears)
131
+ - Prohibit skip `ActiveRecord` directly update/delete data!
132
+
133
+ ## Alternatives
134
+
135
+ There are some other gems implementations for `ActiveRecord` cache such as:
136
+
137
+ - [identity_cache](https://github.com/Shopify/identity_cache)
138
+ - [second_level_cache](https://github.com/hooopo/second_level_cache)
139
+ - [cache-money](https://github.com/ngmoco/cache-money)
140
+
141
+ However, `ArCache` has some differences:
142
+
143
+ - It don’t depend with `ActiveRecord` callbacks, so don’t need to deal with dirty cache manually.
144
+ - It cache real database data, so can use it's cache at other places.
145
+ - It can automatically handle cache iteration, so don't need to update cache version manually.
146
+ - It proxy standard ActiveRecord query, so don't need to modify the code and remember the additional api.
147
+ - The new data need to perform a query before the cache will take effect.
148
+ - The cache will not be updated after the data is updated, but the cache will be expired directly.
149
+
150
+ ## Development
151
+
152
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
153
+
154
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
155
+
156
+ ## Contributing
157
+
158
+ Bug reports and pull requests are welcome on GitHub at <https://github.com/OuYangJinTing/ar_cache>. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/OuYangJinTing/ar_cache/blob/master/CODE_OF_CONDUCT.md).
159
+
160
+ ## License
161
+
162
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
163
+
164
+ ## Code of Conduct
165
+
166
+ Everyone interacting in the `ArCache` project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/OuYangJinTing/ar_cache/blob/master/CODE_OF_CONDUCT.md).
data/Rakefile ADDED
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rake/testtask'
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << 'test'
8
+ t.libs << 'lib'
9
+ t.test_files = FileList['test/**/*_test.rb']
10
+ t.verbose = true
11
+ t.warning = true
12
+ t.options = '--warnings --color'
13
+ end
14
+
15
+ Rake::TestTask.new(:bench) do |t|
16
+ t.libs << 'test'
17
+ t.libs << 'lib'
18
+ t.test_files = FileList['test/**/*_benchmark.rb']
19
+ t.verbose = true
20
+ t.warning = true
21
+ t.options = '--verbose --warnings --color'
22
+ end
23
+
24
+ require 'rubocop/rake_task'
25
+
26
+ RuboCop::RakeTask.new
27
+
28
+ task default: %i[test rubocop]
data/ar_cache.gemspec ADDED
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/ar_cache/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'ar_cache'
7
+ spec.version = ArCache::VERSION
8
+ spec.authors = ['OuYangJinTing']
9
+ spec.email = ['2729877005qq@gmail.com']
10
+
11
+ spec.summary = 'An modern cacheing library for ActiveRecord inspired by cache-money and second_level_cache.'
12
+ spec.description = spec.summary
13
+ spec.homepage = 'https://github.com/OuYangJinTing/ar_cache'
14
+ spec.license = 'MIT'
15
+ spec.required_ruby_version = Gem::Requirement.new('>= 3.0.0')
16
+
17
+ spec.metadata['homepage_uri'] = spec.homepage
18
+ spec.metadata['source_code_uri'] = 'https://github.com/OuYangJinTing/ar_cache.git'
19
+ spec.metadata['changelog_uri'] = 'https://github.com/OuYangJinTing/ar_cache/CHANGELOG.md'
20
+
21
+ # Specify which files should be added to the gem when it is released.
22
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
23
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
24
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
25
+ end
26
+ spec.bindir = 'exe'
27
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
28
+ spec.require_paths = ['lib']
29
+
30
+ spec.add_runtime_dependency 'activerecord', '>= 6.1', '< 7'
31
+
32
+ # For more information and examples about making a new gem, checkout our
33
+ # guide at: https://bundler.io/guides/creating_gem.html
34
+ end
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ if [ ! -d 'tmp/rails/' ]; then
7
+ git clone --branch=6-1-ar_cache-test https://github.com/OuYangJinTing/rails.git tmp/rails && cd tmp/rails
8
+ else
9
+ cd tmp/rails && git pull origin 6-1-ar_cache-test
10
+ fi
11
+
12
+ BUNDLE_GEMFILE=./Gemfile
13
+ bundle install
14
+
15
+ cd activerecord
16
+ BUNDLE_GEMFILE=../Gemfile
17
+ # bundle exec rake rebuild_mysql_databases
18
+ bundle exec rake rebuild_postgresql_databases
19
+ bundle exec rake test:sqlite3
20
+ bundle exec rake test:postgresql
data/bin/console ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/setup'
5
+ require 'ar_cache'
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ require 'irb'
15
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,3 @@
1
+ eval_gemfile '../Gemfile.common'
2
+
3
+ gem 'rails', '~> 6.1'
@@ -0,0 +1,3 @@
1
+ eval_gemfile '../Gemfile.common'
2
+
3
+ gem 'rails', github: 'rails/rails', branch: 'main'
data/lib/ar_cache.rb ADDED
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/cache'
4
+ require 'active_record'
5
+
6
+ require 'ar_cache/version'
7
+ require 'ar_cache/configuration'
8
+ require 'ar_cache/record'
9
+ require 'ar_cache/store'
10
+ require 'ar_cache/marshal'
11
+ require 'ar_cache/table'
12
+ require 'ar_cache/mock_table'
13
+ require 'ar_cache/query'
14
+ require 'ar_cache/where_clause'
15
+ require 'ar_cache/log_subscriber'
16
+ require 'ar_cache/active_record'
17
+
18
+ require_relative './generators/ar_cache/install_generator' if defined?(Rails)
19
+
20
+ module ArCache
21
+ singleton_class.delegate :configure, to: Configuration
22
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ar_cache/active_record/model_schema'
4
+ require 'ar_cache/active_record/relation'
5
+ require 'ar_cache/active_record/core'
6
+ require 'ar_cache/active_record/persistence'
7
+ require 'ar_cache/active_record/insert_all'
8
+ require 'ar_cache/active_record/associations/singular_association'
9
+ require 'ar_cache/active_record/associations/has_one_through_association'
10
+ require 'ar_cache/active_record/connection_adapters/abstract/transaction'
11
+ require 'ar_cache/active_record/connection_adapters/abstract/database_statements'
12
+
13
+ # rubocop:disable Layout/LineLength
14
+ ActiveSupport.on_load(:active_record, run_once: true) do
15
+ ActiveRecord::Core::ClassMethods.prepend(ArCache::ActiveRecord::Core::ClassMethods)
16
+
17
+ ActiveRecord::ModelSchema.prepend(ArCache::ActiveRecord::ModelSchema)
18
+ ActiveRecord::ModelSchema::ClassMethods.prepend(ArCache::ActiveRecord::ModelSchema::ClassMethods)
19
+
20
+ ActiveRecord::Persistence.prepend(ArCache::ActiveRecord::Persistence)
21
+
22
+ ActiveRecord::InsertAll.prepend(ArCache::ActiveRecord::InsertAll)
23
+
24
+ ActiveRecord::Relation.prepend(ArCache::ActiveRecord::Relation)
25
+
26
+ ActiveRecord::Associations::SingularAssociation.prepend(ArCache::ActiveRecord::Associations::SingularAssociation)
27
+ ActiveRecord::Associations::HasOneThroughAssociation.prepend(ArCache::ActiveRecord::Associations::HasOneThroughAssociation)
28
+
29
+ ActiveRecord::ConnectionAdapters::NullTransaction.prepend(ArCache::ActiveRecord::ConnectionAdapters::NullTransaction)
30
+ ActiveRecord::ConnectionAdapters::RealTransaction.prepend(ArCache::ActiveRecord::ConnectionAdapters::Transaction)
31
+ ActiveRecord::ConnectionAdapters::SavepointTransaction.prepend(ArCache::ActiveRecord::ConnectionAdapters::Transaction)
32
+ ActiveRecord::ConnectionAdapters::TransactionManager.prepend(ArCache::ActiveRecord::ConnectionAdapters::TransactionManager)
33
+
34
+ ActiveRecord::ConnectionAdapters::DatabaseStatements.prepend(ArCache::ActiveRecord::ConnectionAdapters::DatabaseStatements)
35
+ end
36
+ # rubocop:enable Layout/LineLength
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ArCache
4
+ module ActiveRecord
5
+ module Associations
6
+ module HasOneThroughAssociation
7
+ private def find_target # rubocop:disable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
8
+ return super if reflection.klass.ar_cache_table.disabled?
9
+ return super if reflection.through_reflection.klass.ar_cache_table.disabled?
10
+
11
+ if owner.strict_loading? && owner.validation_context.nil?
12
+ Base.strict_loading_violation!(owner: owner.class, association: reflection.klass)
13
+ end
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
34
+ super
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ArCache
4
+ module ActiveRecord
5
+ module Associations
6
+ module SingularAssociation
7
+ private def skip_statement_cache?(...)
8
+ # Polymorphic associations do not support computing the class, so can't judge ArCache status.
9
+ # But SingularAssociation query usually can hit the unique index, so here return true directly.
10
+ return true if is_a?(::ActiveRecord::Associations::BelongsToPolymorphicAssociation)
11
+ return true if reflection.klass.ar_cache_table.enabled?
12
+
13
+ super
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ArCache
4
+ module ActiveRecord
5
+ module ConnectionAdapters
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
21
+
22
+ def update(arel, ...)
23
+ super.tap { |num| update_ar_cache_version(arel) unless num.zero? }
24
+ end
25
+
26
+ def delete(arel, ...)
27
+ super.tap { |num| update_ar_cache_version(arel) unless num.zero? }
28
+ end
29
+
30
+ def truncate(table_name, ...)
31
+ super.tap { update_ar_cache_version_by_table(table_name) }
32
+ end
33
+
34
+ def truncate_tables(*table_names)
35
+ super.tap do
36
+ table_names.each { |table_name| update_ar_cache_version_by_table(table_name) }
37
+ end
38
+ end
39
+
40
+ private def update_ar_cache_version(arel_or_sql_string)
41
+ if arel_or_sql_string.is_a?(String)
42
+ update_ar_cache_version_by_sql(arel_or_sql_string)
43
+ else # is Arel::TreeManager
44
+ update_ar_cache_version_by_arel(arel_or_sql_string)
45
+ end
46
+ end
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
52
+ arel_table = arel.ast.relation.is_a?(Arel::Table) ? arel.ast.relation : arel.ast.relation.left
53
+ 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
63
+ end
64
+
65
+ private def update_ar_cache_version_by_sql(sql)
66
+ sql = sql.downcase
67
+ ArCache::Table.all.each { |table| current_transaction.add_ar_cache_table(table) if sql.include?(table.name) }
68
+ end
69
+
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) }
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end