bullet 6.0.2 → 7.0.3

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 (63) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/main.yml +82 -0
  3. data/CHANGELOG.md +52 -0
  4. data/Gemfile.rails-6.0 +1 -1
  5. data/Gemfile.rails-6.1 +15 -0
  6. data/Gemfile.rails-7.0 +10 -0
  7. data/README.md +39 -25
  8. data/lib/bullet/active_job.rb +1 -3
  9. data/lib/bullet/active_record4.rb +9 -23
  10. data/lib/bullet/active_record41.rb +8 -19
  11. data/lib/bullet/active_record42.rb +9 -16
  12. data/lib/bullet/active_record5.rb +188 -170
  13. data/lib/bullet/active_record52.rb +182 -162
  14. data/lib/bullet/active_record60.rb +191 -178
  15. data/lib/bullet/active_record61.rb +272 -0
  16. data/lib/bullet/active_record70.rb +275 -0
  17. data/lib/bullet/bullet_xhr.js +18 -23
  18. data/lib/bullet/dependency.rb +52 -34
  19. data/lib/bullet/detector/association.rb +24 -18
  20. data/lib/bullet/detector/counter_cache.rb +12 -8
  21. data/lib/bullet/detector/n_plus_one_query.rb +20 -10
  22. data/lib/bullet/detector/unused_eager_loading.rb +7 -4
  23. data/lib/bullet/mongoid4x.rb +3 -7
  24. data/lib/bullet/mongoid5x.rb +3 -7
  25. data/lib/bullet/mongoid6x.rb +3 -7
  26. data/lib/bullet/mongoid7x.rb +26 -13
  27. data/lib/bullet/notification/base.rb +14 -18
  28. data/lib/bullet/notification/n_plus_one_query.rb +2 -4
  29. data/lib/bullet/notification/unused_eager_loading.rb +2 -4
  30. data/lib/bullet/notification.rb +2 -1
  31. data/lib/bullet/rack.rb +28 -17
  32. data/lib/bullet/stack_trace_filter.rb +10 -17
  33. data/lib/bullet/version.rb +1 -1
  34. data/lib/bullet.rb +52 -42
  35. data/lib/generators/bullet/install_generator.rb +22 -23
  36. data/perf/benchmark.rb +11 -14
  37. data/spec/bullet/detector/counter_cache_spec.rb +6 -6
  38. data/spec/bullet/detector/n_plus_one_query_spec.rb +8 -4
  39. data/spec/bullet/detector/unused_eager_loading_spec.rb +25 -8
  40. data/spec/bullet/ext/object_spec.rb +1 -1
  41. data/spec/bullet/notification/base_spec.rb +5 -7
  42. data/spec/bullet/notification/n_plus_one_query_spec.rb +16 -3
  43. data/spec/bullet/notification/unused_eager_loading_spec.rb +5 -1
  44. data/spec/bullet/rack_spec.rb +154 -13
  45. data/spec/bullet/registry/association_spec.rb +2 -2
  46. data/spec/bullet/registry/base_spec.rb +1 -1
  47. data/spec/bullet_spec.rb +25 -44
  48. data/spec/integration/active_record/association_spec.rb +104 -130
  49. data/spec/integration/counter_cache_spec.rb +14 -34
  50. data/spec/integration/mongoid/association_spec.rb +19 -33
  51. data/spec/models/attachment.rb +5 -0
  52. data/spec/models/deal.rb +5 -0
  53. data/spec/models/post.rb +2 -0
  54. data/spec/models/role.rb +7 -0
  55. data/spec/models/submission.rb +1 -0
  56. data/spec/models/user.rb +2 -0
  57. data/spec/spec_helper.rb +4 -10
  58. data/spec/support/bullet_ext.rb +8 -9
  59. data/spec/support/mongo_seed.rb +3 -16
  60. data/spec/support/sqlite_seed.rb +38 -0
  61. data/test.sh +2 -0
  62. metadata +17 -7
  63. data/.travis.yml +0 -31
data/lib/bullet/rack.rb CHANGED
@@ -15,15 +15,18 @@ module Bullet
15
15
  status, headers, response = @app.call(env)
16
16
 
17
17
  response_body = nil
18
+
18
19
  if Bullet.notification?
19
- if !file?(headers) && !sse?(headers) && !empty?(response) && status == 200
20
+ if Bullet.inject_into_page? && !file?(headers) && !sse?(headers) && !empty?(response) && status == 200
20
21
  if html_request?(headers, response)
21
22
  response_body = response_body(response)
22
23
  response_body = append_to_html_body(response_body, footer_note) if Bullet.add_footer
23
24
  response_body = append_to_html_body(response_body, Bullet.gather_inline_notifications)
24
- response_body = append_to_html_body(response_body, xhr_script)
25
+ if Bullet.add_footer && !Bullet.skip_http_headers
26
+ response_body = append_to_html_body(response_body, xhr_script)
27
+ end
25
28
  headers['Content-Length'] = response_body.bytesize.to_s
26
- else
29
+ elsif !Bullet.skip_http_headers
27
30
  set_header(headers, 'X-bullet-footer-text', Bullet.footer_info.uniq) if Bullet.add_footer
28
31
  set_header(headers, 'X-bullet-console-text', Bullet.text_notifications) if Bullet.console_enabled?
29
32
  end
@@ -39,12 +42,14 @@ module Bullet
39
42
  def empty?(response)
40
43
  # response may be ["Not Found"], ["Move Permanently"], etc, but
41
44
  # those should not happen if the status is 200
45
+ return true if !response.respond_to?(:body) && !response.respond_to?(:first)
42
46
  body = response_body(response)
43
47
  body.nil? || body.empty?
44
48
  end
45
49
 
46
50
  def append_to_html_body(response_body, content)
47
51
  body = response_body.dup
52
+ content = content.html_safe if content.respond_to?(:html_safe)
48
53
  if body.include?('</body>')
49
54
  position = body.rindex('</body>')
50
55
  body.insert(position, content)
@@ -54,7 +59,7 @@ module Bullet
54
59
  end
55
60
 
56
61
  def footer_note
57
- "<div #{footer_div_attributes}>" + footer_header + '<br>' + Bullet.footer_info.uniq.join('<br>') + '</div>'
62
+ "<details #{details_attributes}><summary #{summary_attributes}>Bullet Warnings</summary><div #{footer_content_attributes}>#{Bullet.footer_info.uniq.join('<br>')}#{footer_console_message}</div></details>"
58
63
  end
59
64
 
60
65
  def set_header(headers, header_name, header_array)
@@ -74,35 +79,41 @@ module Bullet
74
79
  end
75
80
 
76
81
  def html_request?(headers, response)
77
- headers['Content-Type']&.include?('text/html') && response_body(response).include?('<html')
82
+ headers['Content-Type']&.include?('text/html')
78
83
  end
79
84
 
80
85
  def response_body(response)
81
86
  if response.respond_to?(:body)
82
87
  Array === response.body ? response.body.first : response.body
83
- else
88
+ elsif response.respond_to?(:first)
84
89
  response.first
85
90
  end
86
91
  end
87
92
 
88
93
  private
89
94
 
90
- def footer_div_attributes
95
+ def details_attributes
96
+ <<~EOF
97
+ id="bullet-footer" data-is-bullet-footer
98
+ style="cursor: pointer; position: fixed; left: 0px; bottom: 0px; z-index: 9999; background: #fdf2f2; color: #9b1c1c; font-size: 12px; border-radius: 0px 8px 0px 0px; border: 1px solid #9b1c1c;"
99
+ EOF
100
+ end
101
+
102
+ def summary_attributes
91
103
  <<~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;"
104
+ style="font-weight: 600; padding: 2px 8px"
97
105
  EOF
98
106
  end
99
107
 
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>"
108
+ def footer_content_attributes
109
+ <<~EOF
110
+ style="padding: 8px; border-top: 1px solid #9b1c1c;"
111
+ EOF
112
+ end
113
+
114
+ def footer_console_message
102
115
  if Bullet.console_enabled?
103
- "<span>See 'Uniform Notifier' in JS Console for Stacktrace</span>#{cancel_button}"
104
- else
105
- cancel_button
116
+ "<br/><span style='font-style: italic;'>See 'Uniform Notifier' in JS Console for Stacktrace</span>"
106
117
  end
107
118
  end
108
119
 
@@ -1,16 +1,20 @@
1
1
  # frozen_string_literal: true
2
+ require "bundler"
2
3
 
3
4
  module Bullet
4
5
  module StackTraceFilter
5
6
  VENDOR_PATH = '/vendor'
7
+ IS_RUBY_19 = Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.0.0')
6
8
 
7
9
  def caller_in_project
8
10
  vendor_root = Bullet.app_root + VENDOR_PATH
9
11
  bundler_path = Bundler.bundle_path.to_s
10
12
  select_caller_locations do |location|
11
13
  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) }
14
+ caller_path.include?(Bullet.app_root) && !caller_path.include?(vendor_root) &&
15
+ !caller_path.include?(bundler_path) || Bullet.stacktrace_includes.any? { |include_pattern|
16
+ pattern_matches?(location, include_pattern)
17
+ }
14
18
  end
15
19
  end
16
20
 
@@ -46,26 +50,15 @@ module Bullet
46
50
  end
47
51
 
48
52
  def location_as_path(location)
49
- ruby_19? ? location : location.absolute_path.to_s
53
+ IS_RUBY_19 ? location : location.absolute_path.to_s
50
54
  end
51
55
 
52
56
  def select_caller_locations
53
- if ruby_19?
54
- caller.select do |caller_path|
55
- yield caller_path
56
- end
57
+ if IS_RUBY_19
58
+ caller.select { |caller_path| yield caller_path }
57
59
  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')
60
+ caller_locations.select { |location| yield location }
67
61
  end
68
- @ruby_19
69
62
  end
70
63
  end
71
64
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bullet
4
- VERSION = '6.0.2'
4
+ VERSION = '7.0.3'
5
5
  end
data/lib/bullet.rb CHANGED
@@ -20,10 +20,7 @@ module Bullet
20
20
  autoload :Registry, 'bullet/registry'
21
21
  autoload :NotificationCollector, 'bullet/notification_collector'
22
22
 
23
- BULLET_DEBUG = 'BULLET_DEBUG'
24
- TRUE = 'true'
25
-
26
- if defined? Rails::Railtie
23
+ if defined?(Rails::Railtie)
27
24
  class BulletRailtie < Rails::Railtie
28
25
  initializer 'bullet.configure_rails_initialization' do |app|
29
26
  app.middleware.use Bullet::Rack
@@ -32,28 +29,37 @@ module Bullet
32
29
  end
33
30
 
34
31
  class << self
35
- attr_writer :n_plus_one_query_enable, :unused_eager_loading_enable, :counter_cache_enable, :stacktrace_includes, :stacktrace_excludes
36
- attr_reader :whitelist
37
- attr_accessor :add_footer, :orm_pathches_applied
38
-
39
- available_notifiers = UniformNotifier::AVAILABLE_NOTIFIERS.map { |notifier| "#{notifier}=" }
40
- available_notifiers << { to: UniformNotifier }
41
- delegate(*available_notifiers)
32
+ attr_writer :n_plus_one_query_enable,
33
+ :unused_eager_loading_enable,
34
+ :counter_cache_enable,
35
+ :stacktrace_includes,
36
+ :stacktrace_excludes,
37
+ :skip_html_injection
38
+ attr_reader :safelist
39
+ attr_accessor :add_footer, :orm_patches_applied, :skip_http_headers
40
+
41
+ available_notifiers =
42
+ UniformNotifier::AVAILABLE_NOTIFIERS.select { |notifier| notifier != :raise }.map { |notifier| "#{notifier}=" }
43
+ available_notifiers_options = { to: UniformNotifier }
44
+ delegate(*available_notifiers, **available_notifiers_options)
42
45
 
43
46
  def raise=(should_raise)
44
47
  UniformNotifier.raise = (should_raise ? Notification::UnoptimizedQueryError : false)
45
48
  end
46
49
 
47
- DETECTORS = [Bullet::Detector::NPlusOneQuery,
48
- Bullet::Detector::UnusedEagerLoading,
49
- Bullet::Detector::CounterCache].freeze
50
+ DETECTORS = [
51
+ Bullet::Detector::NPlusOneQuery,
52
+ Bullet::Detector::UnusedEagerLoading,
53
+ Bullet::Detector::CounterCache
54
+ ].freeze
50
55
 
51
56
  def enable=(enable)
52
57
  @enable = @n_plus_one_query_enable = @unused_eager_loading_enable = @counter_cache_enable = enable
58
+
53
59
  if enable?
54
- reset_whitelist
55
- unless orm_pathches_applied
56
- self.orm_pathches_applied = true
60
+ reset_safelist
61
+ unless orm_patches_applied
62
+ self.orm_patches_applied = true
57
63
  Bullet::Mongoid.enable if mongoid?
58
64
  Bullet::ActiveRecord.enable if active_record?
59
65
  end
@@ -64,8 +70,9 @@ module Bullet
64
70
  !!@enable
65
71
  end
66
72
 
73
+ # Rails.root might be nil if `railties` is a dependency on a project that does not use Rails
67
74
  def app_root
68
- (defined?(::Rails.root) ? Rails.root.to_s : Dir.pwd).to_s
75
+ @app_root ||= (defined?(::Rails.root) && !::Rails.root.nil? ? Rails.root.to_s : Dir.pwd).to_s
69
76
  end
70
77
 
71
78
  def n_plus_one_query_enable?
@@ -81,36 +88,36 @@ module Bullet
81
88
  end
82
89
 
83
90
  def stacktrace_includes
84
- @stacktrace_includes || []
91
+ @stacktrace_includes ||= []
85
92
  end
86
93
 
87
94
  def stacktrace_excludes
88
- @stacktrace_excludes || []
95
+ @stacktrace_excludes ||= []
89
96
  end
90
97
 
91
- def add_whitelist(options)
92
- reset_whitelist
93
- @whitelist[options[:type]][options[:class_name]] ||= []
94
- @whitelist[options[:type]][options[:class_name]] << options[:association].to_sym
98
+ def add_safelist(options)
99
+ reset_safelist
100
+ @safelist[options[:type]][options[:class_name]] ||= []
101
+ @safelist[options[:type]][options[:class_name]] << options[:association].to_sym
95
102
  end
96
103
 
97
- def delete_whitelist(options)
98
- reset_whitelist
99
- @whitelist[options[:type]][options[:class_name]] ||= []
100
- @whitelist[options[:type]][options[:class_name]].delete(options[:association].to_sym)
101
- @whitelist[options[:type]].delete_if { |_key, val| val.empty? }
104
+ def delete_safelist(options)
105
+ reset_safelist
106
+ @safelist[options[:type]][options[:class_name]] ||= []
107
+ @safelist[options[:type]][options[:class_name]].delete(options[:association].to_sym)
108
+ @safelist[options[:type]].delete_if { |_key, val| val.empty? }
102
109
  end
103
110
 
104
- def get_whitelist_associations(type, class_name)
105
- Array(@whitelist[type][class_name])
111
+ def get_safelist_associations(type, class_name)
112
+ Array.wrap(@safelist[type][class_name])
106
113
  end
107
114
 
108
- def reset_whitelist
109
- @whitelist ||= { n_plus_one_query: {}, unused_eager_loading: {}, counter_cache: {} }
115
+ def reset_safelist
116
+ @safelist ||= { n_plus_one_query: {}, unused_eager_loading: {}, counter_cache: {} }
110
117
  end
111
118
 
112
- def clear_whitelist
113
- @whitelist = nil
119
+ def clear_safelist
120
+ @safelist = nil
114
121
  end
115
122
 
116
123
  def bullet_logger=(active)
@@ -124,7 +131,7 @@ module Bullet
124
131
  end
125
132
 
126
133
  def debug(title, message)
127
- puts "[Bullet][#{title}] #{message}" if ENV[BULLET_DEBUG] == TRUE
134
+ puts "[Bullet][#{title}] #{message}" if ENV['BULLET_DEBUG'] == 'true'
128
135
  end
129
136
 
130
137
  def start_request
@@ -174,9 +181,7 @@ module Bullet
174
181
 
175
182
  def gather_inline_notifications
176
183
  responses = []
177
- for_each_active_notifier_with_notification do |notification|
178
- responses << notification.notify_inline
179
- end
184
+ for_each_active_notifier_with_notification { |notification| responses << notification.notify_inline }
180
185
  responses.join("\n")
181
186
  end
182
187
 
@@ -190,9 +195,7 @@ module Bullet
190
195
 
191
196
  def footer_info
192
197
  info = []
193
- notification_collector.collection.each do |notification|
194
- info << notification.short_notice
195
- end
198
+ notification_collector.collection.each { |notification| info << notification.short_notice }
196
199
  info
197
200
  end
198
201
 
@@ -214,6 +217,7 @@ module Bullet
214
217
 
215
218
  def profile
216
219
  return_value = nil
220
+
217
221
  if Bullet.enable?
218
222
  begin
219
223
  Bullet.start_request
@@ -235,6 +239,12 @@ module Bullet
235
239
  UniformNotifier.active_notifiers.include?(UniformNotifier::JavascriptConsole)
236
240
  end
237
241
 
242
+ def inject_into_page?
243
+ return false if defined?(@skip_html_injection) && @skip_html_injection
244
+
245
+ console_enabled? || add_footer
246
+ end
247
+
238
248
  private
239
249
 
240
250
  def for_each_active_notifier_with_notification
@@ -10,17 +10,16 @@ module Bullet
10
10
 
11
11
  def enable_in_development
12
12
  environment(nil, env: 'development') do
13
- <<-"FILE".strip
14
-
15
- config.after_initialize do
16
- Bullet.enable = true
17
- Bullet.alert = true
18
- Bullet.bullet_logger = true
19
- Bullet.console = true
20
- # Bullet.growl = true
21
- Bullet.rails_logger = true
22
- Bullet.add_footer = true
23
- end
13
+ <<~FILE
14
+ config.after_initialize do
15
+ Bullet.enable = true
16
+ Bullet.alert = true
17
+ Bullet.bullet_logger = true
18
+ Bullet.console = true
19
+ Bullet.rails_logger = true
20
+ Bullet.add_footer = true
21
+ end
22
+
24
23
  FILE
25
24
  end
26
25
 
@@ -28,20 +27,20 @@ module Bullet
28
27
  end
29
28
 
30
29
  def enable_in_test
31
- if yes?('Would you like to enable bullet in test environment? (y/n)')
32
- environment(nil, env: 'test') do
33
- <<-"FILE".strip
34
-
35
- config.after_initialize do
36
- Bullet.enable = true
37
- Bullet.bullet_logger = true
38
- Bullet.raise = true # raise an error if n+1 query occurs
39
- end
40
- FILE
41
- end
30
+ return unless yes?('Would you like to enable bullet in test environment? (y/n)')
42
31
 
43
- say 'Enabled bullet in config/environments/test.rb'
32
+ environment(nil, env: 'test') do
33
+ <<~FILE
34
+ config.after_initialize do
35
+ Bullet.enable = true
36
+ Bullet.bullet_logger = true
37
+ Bullet.raise = true # raise an error if n+1 query occurs
38
+ end
39
+
40
+ FILE
44
41
  end
42
+
43
+ say 'Enabled bullet in config/environments/test.rb'
45
44
  end
46
45
  end
47
46
  end
data/perf/benchmark.rb CHANGED
@@ -29,11 +29,14 @@ class User < ActiveRecord::Base
29
29
  end
30
30
 
31
31
  # create database bullet_benchmark;
32
- ActiveRecord::Base.establish_connection(adapter: 'mysql2', database: 'bullet_benchmark', server: '/tmp/mysql.socket', username: 'root')
32
+ ActiveRecord::Base.establish_connection(
33
+ adapter: 'mysql2',
34
+ database: 'bullet_benchmark',
35
+ server: '/tmp/mysql.socket',
36
+ username: 'root'
37
+ )
33
38
 
34
- ActiveRecord::Base.connection.tables.each do |table|
35
- ActiveRecord::Base.connection.drop_table(table)
36
- end
39
+ ActiveRecord::Base.connection.tables.each { |table| ActiveRecord::Base.connection.drop_table(table) }
37
40
 
38
41
  ActiveRecord::Schema.define(version: 1) do
39
42
  create_table :posts do |t|
@@ -54,26 +57,20 @@ ActiveRecord::Schema.define(version: 1) do
54
57
  end
55
58
 
56
59
  users_size = 100
57
- posts_size = 1000
60
+ posts_size = 1_000
58
61
  comments_size = 10_000
59
62
  users = []
60
- users_size.times do |i|
61
- users << User.new(name: "user#{i}")
62
- end
63
+ users_size.times { |i| users << User.new(name: "user#{i}") }
63
64
  User.import users
64
65
  users = User.all
65
66
 
66
67
  posts = []
67
- posts_size.times do |i|
68
- posts << Post.new(title: "Title #{i}", body: "Body #{i}", user: users[i % 100])
69
- end
68
+ posts_size.times { |i| posts << Post.new(title: "Title #{i}", body: "Body #{i}", user: users[i % 100]) }
70
69
  Post.import posts
71
70
  posts = Post.all
72
71
 
73
72
  comments = []
74
- comments_size.times do |i|
75
- comments << Comment.new(body: "Comment #{i}", post: posts[i % 1000], user: users[i % 100])
76
- end
73
+ comments_size.times { |i| comments << Comment.new(body: "Comment #{i}", post: posts[i % 1_000], user: users[i % 100]) }
77
74
  Comment.import comments
78
75
 
79
76
  puts 'Start benchmarking...'
@@ -12,15 +12,15 @@ module Bullet
12
12
 
13
13
  context '.add_counter_cache' do
14
14
  it 'should create notification if conditions met' do
15
- expect(CounterCache).to receive(:conditions_met?).with(@post1, [:comments]).and_return(true)
16
- expect(CounterCache).to receive(:create_notification).with('Post', [:comments])
17
- CounterCache.add_counter_cache(@post1, [:comments])
15
+ expect(CounterCache).to receive(:conditions_met?).with(@post1, %i[comments]).and_return(true)
16
+ expect(CounterCache).to receive(:create_notification).with('Post', %i[comments])
17
+ CounterCache.add_counter_cache(@post1, %i[comments])
18
18
  end
19
19
 
20
20
  it 'should not create notification if conditions not met' do
21
- expect(CounterCache).to receive(:conditions_met?).with(@post1, [:comments]).and_return(false)
21
+ expect(CounterCache).to receive(:conditions_met?).with(@post1, %i[comments]).and_return(false)
22
22
  expect(CounterCache).to receive(:create_notification).never
23
- CounterCache.add_counter_cache(@post1, [:comments])
23
+ CounterCache.add_counter_cache(@post1, %i[comments])
24
24
  end
25
25
  end
26
26
 
@@ -47,7 +47,7 @@ module Bullet
47
47
  expect(CounterCache.conditions_met?(@post1, :associations)).to eq false
48
48
  end
49
49
 
50
- it 'should be true when object is possible, and impossible' do
50
+ it 'should be false when object is possible, and impossible' do
51
51
  CounterCache.add_possible_objects(@post1)
52
52
  CounterCache.add_impossible_object(@post1)
53
53
  expect(CounterCache.conditions_met?(@post1, :associations)).to eq false
@@ -39,7 +39,7 @@ module Bullet
39
39
 
40
40
  it 'should be false if object, association pair is not existed' do
41
41
  NPlusOneQuery.add_object_associations(@post, :association1)
42
- expect(NPlusOneQuery.association?(@post, :associatio2)).to eq false
42
+ expect(NPlusOneQuery.association?(@post, :association2)).to eq false
43
43
  end
44
44
  end
45
45
 
@@ -76,8 +76,8 @@ module Bullet
76
76
  context '.call_association' do
77
77
  it 'should create notification if conditions met' do
78
78
  expect(NPlusOneQuery).to receive(:conditions_met?).with(@post, :association).and_return(true)
79
- expect(NPlusOneQuery).to receive(:caller_in_project).and_return(['caller'])
80
- expect(NPlusOneQuery).to receive(:create_notification).with(['caller'], 'Post', :association)
79
+ expect(NPlusOneQuery).to receive(:caller_in_project).and_return(%w[caller])
80
+ expect(NPlusOneQuery).to receive(:create_notification).with(%w[caller], 'Post', :association)
81
81
  NPlusOneQuery.call_association(@post, :association)
82
82
  end
83
83
 
@@ -149,7 +149,11 @@ module Bullet
149
149
 
150
150
  expect(NPlusOneQuery).to receive(:caller_locations).and_return([in_project, *included_gems, excluded_gem])
151
151
  expect(NPlusOneQuery).to receive(:conditions_met?).with(@post, :association).and_return(true)
152
- expect(NPlusOneQuery).to receive(:create_notification).with([in_project, *included_gems], 'Post', :association)
152
+ expect(NPlusOneQuery).to receive(:create_notification).with(
153
+ [in_project, *included_gems],
154
+ 'Post',
155
+ :association
156
+ )
153
157
  NPlusOneQuery.call_association(@post, :association)
154
158
  end
155
159
  end
@@ -19,7 +19,9 @@ module Bullet
19
19
  it 'should get call associations if object and association are both in eager_loadings and call_object_associations' do
20
20
  UnusedEagerLoading.add_eager_loadings([@post], :association)
21
21
  UnusedEagerLoading.add_call_object_associations(@post, :association)
22
- expect(UnusedEagerLoading.send(:call_associations, @post.bullet_key, Set.new([:association]))).to eq([:association])
22
+ expect(UnusedEagerLoading.send(:call_associations, @post.bullet_key, Set.new([:association]))).to eq(
23
+ [:association]
24
+ )
23
25
  end
24
26
 
25
27
  it 'should not get call associations if not exist in call_object_associations' do
@@ -30,18 +32,22 @@ module Bullet
30
32
 
31
33
  context '.diff_object_associations' do
32
34
  it 'should return associations not exist in call_association' do
33
- expect(UnusedEagerLoading.send(:diff_object_associations, @post.bullet_key, Set.new([:association]))).to eq([:association])
35
+ expect(UnusedEagerLoading.send(:diff_object_associations, @post.bullet_key, Set.new([:association]))).to eq(
36
+ [:association]
37
+ )
34
38
  end
35
39
 
36
40
  it 'should return empty if associations exist in call_association' do
37
41
  UnusedEagerLoading.add_eager_loadings([@post], :association)
38
42
  UnusedEagerLoading.add_call_object_associations(@post, :association)
39
- expect(UnusedEagerLoading.send(:diff_object_associations, @post.bullet_key, Set.new([:association]))).to be_empty
43
+ expect(
44
+ UnusedEagerLoading.send(:diff_object_associations, @post.bullet_key, Set.new([:association]))
45
+ ).to be_empty
40
46
  end
41
47
  end
42
48
 
43
49
  context '.check_unused_preload_associations' do
44
- let(:paths) { ['/dir1', '/dir1/subdir'] }
50
+ let(:paths) { %w[/dir1 /dir1/subdir] }
45
51
  it 'should create notification if object_association_diff is not empty' do
46
52
  UnusedEagerLoading.add_object_associations(@post, :association)
47
53
  allow(UnusedEagerLoading).to receive(:caller_in_project).and_return(paths)
@@ -53,7 +59,9 @@ module Bullet
53
59
  UnusedEagerLoading.add_object_associations(@post, :association)
54
60
  UnusedEagerLoading.add_eager_loadings([@post], :association)
55
61
  UnusedEagerLoading.add_call_object_associations(@post, :association)
56
- expect(UnusedEagerLoading.send(:diff_object_associations, @post.bullet_key, Set.new([:association]))).to be_empty
62
+ expect(
63
+ UnusedEagerLoading.send(:diff_object_associations, @post.bullet_key, Set.new([:association]))
64
+ ).to be_empty
57
65
  expect(UnusedEagerLoading).not_to receive(:create_notification).with('Post', [:association])
58
66
  UnusedEagerLoading.check_unused_preload_associations
59
67
  end
@@ -62,14 +70,23 @@ module Bullet
62
70
  context '.add_eager_loadings' do
63
71
  it 'should add objects, associations pair when eager_loadings are empty' do
64
72
  UnusedEagerLoading.add_eager_loadings([@post, @post2], :associations)
65
- expect(UnusedEagerLoading.send(:eager_loadings)).to be_include([@post.bullet_key, @post2.bullet_key], :associations)
73
+ expect(UnusedEagerLoading.send(:eager_loadings)).to be_include(
74
+ [@post.bullet_key, @post2.bullet_key],
75
+ :associations
76
+ )
66
77
  end
67
78
 
68
79
  it 'should add objects, associations pair for existing eager_loadings' do
69
80
  UnusedEagerLoading.add_eager_loadings([@post, @post2], :association1)
70
81
  UnusedEagerLoading.add_eager_loadings([@post, @post2], :association2)
71
- expect(UnusedEagerLoading.send(:eager_loadings)).to be_include([@post.bullet_key, @post2.bullet_key], :association1)
72
- expect(UnusedEagerLoading.send(:eager_loadings)).to be_include([@post.bullet_key, @post2.bullet_key], :association2)
82
+ expect(UnusedEagerLoading.send(:eager_loadings)).to be_include(
83
+ [@post.bullet_key, @post2.bullet_key],
84
+ :association1
85
+ )
86
+ expect(UnusedEagerLoading.send(:eager_loadings)).to be_include(
87
+ [@post.bullet_key, @post2.bullet_key],
88
+ :association2
89
+ )
73
90
  end
74
91
 
75
92
  it 'should merge objects, associations pair for existing eager_loadings' do
@@ -10,7 +10,7 @@ describe Object do
10
10
  end
11
11
 
12
12
  if mongoid?
13
- it 'should return class with namesapce and id composition' do
13
+ it 'should return class with namespace and id composition' do
14
14
  post = Mongoid::Post.first
15
15
  expect(post.bullet_key).to eq("Mongoid::Post:#{post.id}")
16
16
  end
@@ -26,9 +26,7 @@ module Bullet
26
26
  end
27
27
 
28
28
  it 'should leverage ENV parameter' do
29
- temp_env_variable('USER', 'bogus') do
30
- expect(subject.whoami).to eq('user: bogus')
31
- end
29
+ temp_env_variable('USER', 'bogus') { expect(subject.whoami).to eq('user: bogus') }
32
30
  end
33
31
 
34
32
  it 'should return blank if no user available' do
@@ -76,8 +74,8 @@ module Bullet
76
74
  it 'should send full_notice to notifier' do
77
75
  notifier = double
78
76
  allow(subject).to receive(:notifier).and_return(notifier)
79
- allow(subject).to receive(:notification_data).and_return(foo: :bar)
80
- expect(notifier).to receive(:inline_notify).with(foo: :bar)
77
+ allow(subject).to receive(:notification_data).and_return({ foo: :bar })
78
+ expect(notifier).to receive(:inline_notify).with({ foo: :bar })
81
79
  subject.notify_inline
82
80
  end
83
81
  end
@@ -86,8 +84,8 @@ module Bullet
86
84
  it 'should send full_out_of_channel to notifier' do
87
85
  notifier = double
88
86
  allow(subject).to receive(:notifier).and_return(notifier)
89
- allow(subject).to receive(:notification_data).and_return(foo: :bar)
90
- expect(notifier).to receive(:out_of_channel_notify).with(foo: :bar)
87
+ allow(subject).to receive(:notification_data).and_return({ foo: :bar })
88
+ expect(notifier).to receive(:out_of_channel_notify).with({ foo: :bar })
91
89
  subject.notify_out_of_channel
92
90
  end
93
91
  end
@@ -7,9 +7,22 @@ module Bullet
7
7
  describe NPlusOneQuery do
8
8
  subject { NPlusOneQuery.new([%w[caller1 caller2]], Post, %i[comments votes], 'path') }
9
9
 
10
- it { expect(subject.body_with_caller).to eq(" Post => [:comments, :votes]\n Add to your finder: :includes => [:comments, :votes]\nCall stack\n caller1\n caller2\n") }
11
- it { expect([subject.body_with_caller, subject.body_with_caller]).to eq([" Post => [:comments, :votes]\n Add to your finder: :includes => [:comments, :votes]\nCall stack\n caller1\n caller2\n", " Post => [:comments, :votes]\n Add to your finder: :includes => [:comments, :votes]\nCall stack\n caller1\n caller2\n"]) }
12
- it { expect(subject.body).to eq(" Post => [:comments, :votes]\n Add to your finder: :includes => [:comments, :votes]") }
10
+ it do
11
+ expect(subject.body_with_caller).to eq(
12
+ " Post => [:comments, :votes]\n Add to your query: .includes([:comments, :votes])\nCall stack\n caller1\n caller2\n"
13
+ )
14
+ end
15
+ it do
16
+ expect([subject.body_with_caller, subject.body_with_caller]).to eq(
17
+ [
18
+ " Post => [:comments, :votes]\n Add to your query: .includes([:comments, :votes])\nCall stack\n caller1\n caller2\n",
19
+ " Post => [:comments, :votes]\n Add to your query: .includes([:comments, :votes])\nCall stack\n caller1\n caller2\n"
20
+ ]
21
+ )
22
+ end
23
+ it do
24
+ expect(subject.body).to eq(" Post => [:comments, :votes]\n Add to your query: .includes([:comments, :votes])")
25
+ end
13
26
  it { expect(subject.title).to eq('USE eager loading in path') }
14
27
  end
15
28
  end