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.
@@ -1,107 +1,119 @@
1
- # frozen_string_literal: true
2
- require 'active_model_cachers/active_record/global_callbacks'
3
- require 'active_model_cachers/active_record/attr_model'
4
- require 'active_model_cachers/active_record/cacher'
5
- require 'active_model_cachers/hook/dependencies'
6
- require 'active_model_cachers/hook/associations'
7
- require 'active_model_cachers/hook/on_model_delete'
8
-
9
- module ActiveModelCachers
10
- module ActiveRecord
11
- module Extension
12
- def cache_self(by: :id)
13
- cache_at(nil, expire_by: self.name, primary_key: by, foreign_key: by)
14
- end
15
-
16
- def cache_at(column, query = nil, expire_by: nil, on: nil, foreign_key: nil, primary_key: nil)
17
- attr = AttrModel.new(self, column, foreign_key: foreign_key, primary_key: primary_key)
18
- return cache_belongs_to(attr) if attr.belongs_to?
19
-
20
- loaded = false
21
- class_name, *infos = get_expire_infos(attr, expire_by, foreign_key)
22
- set_klass_to_mapping(attr, class_name) do
23
- next if !loaded
24
- cache_at(column, query, expire_by: expire_by, on: on, foreign_key: foreign_key, primary_key: primary_key)
25
- end
26
- loaded = true
27
-
28
- query ||= ->(id){ attr.query_model(self, id) }
29
- service_klass = CacheServiceFactory.create_for_active_model(attr, query)
30
- Cacher.define_cacher_method(attr, attr.primary_key || :id, [service_klass])
31
-
32
- if class_name
33
- with_id = (expire_by.is_a?(Symbol) || query.parameters.size == 1)
34
- service_klass.define_callback_for_cleaning_cache(class_name, *infos, with_id, on: on)
35
- end
36
-
37
- return service_klass
38
- end
39
-
40
- def has_cacher?(column = nil)
41
- attr = AttrModel.new(self, column)
42
- return CacheServiceFactory.has_cacher?(attr)
43
- end
44
-
45
- private
46
-
47
- def set_klass_to_mapping(attr, class_name)
48
- ActiveSupport::Dependencies.onload(class_name || self.to_s) do
49
- yield if CacheServiceFactory.set_klass_to_mapping(attr, self)
50
- end
51
- end
52
-
53
- def get_expire_infos(attr, expire_by, foreign_key)
54
- if expire_by.is_a?(Symbol)
55
- expire_attr = get_association_attr(expire_by)
56
- expire_by = get_expire_by_from(expire_attr)
57
- else
58
- expire_attr = attr
59
- expire_by ||= get_expire_by_from(expire_attr)
60
- end
61
- return if expire_by == nil
62
-
63
- class_name, column = expire_by.split('#', 2)
64
- foreign_key ||= expire_attr.foreign_key(reverse: true) || 'id'
65
-
66
- return class_name, column, foreign_key.to_s
67
- end
68
-
69
- def get_association_attr(column)
70
- attr = AttrModel.new(self, column)
71
- raise "#{column} is not an association" if not attr.association?
72
- return attr
73
- end
74
-
75
- def get_expire_by_from(attr)
76
- return attr.class_name if attr.association?
77
- return "#{self}##{attr.column}" if column_names.include?(attr.column.to_s)
78
- end
79
-
80
- def cache_belongs_to(attr)
81
- service_klasses = [cache_at(attr.foreign_key)]
82
- Cacher.define_cacher_method(attr, attr.primary_key, service_klasses)
83
- ActiveSupport::Dependencies.onload(attr.class_name) do
84
- service_klasses << cache_self
85
- end
86
- end
87
-
88
- @global_callbacks = nil
89
- def self.global_callbacks
90
- if @global_callbacks == nil
91
- global_callbacks = @global_callbacks = GlobalCallbacks.new
92
- ::ActiveRecord::Base.instance_exec do
93
- after_commit ->{
94
- global_callbacks.after_commit1.exec(self, self.class)
95
- global_callbacks.after_commit2.exec(self, self.class)
96
- }
97
- after_touch ->{
98
- global_callbacks.after_touch1.exec(self, self.class)
99
- global_callbacks.after_touch2.exec(self, self.class)
100
- }
101
- end
102
- end
103
- return @global_callbacks
104
- end
105
- end
106
- end
107
- end
1
+ # frozen_string_literal: true
2
+ require 'active_model_cachers/active_record/global_callbacks'
3
+ require 'active_model_cachers/active_record/attr_model'
4
+ require 'active_model_cachers/active_record/cacher'
5
+ require 'active_model_cachers/hook/dependencies'
6
+ require 'active_model_cachers/hook/associations'
7
+ require 'active_model_cachers/hook/on_model_delete'
8
+
9
+ module ActiveModelCachers
10
+ module ActiveRecord
11
+ module Extension
12
+ def cache_self(by: :id)
13
+ cache_at(nil, expire_by: self.name, primary_key: by, foreign_key: by)
14
+ end
15
+
16
+ def cache_at(column, query = nil, expire_by: nil, on: nil, foreign_key: nil, primary_key: nil)
17
+ attr = AttrModel.new(self, column, foreign_key: foreign_key, primary_key: primary_key)
18
+ return cache_belongs_to(attr) if attr.belongs_to?
19
+
20
+ loaded = false
21
+ class_name, *infos = get_expire_infos(attr, expire_by, foreign_key)
22
+ set_klass_to_mapping(attr, class_name) do
23
+ next if !loaded
24
+ cache_at(column, query, expire_by: expire_by, on: on, foreign_key: foreign_key, primary_key: primary_key)
25
+ end
26
+ loaded = true
27
+
28
+ query ||= ->(id){ attr.query_model(self, id) }
29
+ service_klass = CacheServiceFactory.create_for_active_model(attr, query)
30
+ Cacher.define_cacher_method(attr, attr.primary_key || :id, [service_klass])
31
+
32
+ if class_name
33
+ with_id = (expire_by.is_a?(Symbol) || query.parameters.size == 1)
34
+ service_klass.define_callback_for_cleaning_cache(class_name, *infos, with_id, on: on)
35
+ end
36
+
37
+ return service_klass
38
+ end
39
+
40
+ def has_cacher?(column = nil)
41
+ attr = AttrModel.new(self, column)
42
+ return CacheServiceFactory.has_cacher?(attr)
43
+ end
44
+
45
+ private
46
+
47
+ def set_klass_to_mapping(attr, class_name)
48
+ ActiveSupport::Dependencies.onload(class_name || self.to_s) do
49
+ yield if CacheServiceFactory.set_klass_to_mapping(attr, self)
50
+ end
51
+ end
52
+
53
+ def get_expire_infos(attr, expire_by, foreign_key)
54
+ if expire_by.is_a?(Symbol)
55
+ expire_attr = get_association_attr(expire_by)
56
+ if expire_attr.join_table_class_name
57
+ expire_attr.klass.send(:"after_add_for_#{expire_by}") << gen_has_many_through_callback(attr.column)
58
+ expire_attr.klass.send(:"after_remove_for_#{expire_by}") << gen_has_many_through_callback(attr.column)
59
+ expire_by = expire_attr.join_table_class_name
60
+ else
61
+ expire_by = get_expire_by_from(expire_attr)
62
+ end
63
+ else
64
+ expire_attr = attr
65
+ expire_by ||= get_expire_by_from(expire_attr)
66
+ end
67
+ return if expire_by == nil
68
+
69
+ class_name, column = expire_by.split('#', 2)
70
+ foreign_key ||= expire_attr.foreign_key(reverse: true) || 'id'
71
+
72
+ return class_name, column, foreign_key.to_s
73
+ end
74
+
75
+ NO_MACRO_FOR_AFTER_ADD = Gem::Version.new(::ActiveRecord::VERSION::STRING) < Gem::Version.new('4')
76
+ def gen_has_many_through_callback(column)
77
+ return ->(this, _that){ this.cacher.clean(column) } if NO_MACRO_FOR_AFTER_ADD
78
+ return ->(_, this, _that){ this.cacher.clean(column) }
79
+ end
80
+
81
+ def get_association_attr(column)
82
+ attr = AttrModel.new(self, column)
83
+ raise "#{column} is not an association" if not attr.association?
84
+ return attr
85
+ end
86
+
87
+ def get_expire_by_from(attr)
88
+ return attr.class_name if attr.association?
89
+ return "#{self}##{attr.column}" if column_names.include?(attr.column.to_s)
90
+ end
91
+
92
+ def cache_belongs_to(attr)
93
+ service_klasses = [cache_at(attr.foreign_key)]
94
+ Cacher.define_cacher_method(attr, attr.primary_key, service_klasses)
95
+ ActiveSupport::Dependencies.onload(attr.class_name) do
96
+ service_klasses << cache_self
97
+ end
98
+ end
99
+
100
+ @global_callbacks = nil
101
+ def self.global_callbacks
102
+ if @global_callbacks == nil
103
+ global_callbacks = @global_callbacks = GlobalCallbacks.new
104
+ ::ActiveRecord::Base.instance_exec do
105
+ after_commit ->{
106
+ global_callbacks.after_commit1.exec(self, self.class)
107
+ global_callbacks.after_commit2.exec(self, self.class)
108
+ }
109
+ after_touch ->{
110
+ global_callbacks.after_touch1.exec(self, self.class)
111
+ global_callbacks.after_touch2.exec(self, self.class)
112
+ }
113
+ end
114
+ end
115
+ return @global_callbacks
116
+ end
117
+ end
118
+ end
119
+ end
@@ -1,67 +1,67 @@
1
- module ActiveModelCachers
2
- module ActiveRecord
3
- class GlobalCallbacks
4
- def initialize
5
- @type_callbacks = {}
6
- end
7
-
8
- def before_delete1(class_name = nil, &block)
9
- define_callbacks(:before_delete1, class_name, &block)
10
- end
11
-
12
- def before_delete2(class_name = nil, &block)
13
- define_callbacks(:before_delete2, class_name, &block)
14
- end
15
-
16
- def after_delete(class_name = nil, &block)
17
- define_callbacks(:after_delete, class_name, &block)
18
- end
19
-
20
- def on_nullify(class_name = nil, &block)
21
- define_callbacks(:on_nullify, class_name, &block)
22
- end
23
-
24
- def after_commit1(class_name = nil, &block)
25
- define_callbacks(:after_commit1, class_name, &block)
26
- end
27
-
28
- def after_commit2(class_name = nil, &block)
29
- define_callbacks(:after_commit2, class_name, &block)
30
- end
31
-
32
- def after_touch1(class_name = nil, &block)
33
- define_callbacks(:after_touch1, class_name, &block)
34
- end
35
-
36
- def after_touch2(class_name = nil, &block)
37
- define_callbacks(:after_touch2, class_name, &block)
38
- end
39
-
40
- private
41
-
42
- def define_callbacks(type, class_name, &block)
43
- (@type_callbacks[type] ||= ClassCallbacks.new).tap do |s|
44
- s.add_callback(class_name, &block) if class_name
45
- end
46
- end
47
- end
48
-
49
- class ClassCallbacks
50
- def initialize
51
- @class_callbacks = Hash.new{|h, k| h[k] = [] }
52
- end
53
-
54
- def callbacks_at(class_name)
55
- @class_callbacks[class_name]
56
- end
57
-
58
- def add_callback(class_name, &block)
59
- callbacks_at(class_name) << block
60
- end
61
-
62
- def exec(scope, klass, *args)
63
- callbacks_at(klass.name).each{|s| scope.instance_exec(*args, &s) }
64
- end
65
- end
66
- end
67
- end
1
+ module ActiveModelCachers
2
+ module ActiveRecord
3
+ class GlobalCallbacks
4
+ def initialize
5
+ @type_callbacks = {}
6
+ end
7
+
8
+ def before_delete1(class_name = nil, &block)
9
+ define_callbacks(:before_delete1, class_name, &block)
10
+ end
11
+
12
+ def before_delete2(class_name = nil, &block)
13
+ define_callbacks(:before_delete2, class_name, &block)
14
+ end
15
+
16
+ def after_delete(class_name = nil, &block)
17
+ define_callbacks(:after_delete, class_name, &block)
18
+ end
19
+
20
+ def on_nullify(class_name = nil, &block)
21
+ define_callbacks(:on_nullify, class_name, &block)
22
+ end
23
+
24
+ def after_commit1(class_name = nil, &block)
25
+ define_callbacks(:after_commit1, class_name, &block)
26
+ end
27
+
28
+ def after_commit2(class_name = nil, &block)
29
+ define_callbacks(:after_commit2, class_name, &block)
30
+ end
31
+
32
+ def after_touch1(class_name = nil, &block)
33
+ define_callbacks(:after_touch1, class_name, &block)
34
+ end
35
+
36
+ def after_touch2(class_name = nil, &block)
37
+ define_callbacks(:after_touch2, class_name, &block)
38
+ end
39
+
40
+ private
41
+
42
+ def define_callbacks(type, class_name, &block)
43
+ (@type_callbacks[type] ||= ClassCallbacks.new).tap do |s|
44
+ s.add_callback(class_name, &block) if class_name
45
+ end
46
+ end
47
+ end
48
+
49
+ class ClassCallbacks
50
+ def initialize
51
+ @class_callbacks = Hash.new{|h, k| h[k] = [] }
52
+ end
53
+
54
+ def callbacks_at(class_name)
55
+ @class_callbacks[class_name]
56
+ end
57
+
58
+ def add_callback(class_name, &block)
59
+ callbacks_at(class_name) << block
60
+ end
61
+
62
+ def exec(scope, klass, *args)
63
+ callbacks_at(klass.name).each{|s| scope.instance_exec(*args, &s) }
64
+ end
65
+ end
66
+ end
67
+ end
@@ -1,151 +1,151 @@
1
- # frozen_string_literal: true
2
- require 'active_model_cachers/nil_object'
3
- require 'active_model_cachers/false_object'
4
- require 'active_model_cachers/column_value_cache'
5
-
6
- module ActiveModelCachers
7
- class CacheService
8
- class << self
9
- attr_accessor :cache_key
10
- attr_accessor :query_mapping
11
-
12
- def instance(id)
13
- hash = (RequestStore.store[self] ||= {})
14
- return hash[id] ||= new(id)
15
- end
16
-
17
- def clean_at(id)
18
- instance(id).clean_cache
19
- end
20
-
21
- @@column_value_cache = ActiveModelCachers::ColumnValueCache.new
22
- def define_callback_for_cleaning_cache(class_name, column, foreign_key, with_id, on: nil)
23
- return if @callbacks_defined
24
- @callbacks_defined = true
25
-
26
- clean = ->(id){ clean_at(with_id ? id : nil) }
27
- clean_ids = []
28
- fire_on = Array(on) if on
29
-
30
- ActiveRecord::Extension.global_callbacks.instance_exec do
31
- on_nullify(class_name) do |nullified_column, get_ids|
32
- get_ids.call.each{|s| clean.call(s) } if nullified_column == column
33
- end
34
-
35
- after_touch1(class_name) do
36
- clean.call(@@column_value_cache.add(self.class, class_name, id, foreign_key, self).call)
37
- end
38
-
39
- after_touch2(class_name) do
40
- @@column_value_cache.clean_cache
41
- end
42
-
43
- after_commit1(class_name) do
44
- next if fire_on and not transaction_include_any_action?(fire_on)
45
- changed = column ? previous_changes.key?(column) : previous_changes.present?
46
- if changed || destroyed?
47
- clean.call(@@column_value_cache.add(self.class, class_name, id, foreign_key, self).call)
48
- end
49
- end
50
-
51
- after_commit2(class_name) do
52
- @@column_value_cache.clean_cache
53
- end
54
-
55
- before_delete1(class_name) do |id, model|
56
- clean_ids << @@column_value_cache.add(self, class_name, id, foreign_key, model)
57
- end
58
-
59
- before_delete2(class_name) do |_, model|
60
- clean_ids.each{|s| clean.call(s.call) }
61
- clean_ids = []
62
- end
63
-
64
- after_delete(class_name) do
65
- @@column_value_cache.clean_cache
66
- end
67
- end
68
- end
69
- end
70
-
71
- # ----------------------------------------------------------------
72
- # ● instance methods
73
- # ----------------------------------------------------------------
74
- def initialize(id)
75
- @id = id
76
- end
77
-
78
- def get(binding: nil, reflect: nil)
79
- @cached_data ||= fetch_from_cache(binding: binding, reflect: reflect)
80
- return cache_to_raw_data(@cached_data)
81
- end
82
-
83
- def peek(binding: nil, reflect: nil)
84
- @cached_data ||= get_from_cache
85
- return cache_to_raw_data(@cached_data)
86
- end
87
-
88
- def clean_cache(binding: nil, reflect: nil)
89
- @cached_data = nil
90
- Rails.cache.delete(cache_key)
91
- return nil
92
- end
93
-
94
- private
95
-
96
- def cache_key
97
- key = self.class.cache_key
98
- return @id ? "#{key}_#{@id}" : key
99
- end
100
-
101
- def get_query(binding, reflect)
102
- self.class.query_mapping[reflect] || begin
103
- puts "Warning: cannot find query. possible reflects: #{self.class.query_mapping.keys}, reflect: #{reflect}"
104
- self.class.query_mapping.values.first
105
- end
106
- end
107
-
108
- def get_without_cache(binding, attr)
109
- query = get_query(binding, attr)
110
- return binding ? binding.instance_exec(@id, &query) : query.call(@id) if @id and query.parameters.size == 1
111
- return binding ? binding.instance_exec(&query) : query.call
112
- end
113
-
114
- def raw_to_cache_data(raw)
115
- return NilObject if raw == nil
116
- return FalseObject if raw == false
117
- clean_ar_cache(raw.is_a?(Array) ? raw : [raw])
118
- return raw_without_singleton_methods(raw)
119
- end
120
-
121
- def cache_to_raw_data(cached_data)
122
- return nil if cached_data == NilObject
123
- return false if cached_data == FalseObject
124
- return cached_data
125
- end
126
-
127
- def get_from_cache
128
- ActiveModelCachers.config.store.read(cache_key)
129
- end
130
-
131
- def fetch_from_cache(binding: nil, reflect: nil)
132
- ActiveModelCachers.config.store.fetch(cache_key, expires_in: 30.minutes) do
133
- raw_to_cache_data(get_without_cache(binding, reflect))
134
- end
135
- end
136
-
137
- def clean_ar_cache(models)
138
- return if not models.first.is_a?(::ActiveRecord::Base)
139
- models.each_with_index do |model, index|
140
- model.send(:clear_aggregation_cache)
141
- model.send(:clear_association_cache)
142
- end
143
- end
144
-
145
- def raw_without_singleton_methods(raw)
146
- return raw if raw.singleton_methods.empty?
147
- return raw.class.find_by(id: raw.id) if raw.is_a?(::ActiveRecord::Base) # cannot marshal singleton, so load a new record instead.
148
- return raw # not sure what to do with other cases
149
- end
150
- end
151
- end
1
+ # frozen_string_literal: true
2
+ require 'active_model_cachers/nil_object'
3
+ require 'active_model_cachers/false_object'
4
+ require 'active_model_cachers/column_value_cache'
5
+
6
+ module ActiveModelCachers
7
+ class CacheService
8
+ class << self
9
+ attr_accessor :cache_key
10
+ attr_accessor :query_mapping
11
+
12
+ def instance(id)
13
+ hash = (RequestStore.store[self] ||= {})
14
+ return hash[id] ||= new(id)
15
+ end
16
+
17
+ def clean_at(id)
18
+ instance(id).clean_cache
19
+ end
20
+
21
+ @@column_value_cache = ActiveModelCachers::ColumnValueCache.new
22
+ def define_callback_for_cleaning_cache(class_name, column, foreign_key, with_id, on: nil)
23
+ return if @callbacks_defined
24
+ @callbacks_defined = true
25
+
26
+ clean = ->(id){ clean_at(with_id ? id : nil) }
27
+ clean_ids = []
28
+ fire_on = Array(on) if on
29
+
30
+ ActiveRecord::Extension.global_callbacks.instance_exec do
31
+ on_nullify(class_name) do |nullified_column, get_ids|
32
+ get_ids.call.each{|s| clean.call(s) } if nullified_column == column
33
+ end
34
+
35
+ after_touch1(class_name) do
36
+ clean.call(@@column_value_cache.add(self.class, class_name, id, foreign_key, self).call)
37
+ end
38
+
39
+ after_touch2(class_name) do
40
+ @@column_value_cache.clean_cache
41
+ end
42
+
43
+ after_commit1(class_name) do
44
+ next if fire_on and not transaction_include_any_action?(fire_on)
45
+ changed = column ? previous_changes.key?(column) : previous_changes.present?
46
+ if changed || destroyed?
47
+ clean.call(@@column_value_cache.add(self.class, class_name, id, foreign_key, self).call)
48
+ end
49
+ end
50
+
51
+ after_commit2(class_name) do
52
+ @@column_value_cache.clean_cache
53
+ end
54
+
55
+ before_delete1(class_name) do |id, model|
56
+ clean_ids << @@column_value_cache.add(self, class_name, id, foreign_key, model)
57
+ end
58
+
59
+ before_delete2(class_name) do |_, model|
60
+ clean_ids.each{|s| clean.call(s.call) }
61
+ clean_ids = []
62
+ end
63
+
64
+ after_delete(class_name) do
65
+ @@column_value_cache.clean_cache
66
+ end
67
+ end
68
+ end
69
+ end
70
+
71
+ # ----------------------------------------------------------------
72
+ # ● instance methods
73
+ # ----------------------------------------------------------------
74
+ def initialize(id)
75
+ @id = id
76
+ end
77
+
78
+ def get(binding: nil, reflect: nil)
79
+ @cached_data ||= fetch_from_cache(binding: binding, reflect: reflect)
80
+ return cache_to_raw_data(@cached_data)
81
+ end
82
+
83
+ def peek(binding: nil, reflect: nil)
84
+ @cached_data ||= get_from_cache
85
+ return cache_to_raw_data(@cached_data)
86
+ end
87
+
88
+ def clean_cache(binding: nil, reflect: nil)
89
+ @cached_data = nil
90
+ Rails.cache.delete(cache_key)
91
+ return nil
92
+ end
93
+
94
+ private
95
+
96
+ def cache_key
97
+ key = self.class.cache_key
98
+ return @id ? "#{key}_#{@id}" : key
99
+ end
100
+
101
+ def get_query(binding, reflect)
102
+ self.class.query_mapping[reflect] || begin
103
+ puts "Warning: cannot find query. possible reflects: #{self.class.query_mapping.keys}, reflect: #{reflect}"
104
+ self.class.query_mapping.values.first
105
+ end
106
+ end
107
+
108
+ def get_without_cache(binding, attr)
109
+ query = get_query(binding, attr)
110
+ return binding ? binding.instance_exec(@id, &query) : query.call(@id) if @id and query.parameters.size == 1
111
+ return binding ? binding.instance_exec(&query) : query.call
112
+ end
113
+
114
+ def raw_to_cache_data(raw)
115
+ return NilObject if raw == nil
116
+ return FalseObject if raw == false
117
+ clean_ar_cache(raw.is_a?(Array) ? raw : [raw])
118
+ return raw_without_singleton_methods(raw)
119
+ end
120
+
121
+ def cache_to_raw_data(cached_data)
122
+ return nil if cached_data == NilObject
123
+ return false if cached_data == FalseObject
124
+ return cached_data
125
+ end
126
+
127
+ def get_from_cache
128
+ ActiveModelCachers.config.store.read(cache_key)
129
+ end
130
+
131
+ def fetch_from_cache(binding: nil, reflect: nil)
132
+ ActiveModelCachers.config.store.fetch(cache_key, expires_in: 30.minutes) do
133
+ raw_to_cache_data(get_without_cache(binding, reflect))
134
+ end
135
+ end
136
+
137
+ def clean_ar_cache(models)
138
+ return if not models.first.is_a?(::ActiveRecord::Base)
139
+ models.each_with_index do |model, index|
140
+ model.send(:clear_aggregation_cache)
141
+ model.send(:clear_association_cache)
142
+ end
143
+ end
144
+
145
+ def raw_without_singleton_methods(raw)
146
+ return raw if raw.singleton_methods.empty?
147
+ return raw.class.find_by(id: raw.id) if raw.is_a?(::ActiveRecord::Base) # cannot marshal singleton, so load a new record instead.
148
+ return raw # not sure what to do with other cases
149
+ end
150
+ end
151
+ end