bullet 5.5.0 → 5.8.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 (109) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -5
  3. data/CHANGELOG.md +31 -1
  4. data/Gemfile +6 -1
  5. data/Gemfile.rails-5.0 +1 -1
  6. data/Gemfile.rails-5.1 +15 -0
  7. data/Gemfile.rails-5.2 +15 -0
  8. data/Guardfile +2 -2
  9. data/Hacking.md +1 -1
  10. data/README.md +20 -23
  11. data/Rakefile +20 -21
  12. data/bullet.gemspec +20 -15
  13. data/lib/bullet/active_record4.rb +18 -14
  14. data/lib/bullet/active_record41.rb +16 -14
  15. data/lib/bullet/active_record42.rb +24 -22
  16. data/lib/bullet/active_record5.rb +102 -91
  17. data/lib/bullet/active_record52.rb +232 -0
  18. data/lib/bullet/dependency.rb +9 -1
  19. data/lib/bullet/detector/association.rb +17 -14
  20. data/lib/bullet/detector/base.rb +2 -0
  21. data/lib/bullet/detector/counter_cache.rb +14 -10
  22. data/lib/bullet/detector/n_plus_one_query.rb +21 -17
  23. data/lib/bullet/detector/unused_eager_loading.rb +30 -24
  24. data/lib/bullet/detector.rb +2 -0
  25. data/lib/bullet/ext/object.rb +6 -4
  26. data/lib/bullet/ext/string.rb +3 -1
  27. data/lib/bullet/mongoid4x.rb +7 -2
  28. data/lib/bullet/mongoid5x.rb +7 -2
  29. data/lib/bullet/mongoid6x.rb +11 -6
  30. data/lib/bullet/notification/base.rb +26 -19
  31. data/lib/bullet/notification/counter_cache.rb +3 -1
  32. data/lib/bullet/notification/n_plus_one_query.rb +7 -4
  33. data/lib/bullet/notification/unused_eager_loading.rb +7 -4
  34. data/lib/bullet/notification.rb +2 -0
  35. data/lib/bullet/notification_collector.rb +2 -1
  36. data/lib/bullet/rack.rb +22 -18
  37. data/lib/bullet/registry/association.rb +2 -0
  38. data/lib/bullet/registry/base.rb +2 -0
  39. data/lib/bullet/registry/object.rb +2 -0
  40. data/lib/bullet/registry.rb +2 -0
  41. data/lib/bullet/stack_trace_filter.rb +56 -18
  42. data/lib/bullet/version.rb +3 -2
  43. data/lib/bullet.rb +43 -33
  44. data/lib/generators/bullet/install_generator.rb +48 -0
  45. data/perf/benchmark.rb +12 -12
  46. data/rails/init.rb +2 -0
  47. data/spec/bullet/detector/association_spec.rb +6 -4
  48. data/spec/bullet/detector/base_spec.rb +2 -0
  49. data/spec/bullet/detector/counter_cache_spec.rb +13 -11
  50. data/spec/bullet/detector/n_plus_one_query_spec.rb +71 -46
  51. data/spec/bullet/detector/unused_eager_loading_spec.rb +21 -19
  52. data/spec/bullet/ext/object_spec.rb +10 -8
  53. data/spec/bullet/ext/string_spec.rb +7 -5
  54. data/spec/bullet/notification/base_spec.rb +39 -38
  55. data/spec/bullet/notification/counter_cache_spec.rb +5 -3
  56. data/spec/bullet/notification/n_plus_one_query_spec.rb +5 -3
  57. data/spec/bullet/notification/unused_eager_loading_spec.rb +4 -2
  58. data/spec/bullet/notification_collector_spec.rb +12 -10
  59. data/spec/bullet/rack_spec.rb +48 -47
  60. data/spec/bullet/registry/association_spec.rb +12 -10
  61. data/spec/bullet/registry/base_spec.rb +22 -20
  62. data/spec/bullet/registry/object_spec.rb +6 -4
  63. data/spec/bullet_spec.rb +24 -3
  64. data/spec/integration/{active_record4 → active_record}/association_spec.rb +176 -114
  65. data/spec/integration/counter_cache_spec.rb +15 -13
  66. data/spec/integration/mongoid/association_spec.rb +35 -33
  67. data/spec/models/address.rb +2 -0
  68. data/spec/models/author.rb +2 -0
  69. data/spec/models/base_user.rb +2 -0
  70. data/spec/models/category.rb +2 -0
  71. data/spec/models/city.rb +2 -0
  72. data/spec/models/client.rb +4 -0
  73. data/spec/models/comment.rb +3 -1
  74. data/spec/models/company.rb +2 -0
  75. data/spec/models/country.rb +2 -0
  76. data/spec/models/document.rb +4 -2
  77. data/spec/models/entry.rb +2 -0
  78. data/spec/models/firm.rb +3 -0
  79. data/spec/models/folder.rb +2 -0
  80. data/spec/models/group.rb +4 -0
  81. data/spec/models/mongoid/address.rb +3 -1
  82. data/spec/models/mongoid/category.rb +4 -2
  83. data/spec/models/mongoid/comment.rb +3 -1
  84. data/spec/models/mongoid/company.rb +3 -1
  85. data/spec/models/mongoid/entry.rb +3 -1
  86. data/spec/models/mongoid/post.rb +6 -4
  87. data/spec/models/mongoid/user.rb +2 -0
  88. data/spec/models/newspaper.rb +3 -1
  89. data/spec/models/page.rb +2 -0
  90. data/spec/models/person.rb +2 -0
  91. data/spec/models/pet.rb +2 -0
  92. data/spec/models/post.rb +17 -0
  93. data/spec/models/relationship.rb +2 -0
  94. data/spec/models/reply.rb +2 -0
  95. data/spec/models/student.rb +2 -0
  96. data/spec/models/submission.rb +2 -0
  97. data/spec/models/teacher.rb +2 -0
  98. data/spec/models/user.rb +2 -0
  99. data/spec/models/writer.rb +2 -0
  100. data/spec/spec_helper.rb +15 -13
  101. data/spec/support/bullet_ext.rb +2 -0
  102. data/spec/support/mongo_seed.rb +36 -42
  103. data/spec/support/rack_double.rb +13 -19
  104. data/spec/support/sqlite_seed.rb +84 -76
  105. data/tasks/bullet_tasks.rake +4 -2
  106. data/test.sh +2 -0
  107. data/update.sh +2 -0
  108. metadata +17 -11
  109. data/spec/integration/active_record5/association_spec.rb +0 -768
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bullet
2
4
  module Detector
3
5
  class CounterCache < Base
@@ -7,7 +9,7 @@ module Bullet
7
9
  return unless Bullet.counter_cache_enable?
8
10
  return unless object.primary_key_value
9
11
 
10
- Bullet.debug("Detector::CounterCache#add_counter_cache", "object: #{object.bullet_key}, associations: #{associations}")
12
+ Bullet.debug('Detector::CounterCache#add_counter_cache', "object: #{object.bullet_key}, associations: #{associations}")
11
13
  if conditions_met?(object, associations)
12
14
  create_notification object.class.to_s, associations
13
15
  end
@@ -16,10 +18,11 @@ 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
23
  return if objects.map(&:primary_key_value).compact.empty?
21
24
 
22
- Bullet.debug("Detector::CounterCache#add_possible_objects", "objects: #{objects.map(&:bullet_key).join(', ')}")
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 }
24
27
  end
25
28
 
@@ -28,11 +31,11 @@ module Bullet
28
31
  return unless Bullet.counter_cache_enable?
29
32
  return unless object.primary_key_value
30
33
 
31
- Bullet.debug("Detector::CounterCache#add_impossible_object", "object: #{object.bullet_key}")
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
 
@@ -45,14 +48,15 @@ module Bullet
45
48
  end
46
49
 
47
50
  private
48
- def create_notification(klazz, associations)
49
- notify_associations = Array(associations) - Bullet.get_whitelist_associations(:counter_cache, klazz)
50
51
 
51
- if notify_associations.present?
52
- notice = Bullet::Notification::CounterCache.new klazz, notify_associations
53
- Bullet.notification_collector.add notice
54
- end
52
+ def create_notification(klazz, associations)
53
+ notify_associations = Array(associations) - Bullet.get_whitelist_associations(:counter_cache, klazz)
54
+
55
+ if notify_associations.present?
56
+ notice = Bullet::Notification::CounterCache.new klazz, notify_associations
57
+ Bullet.notification_collector.add notice
55
58
  end
59
+ end
56
60
  end
57
61
  end
58
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
@@ -11,13 +13,15 @@ module Bullet
11
13
  # if it is, keeps this unpreload associations and caller.
12
14
  def call_association(object, associations)
13
15
  return unless Bullet.start?
16
+ return unless Bullet.n_plus_one_query_enable?
14
17
  return unless object.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('Detector::NPlusOneQuery#call_association', "object: #{object.bullet_key}, associations: #{associations}")
19
23
  if !excluded_stacktrace_path? && conditions_met?(object, associations)
20
- Bullet.debug("detect n + 1 query", "object: #{object.bullet_key}, associations: #{associations}")
24
+ Bullet.debug('detect n + 1 query', "object: #{object.bullet_key}, associations: #{associations}")
21
25
  create_notification caller_in_project, object.class.to_s, associations
22
26
  end
23
27
  end
@@ -25,10 +29,11 @@ module Bullet
25
29
  def add_possible_objects(object_or_objects)
26
30
  return unless Bullet.start?
27
31
  return unless Bullet.n_plus_one_query_enable?
32
+
28
33
  objects = Array(object_or_objects)
29
34
  return if objects.map(&:primary_key_value).compact.empty?
30
35
 
31
- Bullet.debug("Detector::NPlusOneQuery#add_possible_objects", "objects: #{objects.map(&:bullet_key).join(', ')}")
36
+ Bullet.debug('Detector::NPlusOneQuery#add_possible_objects', "objects: #{objects.map(&:bullet_key).join(', ')}")
32
37
  objects.each { |object| possible_objects.add object.bullet_key }
33
38
  end
34
39
 
@@ -37,7 +42,7 @@ module Bullet
37
42
  return unless Bullet.n_plus_one_query_enable?
38
43
  return unless object.primary_key_value
39
44
 
40
- Bullet.debug("Detector::NPlusOneQuery#add_impossible_object", "object: #{object.bullet_key}")
45
+ Bullet.debug('Detector::NPlusOneQuery#add_impossible_object', "object: #{object.bullet_key}")
41
46
  impossible_objects.add object.bullet_key
42
47
  end
43
48
 
@@ -46,7 +51,7 @@ module Bullet
46
51
  return unless Bullet.n_plus_one_query_enable?
47
52
  return unless object.primary_key_value
48
53
 
49
- Bullet.debug("Detector::NPlusOneQuery#add_inversed_object", "object: #{object.bullet_key}, association: #{association}")
54
+ Bullet.debug('Detector::NPlusOneQuery#add_inversed_object', "object: #{object.bullet_key}, association: #{association}")
50
55
  inversed_objects.add object.bullet_key, association
51
56
  end
52
57
 
@@ -66,28 +71,27 @@ module Bullet
66
71
  # check if object => associations already exists in object_associations.
67
72
  def association?(object, associations)
68
73
  value = object_associations[object.bullet_key]
69
- if value
70
- value.each do |v|
71
- # associations == v comparision order is important here because
74
+ value&.each do |v|
75
+ # associations == v comparison order is important here because
72
76
  # v variable might be a squeel node where :== method is redefined,
73
77
  # 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
78
+ result = v.is_a?(Hash) ? v.key?(associations) : associations == v
79
+ return true if result
77
80
  end
78
81
 
79
82
  false
80
83
  end
81
84
 
82
85
  private
83
- def create_notification(callers, klazz, associations)
84
- notify_associations = Array(associations) - Bullet.get_whitelist_associations(:n_plus_one_query, klazz)
85
86
 
86
- if notify_associations.present?
87
- notice = Bullet::Notification::NPlusOneQuery.new(callers, klazz, notify_associations)
88
- Bullet.notification_collector.add(notice)
89
- end
87
+ def create_notification(callers, klazz, associations)
88
+ notify_associations = Array(associations) - Bullet.get_whitelist_associations(:n_plus_one_query, klazz)
89
+
90
+ if notify_associations.present?
91
+ notice = Bullet::Notification::NPlusOneQuery.new(callers, klazz, notify_associations)
92
+ Bullet.notification_collector.add(notice)
90
93
  end
94
+ end
91
95
  end
92
96
  end
93
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
@@ -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
@@ -27,20 +29,22 @@ module Bullet
27
29
  return unless Bullet.unused_eager_loading_enable?
28
30
  return if objects.map(&: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('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
43
- to_merge << [key_objects_overlap, ( eager_loadings[k].dup << associations )]
47
+ to_merge << [key_objects_overlap, (eager_loadings[k].dup << associations)]
44
48
 
45
49
  keys_without_objects = k - key_objects_overlap
46
50
  to_merge << [keys_without_objects, eager_loadings[k]]
@@ -56,29 +60,31 @@ module Bullet
56
60
  end
57
61
 
58
62
  private
59
- def create_notification(callers, klazz, associations)
60
- notify_associations = Array(associations) - Bullet.get_whitelist_associations(:unused_eager_loading, klazz)
61
63
 
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
64
+ def create_notification(callers, klazz, associations)
65
+ notify_associations = Array(associations) - Bullet.get_whitelist_associations(:unused_eager_loading, klazz)
67
66
 
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
67
+ if notify_associations.present?
68
+ notice = Bullet::Notification::UnusedEagerLoading.new(callers, klazz, notify_associations)
69
+ Bullet.notification_collector.add(notice)
76
70
  end
71
+ end
77
72
 
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) }
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
+
79
+ all.merge coa
81
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
82
88
  end
83
89
  end
84
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,17 @@
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}:#{primary_key_value}"
4
6
  end
5
7
 
6
8
  def primary_key_value
7
9
  if self.class.respond_to?(:primary_keys) && self.class.primary_keys
8
- self.class.primary_keys.map { |primary_key| self.send primary_key }.join(',')
10
+ self.class.primary_keys.map { |primary_key| send primary_key }.join(',')
9
11
  elsif self.class.respond_to?(:primary_key) && self.class.primary_key
10
- self.send self.class.primary_key
12
+ send self.class.primary_key
11
13
  else
12
- self.id
14
+ id
13
15
  end
14
16
  end
15
17
  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,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)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bullet
2
4
  module Notification
3
5
  class Base
@@ -6,28 +8,32 @@ module Bullet
6
8
 
7
9
  def initialize(base_class, association_or_associations, path = nil)
8
10
  @base_class = base_class
9
- @associations = association_or_associations.is_a?(Array) ? association_or_associations : [association_or_associations]
11
+ @associations = association_or_associations.is_a?(Array) ? association_or_associations : [association_or_associations]
10
12
  @path = path
11
13
  end
12
14
 
13
15
  def title
14
- raise NoMethodError.new("no method title defined")
16
+ raise NoMethodError, 'no method title defined'
15
17
  end
16
18
 
17
19
  def body
18
- raise NoMethodError.new("no method body defined")
20
+ raise NoMethodError, 'no method body defined'
19
21
  end
20
22
 
21
23
  def call_stack_messages
22
- ""
24
+ ''
23
25
  end
24
26
 
25
27
  def whoami
26
- @user ||= ENV['USER'].presence || (`whoami`.chomp rescue "")
28
+ @user ||= ENV['USER'].presence || (begin
29
+ `whoami`.chomp
30
+ rescue StandardError
31
+ ''
32
+ end)
27
33
  if @user.present?
28
34
  "user: #{@user}"
29
35
  else
30
- ""
36
+ ''
31
37
  end
32
38
  end
33
39
 
@@ -36,23 +42,23 @@ module Bullet
36
42
  end
37
43
 
38
44
  def notify_inline
39
- self.notifier.inline_notify(notification_data)
45
+ notifier.inline_notify(notification_data)
40
46
  end
41
47
 
42
48
  def notify_out_of_channel
43
- self.notifier.out_of_channel_notify(notification_data)
49
+ notifier.out_of_channel_notify(notification_data)
44
50
  end
45
51
 
46
52
  def short_notice
47
- [whoami.presence, url, title, body].compact.join(" ")
53
+ [whoami.presence, url, title, body].compact.join(' ')
48
54
  end
49
55
 
50
56
  def notification_data
51
57
  {
52
- :user => whoami,
53
- :url => url,
54
- :title => title,
55
- :body => body_with_caller,
58
+ user: whoami,
59
+ url: url,
60
+ title: title,
61
+ body: body_with_caller
56
62
  }
57
63
  end
58
64
 
@@ -65,13 +71,14 @@ module Bullet
65
71
  end
66
72
 
67
73
  protected
68
- def klazz_associations_str
69
- " #{@base_class} => [#{@associations.map(&:inspect).join(', ')}]"
70
- end
71
74
 
72
- def associations_str
73
- ":includes => #{@associations.map{ |a| a.to_s.to_sym unless a.is_a? Hash }.inspect}"
74
- end
75
+ def klazz_associations_str
76
+ " #{@base_class} => [#{@associations.map(&:inspect).join(', ')}]"
77
+ end
78
+
79
+ def associations_str
80
+ ":includes => #{@associations.map { |a| a.to_s.to_sym unless a.is_a? Hash }.inspect}"
81
+ end
75
82
  end
76
83
  end
77
84
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bullet
2
4
  module Notification
3
5
  class CounterCache < Base
@@ -6,7 +8,7 @@ module Bullet
6
8
  end
7
9
 
8
10
  def title
9
- "Need Counter Cache"
11
+ 'Need Counter Cache'
10
12
  end
11
13
  end
12
14
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bullet
2
4
  module Notification
3
5
  class NPlusOneQuery < Base
@@ -17,14 +19,15 @@ module Bullet
17
19
 
18
20
  def notification_data
19
21
  super.merge(
20
- :backtrace => []
22
+ backtrace: []
21
23
  )
22
24
  end
23
25
 
24
26
  protected
25
- def call_stack_messages
26
- (['Call stack'] + @callers).join( "\n " )
27
- end
27
+
28
+ def call_stack_messages
29
+ (['Call stack'] + @callers).join("\n ")
30
+ end
28
31
  end
29
32
  end
30
33
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bullet
2
4
  module Notification
3
5
  class UnusedEagerLoading < Base
@@ -17,14 +19,15 @@ module Bullet
17
19
 
18
20
  def notification_data
19
21
  super.merge(
20
- :backtrace => []
22
+ backtrace: []
21
23
  )
22
24
  end
23
25
 
24
26
  protected
25
- def call_stack_messages
26
- (['Call stack'] + @callers).join( "\n " )
27
- end
27
+
28
+ def call_stack_messages
29
+ (['Call stack'] + @callers).join("\n ")
30
+ end
28
31
  end
29
32
  end
30
33
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bullet
2
4
  module Notification
3
5
  autoload :Base, 'bullet/notification/base'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'set'
2
4
 
3
5
  module Bullet
@@ -21,4 +23,3 @@ module Bullet
21
23
  end
22
24
  end
23
25
  end
24
-
data/lib/bullet/rack.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bullet
2
4
  class Rack
3
5
  include Dependency
@@ -8,16 +10,17 @@ module Bullet
8
10
 
9
11
  def call(env)
10
12
  return @app.call(env) unless Bullet.enable?
13
+
11
14
  Bullet.start_request
12
15
  status, headers, response = @app.call(env)
13
16
 
14
17
  response_body = nil
15
18
  if Bullet.notification?
16
19
  if !file?(headers) && !sse?(headers) && !empty?(response) &&
17
- status == 200 && !response_body(response).frozen? && html_request?(headers, response)
20
+ status == 200 && html_request?(headers, response)
18
21
  response_body = response_body(response)
19
- append_to_html_body(response_body, footer_note) if Bullet.add_footer
20
- append_to_html_body(response_body, Bullet.gather_inline_notifications)
22
+ response_body = append_to_html_body(response_body, footer_note) if Bullet.add_footer
23
+ response_body = append_to_html_body(response_body, Bullet.gather_inline_notifications)
21
24
  headers['Content-Length'] = response_body.bytesize.to_s
22
25
  end
23
26
  Bullet.perform_out_of_channel_notifications(env)
@@ -42,28 +45,29 @@ module Bullet
42
45
  end
43
46
 
44
47
  def append_to_html_body(response_body, content)
45
- if response_body.include?('</body>')
46
- position = response_body.rindex('</body>')
47
- response_body.insert(position, content)
48
+ body = response_body.dup
49
+ if body.include?('</body>')
50
+ position = body.rindex('</body>')
51
+ body.insert(position, content)
48
52
  else
49
- response_body << content
53
+ body << content
50
54
  end
51
55
  end
52
56
 
53
57
  def footer_note
54
- "<div #{footer_div_attributes}>" + footer_close_button + Bullet.footer_info.uniq.join("<br>") + "</div>"
58
+ "<div #{footer_div_attributes}>" + footer_close_button + Bullet.footer_info.uniq.join('<br>') + '</div>'
55
59
  end
56
60
 
57
61
  def file?(headers)
58
- headers["Content-Transfer-Encoding"] == "binary"
62
+ headers['Content-Transfer-Encoding'] == 'binary' || headers['Content-Disposition']
59
63
  end
60
64
 
61
65
  def sse?(headers)
62
- headers["Content-Type"] == "text/event-stream"
66
+ headers['Content-Type'] == 'text/event-stream'
63
67
  end
64
68
 
65
69
  def html_request?(headers, response)
66
- headers['Content-Type'] && headers['Content-Type'].include?('text/html') && response_body(response).include?("<html")
70
+ headers['Content-Type']&.include?('text/html') && response_body(response).include?('<html')
67
71
  end
68
72
 
69
73
  def response_body(response)
@@ -77,13 +81,13 @@ module Bullet
77
81
  private
78
82
 
79
83
  def footer_div_attributes
80
- <<EOF
81
- data-is-bullet-footer ondblclick="this.parentNode.removeChild(this);" style="position: fixed; bottom: 0pt; left: 0pt; cursor: pointer; border-style: solid; border-color: rgb(153, 153, 153);
82
- -moz-border-top-colors: none; -moz-border-right-colors: none; -moz-border-bottom-colors: none;
83
- -moz-border-left-colors: none; -moz-border-image: none; border-width: 2pt 2pt 0px 0px;
84
- padding: 3px 5px; border-radius: 0pt 10pt 0pt 0px; background: none repeat scroll 0% 0% rgba(200, 200, 200, 0.8);
85
- color: rgb(119, 119, 119); font-size: 16px; font-family: 'Arial', sans-serif; z-index:9999;"
86
- EOF
84
+ <<~EOF
85
+ data-is-bullet-footer ondblclick="this.parentNode.removeChild(this);" style="position: fixed; bottom: 0pt; left: 0pt; cursor: pointer; border-style: solid; border-color: rgb(153, 153, 153);
86
+ -moz-border-top-colors: none; -moz-border-right-colors: none; -moz-border-bottom-colors: none;
87
+ -moz-border-left-colors: none; -moz-border-image: none; border-width: 2pt 2pt 0px 0px;
88
+ padding: 3px 5px; border-radius: 0pt 10pt 0pt 0px; background: none repeat scroll 0% 0% rgba(200, 200, 200, 0.8);
89
+ color: rgb(119, 119, 119); font-size: 16px; font-family: 'Arial', sans-serif; z-index:9999;"
90
+ EOF
87
91
  end
88
92
 
89
93
  def footer_close_button
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bullet
2
4
  module Registry
3
5
  class Association < Base
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bullet
2
4
  module Registry
3
5
  class Base