active_model_cachers 2.1.5 → 2.1.6
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 +4 -4
- data/.gitignore +9 -9
- data/.travis.yml +2 -2
- data/CHANGELOG.md +62 -57
- data/CODE_OF_CONDUCT.md +48 -48
- data/LICENSE.txt +21 -21
- data/README.md +23 -10
- data/Rakefile +10 -10
- data/active_model_cachers.gemspec +1 -1
- data/bin/console +14 -14
- data/bin/setup +8 -8
- data/gemfiles/3.2.gemfile +11 -11
- data/gemfiles/4.2.gemfile +11 -11
- data/gemfiles/5.0.gemfile +11 -11
- data/gemfiles/5.1.gemfile +11 -11
- data/gemfiles/5.2.gemfile +11 -11
- data/lib/active_model_cachers.rb +0 -0
- data/lib/active_model_cachers/active_record/attr_model.rb +124 -103
- data/lib/active_model_cachers/active_record/cacher.rb +97 -97
- data/lib/active_model_cachers/active_record/extension.rb +119 -107
- data/lib/active_model_cachers/active_record/global_callbacks.rb +67 -67
- data/lib/active_model_cachers/cache_service.rb +151 -151
- data/lib/active_model_cachers/cache_service_factory.rb +55 -55
- data/lib/active_model_cachers/column_value_cache.rb +47 -47
- data/lib/active_model_cachers/config.rb +6 -6
- data/lib/active_model_cachers/false_object.rb +5 -5
- data/lib/active_model_cachers/hook/associations.rb +43 -43
- data/lib/active_model_cachers/hook/dependencies.rb +38 -38
- data/lib/active_model_cachers/hook/on_model_delete.rb +29 -29
- data/lib/active_model_cachers/nil_object.rb +5 -5
- data/lib/active_model_cachers/patches/patch_rails_3.rb +49 -49
- data/lib/active_model_cachers/patches/uninitialized_attribute.rb +9 -9
- data/lib/active_model_cachers/version.rb +4 -4
- metadata +13 -7
@@ -1,55 +1,55 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
require 'request_store'
|
3
|
-
require 'active_model_cachers/cache_service'
|
4
|
-
|
5
|
-
module ActiveModelCachers
|
6
|
-
class CacheServiceFactory
|
7
|
-
@key_class_mapping = {}
|
8
|
-
@cache_key_klass_mapping = {}
|
9
|
-
|
10
|
-
class << self
|
11
|
-
def has_cacher?(attr)
|
12
|
-
return (@key_class_mapping[get_cache_key(attr)] != nil)
|
13
|
-
end
|
14
|
-
|
15
|
-
def create_for_active_model(attr, query)
|
16
|
-
cache_key = get_cache_key(attr)
|
17
|
-
|
18
|
-
klass = @key_class_mapping[cache_key] ||= ->{
|
19
|
-
klass = Class.new(CacheService)
|
20
|
-
klass.cache_key = cache_key
|
21
|
-
klass.query_mapping = {}
|
22
|
-
klass.instance_variable_set(:@callbacks_defined, false) # to remove warning: instance variable @callbacks_defined not initialized
|
23
|
-
next klass
|
24
|
-
}[]
|
25
|
-
|
26
|
-
klass.query_mapping[attr.reflect] = query
|
27
|
-
return klass
|
28
|
-
end
|
29
|
-
|
30
|
-
def set_klass_to_mapping(attr, current_klass)
|
31
|
-
cache_key = get_cache_key(attr)
|
32
|
-
changed = clean_klass_cache_if_reloaded!(cache_key, current_klass, attr)
|
33
|
-
@cache_key_klass_mapping[cache_key] = current_klass
|
34
|
-
return changed
|
35
|
-
end
|
36
|
-
|
37
|
-
private
|
38
|
-
|
39
|
-
def get_cache_key(attr)
|
40
|
-
class_name, column = attr.extract_class_and_column
|
41
|
-
return "active_model_cachers_#{class_name}_at_#{column}" if column
|
42
|
-
foreign_key = attr.foreign_key(reverse: true)
|
43
|
-
return "active_model_cachers_#{class_name}_by_#{foreign_key}" if foreign_key and foreign_key.to_s != 'id'
|
44
|
-
return "active_model_cachers_#{class_name}"
|
45
|
-
end
|
46
|
-
|
47
|
-
def clean_klass_cache_if_reloaded!(cache_key, current_klass, attr)
|
48
|
-
origin_klass, @cache_key_klass_mapping[cache_key] = @cache_key_klass_mapping[cache_key], current_klass
|
49
|
-
return false if origin_klass == nil or origin_klass == current_klass # when code reloaded in development.
|
50
|
-
@key_class_mapping[cache_key] = nil
|
51
|
-
return true
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'request_store'
|
3
|
+
require 'active_model_cachers/cache_service'
|
4
|
+
|
5
|
+
module ActiveModelCachers
|
6
|
+
class CacheServiceFactory
|
7
|
+
@key_class_mapping = {}
|
8
|
+
@cache_key_klass_mapping = {}
|
9
|
+
|
10
|
+
class << self
|
11
|
+
def has_cacher?(attr)
|
12
|
+
return (@key_class_mapping[get_cache_key(attr)] != nil)
|
13
|
+
end
|
14
|
+
|
15
|
+
def create_for_active_model(attr, query)
|
16
|
+
cache_key = get_cache_key(attr)
|
17
|
+
|
18
|
+
klass = @key_class_mapping[cache_key] ||= ->{
|
19
|
+
klass = Class.new(CacheService)
|
20
|
+
klass.cache_key = cache_key
|
21
|
+
klass.query_mapping = {}
|
22
|
+
klass.instance_variable_set(:@callbacks_defined, false) # to remove warning: instance variable @callbacks_defined not initialized
|
23
|
+
next klass
|
24
|
+
}[]
|
25
|
+
|
26
|
+
klass.query_mapping[attr.reflect] = query
|
27
|
+
return klass
|
28
|
+
end
|
29
|
+
|
30
|
+
def set_klass_to_mapping(attr, current_klass)
|
31
|
+
cache_key = get_cache_key(attr)
|
32
|
+
changed = clean_klass_cache_if_reloaded!(cache_key, current_klass, attr)
|
33
|
+
@cache_key_klass_mapping[cache_key] = current_klass
|
34
|
+
return changed
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def get_cache_key(attr)
|
40
|
+
class_name, column = attr.extract_class_and_column
|
41
|
+
return "active_model_cachers_#{class_name}_at_#{column}" if column
|
42
|
+
foreign_key = attr.foreign_key(reverse: true)
|
43
|
+
return "active_model_cachers_#{class_name}_by_#{foreign_key}" if foreign_key and foreign_key.to_s != 'id'
|
44
|
+
return "active_model_cachers_#{class_name}"
|
45
|
+
end
|
46
|
+
|
47
|
+
def clean_klass_cache_if_reloaded!(cache_key, current_klass, attr)
|
48
|
+
origin_klass, @cache_key_klass_mapping[cache_key] = @cache_key_klass_mapping[cache_key], current_klass
|
49
|
+
return false if origin_klass == nil or origin_klass == current_klass # when code reloaded in development.
|
50
|
+
@key_class_mapping[cache_key] = nil
|
51
|
+
return true
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -1,47 +1,47 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class ActiveModelCachers::ColumnValueCache
|
4
|
-
def initialize
|
5
|
-
@cache1 = Hash.new{|h, k| h[k] = {} }
|
6
|
-
@cache2 = Hash.new{|h, k| h[k] = {} }
|
7
|
-
end
|
8
|
-
|
9
|
-
def add(object, class_name, id, foreign_key, model)
|
10
|
-
value = (@cache1[class_name][[id, foreign_key]] ||= get_id_from(object, id, foreign_key, model))
|
11
|
-
return ->{ (value == :not_set ? query_value(object, class_name, id, foreign_key) : value)}
|
12
|
-
end
|
13
|
-
|
14
|
-
def query_value(object, class_name, id, foreign_key)
|
15
|
-
cache = @cache2[class_name]
|
16
|
-
if cache.empty?
|
17
|
-
no_data_keys = @cache1[class_name].select{|k, v| v == :not_set }.keys
|
18
|
-
ids = no_data_keys.map(&:first).uniq
|
19
|
-
columns = ['id', *no_data_keys.map(&:second)].uniq
|
20
|
-
pluck_columns(object, object.where(id: ids).limit(ids.size), columns).each do |columns_data|
|
21
|
-
model_id = columns_data.first
|
22
|
-
columns.each_with_index do |column, index|
|
23
|
-
cache[[model_id, column]] = columns_data[index]
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
return cache[[id, foreign_key]]
|
28
|
-
end
|
29
|
-
|
30
|
-
def clean_cache
|
31
|
-
@cache1.clear
|
32
|
-
@cache2.clear
|
33
|
-
end
|
34
|
-
|
35
|
-
private
|
36
|
-
|
37
|
-
def pluck_columns(_, relation, columns)
|
38
|
-
relation.pluck(*columns)
|
39
|
-
end
|
40
|
-
|
41
|
-
def get_id_from(object, id, column, model)
|
42
|
-
return id if column == 'id'
|
43
|
-
model ||= object.cacher.peek_by(id: id) if object.has_cacher?
|
44
|
-
return model.send(column) if model and model.has_attribute?(column)
|
45
|
-
return :not_set
|
46
|
-
end
|
47
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ActiveModelCachers::ColumnValueCache
|
4
|
+
def initialize
|
5
|
+
@cache1 = Hash.new{|h, k| h[k] = {} }
|
6
|
+
@cache2 = Hash.new{|h, k| h[k] = {} }
|
7
|
+
end
|
8
|
+
|
9
|
+
def add(object, class_name, id, foreign_key, model)
|
10
|
+
value = (@cache1[class_name][[id, foreign_key]] ||= get_id_from(object, id, foreign_key, model))
|
11
|
+
return ->{ (value == :not_set ? query_value(object, class_name, id, foreign_key) : value)}
|
12
|
+
end
|
13
|
+
|
14
|
+
def query_value(object, class_name, id, foreign_key)
|
15
|
+
cache = @cache2[class_name]
|
16
|
+
if cache.empty?
|
17
|
+
no_data_keys = @cache1[class_name].select{|k, v| v == :not_set }.keys
|
18
|
+
ids = no_data_keys.map(&:first).uniq
|
19
|
+
columns = ['id', *no_data_keys.map(&:second)].uniq
|
20
|
+
pluck_columns(object, object.where(id: ids).limit(ids.size), columns).each do |columns_data|
|
21
|
+
model_id = columns_data.first
|
22
|
+
columns.each_with_index do |column, index|
|
23
|
+
cache[[model_id, column]] = columns_data[index]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
return cache[[id, foreign_key]]
|
28
|
+
end
|
29
|
+
|
30
|
+
def clean_cache
|
31
|
+
@cache1.clear
|
32
|
+
@cache2.clear
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def pluck_columns(_, relation, columns)
|
38
|
+
relation.pluck(*columns)
|
39
|
+
end
|
40
|
+
|
41
|
+
def get_id_from(object, id, column, model)
|
42
|
+
return id if column == 'id'
|
43
|
+
model ||= object.cacher.peek_by(id: id) if object.has_cacher?
|
44
|
+
return model.send(column) if model and model.has_attribute?(column)
|
45
|
+
return :not_set
|
46
|
+
end
|
47
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
module ActiveModelCachers
|
3
|
-
class Config
|
4
|
-
attr_accessor :store
|
5
|
-
end
|
6
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module ActiveModelCachers
|
3
|
+
class Config
|
4
|
+
attr_accessor :store
|
5
|
+
end
|
6
|
+
end
|
@@ -1,5 +1,5 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
module ActiveModelCachers
|
3
|
-
class FalseObject
|
4
|
-
end
|
5
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module ActiveModelCachers
|
3
|
+
class FalseObject
|
4
|
+
end
|
5
|
+
end
|
@@ -1,43 +1,43 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
require 'active_record'
|
3
|
-
require 'active_record/associations/has_many_association'
|
4
|
-
require 'active_model_cachers/hook/on_model_delete'
|
5
|
-
|
6
|
-
module ActiveModelCachers::Hook
|
7
|
-
module Associations
|
8
|
-
def delete_count(method, scope)
|
9
|
-
if method == :delete_all
|
10
|
-
# TODO:
|
11
|
-
else # nullify
|
12
|
-
call_hooks{ scope.pluck(:id) }
|
13
|
-
end
|
14
|
-
super
|
15
|
-
end
|
16
|
-
|
17
|
-
def delete_records(records, method)
|
18
|
-
case method
|
19
|
-
when :destroy
|
20
|
-
when :delete_all
|
21
|
-
# TODO:
|
22
|
-
else
|
23
|
-
call_hooks{ records.map(&:id) }
|
24
|
-
end
|
25
|
-
super
|
26
|
-
end
|
27
|
-
|
28
|
-
private
|
29
|
-
|
30
|
-
def call_hooks(&get_ids)
|
31
|
-
ids = nil
|
32
|
-
get_ids_with_cache = ->{ ids ||= get_ids.call }
|
33
|
-
ActiveModelCachers::ActiveRecord::Extension.global_callbacks.on_nullify.exec(
|
34
|
-
self,
|
35
|
-
reflection.klass,
|
36
|
-
reflection.foreign_key,
|
37
|
-
get_ids_with_cache,
|
38
|
-
)
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
ActiveRecord::Associations::HasManyAssociation.send(:prepend, ActiveModelCachers::Hook::Associations)
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'active_record'
|
3
|
+
require 'active_record/associations/has_many_association'
|
4
|
+
require 'active_model_cachers/hook/on_model_delete'
|
5
|
+
|
6
|
+
module ActiveModelCachers::Hook
|
7
|
+
module Associations
|
8
|
+
def delete_count(method, scope)
|
9
|
+
if method == :delete_all
|
10
|
+
# TODO:
|
11
|
+
else # nullify
|
12
|
+
call_hooks{ scope.pluck(:id) }
|
13
|
+
end
|
14
|
+
super
|
15
|
+
end
|
16
|
+
|
17
|
+
def delete_records(records, method)
|
18
|
+
case method
|
19
|
+
when :destroy
|
20
|
+
when :delete_all
|
21
|
+
# TODO:
|
22
|
+
else
|
23
|
+
call_hooks{ records.map(&:id) }
|
24
|
+
end
|
25
|
+
super
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def call_hooks(&get_ids)
|
31
|
+
ids = nil
|
32
|
+
get_ids_with_cache = ->{ ids ||= get_ids.call }
|
33
|
+
ActiveModelCachers::ActiveRecord::Extension.global_callbacks.on_nullify.exec(
|
34
|
+
self,
|
35
|
+
reflection.klass,
|
36
|
+
reflection.foreign_key,
|
37
|
+
get_ids_with_cache,
|
38
|
+
)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
ActiveRecord::Associations::HasManyAssociation.send(:prepend, ActiveModelCachers::Hook::Associations)
|
@@ -1,38 +1,38 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
require 'active_support/dependencies'
|
3
|
-
|
4
|
-
module ActiveModelCachers::Hook
|
5
|
-
module Depdenencies
|
6
|
-
def onload(const_name, times: 1, &block)
|
7
|
-
const = const_name if not const_name.is_a?(String)
|
8
|
-
if const or Module.const_defined?(const_name)
|
9
|
-
(const || const_name.constantize).instance_exec(&block)
|
10
|
-
else
|
11
|
-
load_hooks[const_name].push(block: block, times: times)
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
def load_hooks
|
16
|
-
@load_hooks ||= Hash.new{|h, k| h[k] = [] }
|
17
|
-
end
|
18
|
-
|
19
|
-
def new_constants_in(*)
|
20
|
-
new_constants = super.each do |const_name|
|
21
|
-
hooks = load_hooks[const_name]
|
22
|
-
need_compact = false
|
23
|
-
hooks.each_with_index do |hook, idx|
|
24
|
-
if (hook[:times] -= 1) < 0
|
25
|
-
hooks[idx] = nil
|
26
|
-
need_compact = true
|
27
|
-
next
|
28
|
-
end
|
29
|
-
const_name.constantize.instance_exec(&hook[:block])
|
30
|
-
end
|
31
|
-
hooks.compact! if need_compact
|
32
|
-
end
|
33
|
-
return new_constants
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
ActiveSupport::Dependencies.send(:extend, ActiveModelCachers::Hook::Depdenencies)
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'active_support/dependencies'
|
3
|
+
|
4
|
+
module ActiveModelCachers::Hook
|
5
|
+
module Depdenencies
|
6
|
+
def onload(const_name, times: 1, &block)
|
7
|
+
const = const_name if not const_name.is_a?(String)
|
8
|
+
if const or Module.const_defined?(const_name)
|
9
|
+
(const || const_name.constantize).instance_exec(&block)
|
10
|
+
else
|
11
|
+
load_hooks[const_name].push(block: block, times: times)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def load_hooks
|
16
|
+
@load_hooks ||= Hash.new{|h, k| h[k] = [] }
|
17
|
+
end
|
18
|
+
|
19
|
+
def new_constants_in(*)
|
20
|
+
new_constants = super.each do |const_name|
|
21
|
+
hooks = load_hooks[const_name]
|
22
|
+
need_compact = false
|
23
|
+
hooks.each_with_index do |hook, idx|
|
24
|
+
if (hook[:times] -= 1) < 0
|
25
|
+
hooks[idx] = nil
|
26
|
+
need_compact = true
|
27
|
+
next
|
28
|
+
end
|
29
|
+
const_name.constantize.instance_exec(&hook[:block])
|
30
|
+
end
|
31
|
+
hooks.compact! if need_compact
|
32
|
+
end
|
33
|
+
return new_constants
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
ActiveSupport::Dependencies.send(:extend, ActiveModelCachers::Hook::Depdenencies)
|
@@ -1,29 +1,29 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
require 'active_record'
|
3
|
-
|
4
|
-
module ActiveModelCachers::Hook
|
5
|
-
module OnModelDelete
|
6
|
-
module InstanceMethods
|
7
|
-
def delete
|
8
|
-
self.class.delete(id, self) if persisted?
|
9
|
-
@destroyed = true
|
10
|
-
freeze
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
module ClassMethods
|
15
|
-
def delete(id, model = nil)
|
16
|
-
ActiveModelCachers::ActiveRecord::Extension.global_callbacks.before_delete1.exec(self, self, id, model)
|
17
|
-
ActiveModelCachers::ActiveRecord::Extension.global_callbacks.before_delete2.exec(self, self, id, model)
|
18
|
-
|
19
|
-
result = super(id)
|
20
|
-
|
21
|
-
ActiveModelCachers::ActiveRecord::Extension.global_callbacks.after_delete.exec(self, self, id, model)
|
22
|
-
return result
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
ActiveRecord::Base.send(:include, ActiveModelCachers::Hook::OnModelDelete::InstanceMethods)
|
29
|
-
ActiveRecord::Base.send(:extend, ActiveModelCachers::Hook::OnModelDelete::ClassMethods)
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'active_record'
|
3
|
+
|
4
|
+
module ActiveModelCachers::Hook
|
5
|
+
module OnModelDelete
|
6
|
+
module InstanceMethods
|
7
|
+
def delete
|
8
|
+
self.class.delete(id, self) if persisted?
|
9
|
+
@destroyed = true
|
10
|
+
freeze
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
module ClassMethods
|
15
|
+
def delete(id, model = nil)
|
16
|
+
ActiveModelCachers::ActiveRecord::Extension.global_callbacks.before_delete1.exec(self, self, id, model)
|
17
|
+
ActiveModelCachers::ActiveRecord::Extension.global_callbacks.before_delete2.exec(self, self, id, model)
|
18
|
+
|
19
|
+
result = super(id)
|
20
|
+
|
21
|
+
ActiveModelCachers::ActiveRecord::Extension.global_callbacks.after_delete.exec(self, self, id, model)
|
22
|
+
return result
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
ActiveRecord::Base.send(:include, ActiveModelCachers::Hook::OnModelDelete::InstanceMethods)
|
29
|
+
ActiveRecord::Base.send(:extend, ActiveModelCachers::Hook::OnModelDelete::ClassMethods)
|