bullet 7.0.7 → 7.1.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (113) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +35 -2
  3. data/README.md +7 -3
  4. data/lib/bullet/active_record4.rb +21 -6
  5. data/lib/bullet/active_record41.rb +21 -6
  6. data/lib/bullet/active_record42.rb +32 -12
  7. data/lib/bullet/active_record5.rb +40 -12
  8. data/lib/bullet/active_record52.rb +40 -12
  9. data/lib/bullet/active_record60.rb +40 -12
  10. data/lib/bullet/active_record61.rb +40 -12
  11. data/lib/bullet/active_record70.rb +54 -20
  12. data/lib/bullet/active_record71.rb +318 -0
  13. data/lib/bullet/dependency.rb +12 -0
  14. data/lib/bullet/detector/n_plus_one_query.rb +2 -5
  15. data/lib/bullet/detector/unused_eager_loading.rb +5 -2
  16. data/lib/bullet/ext/object.rb +14 -3
  17. data/lib/bullet/mongoid8x.rb +59 -0
  18. data/lib/bullet/notification/base.rb +22 -10
  19. data/lib/bullet/notification/counter_cache.rb +1 -1
  20. data/lib/bullet/rack.rb +4 -2
  21. data/lib/bullet/registry/association.rb +2 -1
  22. data/lib/bullet/stack_trace_filter.rb +3 -2
  23. data/lib/bullet/version.rb +1 -1
  24. data/lib/bullet.rb +11 -2
  25. metadata +7 -155
  26. data/.github/workflows/main.yml +0 -82
  27. data/.gitignore +0 -15
  28. data/.rspec +0 -2
  29. data/Gemfile +0 -24
  30. data/Gemfile.mongoid +0 -12
  31. data/Gemfile.mongoid-4.0 +0 -15
  32. data/Gemfile.mongoid-5.0 +0 -15
  33. data/Gemfile.mongoid-6.0 +0 -15
  34. data/Gemfile.mongoid-7.0 +0 -15
  35. data/Gemfile.rails-4.0 +0 -16
  36. data/Gemfile.rails-4.1 +0 -16
  37. data/Gemfile.rails-4.2 +0 -16
  38. data/Gemfile.rails-5.0 +0 -15
  39. data/Gemfile.rails-5.1 +0 -15
  40. data/Gemfile.rails-5.2 +0 -15
  41. data/Gemfile.rails-6.0 +0 -15
  42. data/Gemfile.rails-6.1 +0 -15
  43. data/Gemfile.rails-7.0 +0 -10
  44. data/Guardfile +0 -8
  45. data/Hacking.md +0 -75
  46. data/Rakefile +0 -51
  47. data/bullet.gemspec +0 -33
  48. data/perf/benchmark.rb +0 -118
  49. data/rails/init.rb +0 -3
  50. data/spec/bullet/detector/association_spec.rb +0 -28
  51. data/spec/bullet/detector/base_spec.rb +0 -10
  52. data/spec/bullet/detector/counter_cache_spec.rb +0 -58
  53. data/spec/bullet/detector/n_plus_one_query_spec.rb +0 -150
  54. data/spec/bullet/detector/unused_eager_loading_spec.rb +0 -126
  55. data/spec/bullet/ext/object_spec.rb +0 -44
  56. data/spec/bullet/ext/string_spec.rb +0 -15
  57. data/spec/bullet/notification/base_spec.rb +0 -94
  58. data/spec/bullet/notification/counter_cache_spec.rb +0 -14
  59. data/spec/bullet/notification/n_plus_one_query_spec.rb +0 -29
  60. data/spec/bullet/notification/unused_eager_loading_spec.rb +0 -18
  61. data/spec/bullet/notification_collector_spec.rb +0 -34
  62. data/spec/bullet/rack_spec.rb +0 -296
  63. data/spec/bullet/registry/association_spec.rb +0 -28
  64. data/spec/bullet/registry/base_spec.rb +0 -46
  65. data/spec/bullet/registry/object_spec.rb +0 -26
  66. data/spec/bullet/stack_trace_filter_spec.rb +0 -26
  67. data/spec/bullet_spec.rb +0 -136
  68. data/spec/integration/active_record/association_spec.rb +0 -822
  69. data/spec/integration/counter_cache_spec.rb +0 -68
  70. data/spec/integration/mongoid/association_spec.rb +0 -246
  71. data/spec/models/address.rb +0 -5
  72. data/spec/models/attachment.rb +0 -5
  73. data/spec/models/author.rb +0 -5
  74. data/spec/models/base_user.rb +0 -7
  75. data/spec/models/category.rb +0 -12
  76. data/spec/models/city.rb +0 -5
  77. data/spec/models/client.rb +0 -8
  78. data/spec/models/comment.rb +0 -8
  79. data/spec/models/company.rb +0 -5
  80. data/spec/models/country.rb +0 -5
  81. data/spec/models/deal.rb +0 -5
  82. data/spec/models/document.rb +0 -7
  83. data/spec/models/entry.rb +0 -5
  84. data/spec/models/firm.rb +0 -7
  85. data/spec/models/folder.rb +0 -4
  86. data/spec/models/group.rb +0 -4
  87. data/spec/models/mongoid/address.rb +0 -9
  88. data/spec/models/mongoid/category.rb +0 -10
  89. data/spec/models/mongoid/comment.rb +0 -9
  90. data/spec/models/mongoid/company.rb +0 -9
  91. data/spec/models/mongoid/entry.rb +0 -9
  92. data/spec/models/mongoid/post.rb +0 -14
  93. data/spec/models/mongoid/user.rb +0 -7
  94. data/spec/models/newspaper.rb +0 -5
  95. data/spec/models/page.rb +0 -4
  96. data/spec/models/person.rb +0 -5
  97. data/spec/models/pet.rb +0 -5
  98. data/spec/models/post.rb +0 -34
  99. data/spec/models/relationship.rb +0 -6
  100. data/spec/models/reply.rb +0 -5
  101. data/spec/models/role.rb +0 -7
  102. data/spec/models/student.rb +0 -5
  103. data/spec/models/submission.rb +0 -7
  104. data/spec/models/teacher.rb +0 -5
  105. data/spec/models/user.rb +0 -8
  106. data/spec/models/writer.rb +0 -4
  107. data/spec/spec_helper.rb +0 -97
  108. data/spec/support/bullet_ext.rb +0 -56
  109. data/spec/support/mongo_seed.rb +0 -59
  110. data/spec/support/rack_double.rb +0 -49
  111. data/spec/support/sqlite_seed.rb +0 -284
  112. data/test.sh +0 -15
  113. 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