active_model_cachers 2.1.7 → 2.1.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/ruby.yml +59 -0
  3. data/.gitignore +9 -9
  4. data/CHANGELOG.md +71 -67
  5. data/CODE_OF_CONDUCT.md +48 -48
  6. data/LICENSE.txt +21 -21
  7. data/README.md +399 -390
  8. data/Rakefile +10 -10
  9. data/active_model_cachers.gemspec +45 -45
  10. data/bin/console +14 -14
  11. data/bin/setup +8 -8
  12. data/gemfiles/3.2.gemfile +11 -11
  13. data/gemfiles/4.2.gemfile +11 -11
  14. data/gemfiles/5.0.gemfile +11 -11
  15. data/gemfiles/5.1.gemfile +11 -11
  16. data/gemfiles/5.2.gemfile +11 -11
  17. data/gemfiles/6.0.gemfile +11 -11
  18. data/lib/active_model_cachers/active_record/attr_model.rb +124 -124
  19. data/lib/active_model_cachers/active_record/cacher.rb +97 -97
  20. data/lib/active_model_cachers/active_record/extension.rb +119 -119
  21. data/lib/active_model_cachers/active_record/global_callbacks.rb +67 -67
  22. data/lib/active_model_cachers/cache_service.rb +151 -151
  23. data/lib/active_model_cachers/cache_service_factory.rb +55 -55
  24. data/lib/active_model_cachers/column_value_cache.rb +47 -47
  25. data/lib/active_model_cachers/config.rb +6 -6
  26. data/lib/active_model_cachers/false_object.rb +5 -5
  27. data/lib/active_model_cachers/hook/associations.rb +43 -43
  28. data/lib/active_model_cachers/hook/dependencies.rb +38 -38
  29. data/lib/active_model_cachers/hook/on_model_delete.rb +29 -29
  30. data/lib/active_model_cachers/nil_object.rb +5 -5
  31. data/lib/active_model_cachers/patches/patch_rails_3.rb +49 -49
  32. data/lib/active_model_cachers/patches/uninitialized_attribute.rb +9 -9
  33. data/lib/active_model_cachers/version.rb +4 -4
  34. metadata +12 -7
  35. data/.travis.yml +0 -33
@@ -1,97 +1,97 @@
1
- # frozen_string_literal: true
2
- module ActiveModelCachers
3
- module ActiveRecord
4
- class Cacher
5
- @defined_map = {}
6
-
7
- class << self
8
- def get_cacher_klass(klass)
9
- @defined_map[klass] ||= create_cacher_klass_at(klass)
10
- end
11
-
12
- def define_cacher_method(attr, primary_key, service_klasses)
13
- cacher_klass = get_cacher_klass(attr.klass)
14
- method = attr.column
15
- return cacher_klass.define_find_by(attr, primary_key, service_klasses) if method == nil
16
- cacher_klass.send(:define_methods, method, {
17
- method => ->{ exec_by(attr, primary_key, service_klasses, :get) },
18
- "peek_#{method}" => ->{ exec_by(attr, primary_key, service_klasses, :peek) },
19
- "clean_#{method}" => ->{ exec_by(attr, primary_key, service_klasses, :clean_cache) },
20
- })
21
- end
22
-
23
- def define_find_by(attr, primary_key, service_klasses)
24
- if @find_by_mapping == nil
25
- @find_by_mapping = {}
26
- define_methods(:find_by, {
27
- :find_by => ->(args){ exec_find_by(args, :get) },
28
- :peek_by => ->(args){ exec_find_by(args, :peek) },
29
- :clean_by => ->(args){ exec_find_by(args, :clean_cache) },
30
- })
31
- end
32
- @find_by_mapping[primary_key] = [attr, service_klasses]
33
- end
34
-
35
- private
36
-
37
- def define_methods(attribute, methods_mapping)
38
- if attributes.include?(attribute)
39
- methods_mapping.keys.each{|s| undef_method(s) }
40
- else
41
- attributes << attribute
42
- end
43
- methods_mapping.each{|method, block| define_method(method, &block) }
44
- end
45
-
46
- def get_data_from_find_by_mapping(primary_key)
47
- return if @find_by_mapping == nil
48
- return @find_by_mapping[primary_key]
49
- end
50
-
51
- def create_cacher_klass_at(target)
52
- cacher_klass = Class.new(self)
53
- cacher_klass.instance_variable_set(:@find_by_mapping, nil) # to remove warning: instance variable @find_by_mapping not initialized
54
- cacher_klass.define_singleton_method(:attributes){ @attributes ||= [] }
55
- cacher_klass.send(:define_method, 'peek'){|column| send("peek_#{column}") }
56
- cacher_klass.send(:define_method, 'clean'){|column| send("clean_#{column}") }
57
-
58
- target.define_singleton_method(:cacher_at){|id| cacher_klass.new(id: id) }
59
- target.define_singleton_method(:cacher){ cacher_klass.new }
60
- target.send(:define_method, :cacher){ cacher_klass.new(model: self) }
61
- return cacher_klass
62
- end
63
- end
64
-
65
- def initialize(id: nil, model: nil)
66
- @id = id
67
- @model = model
68
- end
69
-
70
- private
71
-
72
- def exec_find_by(args, method) # e.g. args = {course_id: xx}
73
- primary_key = args.keys.sort.first # Support only one key now.
74
- attr, service_klasses = self.class.send(:get_data_from_find_by_mapping, primary_key)
75
- return if service_klasses == nil
76
- return exec_by(attr, primary_key, service_klasses, method, data: args[primary_key])
77
- end
78
-
79
- def exec_by(attr, primary_key, service_klasses, method, data: nil)
80
- bindings = [@model]
81
- reflects = (attr.belongs_to? ? [] : [attr.reflect])
82
- if @model and attr.association?
83
- if attr.belongs_to? and method != :clean_cache # no need to load binding when just cleaning cache
84
- association = @model.association(attr.column)
85
- bindings << association.load_target if association.loaded?
86
- end
87
- end
88
- data ||= (@model ? @model.send(primary_key) : nil) || @id
89
- service_klasses.each_with_index do |service_klass, index|
90
- data = service_klass.instance(data).send(method, binding: bindings[index], reflect: reflects[index])
91
- return if data == nil
92
- end
93
- return data
94
- end
95
- end
96
- end
97
- end
1
+ # frozen_string_literal: true
2
+ module ActiveModelCachers
3
+ module ActiveRecord
4
+ class Cacher
5
+ @defined_map = {}
6
+
7
+ class << self
8
+ def get_cacher_klass(klass)
9
+ @defined_map[klass] ||= create_cacher_klass_at(klass)
10
+ end
11
+
12
+ def define_cacher_method(attr, primary_key, service_klasses)
13
+ cacher_klass = get_cacher_klass(attr.klass)
14
+ method = attr.column
15
+ return cacher_klass.define_find_by(attr, primary_key, service_klasses) if method == nil
16
+ cacher_klass.send(:define_methods, method, {
17
+ method => ->{ exec_by(attr, primary_key, service_klasses, :get) },
18
+ "peek_#{method}" => ->{ exec_by(attr, primary_key, service_klasses, :peek) },
19
+ "clean_#{method}" => ->{ exec_by(attr, primary_key, service_klasses, :clean_cache) },
20
+ })
21
+ end
22
+
23
+ def define_find_by(attr, primary_key, service_klasses)
24
+ if @find_by_mapping == nil
25
+ @find_by_mapping = {}
26
+ define_methods(:find_by, {
27
+ :find_by => ->(args){ exec_find_by(args, :get) },
28
+ :peek_by => ->(args){ exec_find_by(args, :peek) },
29
+ :clean_by => ->(args){ exec_find_by(args, :clean_cache) },
30
+ })
31
+ end
32
+ @find_by_mapping[primary_key] = [attr, service_klasses]
33
+ end
34
+
35
+ private
36
+
37
+ def define_methods(attribute, methods_mapping)
38
+ if attributes.include?(attribute)
39
+ methods_mapping.keys.each{|s| undef_method(s) }
40
+ else
41
+ attributes << attribute
42
+ end
43
+ methods_mapping.each{|method, block| define_method(method, &block) }
44
+ end
45
+
46
+ def get_data_from_find_by_mapping(primary_key)
47
+ return if @find_by_mapping == nil
48
+ return @find_by_mapping[primary_key]
49
+ end
50
+
51
+ def create_cacher_klass_at(target)
52
+ cacher_klass = Class.new(self)
53
+ cacher_klass.instance_variable_set(:@find_by_mapping, nil) # to remove warning: instance variable @find_by_mapping not initialized
54
+ cacher_klass.define_singleton_method(:attributes){ @attributes ||= [] }
55
+ cacher_klass.send(:define_method, 'peek'){|column| send("peek_#{column}") }
56
+ cacher_klass.send(:define_method, 'clean'){|column| send("clean_#{column}") }
57
+
58
+ target.define_singleton_method(:cacher_at){|id| cacher_klass.new(id: id) }
59
+ target.define_singleton_method(:cacher){ cacher_klass.new }
60
+ target.send(:define_method, :cacher){ cacher_klass.new(model: self) }
61
+ return cacher_klass
62
+ end
63
+ end
64
+
65
+ def initialize(id: nil, model: nil)
66
+ @id = id
67
+ @model = model
68
+ end
69
+
70
+ private
71
+
72
+ def exec_find_by(args, method) # e.g. args = {course_id: xx}
73
+ primary_key = args.keys.sort.first # Support only one key now.
74
+ attr, service_klasses = self.class.send(:get_data_from_find_by_mapping, primary_key)
75
+ return if service_klasses == nil
76
+ return exec_by(attr, primary_key, service_klasses, method, data: args[primary_key])
77
+ end
78
+
79
+ def exec_by(attr, primary_key, service_klasses, method, data: nil)
80
+ bindings = [@model]
81
+ reflects = (attr.belongs_to? ? [] : [attr.reflect])
82
+ if @model and attr.association?
83
+ if attr.belongs_to? and method != :clean_cache # no need to load binding when just cleaning cache
84
+ association = @model.association(attr.column)
85
+ bindings << association.load_target if association.loaded?
86
+ end
87
+ end
88
+ data ||= (@model ? @model.send(primary_key) : nil) || @id
89
+ service_klasses.each_with_index do |service_klass, index|
90
+ data = service_klass.instance(data).send(method, binding: bindings[index], reflect: reflects[index])
91
+ return if data == nil
92
+ end
93
+ return data
94
+ end
95
+ end
96
+ end
97
+ end
@@ -1,119 +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
- 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
+ # 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