bullet 5.7.0 → 6.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 (114) hide show
  1. checksums.yaml +5 -5
  2. data/.travis.yml +2 -0
  3. data/CHANGELOG.md +27 -1
  4. data/Gemfile.mongoid-7.0 +15 -0
  5. data/Gemfile.rails-4.0 +1 -1
  6. data/Gemfile.rails-4.1 +1 -1
  7. data/Gemfile.rails-4.2 +1 -1
  8. data/Gemfile.rails-5.0 +1 -1
  9. data/Gemfile.rails-5.1 +1 -1
  10. data/Gemfile.rails-5.2 +2 -2
  11. data/Gemfile.rails-6.0 +15 -0
  12. data/Guardfile +1 -1
  13. data/README.md +10 -5
  14. data/Rakefile +5 -5
  15. data/bullet.gemspec +12 -6
  16. data/lib/bullet/active_record4.rb +10 -14
  17. data/lib/bullet/active_record41.rb +8 -14
  18. data/lib/bullet/active_record42.rb +16 -22
  19. data/lib/bullet/active_record5.rb +69 -49
  20. data/lib/bullet/active_record52.rb +40 -40
  21. data/lib/bullet/active_record60.rb +245 -0
  22. data/lib/bullet/bullet_xhr.js +58 -0
  23. data/lib/bullet/dependency.rb +19 -5
  24. data/lib/bullet/detector/association.rb +18 -16
  25. data/lib/bullet/detector/base.rb +2 -0
  26. data/lib/bullet/detector/counter_cache.rb +13 -10
  27. data/lib/bullet/detector/n_plus_one_query.rb +21 -19
  28. data/lib/bullet/detector/unused_eager_loading.rb +27 -22
  29. data/lib/bullet/detector.rb +2 -0
  30. data/lib/bullet/ext/object.rb +9 -5
  31. data/lib/bullet/ext/string.rb +3 -1
  32. data/lib/bullet/mongoid4x.rb +7 -2
  33. data/lib/bullet/mongoid5x.rb +7 -2
  34. data/lib/bullet/mongoid6x.rb +11 -6
  35. data/lib/bullet/mongoid7x.rb +61 -0
  36. data/lib/bullet/notification/base.rb +21 -15
  37. data/lib/bullet/notification/counter_cache.rb +2 -0
  38. data/lib/bullet/notification/n_plus_one_query.rb +6 -4
  39. data/lib/bullet/notification/unused_eager_loading.rb +6 -4
  40. data/lib/bullet/notification.rb +2 -0
  41. data/lib/bullet/notification_collector.rb +2 -1
  42. data/lib/bullet/rack.rb +52 -31
  43. data/lib/bullet/registry/association.rb +2 -0
  44. data/lib/bullet/registry/base.rb +2 -0
  45. data/lib/bullet/registry/object.rb +2 -0
  46. data/lib/bullet/registry.rb +2 -0
  47. data/lib/bullet/stack_trace_filter.rb +57 -21
  48. data/lib/bullet/version.rb +2 -1
  49. data/lib/bullet.rb +48 -31
  50. data/lib/generators/bullet/install_generator.rb +5 -3
  51. data/perf/benchmark.rb +9 -7
  52. data/rails/init.rb +2 -0
  53. data/spec/bullet/detector/association_spec.rb +2 -0
  54. data/spec/bullet/detector/base_spec.rb +2 -0
  55. data/spec/bullet/detector/counter_cache_spec.rb +2 -0
  56. data/spec/bullet/detector/n_plus_one_query_spec.rb +33 -8
  57. data/spec/bullet/detector/unused_eager_loading_spec.rb +2 -0
  58. data/spec/bullet/ext/object_spec.rb +12 -5
  59. data/spec/bullet/ext/string_spec.rb +2 -0
  60. data/spec/bullet/notification/base_spec.rb +8 -6
  61. data/spec/bullet/notification/counter_cache_spec.rb +3 -1
  62. data/spec/bullet/notification/n_plus_one_query_spec.rb +3 -1
  63. data/spec/bullet/notification/unused_eager_loading_spec.rb +3 -1
  64. data/spec/bullet/notification_collector_spec.rb +2 -0
  65. data/spec/bullet/rack_spec.rb +22 -12
  66. data/spec/bullet/registry/association_spec.rb +4 -2
  67. data/spec/bullet/registry/base_spec.rb +5 -3
  68. data/spec/bullet/registry/object_spec.rb +2 -0
  69. data/spec/bullet_spec.rb +9 -7
  70. data/spec/integration/active_record/association_spec.rb +70 -19
  71. data/spec/integration/counter_cache_spec.rb +5 -3
  72. data/spec/integration/mongoid/association_spec.rb +2 -0
  73. data/spec/models/address.rb +2 -0
  74. data/spec/models/author.rb +2 -0
  75. data/spec/models/base_user.rb +2 -0
  76. data/spec/models/category.rb +2 -0
  77. data/spec/models/city.rb +2 -0
  78. data/spec/models/client.rb +4 -0
  79. data/spec/models/comment.rb +2 -0
  80. data/spec/models/company.rb +2 -0
  81. data/spec/models/country.rb +2 -0
  82. data/spec/models/document.rb +2 -0
  83. data/spec/models/entry.rb +2 -0
  84. data/spec/models/firm.rb +3 -0
  85. data/spec/models/folder.rb +2 -0
  86. data/spec/models/group.rb +4 -0
  87. data/spec/models/mongoid/address.rb +3 -1
  88. data/spec/models/mongoid/category.rb +4 -2
  89. data/spec/models/mongoid/comment.rb +3 -1
  90. data/spec/models/mongoid/company.rb +3 -1
  91. data/spec/models/mongoid/entry.rb +3 -1
  92. data/spec/models/mongoid/post.rb +6 -4
  93. data/spec/models/mongoid/user.rb +2 -0
  94. data/spec/models/newspaper.rb +2 -0
  95. data/spec/models/page.rb +2 -0
  96. data/spec/models/person.rb +2 -0
  97. data/spec/models/pet.rb +2 -0
  98. data/spec/models/post.rb +17 -0
  99. data/spec/models/relationship.rb +2 -0
  100. data/spec/models/reply.rb +2 -0
  101. data/spec/models/student.rb +2 -0
  102. data/spec/models/submission.rb +2 -0
  103. data/spec/models/teacher.rb +2 -0
  104. data/spec/models/user.rb +2 -0
  105. data/spec/models/writer.rb +2 -0
  106. data/spec/spec_helper.rb +3 -1
  107. data/spec/support/bullet_ext.rb +2 -0
  108. data/spec/support/mongo_seed.rb +32 -30
  109. data/spec/support/rack_double.rb +9 -16
  110. data/spec/support/sqlite_seed.rb +84 -75
  111. data/tasks/bullet_tasks.rake +2 -0
  112. data/test.sh +2 -0
  113. data/update.sh +1 -0
  114. metadata +17 -9
@@ -0,0 +1,58 @@
1
+ (function() {
2
+ var oldOpen = window.XMLHttpRequest.prototype.open;
3
+ var oldSend = window.XMLHttpRequest.prototype.send;
4
+ function newOpen(method, url, async, user, password) {
5
+ this._storedUrl = url;
6
+ return oldOpen.apply(this, arguments);
7
+ }
8
+ function newSend(data) {
9
+ if (this.onload) {
10
+ this._storedOnload = this.onload;
11
+ }
12
+ this.onload = newOnload;
13
+ return oldSend.apply(this, arguments);
14
+ }
15
+ function newOnload() {
16
+ if (
17
+ this._storedUrl.startsWith(
18
+ window.location.protocol + '//' + window.location.host,
19
+ ) ||
20
+ !this._storedUrl.startsWith('http') // For relative paths
21
+ ) {
22
+ var bulletFooterText = this.getResponseHeader('X-bullet-footer-text');
23
+ if (bulletFooterText) {
24
+ setTimeout(() => {
25
+ var oldHtml = document
26
+ .getElementById('bullet-footer')
27
+ .innerHTML.split('<br>');
28
+ var header = oldHtml[0];
29
+ oldHtml = oldHtml.slice(1, oldHtml.length);
30
+ var newHtml = oldHtml.concat(JSON.parse(bulletFooterText));
31
+ newHtml = newHtml.slice(newHtml.length - 10, newHtml.length); // rotate through 10 most recent
32
+ document.getElementById(
33
+ 'bullet-footer',
34
+ ).innerHTML = `${header}<br>${newHtml.join('<br>')}`;
35
+ }, 0);
36
+ }
37
+ var bulletConsoleText = this.getResponseHeader('X-bullet-console-text');
38
+ if (bulletConsoleText && typeof console !== 'undefined' && console.log) {
39
+ setTimeout(() => {
40
+ JSON.parse(bulletConsoleText).forEach(message => {
41
+ if (console.groupCollapsed && console.groupEnd) {
42
+ console.groupCollapsed('Uniform Notifier');
43
+ console.log(message);
44
+ console.groupEnd();
45
+ } else {
46
+ console.log(message);
47
+ }
48
+ });
49
+ }, 0);
50
+ }
51
+ }
52
+ if (this._storedOnload) {
53
+ return this._storedOnload.apply(this, arguments);
54
+ }
55
+ }
56
+ window.XMLHttpRequest.prototype.open = newOpen;
57
+ window.XMLHttpRequest.prototype.send = newSend;
58
+ })();
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bullet
2
4
  module Dependency
3
5
  def mongoid?
@@ -8,10 +10,6 @@ module Bullet
8
10
  @active_record ||= defined? ::ActiveRecord
9
11
  end
10
12
 
11
- def rails?
12
- @rails ||= defined? ::Rails
13
- end
14
-
15
13
  def active_record_version
16
14
  @active_record_version ||= begin
17
15
  if active_record40?
@@ -26,8 +24,10 @@ module Bullet
26
24
  'active_record5'
27
25
  elsif active_record52?
28
26
  'active_record52'
27
+ elsif active_record60?
28
+ 'active_record60'
29
29
  else
30
- raise "Bullet does not support active_record #{::ActiveRecord::VERSION} yet"
30
+ raise "Bullet does not support active_record #{::ActiveRecord::VERSION::STRING} yet"
31
31
  end
32
32
  end
33
33
  end
@@ -40,6 +40,8 @@ module Bullet
40
40
  'mongoid5x'
41
41
  elsif mongoid6x?
42
42
  'mongoid6x'
43
+ elsif mongoid7x?
44
+ 'mongoid7x'
43
45
  else
44
46
  raise "Bullet does not support mongoid #{::Mongoid::VERSION} yet"
45
47
  end
@@ -54,6 +56,10 @@ module Bullet
54
56
  active_record? && ::ActiveRecord::VERSION::MAJOR == 5
55
57
  end
56
58
 
59
+ def active_record6?
60
+ active_record? && ::ActiveRecord::VERSION::MAJOR == 6
61
+ end
62
+
57
63
  def active_record40?
58
64
  active_record4? && ::ActiveRecord::VERSION::MINOR == 0
59
65
  end
@@ -78,6 +84,10 @@ module Bullet
78
84
  active_record5? && ::ActiveRecord::VERSION::MINOR == 2
79
85
  end
80
86
 
87
+ def active_record60?
88
+ active_record6? && ::ActiveRecord::VERSION::MINOR == 0
89
+ end
90
+
81
91
  def mongoid4x?
82
92
  mongoid? && ::Mongoid::VERSION =~ /\A4/
83
93
  end
@@ -89,5 +99,9 @@ module Bullet
89
99
  def mongoid6x?
90
100
  mongoid? && ::Mongoid::VERSION =~ /\A6/
91
101
  end
102
+
103
+ def mongoid7x?
104
+ mongoid? && ::Mongoid::VERSION =~ /\A7/
105
+ end
92
106
  end
93
107
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bullet
2
4
  module Detector
3
5
  class Association < Base
@@ -5,18 +7,18 @@ module Bullet
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'.freeze, "object: #{object.bullet_key}, associations: #{associations}")
12
+ Bullet.debug('Detector::Association#add_object_associations', "object: #{object.bullet_key}, associations: #{associations}")
11
13
  object_associations.add(object.bullet_key, associations)
12
14
  end
13
15
 
14
16
  def add_call_object_associations(object, associations)
15
17
  return unless Bullet.start?
16
18
  return if !Bullet.n_plus_one_query_enable? && !Bullet.unused_eager_loading_enable?
17
- return unless object.primary_key_value
19
+ return unless object.bullet_primary_key_value
18
20
 
19
- Bullet.debug('Detector::Association#add_call_object_associations'.freeze, "object: #{object.bullet_key}, associations: #{associations}")
21
+ Bullet.debug('Detector::Association#add_call_object_associations', "object: #{object.bullet_key}, associations: #{associations}")
20
22
  call_object_associations.add(object.bullet_key, associations)
21
23
  end
22
24
 
@@ -43,31 +45,31 @@ module Bullet
43
45
  # e.g. { "Post:1" => [:comments] }
44
46
  # the object_associations keep all associations that may be or may no be
45
47
  # unpreload associations or unused preload associations.
46
- def object_associations
47
- Thread.current[:bullet_object_associations]
48
- end
48
+ def object_associations
49
+ Thread.current[:bullet_object_associations]
50
+ end
49
51
 
50
52
  # call_object_associations keep the object relationships
51
53
  # that object.associations is called.
52
54
  # e.g. { "Post:1" => [:comments] }
53
55
  # they are used to detect unused preload associations.
54
- def call_object_associations
55
- Thread.current[:bullet_call_object_associations]
56
- end
56
+ def call_object_associations
57
+ Thread.current[:bullet_call_object_associations]
58
+ end
57
59
 
58
60
  # inversed_objects keeps object relationships
59
61
  # that association is inversed.
60
62
  # e.g. { "Comment:1" => ["post"] }
61
- def inversed_objects
62
- Thread.current[:bullet_inversed_objects]
63
- end
63
+ def inversed_objects
64
+ Thread.current[:bullet_inversed_objects]
65
+ end
64
66
 
65
67
  # eager_loadings keep the object relationships
66
68
  # that the associations are preloaded by find :include.
67
69
  # e.g. { ["Post:1", "Post:2"] => [:comments, :user] }
68
- def eager_loadings
69
- Thread.current[:bullet_eager_loadings]
70
- end
70
+ def eager_loadings
71
+ Thread.current[:bullet_eager_loadings]
72
+ end
71
73
  end
72
74
  end
73
75
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bullet
2
4
  module Detector
3
5
  class Base
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bullet
2
4
  module Detector
3
5
  class CounterCache < Base
@@ -5,7 +7,7 @@ module Bullet
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
12
  Bullet.debug('Detector::CounterCache#add_counter_cache', "object: #{object.bullet_key}, associations: #{associations}")
11
13
  if conditions_met?(object, associations)
@@ -16,8 +18,9 @@ module Bullet
16
18
  def add_possible_objects(object_or_objects)
17
19
  return unless Bullet.start?
18
20
  return unless Bullet.counter_cache_enable?
21
+
19
22
  objects = Array(object_or_objects)
20
- return if objects.map(&:primary_key_value).compact.empty?
23
+ return if objects.map(&:bullet_primary_key_value).compact.empty?
21
24
 
22
25
  Bullet.debug('Detector::CounterCache#add_possible_objects', "objects: #{objects.map(&:bullet_key).join(', ')}")
23
26
  objects.each { |object| possible_objects.add object.bullet_key }
@@ -26,13 +29,13 @@ module Bullet
26
29
  def add_impossible_object(object)
27
30
  return unless Bullet.start?
28
31
  return unless Bullet.counter_cache_enable?
29
- return unless object.primary_key_value
32
+ return unless object.bullet_primary_key_value
30
33
 
31
34
  Bullet.debug('Detector::CounterCache#add_impossible_object', "object: #{object.bullet_key}")
32
35
  impossible_objects.add object.bullet_key
33
36
  end
34
37
 
35
- def conditions_met?(object, associations)
38
+ def conditions_met?(object, _associations)
36
39
  possible_objects.include?(object.bullet_key) && !impossible_objects.include?(object.bullet_key)
37
40
  end
38
41
 
@@ -46,14 +49,14 @@ module Bullet
46
49
 
47
50
  private
48
51
 
49
- def create_notification(klazz, associations)
50
- notify_associations = Array(associations) - Bullet.get_whitelist_associations(:counter_cache, klazz)
52
+ def create_notification(klazz, associations)
53
+ notify_associations = Array(associations) - Bullet.get_whitelist_associations(:counter_cache, klazz)
51
54
 
52
- if notify_associations.present?
53
- notice = Bullet::Notification::CounterCache.new klazz, notify_associations
54
- Bullet.notification_collector.add notice
55
- end
55
+ if notify_associations.present?
56
+ notice = Bullet::Notification::CounterCache.new klazz, notify_associations
57
+ Bullet.notification_collector.add notice
56
58
  end
59
+ end
57
60
  end
58
61
  end
59
62
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bullet
2
4
  module Detector
3
5
  class NPlusOneQuery < Association
@@ -12,11 +14,12 @@ module Bullet
12
14
  def call_association(object, associations)
13
15
  return unless Bullet.start?
14
16
  return unless Bullet.n_plus_one_query_enable?
15
- return unless object.primary_key_value
17
+ return unless object.bullet_primary_key_value
16
18
  return if inversed_objects.include?(object.bullet_key, associations)
19
+
17
20
  add_call_object_associations(object, associations)
18
21
 
19
- Bullet.debug('Detector::NPlusOneQuery#call_association'.freeze, "object: #{object.bullet_key}, associations: #{associations}")
22
+ Bullet.debug('Detector::NPlusOneQuery#call_association', "object: #{object.bullet_key}, associations: #{associations}")
20
23
  if !excluded_stacktrace_path? && conditions_met?(object, associations)
21
24
  Bullet.debug('detect n + 1 query', "object: #{object.bullet_key}, associations: #{associations}")
22
25
  create_notification caller_in_project, object.class.to_s, associations
@@ -26,28 +29,29 @@ module Bullet
26
29
  def add_possible_objects(object_or_objects)
27
30
  return unless Bullet.start?
28
31
  return unless Bullet.n_plus_one_query_enable?
32
+
29
33
  objects = Array(object_or_objects)
30
- return if objects.map(&:primary_key_value).compact.empty?
34
+ return if objects.map(&:bullet_primary_key_value).compact.empty?
31
35
 
32
- Bullet.debug('Detector::NPlusOneQuery#add_possible_objects'.freeze, "objects: #{objects.map(&:bullet_key).join(', '.freeze)}")
36
+ Bullet.debug('Detector::NPlusOneQuery#add_possible_objects', "objects: #{objects.map(&:bullet_key).join(', ')}")
33
37
  objects.each { |object| possible_objects.add object.bullet_key }
34
38
  end
35
39
 
36
40
  def add_impossible_object(object)
37
41
  return unless Bullet.start?
38
42
  return unless Bullet.n_plus_one_query_enable?
39
- return unless object.primary_key_value
43
+ return unless object.bullet_primary_key_value
40
44
 
41
- Bullet.debug('Detector::NPlusOneQuery#add_impossible_object'.freeze, "object: #{object.bullet_key}")
45
+ Bullet.debug('Detector::NPlusOneQuery#add_impossible_object', "object: #{object.bullet_key}")
42
46
  impossible_objects.add object.bullet_key
43
47
  end
44
48
 
45
49
  def add_inversed_object(object, association)
46
50
  return unless Bullet.start?
47
51
  return unless Bullet.n_plus_one_query_enable?
48
- return unless object.primary_key_value
52
+ return unless object.bullet_primary_key_value
49
53
 
50
- Bullet.debug('Detector::NPlusOneQuery#add_inversed_object'.freeze, "object: #{object.bullet_key}, association: #{association}")
54
+ Bullet.debug('Detector::NPlusOneQuery#add_inversed_object', "object: #{object.bullet_key}, association: #{association}")
51
55
  inversed_objects.add object.bullet_key, association
52
56
  end
53
57
 
@@ -67,14 +71,12 @@ module Bullet
67
71
  # check if object => associations already exists in object_associations.
68
72
  def association?(object, associations)
69
73
  value = object_associations[object.bullet_key]
70
- if value
71
- value.each do |v|
74
+ value&.each do |v|
72
75
  # associations == v comparison order is important here because
73
76
  # v variable might be a squeel node where :== method is redefined,
74
77
  # so it does not compare values at all and return unexpected results
75
- result = v.is_a?(Hash) ? v.key?(associations) : associations == v
76
- return true if result
77
- end
78
+ result = v.is_a?(Hash) ? v.key?(associations) : associations == v
79
+ return true if result
78
80
  end
79
81
 
80
82
  false
@@ -82,14 +84,14 @@ module Bullet
82
84
 
83
85
  private
84
86
 
85
- def create_notification(callers, klazz, associations)
86
- notify_associations = Array(associations) - Bullet.get_whitelist_associations(:n_plus_one_query, klazz)
87
+ def create_notification(callers, klazz, associations)
88
+ notify_associations = Array(associations) - Bullet.get_whitelist_associations(:n_plus_one_query, klazz)
87
89
 
88
- if notify_associations.present?
89
- notice = Bullet::Notification::NPlusOneQuery.new(callers, klazz, notify_associations)
90
- Bullet.notification_collector.add(notice)
91
- end
90
+ if notify_associations.present?
91
+ notice = Bullet::Notification::NPlusOneQuery.new(callers, klazz, notify_associations)
92
+ Bullet.notification_collector.add(notice)
92
93
  end
94
+ end
93
95
  end
94
96
  end
95
97
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bullet
2
4
  module Detector
3
5
  class UnusedEagerLoading < Association
@@ -25,18 +27,20 @@ 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
32
  Bullet.debug('Detector::UnusedEagerLoading#add_eager_loadings', "objects: #{objects.map(&:bullet_key).join(', ')}, associations: #{associations}")
31
33
  bullet_keys = objects.map(&:bullet_key)
32
34
 
33
- to_add, to_merge, to_delete = [], [], []
34
- eager_loadings.each do |k, v|
35
+ to_add = []
36
+ to_merge = []
37
+ to_delete = []
38
+ eager_loadings.each do |k, _v|
35
39
  key_objects_overlap = k & bullet_keys
36
40
 
37
41
  next if key_objects_overlap.empty?
38
42
 
39
- bullet_keys = bullet_keys - k
43
+ bullet_keys -= k
40
44
  if key_objects_overlap == k
41
45
  to_add << [k, associations]
42
46
  else
@@ -57,29 +61,30 @@ module Bullet
57
61
 
58
62
  private
59
63
 
60
- def create_notification(callers, klazz, associations)
61
- notify_associations = Array(associations) - Bullet.get_whitelist_associations(:unused_eager_loading, klazz)
64
+ def create_notification(callers, klazz, associations)
65
+ notify_associations = Array(associations) - Bullet.get_whitelist_associations(:unused_eager_loading, klazz)
62
66
 
63
- if notify_associations.present?
64
- notice = Bullet::Notification::UnusedEagerLoading.new(callers, klazz, notify_associations)
65
- Bullet.notification_collector.add(notice)
66
- end
67
+ if notify_associations.present?
68
+ notice = Bullet::Notification::UnusedEagerLoading.new(callers, klazz, notify_associations)
69
+ Bullet.notification_collector.add(notice)
67
70
  end
71
+ end
68
72
 
69
- def call_associations(bullet_key, associations)
70
- all = Set.new
71
- eager_loadings.similarly_associated(bullet_key, associations).each do |related_bullet_key|
72
- coa = call_object_associations[related_bullet_key]
73
- next if coa.nil?
74
- all.merge coa
75
- end
76
- all.to_a
77
- end
73
+ def call_associations(bullet_key, associations)
74
+ all = Set.new
75
+ eager_loadings.similarly_associated(bullet_key, associations).each do |related_bullet_key|
76
+ coa = call_object_associations[related_bullet_key]
77
+ next if coa.nil?
78
78
 
79
- def diff_object_associations(bullet_key, associations)
80
- potential_associations = associations - call_associations(bullet_key, associations)
81
- potential_associations.reject { |a| a.is_a?(Hash) }
79
+ all.merge coa
82
80
  end
81
+ all.to_a
82
+ end
83
+
84
+ def diff_object_associations(bullet_key, associations)
85
+ potential_associations = associations - call_associations(bullet_key, associations)
86
+ potential_associations.reject { |a| a.is_a?(Hash) }
87
+ end
83
88
  end
84
89
  end
85
90
  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(','.freeze)
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(/:[^:]*?$/, ''.freeze)
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,13 +23,16 @@ 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_given?
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)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bullet
2
4
  module Mongoid
3
5
  def self.enable
@@ -21,13 +23,16 @@ module Bullet
21
23
  end
22
24
 
23
25
  def each(&block)
24
- records = view.map { |doc| ::Mongoid::Factory.from_db(klass, doc) }
26
+ return to_enum unless block_given?
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)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bullet
2
4
  module Mongoid
3
5
  def self.enable
@@ -8,26 +10,29 @@ module Bullet
8
10
  alias_method :origin_each, :each
9
11
  alias_method :origin_eager_load, :eager_load
10
12
 
11
- def first
12
- result = origin_first
13
+ def first(opt = {})
14
+ result = origin_first(opt)
13
15
  Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
14
16
  result
15
17
  end
16
18
 
17
- def last
18
- result = origin_last
19
+ def last(opt = {})
20
+ result = origin_last(opt)
19
21
  Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
20
22
  result
21
23
  end
22
24
 
23
25
  def each(&block)
24
- records = view.map { |doc| ::Mongoid::Factory.from_db(klass, doc) }
26
+ return to_enum unless block_given?
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)
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bullet
4
+ module Mongoid
5
+ def self.enable
6
+ require 'mongoid'
7
+ ::Mongoid::Contextual::Mongo.class_eval do
8
+ alias_method :origin_first, :first
9
+ alias_method :origin_last, :last
10
+ alias_method :origin_each, :each
11
+ alias_method :origin_eager_load, :eager_load
12
+
13
+ def first(opts = {})
14
+ result = origin_first(opts)
15
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
16
+ result
17
+ end
18
+
19
+ def last(opts = {})
20
+ result = origin_last(opts)
21
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
22
+ result
23
+ end
24
+
25
+ def each(&block)
26
+ return to_enum unless block_given?
27
+
28
+ records = []
29
+ origin_each { |record| records << record }
30
+ if records.length > 1
31
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
32
+ elsif records.size == 1
33
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
34
+ end
35
+ records.each(&block)
36
+ end
37
+
38
+ def eager_load(docs)
39
+ associations = criteria.inclusions.map(&:name)
40
+ docs.each do |doc|
41
+ Bullet::Detector::NPlusOneQuery.add_object_associations(doc, associations)
42
+ end
43
+ Bullet::Detector::UnusedEagerLoading.add_eager_loadings(docs, associations)
44
+ origin_eager_load(docs)
45
+ end
46
+ end
47
+
48
+ ::Mongoid::Association::Accessors.class_eval do
49
+ alias_method :origin_get_relation, :get_relation
50
+
51
+ def get_relation(name, association, object, reload = false)
52
+ result = origin_get_relation(name, association, object, reload)
53
+ unless association.embedded?
54
+ Bullet::Detector::NPlusOneQuery.call_association(self, name)
55
+ end
56
+ result
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end