bullet 7.0.6 → 7.1.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 (108) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +13 -1
  3. data/README.md +3 -0
  4. data/lib/bullet/active_record4.rb +9 -0
  5. data/lib/bullet/active_record41.rb +9 -0
  6. data/lib/bullet/active_record42.rb +9 -0
  7. data/lib/bullet/active_record5.rb +11 -0
  8. data/lib/bullet/active_record52.rb +11 -0
  9. data/lib/bullet/active_record60.rb +11 -0
  10. data/lib/bullet/active_record61.rb +11 -0
  11. data/lib/bullet/active_record70.rb +19 -6
  12. data/lib/bullet/active_record71.rb +297 -0
  13. data/lib/bullet/dependency.rb +12 -0
  14. data/lib/bullet/detector/unused_eager_loading.rb +1 -1
  15. data/lib/bullet/mongoid8x.rb +59 -0
  16. data/lib/bullet/notification/counter_cache.rb +1 -1
  17. data/lib/bullet/rack.rb +1 -1
  18. data/lib/bullet/version.rb +1 -1
  19. data/lib/bullet.rb +6 -2
  20. metadata +7 -155
  21. data/.github/workflows/main.yml +0 -82
  22. data/.gitignore +0 -15
  23. data/.rspec +0 -2
  24. data/Gemfile +0 -24
  25. data/Gemfile.mongoid +0 -12
  26. data/Gemfile.mongoid-4.0 +0 -15
  27. data/Gemfile.mongoid-5.0 +0 -15
  28. data/Gemfile.mongoid-6.0 +0 -15
  29. data/Gemfile.mongoid-7.0 +0 -15
  30. data/Gemfile.rails-4.0 +0 -16
  31. data/Gemfile.rails-4.1 +0 -16
  32. data/Gemfile.rails-4.2 +0 -16
  33. data/Gemfile.rails-5.0 +0 -15
  34. data/Gemfile.rails-5.1 +0 -15
  35. data/Gemfile.rails-5.2 +0 -15
  36. data/Gemfile.rails-6.0 +0 -15
  37. data/Gemfile.rails-6.1 +0 -15
  38. data/Gemfile.rails-7.0 +0 -10
  39. data/Guardfile +0 -8
  40. data/Hacking.md +0 -75
  41. data/Rakefile +0 -51
  42. data/bullet.gemspec +0 -33
  43. data/perf/benchmark.rb +0 -118
  44. data/rails/init.rb +0 -3
  45. data/spec/bullet/detector/association_spec.rb +0 -28
  46. data/spec/bullet/detector/base_spec.rb +0 -10
  47. data/spec/bullet/detector/counter_cache_spec.rb +0 -58
  48. data/spec/bullet/detector/n_plus_one_query_spec.rb +0 -150
  49. data/spec/bullet/detector/unused_eager_loading_spec.rb +0 -126
  50. data/spec/bullet/ext/object_spec.rb +0 -44
  51. data/spec/bullet/ext/string_spec.rb +0 -15
  52. data/spec/bullet/notification/base_spec.rb +0 -94
  53. data/spec/bullet/notification/counter_cache_spec.rb +0 -14
  54. data/spec/bullet/notification/n_plus_one_query_spec.rb +0 -29
  55. data/spec/bullet/notification/unused_eager_loading_spec.rb +0 -18
  56. data/spec/bullet/notification_collector_spec.rb +0 -34
  57. data/spec/bullet/rack_spec.rb +0 -296
  58. data/spec/bullet/registry/association_spec.rb +0 -28
  59. data/spec/bullet/registry/base_spec.rb +0 -46
  60. data/spec/bullet/registry/object_spec.rb +0 -26
  61. data/spec/bullet/stack_trace_filter_spec.rb +0 -26
  62. data/spec/bullet_spec.rb +0 -136
  63. data/spec/integration/active_record/association_spec.rb +0 -822
  64. data/spec/integration/counter_cache_spec.rb +0 -68
  65. data/spec/integration/mongoid/association_spec.rb +0 -246
  66. data/spec/models/address.rb +0 -5
  67. data/spec/models/attachment.rb +0 -5
  68. data/spec/models/author.rb +0 -5
  69. data/spec/models/base_user.rb +0 -7
  70. data/spec/models/category.rb +0 -12
  71. data/spec/models/city.rb +0 -5
  72. data/spec/models/client.rb +0 -8
  73. data/spec/models/comment.rb +0 -8
  74. data/spec/models/company.rb +0 -5
  75. data/spec/models/country.rb +0 -5
  76. data/spec/models/deal.rb +0 -5
  77. data/spec/models/document.rb +0 -7
  78. data/spec/models/entry.rb +0 -5
  79. data/spec/models/firm.rb +0 -7
  80. data/spec/models/folder.rb +0 -4
  81. data/spec/models/group.rb +0 -4
  82. data/spec/models/mongoid/address.rb +0 -9
  83. data/spec/models/mongoid/category.rb +0 -10
  84. data/spec/models/mongoid/comment.rb +0 -9
  85. data/spec/models/mongoid/company.rb +0 -9
  86. data/spec/models/mongoid/entry.rb +0 -9
  87. data/spec/models/mongoid/post.rb +0 -14
  88. data/spec/models/mongoid/user.rb +0 -7
  89. data/spec/models/newspaper.rb +0 -5
  90. data/spec/models/page.rb +0 -4
  91. data/spec/models/person.rb +0 -5
  92. data/spec/models/pet.rb +0 -5
  93. data/spec/models/post.rb +0 -34
  94. data/spec/models/relationship.rb +0 -6
  95. data/spec/models/reply.rb +0 -5
  96. data/spec/models/role.rb +0 -7
  97. data/spec/models/student.rb +0 -5
  98. data/spec/models/submission.rb +0 -7
  99. data/spec/models/teacher.rb +0 -5
  100. data/spec/models/user.rb +0 -8
  101. data/spec/models/writer.rb +0 -4
  102. data/spec/spec_helper.rb +0 -97
  103. data/spec/support/bullet_ext.rb +0 -56
  104. data/spec/support/mongo_seed.rb +0 -59
  105. data/spec/support/rack_double.rb +0 -49
  106. data/spec/support/sqlite_seed.rb +0 -284
  107. data/test.sh +0 -15
  108. data/update.sh +0 -10
data/Hacking.md DELETED
@@ -1,75 +0,0 @@
1
- # Bullet Overview for Developers
2
-
3
- This file aims to give developers a quick tour of the bullet internals, making
4
- it (hopefully) easier to extend or enhance the Bullet gem.
5
-
6
- ## General Control Flow aka. 10000 Meter View
7
-
8
- When Rails is initialized, Bullet will extend ActiveRecord (and if you're using
9
- Rails 2.x ActiveController too) with the relevant modules and methods found
10
- in lib/bullet/active_recordX.rb and lib/bullet/action_controller2.rb. If you're
11
- running Rails 3, Bullet will integrate itself as a middleware into the Rack
12
- stack, so ActionController does not need to be extended.
13
-
14
- The ActiveRecord extensions will call methods in a given detector class, when
15
- certain methods are called.
16
-
17
- Detector classes contain all the logic to recognize
18
- a noteworthy event. If such an event is detected, an instance of the
19
- corresponding Notification class is created and stored in a Set instance in the
20
- main Bullet module (the 'notification collector').
21
-
22
- Notification instances contain the message that will be displayed, and will
23
- use a Presenter class to display their message to the user.
24
-
25
- So the flow of a request goes like this:
26
-
27
- 1. Bullet.start_request is called, which resets all the detectors and empties
28
- the notification collector
29
- 2. The request is handled by Rails, and the installed ActiveRecord extensions
30
- trigger Detector callbacks
31
- 3. Detectors once called, will determine whether something noteworthy happened.
32
- If yes, then a Notification is created and stored in the notification collector.
33
- 4. Rails finishes handling the request
34
- 5. For each notification in the collector, Bullet will iterate over each
35
- Presenter and will try to generate an inline message that will be appended to
36
- the generated response body.
37
- 6. The response is returned to the client.
38
- 7. Bullet will try to generate an out-of-channel message for each notification.
39
- 8. Bullet calls end_request for each detector.
40
- 9. Goto 1.
41
-
42
- ## Adding Notification Types
43
-
44
- If you want to add more kinds of things that Bullet can detect, a little more
45
- work is needed than if you were just adding a Presenter, but the concepts are
46
- similar.
47
-
48
- * Add the class to the DETECTORS constant in the main Bullet module
49
- * Add (if needed) Rails monkey patches to Bullet.enable
50
- * Add an autoload directive to lib/bullet/detector.rb
51
- * Create a corresponding notification class in the Bullet::Notification namespace
52
- * Add an autoload directive to lib/bullet/notification.rb
53
-
54
- As a rule of thumb, you can assume that each Detector will have its own
55
- Notification class. If you follow the principle of Separation of Concerns I
56
- can't really think of an example where one would deviate from this rule.
57
-
58
- Since the detection of pathological associations is a bit hairy, I'd recommend
59
- having a look at the counter cache detector and associated notification to get
60
- a feel for what is needed to get off the ground.
61
-
62
- ### Detectors
63
-
64
- The only things you'll need to consider when building your Detector class is
65
- that it will need to supply the .start_request, .end_request and .clear class
66
- methods.
67
-
68
- Simple implementations are provided by Bullet::Detector::Base for start_request
69
- and end_request, you will have to supply your own clear method.
70
-
71
- ### Notifications
72
-
73
- For notifications you will want to supply a #title and #body instance method,
74
- and check to see if the #initialize and #full_notice methods in the
75
- Bullet::Notification::Base class fit your needs.
data/Rakefile DELETED
@@ -1,51 +0,0 @@
1
- $LOAD_PATH.unshift File.expand_path('lib', __dir__)
2
- require 'bundler'
3
- Bundler.setup
4
-
5
- require 'rake'
6
- require 'rspec'
7
- require 'rspec/core/rake_task'
8
-
9
- require 'bullet/version'
10
-
11
- task :build do
12
- system 'gem build bullet.gemspec'
13
- end
14
-
15
- task install: :build do
16
- system "sudo gem install bullet-#{Bullet::VERSION}.gem"
17
- end
18
-
19
- task release: :build do
20
- puts "Tagging #{Bullet::VERSION}..."
21
- system "git tag -a #{Bullet::VERSION} -m 'Tagging #{Bullet::VERSION}'"
22
- puts 'Pushing to Github...'
23
- system 'git push --tags'
24
- puts 'Pushing to rubygems.org...'
25
- system "gem push bullet-#{Bullet::VERSION}.gem"
26
- end
27
-
28
- RSpec::Core::RakeTask.new(:spec) do |spec|
29
- spec.pattern = 'spec/**/*_spec.rb'
30
- end
31
-
32
- RSpec::Core::RakeTask.new('spec:progress') do |spec|
33
- spec.rspec_opts = %w[--format progress]
34
- spec.pattern = 'spec/**/*_spec.rb'
35
- end
36
-
37
- begin
38
- require 'rdoc/task'
39
-
40
- desc 'Generate documentation for the plugin.'
41
- Rake::RDocTask.new do |rdoc|
42
- rdoc.rdoc_dir = 'rdoc'
43
- rdoc.title = "bullet #{Bullet::VERSION}"
44
- rdoc.rdoc_files.include('README*')
45
- rdoc.rdoc_files.include('lib/**/*.rb')
46
- end
47
- rescue LoadError
48
- puts 'RDocTask is not supported for this platform'
49
- end
50
-
51
- task default: :spec
data/bullet.gemspec DELETED
@@ -1,33 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- lib = File.expand_path('lib', __dir__)
4
- $LOAD_PATH.unshift lib unless $LOAD_PATH.include?(lib)
5
-
6
- require 'bullet/version'
7
-
8
- Gem::Specification.new do |s|
9
- s.name = 'bullet'
10
- s.version = Bullet::VERSION
11
- s.platform = Gem::Platform::RUBY
12
- s.authors = ['Richard Huang']
13
- s.email = ['flyerhzm@gmail.com']
14
- s.homepage = 'https://github.com/flyerhzm/bullet'
15
- s.summary = 'help to kill N+1 queries and unused eager loading.'
16
- s.description = 'help to kill N+1 queries and unused eager loading.'
17
- s.metadata = {
18
- 'changelog_uri' => 'https://github.com/flyerhzm/bullet/blob/master/CHANGELOG.md',
19
- 'source_code_uri' => 'https://github.com/flyerhzm/bullet'
20
- }
21
-
22
- s.license = 'MIT'
23
-
24
- s.required_ruby_version = '>= 2.3'
25
- s.required_rubygems_version = '>= 1.3.6'
26
-
27
- s.add_runtime_dependency 'activesupport', '>= 3.0.0'
28
- s.add_runtime_dependency 'uniform_notifier', '~> 1.11'
29
-
30
- s.files = `git ls-files`.split("\n")
31
- s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
32
- s.require_paths = ['lib']
33
- end
data/perf/benchmark.rb DELETED
@@ -1,118 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- $LOAD_PATH << 'lib'
4
- require 'benchmark'
5
- require 'rails'
6
- require 'active_record'
7
- require 'activerecord-import'
8
- require 'bullet'
9
-
10
- begin
11
- require 'perftools'
12
- rescue LoadError
13
- puts "Could not load perftools.rb, profiling won't be possible"
14
- end
15
-
16
- class Post < ActiveRecord::Base
17
- belongs_to :user
18
- has_many :comments
19
- end
20
-
21
- class Comment < ActiveRecord::Base
22
- belongs_to :user
23
- belongs_to :post
24
- end
25
-
26
- class User < ActiveRecord::Base
27
- has_many :posts
28
- has_many :comments
29
- end
30
-
31
- # create database bullet_benchmark;
32
- ActiveRecord::Base.establish_connection(
33
- adapter: 'mysql2',
34
- database: 'bullet_benchmark',
35
- server: '/tmp/mysql.socket',
36
- username: 'root'
37
- )
38
-
39
- ActiveRecord::Base.connection.tables.each { |table| ActiveRecord::Base.connection.drop_table(table) }
40
-
41
- ActiveRecord::Schema.define(version: 1) do
42
- create_table :posts do |t|
43
- t.column :title, :string
44
- t.column :body, :string
45
- t.column :user_id, :integer
46
- end
47
-
48
- create_table :comments do |t|
49
- t.column :body, :string
50
- t.column :post_id, :integer
51
- t.column :user_id, :integer
52
- end
53
-
54
- create_table :users do |t|
55
- t.column :name, :string
56
- end
57
- end
58
-
59
- users_size = 100
60
- posts_size = 1_000
61
- comments_size = 10_000
62
- users = []
63
- users_size.times { |i| users << User.new(name: "user#{i}") }
64
- User.import users
65
- users = User.all
66
-
67
- posts = []
68
- posts_size.times { |i| posts << Post.new(title: "Title #{i}", body: "Body #{i}", user: users[i % 100]) }
69
- Post.import posts
70
- posts = Post.all
71
-
72
- comments = []
73
- comments_size.times { |i| comments << Comment.new(body: "Comment #{i}", post: posts[i % 1_000], user: users[i % 100]) }
74
- Comment.import comments
75
-
76
- puts 'Start benchmarking...'
77
-
78
- Bullet.enable = true
79
-
80
- Benchmark.bm(70) do |bm|
81
- bm.report("Querying & Iterating #{posts_size} Posts with #{comments_size} Comments and #{users_size} Users") do
82
- 10.times do
83
- Bullet.start_request
84
- Post.select('SQL_NO_CACHE *').includes(:user, comments: :user).each do |p|
85
- p.title
86
- p.user.name
87
- p.comments.each do |c|
88
- c.body
89
- c.user.name
90
- end
91
- end
92
- Bullet.end_request
93
- end
94
- end
95
- end
96
-
97
- puts 'End benchmarking...'
98
-
99
- # Run benchmark with bundler
100
- #
101
- # bundle exec ruby perf/benchmark.rb
102
- #
103
- # bullet 2.3.0 with rails 3.2.2
104
- # user system total real
105
- # Querying & Iterating 1000 Posts with 10000 Comments and 100 Users 16.460000 0.190000 16.650000 ( 16.968246)
106
- #
107
- # bullet 2.3.0 with rails 3.1.4
108
- # user system total real
109
- # Querying & Iterating 1000 Posts with 10000 Comments and 100 Users 14.600000 0.130000 14.730000 ( 14.937590)
110
- #
111
- # bullet 2.3.0 with rails 3.0.12
112
- # user system total real
113
- # Querying & Iterating 1000 Posts with 10000 Comments and 100 Users 26.120000 0.430000 26.550000 ( 27.179304)
114
- #
115
- #
116
- # bullet 2.2.1 with rails 3.0.12
117
- # user system total real
118
- # Querying & Iterating 1000 Posts with 10000 Comments and 100 Users 29.970000 0.270000 30.240000 ( 30.452083)
data/rails/init.rb DELETED
@@ -1,3 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'bullet'
@@ -1,28 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'spec_helper'
4
-
5
- module Bullet
6
- module Detector
7
- describe Association do
8
- before :all do
9
- @post1 = Post.first
10
- @post2 = Post.last
11
- end
12
-
13
- context '.add_object_association' do
14
- it 'should add object, associations pair' do
15
- Association.add_object_associations(@post1, :associations)
16
- expect(Association.send(:object_associations)).to be_include(@post1.bullet_key, :associations)
17
- end
18
- end
19
-
20
- context '.add_call_object_associations' do
21
- it 'should add call object, associations pair' do
22
- Association.add_call_object_associations(@post1, :associations)
23
- expect(Association.send(:call_object_associations)).to be_include(@post1.bullet_key, :associations)
24
- end
25
- end
26
- end
27
- end
28
- end
@@ -1,10 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'spec_helper'
4
-
5
- module Bullet
6
- module Detector
7
- describe Base do
8
- end
9
- end
10
- end
@@ -1,58 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'spec_helper'
4
-
5
- module Bullet
6
- module Detector
7
- describe CounterCache do
8
- before :all do
9
- @post1 = Post.first
10
- @post2 = Post.last
11
- end
12
-
13
- context '.add_counter_cache' do
14
- it 'should create notification if conditions met' do
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
- end
19
-
20
- it 'should not create notification if conditions not met' do
21
- expect(CounterCache).to receive(:conditions_met?).with(@post1, %i[comments]).and_return(false)
22
- expect(CounterCache).to receive(:create_notification).never
23
- CounterCache.add_counter_cache(@post1, %i[comments])
24
- end
25
- end
26
-
27
- context '.add_possible_objects' do
28
- it 'should add possible objects' do
29
- CounterCache.add_possible_objects([@post1, @post2])
30
- expect(CounterCache.possible_objects).to be_include(@post1.bullet_key)
31
- expect(CounterCache.possible_objects).to be_include(@post2.bullet_key)
32
- end
33
-
34
- it 'should add impossible object' do
35
- CounterCache.add_impossible_object(@post1)
36
- expect(CounterCache.impossible_objects).to be_include(@post1.bullet_key)
37
- end
38
- end
39
-
40
- context '.conditions_met?' do
41
- it 'should be true when object is possible, not impossible' do
42
- CounterCache.add_possible_objects(@post1)
43
- expect(CounterCache.conditions_met?(@post1, :associations)).to eq true
44
- end
45
-
46
- it 'should be false when object is not possible' do
47
- expect(CounterCache.conditions_met?(@post1, :associations)).to eq false
48
- end
49
-
50
- it 'should be false when object is possible, and impossible' do
51
- CounterCache.add_possible_objects(@post1)
52
- CounterCache.add_impossible_object(@post1)
53
- expect(CounterCache.conditions_met?(@post1, :associations)).to eq false
54
- end
55
- end
56
- end
57
- end
58
- end
@@ -1,150 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'spec_helper'
4
-
5
- module Bullet
6
- module Detector
7
- describe NPlusOneQuery do
8
- before(:all) do
9
- @post = Post.first
10
- @post2 = Post.last
11
- end
12
-
13
- context '.call_association' do
14
- it 'should add call_object_associations' do
15
- expect(NPlusOneQuery).to receive(:add_call_object_associations).with(@post, :associations)
16
- NPlusOneQuery.call_association(@post, :associations)
17
- end
18
- end
19
-
20
- context '.possible?' do
21
- it 'should be true if possible_objects contain' do
22
- NPlusOneQuery.add_possible_objects(@post)
23
- expect(NPlusOneQuery.possible?(@post)).to eq true
24
- end
25
- end
26
-
27
- context '.impossible?' do
28
- it 'should be true if impossible_objects contain' do
29
- NPlusOneQuery.add_impossible_object(@post)
30
- expect(NPlusOneQuery.impossible?(@post)).to eq true
31
- end
32
- end
33
-
34
- context '.association?' do
35
- it 'should be true if object, associations pair is already existed' do
36
- NPlusOneQuery.add_object_associations(@post, :association)
37
- expect(NPlusOneQuery.association?(@post, :association)).to eq true
38
- end
39
-
40
- it 'should be false if object, association pair is not existed' do
41
- NPlusOneQuery.add_object_associations(@post, :association1)
42
- expect(NPlusOneQuery.association?(@post, :association2)).to eq false
43
- end
44
- end
45
-
46
- context '.conditions_met?' do
47
- it 'should be true if object is possible, not impossible and object, associations pair is not already existed' do
48
- allow(NPlusOneQuery).to receive(:possible?).with(@post).and_return(true)
49
- allow(NPlusOneQuery).to receive(:impossible?).with(@post).and_return(false)
50
- allow(NPlusOneQuery).to receive(:association?).with(@post, :associations).and_return(false)
51
- expect(NPlusOneQuery.conditions_met?(@post, :associations)).to eq true
52
- end
53
-
54
- it 'should be false if object is not possible, not impossible and object, associations pair is not already existed' do
55
- allow(NPlusOneQuery).to receive(:possible?).with(@post).and_return(false)
56
- allow(NPlusOneQuery).to receive(:impossible?).with(@post).and_return(false)
57
- allow(NPlusOneQuery).to receive(:association?).with(@post, :associations).and_return(false)
58
- expect(NPlusOneQuery.conditions_met?(@post, :associations)).to eq false
59
- end
60
-
61
- it 'should be false if object is possible, but impossible and object, associations pair is not already existed' do
62
- allow(NPlusOneQuery).to receive(:possible?).with(@post).and_return(true)
63
- allow(NPlusOneQuery).to receive(:impossible?).with(@post).and_return(true)
64
- allow(NPlusOneQuery).to receive(:association?).with(@post, :associations).and_return(false)
65
- expect(NPlusOneQuery.conditions_met?(@post, :associations)).to eq false
66
- end
67
-
68
- it 'should be false if object is possible, not impossible and object, associations pair is already existed' do
69
- allow(NPlusOneQuery).to receive(:possible?).with(@post).and_return(true)
70
- allow(NPlusOneQuery).to receive(:impossible?).with(@post).and_return(false)
71
- allow(NPlusOneQuery).to receive(:association?).with(@post, :associations).and_return(true)
72
- expect(NPlusOneQuery.conditions_met?(@post, :associations)).to eq false
73
- end
74
- end
75
-
76
- context '.call_association' do
77
- it 'should create notification if conditions met' do
78
- expect(NPlusOneQuery).to receive(:conditions_met?).with(@post, :association).and_return(true)
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
- NPlusOneQuery.call_association(@post, :association)
82
- end
83
-
84
- it 'should not create notification if conditions not met' do
85
- expect(NPlusOneQuery).to receive(:conditions_met?).with(@post, :association).and_return(false)
86
- expect(NPlusOneQuery).not_to receive(:caller_in_project!)
87
- expect(NPlusOneQuery).not_to receive(:create_notification).with('Post', :association)
88
- NPlusOneQuery.call_association(@post, :association)
89
- end
90
-
91
- context 'stacktrace_excludes' do
92
- before { Bullet.stacktrace_excludes = [/def/] }
93
- after { Bullet.stacktrace_excludes = nil }
94
-
95
- it 'should not create notification when stacktrace contains paths that are in the exclude list' do
96
- in_project = OpenStruct.new(absolute_path: File.join(Dir.pwd, 'abc', 'abc.rb'))
97
- included_path = OpenStruct.new(absolute_path: '/ghi/ghi.rb')
98
- excluded_path = OpenStruct.new(absolute_path: '/def/def.rb')
99
-
100
- expect(NPlusOneQuery).to receive(:caller_locations).and_return([in_project, included_path, excluded_path])
101
- expect(NPlusOneQuery).to_not receive(:create_notification)
102
- NPlusOneQuery.call_association(@post, :association)
103
- end
104
-
105
- # just a sanity spec to make sure the following spec works correctly
106
- it "should create notification when stacktrace contains methods that aren't in the exclude list" do
107
- method = NPlusOneQuery.method(:excluded_stacktrace_path?).source_location
108
- in_project = OpenStruct.new(absolute_path: File.join(Dir.pwd, 'abc', 'abc.rb'))
109
- excluded_path = OpenStruct.new(absolute_path: method.first, lineno: method.last)
110
-
111
- expect(NPlusOneQuery).to receive(:caller_locations).at_least(1).and_return([in_project, excluded_path])
112
- expect(NPlusOneQuery).to receive(:conditions_met?).and_return(true)
113
- expect(NPlusOneQuery).to receive(:create_notification)
114
- NPlusOneQuery.call_association(@post, :association)
115
- end
116
-
117
- it 'should not create notification when stacktrace contains methods that are in the exclude list' do
118
- method = NPlusOneQuery.method(:excluded_stacktrace_path?).source_location
119
- Bullet.stacktrace_excludes = [method]
120
- in_project = OpenStruct.new(absolute_path: File.join(Dir.pwd, 'abc', 'abc.rb'))
121
- excluded_path = OpenStruct.new(absolute_path: method.first, lineno: method.last)
122
-
123
- expect(NPlusOneQuery).to receive(:caller_locations).and_return([in_project, excluded_path])
124
- expect(NPlusOneQuery).to_not receive(:create_notification)
125
- NPlusOneQuery.call_association(@post, :association)
126
- end
127
- end
128
- end
129
-
130
- context '.add_possible_objects' do
131
- it 'should add possible objects' do
132
- NPlusOneQuery.add_possible_objects([@post, @post2])
133
- expect(NPlusOneQuery.possible_objects).to be_include(@post.bullet_key)
134
- expect(NPlusOneQuery.possible_objects).to be_include(@post2.bullet_key)
135
- end
136
-
137
- it 'should not raise error if object is nil' do
138
- expect { NPlusOneQuery.add_possible_objects(nil) }.not_to raise_error
139
- end
140
- end
141
-
142
- context '.add_impossible_object' do
143
- it 'should add impossible object' do
144
- NPlusOneQuery.add_impossible_object(@post)
145
- expect(NPlusOneQuery.impossible_objects).to be_include(@post.bullet_key)
146
- end
147
- end
148
- end
149
- end
150
- end
@@ -1,126 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'spec_helper'
4
-
5
- module Bullet
6
- module Detector
7
- describe UnusedEagerLoading do
8
- before(:all) do
9
- @post = Post.first
10
- @post2 = Post.all[1]
11
- @post3 = Post.last
12
- end
13
-
14
- context '.call_associations' do
15
- it 'should get empty array if eager_loadings' do
16
- expect(UnusedEagerLoading.send(:call_associations, @post.bullet_key, Set.new([:association]))).to be_empty
17
- end
18
-
19
- it 'should get call associations if object and association are both in eager_loadings and call_object_associations' do
20
- UnusedEagerLoading.add_eager_loadings([@post], :association)
21
- UnusedEagerLoading.add_call_object_associations(@post, :association)
22
- expect(UnusedEagerLoading.send(:call_associations, @post.bullet_key, Set.new([:association]))).to eq(
23
- [:association]
24
- )
25
- end
26
-
27
- it 'should not get call associations if not exist in call_object_associations' do
28
- UnusedEagerLoading.add_eager_loadings([@post], :association)
29
- expect(UnusedEagerLoading.send(:call_associations, @post.bullet_key, Set.new([:association]))).to be_empty
30
- end
31
- end
32
-
33
- context '.diff_object_associations' do
34
- it 'should return associations not exist in call_association' do
35
- expect(UnusedEagerLoading.send(:diff_object_associations, @post.bullet_key, Set.new([:association]))).to eq(
36
- [:association]
37
- )
38
- end
39
-
40
- it 'should return empty if associations exist in call_association' do
41
- UnusedEagerLoading.add_eager_loadings([@post], :association)
42
- UnusedEagerLoading.add_call_object_associations(@post, :association)
43
- expect(
44
- UnusedEagerLoading.send(:diff_object_associations, @post.bullet_key, Set.new([:association]))
45
- ).to be_empty
46
- end
47
- end
48
-
49
- context '.check_unused_preload_associations' do
50
- let(:paths) { %w[/dir1 /dir1/subdir] }
51
- it 'should create notification if object_association_diff is not empty' do
52
- UnusedEagerLoading.add_object_associations(@post, :association)
53
- allow(UnusedEagerLoading).to receive(:caller_in_project).and_return(paths)
54
- expect(UnusedEagerLoading).to receive(:create_notification).with(paths, 'Post', [:association])
55
- UnusedEagerLoading.check_unused_preload_associations
56
- end
57
-
58
- it 'should not create notification if object_association_diff is empty' do
59
- UnusedEagerLoading.add_object_associations(@post, :association)
60
- UnusedEagerLoading.add_eager_loadings([@post], :association)
61
- UnusedEagerLoading.add_call_object_associations(@post, :association)
62
- expect(
63
- UnusedEagerLoading.send(:diff_object_associations, @post.bullet_key, Set.new([:association]))
64
- ).to be_empty
65
- expect(UnusedEagerLoading).not_to receive(:create_notification).with('Post', [:association])
66
- UnusedEagerLoading.check_unused_preload_associations
67
- end
68
-
69
- it 'should create call stack for notification' do
70
- UnusedEagerLoading.add_object_associations(@post, :association)
71
- expect(UnusedEagerLoading.send(:call_stacks).registry).not_to be_empty
72
- end
73
- end
74
-
75
- context '.add_eager_loadings' do
76
- it 'should add objects, associations pair when eager_loadings are empty' do
77
- UnusedEagerLoading.add_eager_loadings([@post, @post2], :associations)
78
- expect(UnusedEagerLoading.send(:eager_loadings)).to be_include(
79
- [@post.bullet_key, @post2.bullet_key],
80
- :associations
81
- )
82
- end
83
-
84
- it 'should add objects, associations pair for existing eager_loadings' do
85
- UnusedEagerLoading.add_eager_loadings([@post, @post2], :association1)
86
- UnusedEagerLoading.add_eager_loadings([@post, @post2], :association2)
87
- expect(UnusedEagerLoading.send(:eager_loadings)).to be_include(
88
- [@post.bullet_key, @post2.bullet_key],
89
- :association1
90
- )
91
- expect(UnusedEagerLoading.send(:eager_loadings)).to be_include(
92
- [@post.bullet_key, @post2.bullet_key],
93
- :association2
94
- )
95
- end
96
-
97
- it 'should merge objects, associations pair for existing eager_loadings' do
98
- UnusedEagerLoading.add_eager_loadings([@post], :association1)
99
- UnusedEagerLoading.add_eager_loadings([@post, @post2], :association2)
100
- expect(UnusedEagerLoading.send(:eager_loadings)).to be_include([@post.bullet_key], :association1)
101
- expect(UnusedEagerLoading.send(:eager_loadings)).to be_include([@post.bullet_key], :association2)
102
- expect(UnusedEagerLoading.send(:eager_loadings)).to be_include([@post2.bullet_key], :association2)
103
- end
104
-
105
- it 'should vmerge objects recursively, associations pair for existing eager_loadings' do
106
- UnusedEagerLoading.add_eager_loadings([@post, @post2], :association1)
107
- UnusedEagerLoading.add_eager_loadings([@post, @post3], :association1)
108
- UnusedEagerLoading.add_eager_loadings([@post, @post3], :association2)
109
- expect(UnusedEagerLoading.send(:eager_loadings)).to be_include([@post.bullet_key], :association1)
110
- expect(UnusedEagerLoading.send(:eager_loadings)).to be_include([@post.bullet_key], :association2)
111
- expect(UnusedEagerLoading.send(:eager_loadings)).to be_include([@post2.bullet_key], :association1)
112
- expect(UnusedEagerLoading.send(:eager_loadings)).to be_include([@post3.bullet_key], :association1)
113
- expect(UnusedEagerLoading.send(:eager_loadings)).to be_include([@post3.bullet_key], :association2)
114
- end
115
-
116
- it 'should delete objects, associations pair for existing eager_loadings' do
117
- UnusedEagerLoading.add_eager_loadings([@post, @post2], :association1)
118
- UnusedEagerLoading.add_eager_loadings([@post], :association2)
119
- expect(UnusedEagerLoading.send(:eager_loadings)).to be_include([@post.bullet_key], :association1)
120
- expect(UnusedEagerLoading.send(:eager_loadings)).to be_include([@post.bullet_key], :association2)
121
- expect(UnusedEagerLoading.send(:eager_loadings)).to be_include([@post2.bullet_key], :association1)
122
- end
123
- end
124
- end
125
- end
126
- end