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
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bullet
2
4
  module Notification
3
5
  class Base
@@ -11,11 +13,11 @@ module Bullet
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
@@ -23,7 +25,11 @@ module Bullet
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
@@ -36,11 +42,11 @@ 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
@@ -49,10 +55,10 @@ module Bullet
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
 
@@ -66,13 +72,13 @@ module Bullet
66
72
 
67
73
  protected
68
74
 
69
- def klazz_associations_str
70
- " #{@base_class} => [#{@associations.map(&:inspect).join(', '.freeze)}]"
71
- end
75
+ def klazz_associations_str
76
+ " #{@base_class} => [#{@associations.map(&:inspect).join(', ')}]"
77
+ end
72
78
 
73
- def associations_str
74
- ":includes => #{@associations.map { |a| a.to_s.to_sym unless a.is_a? Hash }.inspect}"
75
- end
79
+ def associations_str
80
+ ":includes => #{@associations.map { |a| a.to_s.to_sym unless a.is_a? Hash }.inspect}"
81
+ end
76
82
  end
77
83
  end
78
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
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bullet
2
4
  module Notification
3
5
  class NPlusOneQuery < Base
@@ -17,15 +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
27
 
26
- def call_stack_messages
27
- (['Call stack'] + @callers).join("\n ")
28
- end
28
+ def call_stack_messages
29
+ (['Call stack'] + @callers).join("\n ")
30
+ end
29
31
  end
30
32
  end
31
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,15 +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
27
 
26
- def call_stack_messages
27
- (['Call stack'] + @callers).join("\n ")
28
- end
28
+ def call_stack_messages
29
+ (['Call stack'] + @callers).join("\n ")
30
+ end
29
31
  end
30
32
  end
31
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,17 +10,23 @@ 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
- if !file?(headers) && !sse?(headers) && !empty?(response) &&
17
- status == 200 && !response_body(response).frozen? && html_request?(headers, response)
18
- 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)
21
- headers['Content-Length'] = response_body.bytesize.to_s
19
+ if !file?(headers) && !sse?(headers) && !empty?(response) && status == 200
20
+ if html_request?(headers, response)
21
+ response_body = response_body(response)
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)
24
+ response_body = append_to_html_body(response_body, xhr_script)
25
+ headers['Content-Length'] = response_body.bytesize.to_s
26
+ else
27
+ set_header(headers, 'X-bullet-footer-text', Bullet.footer_info.uniq) if Bullet.add_footer
28
+ set_header(headers, 'X-bullet-console-text', Bullet.text_notifications) if Bullet.console_enabled?
29
+ end
22
30
  end
23
31
  Bullet.perform_out_of_channel_notifications(env)
24
32
  end
@@ -29,29 +37,32 @@ module Bullet
29
37
 
30
38
  # fix issue if response's body is a Proc
31
39
  def empty?(response)
32
- # response may be ["Not Found"], ["Move Permanently"], etc.
33
- if rails?
34
- (response.is_a?(Array) && response.size <= 1) ||
35
- !response.respond_to?(:body) ||
36
- !response_body(response).respond_to?(:empty?) ||
37
- response_body(response).empty?
38
- else
39
- body = response_body(response)
40
- body.nil? || body.empty?
41
- end
40
+ # response may be ["Not Found"], ["Move Permanently"], etc, but
41
+ # those should not happen if the status is 200
42
+ body = response_body(response)
43
+ body.nil? || body.empty?
42
44
  end
43
45
 
44
46
  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)
47
+ body = response_body.dup
48
+ if body.include?('</body>')
49
+ position = body.rindex('</body>')
50
+ body.insert(position, content)
48
51
  else
49
- response_body << content
52
+ body << content
50
53
  end
51
54
  end
52
55
 
53
56
  def footer_note
54
- "<div #{footer_div_attributes}>" + footer_close_button + Bullet.footer_info.uniq.join('<br>') + '</div>'
57
+ "<div #{footer_div_attributes}>" + footer_header + '<br>' + Bullet.footer_info.uniq.join('<br>') + '</div>'
58
+ end
59
+
60
+ def set_header(headers, header_name, header_array)
61
+ # Many proxy applications such as Nginx and AWS ELB limit
62
+ # the size a header to 8KB, so truncate the list of reports to
63
+ # be under that limit
64
+ header_array.pop while header_array.to_json.length > 8 * 1024
65
+ headers[header_name] = header_array.to_json
55
66
  end
56
67
 
57
68
  def file?(headers)
@@ -63,7 +74,7 @@ module Bullet
63
74
  end
64
75
 
65
76
  def html_request?(headers, response)
66
- headers['Content-Type'] && headers['Content-Type'].include?('text/html') && response_body(response).include?('<html')
77
+ headers['Content-Type']&.include?('text/html') && response_body(response).include?('<html')
67
78
  end
68
79
 
69
80
  def response_body(response)
@@ -77,17 +88,27 @@ module Bullet
77
88
  private
78
89
 
79
90
  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
91
+ <<~EOF
92
+ id="bullet-footer" 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);
93
+ -moz-border-top-colors: none; -moz-border-right-colors: none; -moz-border-bottom-colors: none;
94
+ -moz-border-left-colors: none; -moz-border-image: none; border-width: 2pt 2pt 0px 0px;
95
+ padding: 3px 5px; border-radius: 0pt 10pt 0pt 0px; background: none repeat scroll 0% 0% rgba(200, 200, 200, 0.8);
96
+ color: rgb(119, 119, 119); font-size: 16px; font-family: 'Arial', sans-serif; z-index:9999;"
97
+ EOF
98
+ end
99
+
100
+ def footer_header
101
+ cancel_button = "<span onclick='this.parentNode.remove()' style='position:absolute; right: 10px; top: 0px; font-weight: bold; color: #333;'>&times;</span>"
102
+ if Bullet.console_enabled?
103
+ "<span>See 'Uniform Notifier' in JS Console for Stacktrace</span>#{cancel_button}"
104
+ else
105
+ cancel_button
106
+ end
87
107
  end
88
108
 
89
- def footer_close_button
90
- "<span onclick='this.parentNode.remove()' style='position:absolute; right: 10px; top: 0px; font-weight: bold; color: #333;'>&times;</span>"
109
+ # Make footer work for XHR requests by appending data to the footer
110
+ def xhr_script
111
+ "<script type='text/javascript'>#{File.read("#{__dir__}/bullet_xhr.js")}</script>"
91
112
  end
92
113
  end
93
114
  end
@@ -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
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bullet
2
4
  module Registry
3
5
  class Object < Base
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bullet
2
4
  module Registry
3
5
  autoload :Base, 'bullet/registry/base'
@@ -1,35 +1,71 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bullet
2
4
  module StackTraceFilter
3
- VENDOR_PATH = '/vendor'.freeze
5
+ VENDOR_PATH = '/vendor'
4
6
 
5
7
  def caller_in_project
6
- app_root = rails? ? Rails.root.to_s : Dir.pwd
7
- vendor_root = app_root + VENDOR_PATH
8
- caller_locations.select do |location|
9
- caller_path = location.absolute_path.to_s
10
- caller_path.include?(app_root) && !caller_path.include?(vendor_root) ||
11
- Bullet.stacktrace_includes.any? do |include_pattern|
12
- case include_pattern
13
- when String
14
- caller_path.include?(include_pattern)
15
- when Regexp
16
- caller_path =~ include_pattern
17
- end
18
- end
8
+ vendor_root = Bullet.app_root + VENDOR_PATH
9
+ bundler_path = Bundler.bundle_path.to_s
10
+ select_caller_locations do |location|
11
+ caller_path = location_as_path(location)
12
+ caller_path.include?(Bullet.app_root) && !caller_path.include?(vendor_root) && !caller_path.include?(bundler_path) ||
13
+ Bullet.stacktrace_includes.any? { |include_pattern| pattern_matches?(location, include_pattern) }
19
14
  end
20
15
  end
21
16
 
22
17
  def excluded_stacktrace_path?
23
18
  Bullet.stacktrace_excludes.any? do |exclude_pattern|
24
- caller_in_project.any? do |caller_path|
25
- case exclude_pattern
26
- when String
27
- caller_path.include?(exclude_pattern)
28
- when Regexp
29
- caller_path =~ exclude_pattern
30
- end
19
+ caller_in_project.any? { |location| pattern_matches?(location, exclude_pattern) }
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def pattern_matches?(location, pattern)
26
+ path = location_as_path(location)
27
+ case pattern
28
+ when Array
29
+ pattern_path = pattern.first
30
+ filter = pattern.last
31
+ return false unless pattern_matches?(location, pattern_path)
32
+
33
+ case filter
34
+ when Range
35
+ filter.include?(location.lineno)
36
+ when Integer
37
+ filter == location.lineno
38
+ when String
39
+ filter == location.base_label
31
40
  end
41
+ when String
42
+ path.include?(pattern)
43
+ when Regexp
44
+ path =~ pattern
45
+ end
46
+ end
47
+
48
+ def location_as_path(location)
49
+ ruby_19? ? location : location.absolute_path.to_s
50
+ end
51
+
52
+ def select_caller_locations
53
+ if ruby_19?
54
+ caller.select do |caller_path|
55
+ yield caller_path
56
+ end
57
+ else
58
+ caller_locations.select do |location|
59
+ yield location
60
+ end
61
+ end
62
+ end
63
+
64
+ def ruby_19?
65
+ if @ruby_19.nil?
66
+ @ruby_19 = Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.0.0')
32
67
  end
68
+ @ruby_19
33
69
  end
34
70
  end
35
71
  end
@@ -1,4 +1,5 @@
1
+ # frozen_string_literal: true
1
2
 
2
3
  module Bullet
3
- VERSION = '5.7.0'.freeze
4
+ VERSION = '6.0.0'
4
5
  end
data/lib/bullet.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_support/core_ext/module/delegation'
2
4
  require 'set'
3
5
  require 'uniform_notifier'
@@ -17,8 +19,8 @@ module Bullet
17
19
  autoload :Registry, 'bullet/registry'
18
20
  autoload :NotificationCollector, 'bullet/notification_collector'
19
21
 
20
- BULLET_DEBUG = 'BULLET_DEBUG'.freeze
21
- TRUE = 'true'.freeze
22
+ BULLET_DEBUG = 'BULLET_DEBUG'
23
+ TRUE = 'true'
22
24
 
23
25
  if defined? Rails::Railtie
24
26
  class BulletRailtie < Rails::Railtie
@@ -29,21 +31,21 @@ module Bullet
29
31
  end
30
32
 
31
33
  class << self
32
- attr_writer :enable, :n_plus_one_query_enable, :unused_eager_loading_enable, :counter_cache_enable, :stacktrace_includes, :stacktrace_excludes
33
- attr_reader :notification_collector, :whitelist
34
+ attr_writer :n_plus_one_query_enable, :unused_eager_loading_enable, :counter_cache_enable, :stacktrace_includes, :stacktrace_excludes
35
+ attr_reader :whitelist
34
36
  attr_accessor :add_footer, :orm_pathches_applied
35
37
 
36
38
  available_notifiers = UniformNotifier::AVAILABLE_NOTIFIERS.map { |notifier| "#{notifier}=" }
37
- available_notifiers << { :to => UniformNotifier }
38
- delegate *available_notifiers
39
+ available_notifiers << { to: UniformNotifier }
40
+ delegate(*available_notifiers)
39
41
 
40
42
  def raise=(should_raise)
41
43
  UniformNotifier.raise = (should_raise ? Notification::UnoptimizedQueryError : false)
42
44
  end
43
45
 
44
46
  DETECTORS = [Bullet::Detector::NPlusOneQuery,
45
- Bullet::Detector::UnusedEagerLoading,
46
- Bullet::Detector::CounterCache].freeze
47
+ Bullet::Detector::UnusedEagerLoading,
48
+ Bullet::Detector::CounterCache].freeze
47
49
 
48
50
  def enable=(enable)
49
51
  @enable = @n_plus_one_query_enable = @unused_eager_loading_enable = @counter_cache_enable = enable
@@ -61,16 +63,20 @@ module Bullet
61
63
  !!@enable
62
64
  end
63
65
 
66
+ def app_root
67
+ (defined?(::Rails.root) ? Rails.root.to_s : Dir.pwd).to_s
68
+ end
69
+
64
70
  def n_plus_one_query_enable?
65
- self.enable? && !!@n_plus_one_query_enable
71
+ enable? && !!@n_plus_one_query_enable
66
72
  end
67
73
 
68
74
  def unused_eager_loading_enable?
69
- self.enable? && !!@unused_eager_loading_enable
75
+ enable? && !!@unused_eager_loading_enable
70
76
  end
71
77
 
72
78
  def counter_cache_enable?
73
- self.enable? && !!@counter_cache_enable
79
+ enable? && !!@counter_cache_enable
74
80
  end
75
81
 
76
82
  def stacktrace_includes
@@ -91,7 +97,7 @@ module Bullet
91
97
  reset_whitelist
92
98
  @whitelist[options[:type]][options[:class_name]] ||= []
93
99
  @whitelist[options[:type]][options[:class_name]].delete(options[:association].to_sym)
94
- @whitelist[options[:type]].delete_if { |key, val| val.empty? }
100
+ @whitelist[options[:type]].delete_if { |_key, val| val.empty? }
95
101
  end
96
102
 
97
103
  def get_whitelist_associations(type, class_name)
@@ -99,7 +105,7 @@ module Bullet
99
105
  end
100
106
 
101
107
  def reset_whitelist
102
- @whitelist ||= { :n_plus_one_query => {}, :unused_eager_loading => {}, :counter_cache => {} }
108
+ @whitelist ||= { n_plus_one_query: {}, unused_eager_loading: {}, counter_cache: {} }
103
109
  end
104
110
 
105
111
  def clear_whitelist
@@ -109,9 +115,8 @@ module Bullet
109
115
  def bullet_logger=(active)
110
116
  if active
111
117
  require 'fileutils'
112
- root_path = "#{rails? ? Rails.root.to_s : Dir.pwd}"
113
- FileUtils.mkdir_p(root_path + '/log')
114
- bullet_log_file = File.open("#{root_path}/log/bullet.log", 'a+')
118
+ FileUtils.mkdir_p(app_root + '/log')
119
+ bullet_log_file = File.open("#{app_root}/log/bullet.log", 'a+')
115
120
  bullet_log_file.sync = true
116
121
  UniformNotifier.customized_logger = bullet_log_file
117
122
  end
@@ -161,6 +166,7 @@ module Bullet
161
166
 
162
167
  def notification?
163
168
  return unless start?
169
+
164
170
  Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
165
171
  notification_collector.notifications_present?
166
172
  end
@@ -189,12 +195,19 @@ module Bullet
189
195
  info
190
196
  end
191
197
 
198
+ def text_notifications
199
+ info = []
200
+ notification_collector.collection.each do |notification|
201
+ info << notification.notification_data.values.compact.join("\n")
202
+ end
203
+ info
204
+ end
205
+
192
206
  def warnings
193
- notification_collector.collection.inject({}) do |warnings, notification|
207
+ notification_collector.collection.each_with_object({}) do |notification, warnings|
194
208
  warning_type = notification.class.to_s.split(':').last.tableize
195
209
  warnings[warning_type] ||= []
196
210
  warnings[warning_type] << notification
197
- warnings
198
211
  end
199
212
  end
200
213
 
@@ -217,25 +230,29 @@ module Bullet
217
230
  return_value
218
231
  end
219
232
 
233
+ def console_enabled?
234
+ UniformNotifier.active_notifiers.include?(UniformNotifier::JavascriptConsole)
235
+ end
236
+
220
237
  private
221
238
 
222
- def for_each_active_notifier_with_notification
223
- UniformNotifier.active_notifiers.each do |notifier|
224
- notification_collector.collection.each do |notification|
225
- notification.notifier = notifier
226
- yield notification
227
- end
239
+ def for_each_active_notifier_with_notification
240
+ UniformNotifier.active_notifiers.each do |notifier|
241
+ notification_collector.collection.each do |notification|
242
+ notification.notifier = notifier
243
+ yield notification
228
244
  end
229
245
  end
246
+ end
230
247
 
231
- def build_request_uri(env)
232
- return "#{env['REQUEST_METHOD']} #{env['REQUEST_URI']}" if env['REQUEST_URI']
248
+ def build_request_uri(env)
249
+ return "#{env['REQUEST_METHOD']} #{env['REQUEST_URI']}" if env['REQUEST_URI']
233
250
 
234
- if env['QUERY_STRING'].present?
235
- "#{env['REQUEST_METHOD']} #{env['PATH_INFO']}?#{env['QUERY_STRING']}"
236
- else
237
- "#{env['REQUEST_METHOD']} #{env['PATH_INFO']}"
238
- end
251
+ if env['QUERY_STRING'].present?
252
+ "#{env['REQUEST_METHOD']} #{env['PATH_INFO']}?#{env['QUERY_STRING']}"
253
+ else
254
+ "#{env['REQUEST_METHOD']} #{env['PATH_INFO']}"
239
255
  end
256
+ end
240
257
  end
241
258
  end
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bullet
2
4
  module Generators
3
5
  class InstallGenerator < ::Rails::Generators::Base
4
- desc <<-DESC
5
- Description:
6
- Enable bullet in development/test for your application.
6
+ desc <<~DESC
7
+ Description:
8
+ Enable bullet in development/test for your application.
7
9
  DESC
8
10
 
9
11
  def enable_in_development
data/perf/benchmark.rb CHANGED
@@ -1,4 +1,6 @@
1
- $: << 'lib'
1
+ # frozen_string_literal: true
2
+
3
+ $LOAD_PATH << 'lib'
2
4
  require 'benchmark'
3
5
  require 'rails'
4
6
  require 'active_record'
@@ -27,13 +29,13 @@ class User < ActiveRecord::Base
27
29
  end
28
30
 
29
31
  # create database bullet_benchmark;
30
- ActiveRecord::Base.establish_connection(:adapter => 'mysql2', :database => 'bullet_benchmark', :server => '/tmp/mysql.socket', :username => 'root')
32
+ ActiveRecord::Base.establish_connection(adapter: 'mysql2', database: 'bullet_benchmark', server: '/tmp/mysql.socket', username: 'root')
31
33
 
32
34
  ActiveRecord::Base.connection.tables.each do |table|
33
35
  ActiveRecord::Base.connection.drop_table(table)
34
36
  end
35
37
 
36
- ActiveRecord::Schema.define(:version => 1) do
38
+ ActiveRecord::Schema.define(version: 1) do
37
39
  create_table :posts do |t|
38
40
  t.column :title, :string
39
41
  t.column :body, :string
@@ -56,21 +58,21 @@ posts_size = 1000
56
58
  comments_size = 10_000
57
59
  users = []
58
60
  users_size.times do |i|
59
- users << User.new(:name => "user#{i}")
61
+ users << User.new(name: "user#{i}")
60
62
  end
61
63
  User.import users
62
64
  users = User.all
63
65
 
64
66
  posts = []
65
67
  posts_size.times do |i|
66
- posts << Post.new(:title => "Title #{i}", :body => "Body #{i}", :user => users[i % 100])
68
+ posts << Post.new(title: "Title #{i}", body: "Body #{i}", user: users[i % 100])
67
69
  end
68
70
  Post.import posts
69
71
  posts = Post.all
70
72
 
71
73
  comments = []
72
74
  comments_size.times do |i|
73
- comments << Comment.new(:body => "Comment #{i}", :post => posts[i % 1000], :user => users[i % 100])
75
+ comments << Comment.new(body: "Comment #{i}", post: posts[i % 1000], user: users[i % 100])
74
76
  end
75
77
  Comment.import comments
76
78
 
@@ -82,7 +84,7 @@ Benchmark.bm(70) do |bm|
82
84
  bm.report("Querying & Iterating #{posts_size} Posts with #{comments_size} Comments and #{users_size} Users") do
83
85
  10.times do
84
86
  Bullet.start_request
85
- Post.select('SQL_NO_CACHE *').includes(:user, :comments => :user).each do |p|
87
+ Post.select('SQL_NO_CACHE *').includes(:user, comments: :user).each do |p|
86
88
  p.title
87
89
  p.user.name
88
90
  p.comments.each do |c|
data/rails/init.rb CHANGED
@@ -1 +1,3 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'bullet'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  module Bullet