bullet 5.5.0 → 6.1.5

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 (123) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/main.yml +66 -0
  3. data/CHANGELOG.md +83 -1
  4. data/Gemfile +6 -1
  5. data/Gemfile.mongoid-7.0 +15 -0
  6. data/Gemfile.rails-4.0 +1 -1
  7. data/Gemfile.rails-4.1 +1 -1
  8. data/Gemfile.rails-4.2 +1 -1
  9. data/Gemfile.rails-5.0 +2 -2
  10. data/Gemfile.rails-5.1 +15 -0
  11. data/Gemfile.rails-5.2 +15 -0
  12. data/Gemfile.rails-6.0 +15 -0
  13. data/Gemfile.rails-6.1 +15 -0
  14. data/Guardfile +2 -2
  15. data/Hacking.md +1 -1
  16. data/README.md +58 -37
  17. data/Rakefile +20 -21
  18. data/bullet.gemspec +21 -15
  19. data/lib/bullet/active_job.rb +13 -0
  20. data/lib/bullet/active_record4.rb +15 -34
  21. data/lib/bullet/active_record41.rb +14 -31
  22. data/lib/bullet/active_record42.rb +23 -36
  23. data/lib/bullet/active_record5.rb +178 -158
  24. data/lib/bullet/active_record52.rb +251 -0
  25. data/lib/bullet/active_record60.rb +278 -0
  26. data/lib/bullet/active_record61.rb +278 -0
  27. data/lib/bullet/bullet_xhr.js +64 -0
  28. data/lib/bullet/dependency.rb +60 -32
  29. data/lib/bullet/detector/association.rb +41 -32
  30. data/lib/bullet/detector/base.rb +2 -0
  31. data/lib/bullet/detector/counter_cache.rb +25 -17
  32. data/lib/bullet/detector/n_plus_one_query.rb +38 -24
  33. data/lib/bullet/detector/unused_eager_loading.rb +35 -26
  34. data/lib/bullet/detector.rb +2 -0
  35. data/lib/bullet/ext/object.rb +9 -5
  36. data/lib/bullet/ext/string.rb +3 -1
  37. data/lib/bullet/mongoid4x.rb +9 -8
  38. data/lib/bullet/mongoid5x.rb +9 -8
  39. data/lib/bullet/mongoid6x.rb +13 -12
  40. data/lib/bullet/mongoid7x.rb +57 -0
  41. data/lib/bullet/notification/base.rb +28 -25
  42. data/lib/bullet/notification/counter_cache.rb +3 -1
  43. data/lib/bullet/notification/n_plus_one_query.rb +8 -7
  44. data/lib/bullet/notification/unused_eager_loading.rb +8 -7
  45. data/lib/bullet/notification.rb +4 -1
  46. data/lib/bullet/notification_collector.rb +2 -1
  47. data/lib/bullet/rack.rb +66 -35
  48. data/lib/bullet/registry/association.rb +2 -0
  49. data/lib/bullet/registry/base.rb +2 -0
  50. data/lib/bullet/registry/object.rb +2 -0
  51. data/lib/bullet/registry.rb +2 -0
  52. data/lib/bullet/stack_trace_filter.rb +50 -20
  53. data/lib/bullet/version.rb +3 -2
  54. data/lib/bullet.rb +136 -55
  55. data/lib/generators/bullet/install_generator.rb +48 -0
  56. data/perf/benchmark.rb +19 -22
  57. data/rails/init.rb +2 -0
  58. data/spec/bullet/detector/association_spec.rb +6 -4
  59. data/spec/bullet/detector/base_spec.rb +2 -0
  60. data/spec/bullet/detector/counter_cache_spec.rb +17 -15
  61. data/spec/bullet/detector/n_plus_one_query_spec.rb +75 -46
  62. data/spec/bullet/detector/unused_eager_loading_spec.rb +45 -26
  63. data/spec/bullet/ext/object_spec.rb +18 -11
  64. data/spec/bullet/ext/string_spec.rb +7 -5
  65. data/spec/bullet/notification/base_spec.rb +38 -39
  66. data/spec/bullet/notification/counter_cache_spec.rb +5 -3
  67. data/spec/bullet/notification/n_plus_one_query_spec.rb +20 -5
  68. data/spec/bullet/notification/unused_eager_loading_spec.rb +9 -3
  69. data/spec/bullet/notification_collector_spec.rb +12 -10
  70. data/spec/bullet/rack_spec.rb +200 -48
  71. data/spec/bullet/registry/association_spec.rb +12 -10
  72. data/spec/bullet/registry/base_spec.rb +22 -20
  73. data/spec/bullet/registry/object_spec.rb +6 -4
  74. data/spec/bullet_spec.rb +64 -33
  75. data/spec/integration/{active_record4 → active_record}/association_spec.rb +238 -223
  76. data/spec/integration/counter_cache_spec.rb +26 -44
  77. data/spec/integration/mongoid/association_spec.rb +53 -65
  78. data/spec/models/address.rb +2 -0
  79. data/spec/models/attachment.rb +5 -0
  80. data/spec/models/author.rb +2 -0
  81. data/spec/models/base_user.rb +2 -0
  82. data/spec/models/category.rb +2 -0
  83. data/spec/models/city.rb +2 -0
  84. data/spec/models/client.rb +4 -0
  85. data/spec/models/comment.rb +3 -1
  86. data/spec/models/company.rb +2 -0
  87. data/spec/models/country.rb +2 -0
  88. data/spec/models/deal.rb +5 -0
  89. data/spec/models/document.rb +4 -2
  90. data/spec/models/entry.rb +2 -0
  91. data/spec/models/firm.rb +3 -0
  92. data/spec/models/folder.rb +2 -0
  93. data/spec/models/group.rb +4 -0
  94. data/spec/models/mongoid/address.rb +3 -1
  95. data/spec/models/mongoid/category.rb +4 -2
  96. data/spec/models/mongoid/comment.rb +3 -1
  97. data/spec/models/mongoid/company.rb +3 -1
  98. data/spec/models/mongoid/entry.rb +3 -1
  99. data/spec/models/mongoid/post.rb +6 -4
  100. data/spec/models/mongoid/user.rb +2 -0
  101. data/spec/models/newspaper.rb +3 -1
  102. data/spec/models/page.rb +2 -0
  103. data/spec/models/person.rb +2 -0
  104. data/spec/models/pet.rb +2 -0
  105. data/spec/models/post.rb +19 -0
  106. data/spec/models/relationship.rb +2 -0
  107. data/spec/models/reply.rb +2 -0
  108. data/spec/models/student.rb +2 -0
  109. data/spec/models/submission.rb +3 -0
  110. data/spec/models/teacher.rb +2 -0
  111. data/spec/models/user.rb +3 -0
  112. data/spec/models/writer.rb +2 -0
  113. data/spec/spec_helper.rb +19 -23
  114. data/spec/support/bullet_ext.rb +10 -9
  115. data/spec/support/mongo_seed.rb +38 -57
  116. data/spec/support/rack_double.rb +13 -19
  117. data/spec/support/sqlite_seed.rb +104 -76
  118. data/tasks/bullet_tasks.rake +4 -2
  119. data/test.sh +5 -0
  120. data/update.sh +3 -0
  121. metadata +34 -17
  122. data/.travis.yml +0 -16
  123. data/spec/integration/active_record5/association_spec.rb +0 -768
@@ -1,47 +1,55 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bullet
2
4
  module Dependency
3
5
  def mongoid?
4
- @mongoid ||= defined? ::Mongoid
6
+ @mongoid ||= defined?(::Mongoid)
5
7
  end
6
8
 
7
9
  def active_record?
8
- @active_record ||= defined? ::ActiveRecord
9
- end
10
-
11
- def rails?
12
- @rails ||= defined? ::Rails
10
+ @active_record ||= defined?(::ActiveRecord)
13
11
  end
14
12
 
15
13
  def active_record_version
16
- @active_record_version ||= begin
17
- if active_record40?
18
- 'active_record4'
19
- elsif active_record41?
20
- 'active_record41'
21
- elsif active_record42?
22
- 'active_record42'
23
- elsif active_record50?
24
- 'active_record5'
25
- elsif active_record51?
26
- 'active_record5'
27
- else
28
- raise "Bullet does not support active_record #{::ActiveRecord::VERSION} yet"
29
- end
30
- end
14
+ @active_record_version ||=
15
+ begin
16
+ if active_record40?
17
+ 'active_record4'
18
+ elsif active_record41?
19
+ 'active_record41'
20
+ elsif active_record42?
21
+ 'active_record42'
22
+ elsif active_record50?
23
+ 'active_record5'
24
+ elsif active_record51?
25
+ 'active_record5'
26
+ elsif active_record52?
27
+ 'active_record52'
28
+ elsif active_record60?
29
+ 'active_record60'
30
+ elsif active_record61?
31
+ 'active_record61'
32
+ else
33
+ raise "Bullet does not support active_record #{::ActiveRecord::VERSION::STRING} yet"
34
+ end
35
+ end
31
36
  end
32
37
 
33
38
  def mongoid_version
34
- @mongoid_version ||= begin
35
- if mongoid4x?
36
- 'mongoid4x'
37
- elsif mongoid5x?
38
- 'mongoid5x'
39
- elsif mongoid6x?
40
- 'mongoid6x'
41
- else
42
- raise "Bullet does not support mongoid #{::Mongoid::VERSION} yet"
43
- end
44
- end
39
+ @mongoid_version ||=
40
+ begin
41
+ if mongoid4x?
42
+ 'mongoid4x'
43
+ elsif mongoid5x?
44
+ 'mongoid5x'
45
+ elsif mongoid6x?
46
+ 'mongoid6x'
47
+ elsif mongoid7x?
48
+ 'mongoid7x'
49
+ else
50
+ raise "Bullet does not support mongoid #{::Mongoid::VERSION} yet"
51
+ end
52
+ end
45
53
  end
46
54
 
47
55
  def active_record4?
@@ -52,6 +60,10 @@ module Bullet
52
60
  active_record? && ::ActiveRecord::VERSION::MAJOR == 5
53
61
  end
54
62
 
63
+ def active_record6?
64
+ active_record? && ::ActiveRecord::VERSION::MAJOR == 6
65
+ end
66
+
55
67
  def active_record40?
56
68
  active_record4? && ::ActiveRecord::VERSION::MINOR == 0
57
69
  end
@@ -72,6 +84,18 @@ module Bullet
72
84
  active_record5? && ::ActiveRecord::VERSION::MINOR == 1
73
85
  end
74
86
 
87
+ def active_record52?
88
+ active_record5? && ::ActiveRecord::VERSION::MINOR == 2
89
+ end
90
+
91
+ def active_record60?
92
+ active_record6? && ::ActiveRecord::VERSION::MINOR == 0
93
+ end
94
+
95
+ def active_record61?
96
+ active_record6? && ::ActiveRecord::VERSION::MINOR == 1
97
+ end
98
+
75
99
  def mongoid4x?
76
100
  mongoid? && ::Mongoid::VERSION =~ /\A4/
77
101
  end
@@ -83,5 +107,9 @@ module Bullet
83
107
  def mongoid6x?
84
108
  mongoid? && ::Mongoid::VERSION =~ /\A6/
85
109
  end
110
+
111
+ def mongoid7x?
112
+ mongoid? && ::Mongoid::VERSION =~ /\A7/
113
+ end
86
114
  end
87
115
  end
@@ -1,22 +1,30 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bullet
2
4
  module Detector
3
5
  class Association < Base
4
- class <<self
6
+ class << self
5
7
  def add_object_associations(object, associations)
6
8
  return unless Bullet.start?
7
9
  return if !Bullet.n_plus_one_query_enable? && !Bullet.unused_eager_loading_enable?
8
- return unless object.primary_key_value
10
+ return unless object.bullet_primary_key_value
9
11
 
10
- Bullet.debug("Detector::Association#add_object_associations", "object: #{object.bullet_key}, associations: #{associations}")
12
+ Bullet.debug(
13
+ 'Detector::Association#add_object_associations',
14
+ "object: #{object.bullet_key}, associations: #{associations}"
15
+ )
11
16
  object_associations.add(object.bullet_key, associations)
12
17
  end
13
18
 
14
19
  def add_call_object_associations(object, associations)
15
20
  return unless Bullet.start?
16
21
  return if !Bullet.n_plus_one_query_enable? && !Bullet.unused_eager_loading_enable?
17
- return unless object.primary_key_value
22
+ return unless object.bullet_primary_key_value
18
23
 
19
- Bullet.debug("Detector::Association#add_call_object_associations", "object: #{object.bullet_key}, associations: #{associations}")
24
+ Bullet.debug(
25
+ 'Detector::Association#add_call_object_associations',
26
+ "object: #{object.bullet_key}, associations: #{associations}"
27
+ )
20
28
  call_object_associations.add(object.bullet_key, associations)
21
29
  end
22
30
 
@@ -37,36 +45,37 @@ module Bullet
37
45
  end
38
46
 
39
47
  private
40
- # object_associations keep the object relationships
41
- # that the object has many associations.
42
- # e.g. { "Post:1" => [:comments] }
43
- # the object_associations keep all associations that may be or may no be
44
- # unpreload associations or unused preload associations.
45
- def object_associations
46
- Thread.current[:bullet_object_associations]
47
- end
48
48
 
49
- # call_object_associations keep the object relationships
50
- # that object.associations is called.
51
- # e.g. { "Post:1" => [:comments] }
52
- # they are used to detect unused preload associations.
53
- def call_object_associations
54
- Thread.current[:bullet_call_object_associations]
55
- end
49
+ # object_associations keep the object relationships
50
+ # that the object has many associations.
51
+ # e.g. { "Post:1" => [:comments] }
52
+ # the object_associations keep all associations that may be or may no be
53
+ # unpreload associations or unused preload associations.
54
+ def object_associations
55
+ Thread.current[:bullet_object_associations]
56
+ end
57
+
58
+ # call_object_associations keep the object relationships
59
+ # that object.associations is called.
60
+ # e.g. { "Post:1" => [:comments] }
61
+ # they are used to detect unused preload associations.
62
+ def call_object_associations
63
+ Thread.current[:bullet_call_object_associations]
64
+ end
56
65
 
57
- # inversed_objects keeps object relationships
58
- # that association is inversed.
59
- # e.g. { "Comment:1" => ["post"] }
60
- def inversed_objects
61
- Thread.current[:bullet_inversed_objects]
62
- end
66
+ # inversed_objects keeps object relationships
67
+ # that association is inversed.
68
+ # e.g. { "Comment:1" => ["post"] }
69
+ def inversed_objects
70
+ Thread.current[:bullet_inversed_objects]
71
+ end
63
72
 
64
- # eager_loadings keep the object relationships
65
- # that the associations are preloaded by find :include.
66
- # e.g. { ["Post:1", "Post:2"] => [:comments, :user] }
67
- def eager_loadings
68
- Thread.current[:bullet_eager_loadings]
69
- end
73
+ # eager_loadings keep the object relationships
74
+ # that the associations are preloaded by find :include.
75
+ # e.g. { ["Post:1", "Post:2"] => [:comments, :user] }
76
+ def eager_loadings
77
+ Thread.current[:bullet_eager_loadings]
78
+ end
70
79
  end
71
80
  end
72
81
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bullet
2
4
  module Detector
3
5
  class Base
@@ -1,38 +1,45 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bullet
2
4
  module Detector
3
5
  class CounterCache < Base
4
- class <<self
6
+ class << self
5
7
  def add_counter_cache(object, associations)
6
8
  return unless Bullet.start?
7
9
  return unless Bullet.counter_cache_enable?
8
- return unless object.primary_key_value
10
+ return unless object.bullet_primary_key_value
9
11
 
10
- Bullet.debug("Detector::CounterCache#add_counter_cache", "object: #{object.bullet_key}, associations: #{associations}")
11
- if conditions_met?(object, associations)
12
- create_notification object.class.to_s, associations
13
- end
12
+ Bullet.debug(
13
+ 'Detector::CounterCache#add_counter_cache',
14
+ "object: #{object.bullet_key}, associations: #{associations}"
15
+ )
16
+ create_notification object.class.to_s, associations if conditions_met?(object, associations)
14
17
  end
15
18
 
16
19
  def add_possible_objects(object_or_objects)
17
20
  return unless Bullet.start?
18
21
  return unless Bullet.counter_cache_enable?
22
+
19
23
  objects = Array(object_or_objects)
20
- return if objects.map(&:primary_key_value).compact.empty?
24
+ return if objects.map(&:bullet_primary_key_value).compact.empty?
21
25
 
22
- Bullet.debug("Detector::CounterCache#add_possible_objects", "objects: #{objects.map(&:bullet_key).join(', ')}")
26
+ Bullet.debug(
27
+ 'Detector::CounterCache#add_possible_objects',
28
+ "objects: #{objects.map(&:bullet_key).join(', ')}"
29
+ )
23
30
  objects.each { |object| possible_objects.add object.bullet_key }
24
31
  end
25
32
 
26
33
  def add_impossible_object(object)
27
34
  return unless Bullet.start?
28
35
  return unless Bullet.counter_cache_enable?
29
- return unless object.primary_key_value
36
+ return unless object.bullet_primary_key_value
30
37
 
31
- Bullet.debug("Detector::CounterCache#add_impossible_object", "object: #{object.bullet_key}")
38
+ Bullet.debug('Detector::CounterCache#add_impossible_object', "object: #{object.bullet_key}")
32
39
  impossible_objects.add object.bullet_key
33
40
  end
34
41
 
35
- def conditions_met?(object, associations)
42
+ def conditions_met?(object, _associations)
36
43
  possible_objects.include?(object.bullet_key) && !impossible_objects.include?(object.bullet_key)
37
44
  end
38
45
 
@@ -45,14 +52,15 @@ module Bullet
45
52
  end
46
53
 
47
54
  private
48
- def create_notification(klazz, associations)
49
- notify_associations = Array(associations) - Bullet.get_whitelist_associations(:counter_cache, klazz)
50
55
 
51
- if notify_associations.present?
52
- notice = Bullet::Notification::CounterCache.new klazz, notify_associations
53
- Bullet.notification_collector.add notice
54
- end
56
+ def create_notification(klazz, associations)
57
+ notify_associations = Array(associations) - Bullet.get_safelist_associations(:counter_cache, klazz)
58
+
59
+ if notify_associations.present?
60
+ notice = Bullet::Notification::CounterCache.new klazz, notify_associations
61
+ Bullet.notification_collector.add notice
55
62
  end
63
+ end
56
64
  end
57
65
  end
58
66
  end
@@ -1,23 +1,30 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bullet
2
4
  module Detector
3
5
  class NPlusOneQuery < Association
4
6
  extend Dependency
5
7
  extend StackTraceFilter
6
8
 
7
- class <<self
9
+ class << self
8
10
  # executed when object.assocations is called.
9
11
  # first, it keeps this method call for object.association.
10
12
  # then, it checks if this associations call is unpreload.
11
13
  # if it is, keeps this unpreload associations and caller.
12
14
  def call_association(object, associations)
13
15
  return unless Bullet.start?
14
- return unless object.primary_key_value
16
+ return unless Bullet.n_plus_one_query_enable?
17
+ return unless object.bullet_primary_key_value
15
18
  return if inversed_objects.include?(object.bullet_key, associations)
19
+
16
20
  add_call_object_associations(object, associations)
17
21
 
18
- Bullet.debug("Detector::NPlusOneQuery#call_association", "object: #{object.bullet_key}, associations: #{associations}")
22
+ Bullet.debug(
23
+ 'Detector::NPlusOneQuery#call_association',
24
+ "object: #{object.bullet_key}, associations: #{associations}"
25
+ )
19
26
  if !excluded_stacktrace_path? && conditions_met?(object, associations)
20
- Bullet.debug("detect n + 1 query", "object: #{object.bullet_key}, associations: #{associations}")
27
+ Bullet.debug('detect n + 1 query', "object: #{object.bullet_key}, associations: #{associations}")
21
28
  create_notification caller_in_project, object.class.to_s, associations
22
29
  end
23
30
  end
@@ -25,28 +32,36 @@ module Bullet
25
32
  def add_possible_objects(object_or_objects)
26
33
  return unless Bullet.start?
27
34
  return unless Bullet.n_plus_one_query_enable?
35
+
28
36
  objects = Array(object_or_objects)
29
- return if objects.map(&:primary_key_value).compact.empty?
37
+ return if objects.map(&:bullet_primary_key_value).compact.empty?
38
+ return if objects.all? { |obj| obj.class.name =~ /^HABTM_/ }
30
39
 
31
- Bullet.debug("Detector::NPlusOneQuery#add_possible_objects", "objects: #{objects.map(&:bullet_key).join(', ')}")
40
+ Bullet.debug(
41
+ 'Detector::NPlusOneQuery#add_possible_objects',
42
+ "objects: #{objects.map(&:bullet_key).join(', ')}"
43
+ )
32
44
  objects.each { |object| possible_objects.add object.bullet_key }
33
45
  end
34
46
 
35
47
  def add_impossible_object(object)
36
48
  return unless Bullet.start?
37
49
  return unless Bullet.n_plus_one_query_enable?
38
- return unless object.primary_key_value
50
+ return unless object.bullet_primary_key_value
39
51
 
40
- Bullet.debug("Detector::NPlusOneQuery#add_impossible_object", "object: #{object.bullet_key}")
52
+ Bullet.debug('Detector::NPlusOneQuery#add_impossible_object', "object: #{object.bullet_key}")
41
53
  impossible_objects.add object.bullet_key
42
54
  end
43
55
 
44
56
  def add_inversed_object(object, association)
45
57
  return unless Bullet.start?
46
58
  return unless Bullet.n_plus_one_query_enable?
47
- return unless object.primary_key_value
59
+ return unless object.bullet_primary_key_value
48
60
 
49
- Bullet.debug("Detector::NPlusOneQuery#add_inversed_object", "object: #{object.bullet_key}, association: #{association}")
61
+ Bullet.debug(
62
+ 'Detector::NPlusOneQuery#add_inversed_object',
63
+ "object: #{object.bullet_key}, association: #{association}"
64
+ )
50
65
  inversed_objects.add object.bullet_key, association
51
66
  end
52
67
 
@@ -66,28 +81,27 @@ module Bullet
66
81
  # check if object => associations already exists in object_associations.
67
82
  def association?(object, associations)
68
83
  value = object_associations[object.bullet_key]
69
- if value
70
- value.each do |v|
71
- # associations == v comparision order is important here because
72
- # v variable might be a squeel node where :== method is redefined,
73
- # so it does not compare values at all and return unexpected results
74
- result = v.is_a?(Hash) ? v.key?(associations) : associations == v
75
- return true if result
76
- end
84
+ value&.each do |v|
85
+ # associations == v comparison order is important here because
86
+ # v variable might be a squeel node where :== method is redefined,
87
+ # so it does not compare values at all and return unexpected results
88
+ result = v.is_a?(Hash) ? v.key?(associations) : associations == v
89
+ return true if result
77
90
  end
78
91
 
79
92
  false
80
93
  end
81
94
 
82
95
  private
83
- def create_notification(callers, klazz, associations)
84
- notify_associations = Array(associations) - Bullet.get_whitelist_associations(:n_plus_one_query, klazz)
85
96
 
86
- if notify_associations.present?
87
- notice = Bullet::Notification::NPlusOneQuery.new(callers, klazz, notify_associations)
88
- Bullet.notification_collector.add(notice)
89
- end
97
+ def create_notification(callers, klazz, associations)
98
+ notify_associations = Array(associations) - Bullet.get_safelist_associations(:n_plus_one_query, klazz)
99
+
100
+ if notify_associations.present?
101
+ notice = Bullet::Notification::NPlusOneQuery.new(callers, klazz, notify_associations)
102
+ Bullet.notification_collector.add(notice)
90
103
  end
104
+ end
91
105
  end
92
106
  end
93
107
  end
@@ -1,10 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bullet
2
4
  module Detector
3
5
  class UnusedEagerLoading < Association
4
6
  extend Dependency
5
7
  extend StackTraceFilter
6
8
 
7
- class <<self
9
+ class << self
8
10
  # check if there are unused preload associations.
9
11
  # get related_objects from eager_loadings associated with object and associations
10
12
  # get call_object_association from associations of call_object_associations whose object is in related_objects
@@ -17,7 +19,7 @@ module Bullet
17
19
  object_association_diff = diff_object_associations bullet_key, associations
18
20
  next if object_association_diff.empty?
19
21
 
20
- Bullet.debug("detect unused preload", "object: #{bullet_key}, associations: #{object_association_diff}")
22
+ Bullet.debug('detect unused preload', "object: #{bullet_key}, associations: #{object_association_diff}")
21
23
  create_notification(caller_in_project, bullet_key.bullet_class_name, object_association_diff)
22
24
  end
23
25
  end
@@ -25,22 +27,27 @@ module Bullet
25
27
  def add_eager_loadings(objects, associations)
26
28
  return unless Bullet.start?
27
29
  return unless Bullet.unused_eager_loading_enable?
28
- return if objects.map(&:primary_key_value).compact.empty?
30
+ return if objects.map(&:bullet_primary_key_value).compact.empty?
29
31
 
30
- Bullet.debug("Detector::UnusedEagerLoading#add_eager_loadings", "objects: #{objects.map(&:bullet_key).join(', ')}, associations: #{associations}")
32
+ Bullet.debug(
33
+ 'Detector::UnusedEagerLoading#add_eager_loadings',
34
+ "objects: #{objects.map(&:bullet_key).join(', ')}, associations: #{associations}"
35
+ )
31
36
  bullet_keys = objects.map(&:bullet_key)
32
37
 
33
- to_add, to_merge, to_delete = [], [], []
34
- eager_loadings.each do |k, v|
38
+ to_add = []
39
+ to_merge = []
40
+ to_delete = []
41
+ eager_loadings.each do |k, _v|
35
42
  key_objects_overlap = k & bullet_keys
36
43
 
37
44
  next if key_objects_overlap.empty?
38
45
 
39
- bullet_keys = bullet_keys - k
46
+ bullet_keys -= k
40
47
  if key_objects_overlap == k
41
48
  to_add << [k, associations]
42
49
  else
43
- to_merge << [key_objects_overlap, ( eager_loadings[k].dup << associations )]
50
+ to_merge << [key_objects_overlap, (eager_loadings[k].dup << associations)]
44
51
 
45
52
  keys_without_objects = k - key_objects_overlap
46
53
  to_merge << [keys_without_objects, eager_loadings[k]]
@@ -56,29 +63,31 @@ module Bullet
56
63
  end
57
64
 
58
65
  private
59
- def create_notification(callers, klazz, associations)
60
- notify_associations = Array(associations) - Bullet.get_whitelist_associations(:unused_eager_loading, klazz)
61
66
 
62
- if notify_associations.present?
63
- notice = Bullet::Notification::UnusedEagerLoading.new(callers, klazz, notify_associations)
64
- Bullet.notification_collector.add(notice)
65
- end
66
- end
67
+ def create_notification(callers, klazz, associations)
68
+ notify_associations = Array(associations) - Bullet.get_safelist_associations(:unused_eager_loading, klazz)
67
69
 
68
- def call_associations(bullet_key, associations)
69
- all = Set.new
70
- eager_loadings.similarly_associated(bullet_key, associations).each do |related_bullet_key|
71
- coa = call_object_associations[related_bullet_key]
72
- next if coa.nil?
73
- all.merge coa
74
- end
75
- all.to_a
70
+ if notify_associations.present?
71
+ notice = Bullet::Notification::UnusedEagerLoading.new(callers, klazz, notify_associations)
72
+ Bullet.notification_collector.add(notice)
76
73
  end
74
+ end
77
75
 
78
- def diff_object_associations(bullet_key, associations)
79
- potential_associations = associations - call_associations(bullet_key, associations)
80
- potential_associations.reject { |a| a.is_a?(Hash) }
76
+ def call_associations(bullet_key, associations)
77
+ all = Set.new
78
+ eager_loadings.similarly_associated(bullet_key, associations).each do |related_bullet_key|
79
+ coa = call_object_associations[related_bullet_key]
80
+ next if coa.nil?
81
+
82
+ all.merge coa
81
83
  end
84
+ all.to_a
85
+ end
86
+
87
+ def diff_object_associations(bullet_key, associations)
88
+ potential_associations = associations - call_associations(bullet_key, associations)
89
+ potential_associations.reject { |a| a.is_a?(Hash) }
90
+ end
82
91
  end
83
92
  end
84
93
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bullet
2
4
  module Detector
3
5
  autoload :Base, 'bullet/detector/base'
@@ -1,15 +1,19 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Object
2
4
  def bullet_key
3
- "#{self.class}:#{self.primary_key_value}"
5
+ "#{self.class}:#{bullet_primary_key_value}"
4
6
  end
5
7
 
6
- def primary_key_value
8
+ def bullet_primary_key_value
9
+ return if respond_to?(:persisted?) && !persisted?
10
+
7
11
  if self.class.respond_to?(:primary_keys) && self.class.primary_keys
8
- self.class.primary_keys.map { |primary_key| self.send primary_key }.join(',')
12
+ self.class.primary_keys.map { |primary_key| send primary_key }.join(',')
9
13
  elsif self.class.respond_to?(:primary_key) && self.class.primary_key
10
- self.send self.class.primary_key
14
+ send self.class.primary_key
11
15
  else
12
- self.id
16
+ id
13
17
  end
14
18
  end
15
19
  end
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class String
2
4
  def bullet_class_name
3
- self.sub(/:[^:]*?$/, "")
5
+ sub(/:[^:]*?$/, '')
4
6
  end
5
7
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bullet
2
4
  module Mongoid
3
5
  def self.enable
@@ -21,20 +23,21 @@ module Bullet
21
23
  end
22
24
 
23
25
  def each(&block)
24
- records = query.map{ |doc| ::Mongoid::Factory.from_db(klass, doc) }
26
+ return to_enum unless block
27
+
28
+ records = []
29
+ origin_each { |record| records << record }
25
30
  if records.length > 1
26
31
  Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
27
32
  elsif records.size == 1
28
33
  Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
29
34
  end
30
- origin_each(&block)
35
+ records.each(&block)
31
36
  end
32
37
 
33
38
  def eager_load(docs)
34
39
  associations = criteria.inclusions.map(&:name)
35
- docs.each do |doc|
36
- Bullet::Detector::NPlusOneQuery.add_object_associations(doc, associations)
37
- end
40
+ docs.each { |doc| Bullet::Detector::NPlusOneQuery.add_object_associations(doc, associations) }
38
41
  Bullet::Detector::UnusedEagerLoading.add_eager_loadings(docs, associations)
39
42
  origin_eager_load(docs)
40
43
  end
@@ -45,9 +48,7 @@ module Bullet
45
48
 
46
49
  def get_relation(name, metadata, object, reload = false)
47
50
  result = origin_get_relation(name, metadata, object, reload)
48
- if metadata.macro !~ /embed/
49
- Bullet::Detector::NPlusOneQuery.call_association(self, name)
50
- end
51
+ Bullet::Detector::NPlusOneQuery.call_association(self, name) if metadata.macro !~ /embed/
51
52
  result
52
53
  end
53
54
  end