bullet 5.2.0 → 5.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: dbcef4cb1e9b0db722674cc600a456767eb5d2dd
4
- data.tar.gz: 27107041da317defb0433329dc55375b305cc7b8
3
+ metadata.gz: ed73722e74144833068cf4572e3624687a9db64b
4
+ data.tar.gz: e882a8f5c7e4bfbe1d37254e1255ed79fc3f3948
5
5
  SHA512:
6
- metadata.gz: ea8208a3a4f1a1ef3f4f247667f9485b951a00ba5036cc87b8049005813e8f7452a5d340c1fdb09ea525fd5996b0050587a758afc8ff2a421f93acb21424f605
7
- data.tar.gz: 456466364914301c30c4622bba947888bd16d92dad0603759c8b1108c4bc11ed417a8fca5a8ee2c983f119fa16e587cc140498393d2ec31f1a79c410082f1874
6
+ metadata.gz: a62b1a339dccad8b79e97971c8d7fc73f977eb9d0b1c19758f59259e85531116fb57a9ef51c90e013d6051a27616ec861380d0940f658102155a163f52eb1873
7
+ data.tar.gz: de637d2bb853c67a5e7f2b269f698b18b59a481f5988025407d094e0442df03de805bc3890945e49accba76d3ae3c7a62c4185a8fde8d584550eac251a281836
data/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Next Release
2
2
 
3
+ ## 5.3.0 (15/08/2016)
4
+
5
+ * Fix false alert on through association with join sql #301
6
+ * Fix association.target in through_association can be singular #302
7
+ * Support find_by_sql #303
8
+ * Fix env REQUEST_URI
9
+
3
10
  ## 5.2.0 (07/26/2016)
4
11
 
5
12
  * Fix `has_cached_counter?` is not defined in HABTM #297
@@ -4,6 +4,25 @@ module Bullet
4
4
 
5
5
  def self.enable
6
6
  require 'active_record'
7
+ ::ActiveRecord::Base.class_eval do
8
+ class <<self
9
+ alias_method :origin_find_by_sql, :find_by_sql
10
+ def find_by_sql(sql)
11
+ result = origin_find_by_sql(sql)
12
+ if Bullet.start?
13
+ if result.is_a? Array
14
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
15
+ Bullet::Detector::CounterCache.add_possible_objects(result)
16
+ elsif result.is_a? ::ActiveRecord::Base
17
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(result)
18
+ Bullet::Detector::CounterCache.add_impossible_object(result)
19
+ end
20
+ end
21
+ result
22
+ end
23
+ end
24
+ end
25
+
7
26
  ::ActiveRecord::Relation.class_eval do
8
27
  alias_method :origin_to_a, :to_a
9
28
  # if select a collection of objects, then these objects have possible to cause N+1 query.
@@ -2,6 +2,25 @@ module Bullet
2
2
  module ActiveRecord
3
3
  def self.enable
4
4
  require 'active_record'
5
+ ::ActiveRecord::Base.class_eval do
6
+ class <<self
7
+ alias_method :origin_find_by_sql, :find_by_sql
8
+ def find_by_sql(sql, binds = [])
9
+ result = origin_find_by_sql(sql, binds)
10
+ if Bullet.start?
11
+ if result.is_a? Array
12
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
13
+ Bullet::Detector::CounterCache.add_possible_objects(result)
14
+ elsif result.is_a? ::ActiveRecord::Base
15
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(result)
16
+ Bullet::Detector::CounterCache.add_impossible_object(result)
17
+ end
18
+ end
19
+ result
20
+ end
21
+ end
22
+ end
23
+
5
24
  ::ActiveRecord::Relation.class_eval do
6
25
  alias_method :origin_to_a, :to_a
7
26
  # if select a collection of objects, then these objects have possible to cause N+1 query.
@@ -2,6 +2,25 @@ module Bullet
2
2
  module ActiveRecord
3
3
  def self.enable
4
4
  require 'active_record'
5
+ ::ActiveRecord::Base.class_eval do
6
+ class <<self
7
+ alias_method :origin_find_by_sql, :find_by_sql
8
+ def find_by_sql(sql, binds = [])
9
+ result = origin_find_by_sql(sql, binds)
10
+ if Bullet.start?
11
+ if result.is_a? Array
12
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
13
+ Bullet::Detector::CounterCache.add_possible_objects(result)
14
+ elsif result.is_a? ::ActiveRecord::Base
15
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(result)
16
+ Bullet::Detector::CounterCache.add_impossible_object(result)
17
+ end
18
+ end
19
+ result
20
+ end
21
+ end
22
+ end
23
+
5
24
  ::ActiveRecord::Relation.class_eval do
6
25
  alias_method :origin_to_a, :to_a
7
26
  # if select a collection of objects, then these objects have possible to cause N+1 query.
@@ -2,6 +2,25 @@ module Bullet
2
2
  module ActiveRecord
3
3
  def self.enable
4
4
  require 'active_record'
5
+ ::ActiveRecord::Base.class_eval do
6
+ class <<self
7
+ alias_method :origin_find_by_sql, :find_by_sql
8
+ def find_by_sql(sql, binds = [])
9
+ result = origin_find_by_sql(sql, binds)
10
+ if Bullet.start?
11
+ if result.is_a? Array
12
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
13
+ Bullet::Detector::CounterCache.add_possible_objects(result)
14
+ elsif result.is_a? ::ActiveRecord::Base
15
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(result)
16
+ Bullet::Detector::CounterCache.add_impossible_object(result)
17
+ end
18
+ end
19
+ result
20
+ end
21
+ end
22
+ end
23
+
5
24
  ::ActiveRecord::Relation.class_eval do
6
25
  alias_method :origin_to_a, :to_a
7
26
  # if select a collection of objects, then these objects have possible to cause N+1 query.
@@ -18,6 +18,21 @@ module Bullet
18
18
  end
19
19
  result
20
20
  end
21
+
22
+ alias_method :origin_find_by_sql, :find_by_sql
23
+ def find_by_sql(sql, binds = [])
24
+ result = origin_find_by_sql(sql, binds)
25
+ if Bullet.start?
26
+ if result.is_a? Array
27
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
28
+ Bullet::Detector::CounterCache.add_possible_objects(result)
29
+ elsif result.is_a? ::ActiveRecord::Base
30
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(result)
31
+ Bullet::Detector::CounterCache.add_impossible_object(result)
32
+ end
33
+ end
34
+ result
35
+ end
21
36
  end
22
37
  end
23
38
 
@@ -96,6 +111,7 @@ module Bullet
96
111
 
97
112
  ::ActiveRecord::Associations::JoinDependency.class_eval do
98
113
  alias_method :origin_instantiate, :instantiate
114
+ alias_method :origin_construct, :construct
99
115
  alias_method :origin_construct_model, :construct_model
100
116
 
101
117
  def instantiate(result_set, aliases)
@@ -111,6 +127,27 @@ module Bullet
111
127
  records
112
128
  end
113
129
 
130
+ def construct(ar_parent, parent, row, rs, seen, model_cache, aliases)
131
+ if Bullet.start?
132
+ unless ar_parent.nil?
133
+ parent.children.each do |node|
134
+ key = aliases.column_alias(node, node.primary_key)
135
+ id = row[key]
136
+ if id.nil?
137
+ associations = node.reflection.name
138
+ Bullet::Detector::Association.add_object_associations(ar_parent, associations)
139
+ Bullet::Detector::NPlusOneQuery.call_association(ar_parent, associations)
140
+ @bullet_eager_loadings[ar_parent.class] ||= {}
141
+ @bullet_eager_loadings[ar_parent.class][ar_parent] ||= Set.new
142
+ @bullet_eager_loadings[ar_parent.class][ar_parent] << associations
143
+ end
144
+ end
145
+ end
146
+ end
147
+
148
+ origin_construct(ar_parent, parent, row, rs, seen, model_cache, aliases)
149
+ end
150
+
114
151
  # call join associations
115
152
  def construct_model(record, node, row, model_cache, id, aliases)
116
153
  result = origin_construct_model(record, node, row, model_cache, id, aliases)
@@ -135,9 +172,7 @@ module Bullet
135
172
  records = origin_load_target
136
173
 
137
174
  if Bullet.start?
138
- if records.size > 1
139
- Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name) unless @inversed
140
- end
175
+ Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name) unless @inversed
141
176
  if records.first.class.name !~ /^HABTM_/
142
177
  if records.size > 1
143
178
  Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
@@ -20,9 +20,9 @@ module Bullet
20
20
  require 'active_record'
21
21
  ::ActiveRecord::Base.class_eval do
22
22
  class <<self
23
- alias_method :origin_find, :find
24
- def find(*args)
25
- result = origin_find(*args)
23
+ alias_method :origin_find_by_sql, :find_by_sql
24
+ def find_by_sql(sql, binds = [], preparable: nil)
25
+ result = origin_find_by_sql(sql, binds, preparable: nil)
26
26
  if Bullet.start?
27
27
  if result.is_a? Array
28
28
  Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
@@ -96,6 +96,7 @@ module Bullet
96
96
 
97
97
  ::ActiveRecord::Associations::JoinDependency.class_eval do
98
98
  alias_method :origin_instantiate, :instantiate
99
+ alias_method :origin_construct, :construct
99
100
  alias_method :origin_construct_model, :construct_model
100
101
 
101
102
  def instantiate(result_set, aliases)
@@ -111,6 +112,27 @@ module Bullet
111
112
  records
112
113
  end
113
114
 
115
+ def construct(ar_parent, parent, row, rs, seen, model_cache, aliases)
116
+ if Bullet.start?
117
+ unless ar_parent.nil?
118
+ parent.children.each do |node|
119
+ key = aliases.column_alias(node, node.primary_key)
120
+ id = row[key]
121
+ if id.nil?
122
+ associations = node.reflection.name
123
+ Bullet::Detector::Association.add_object_associations(ar_parent, associations)
124
+ Bullet::Detector::NPlusOneQuery.call_association(ar_parent, associations)
125
+ @bullet_eager_loadings[ar_parent.class] ||= {}
126
+ @bullet_eager_loadings[ar_parent.class][ar_parent] ||= Set.new
127
+ @bullet_eager_loadings[ar_parent.class][ar_parent] << associations
128
+ end
129
+ end
130
+ end
131
+ end
132
+
133
+ origin_construct(ar_parent, parent, row, rs, seen, model_cache, aliases)
134
+ end
135
+
114
136
  # call join associations
115
137
  def construct_model(record, node, row, model_cache, id, aliases)
116
138
  result = origin_construct_model(record, node, row, model_cache, id, aliases)
@@ -135,16 +157,14 @@ module Bullet
135
157
  records = origin_load_target
136
158
 
137
159
  if Bullet.start?
138
- if records.size > 1
139
- if self.is_a? ::ActiveRecord::Associations::ThroughAssociation
140
- Bullet::Detector::NPlusOneQuery.call_association(owner, through_reflection.name)
141
- association = self.owner.association self.through_reflection.name
142
- association.target.each do |through_record|
143
- Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
144
- end
160
+ if self.is_a? ::ActiveRecord::Associations::ThroughAssociation
161
+ Bullet::Detector::NPlusOneQuery.call_association(owner, through_reflection.name)
162
+ association = self.owner.association self.through_reflection.name
163
+ Array(association.target).each do |through_record|
164
+ Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
145
165
  end
146
- Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name) unless @inversed
147
166
  end
167
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name) unless @inversed
148
168
  if records.first.class.name !~ /^HABTM_/
149
169
  if records.size > 1
150
170
  Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
@@ -1,4 +1,4 @@
1
1
  # encoding: utf-8
2
2
  module Bullet
3
- VERSION = "5.2.0"
3
+ VERSION = "5.3.0"
4
4
  end
data/lib/bullet.rb CHANGED
@@ -166,8 +166,9 @@ module Bullet
166
166
  end
167
167
 
168
168
  def perform_out_of_channel_notifications(env = {})
169
+ request_uri = env['REQUEST_URI'] || build_request_uri(env)
169
170
  for_each_active_notifier_with_notification do |notification|
170
- notification.url = env['REQUEST_URI']
171
+ notification.url = request_uri
171
172
  notification.notify_out_of_channel
172
173
  end
173
174
  end
@@ -217,5 +218,13 @@ module Bullet
217
218
  end
218
219
  end
219
220
  end
221
+
222
+ def build_request_uri(env)
223
+ if env['QUERY_STRING'].present?
224
+ "#{env['PATH_INFO']}?#{env['QUERY_STRING']}"
225
+ else
226
+ env['PATH_INFO']
227
+ end
228
+ end
220
229
  end
221
230
  end
@@ -60,7 +60,7 @@ module Bullet
60
60
  it "should return original response body" do
61
61
  expected_response = Support::ResponseDouble.new "Actual body"
62
62
  app.response = expected_response
63
- _, _, response = middleware.call([])
63
+ _, _, response = middleware.call({})
64
64
  expect(response).to eq(expected_response)
65
65
  end
66
66
 
@@ -68,7 +68,7 @@ module Bullet
68
68
  expect(Bullet).to receive(:notification?).and_return(true)
69
69
  expect(Bullet).to receive(:gather_inline_notifications).and_return("<bullet></bullet>")
70
70
  expect(Bullet).to receive(:perform_out_of_channel_notifications)
71
- status, headers, response = middleware.call([200, {"Content-Type" => "text/html"}])
71
+ status, headers, response = middleware.call({"Content-Type" => "text/html"})
72
72
  expect(headers["Content-Length"]).to eq("56")
73
73
  expect(response).to eq(["<html><head></head><body><bullet></bullet></body></html>"])
74
74
  end
@@ -79,7 +79,7 @@ module Bullet
79
79
  app.response = response
80
80
  expect(Bullet).to receive(:notification?).and_return(true)
81
81
  expect(Bullet).to receive(:gather_inline_notifications).and_return("<bullet></bullet>")
82
- status, headers, response = middleware.call([200, {"Content-Type" => "text/html"}])
82
+ status, headers, response = middleware.call({"Content-Type" => "text/html"})
83
83
  expect(headers["Content-Length"]).to eq("58")
84
84
  end
85
85
  end
@@ -89,7 +89,7 @@ module Bullet
89
89
 
90
90
  it "should not call Bullet.start_request" do
91
91
  expect(Bullet).not_to receive(:start_request)
92
- middleware.call([])
92
+ middleware.call({})
93
93
  end
94
94
  end
95
95
  end
data/spec/bullet_spec.rb CHANGED
@@ -94,4 +94,47 @@ describe Bullet, focused: true do
94
94
  end
95
95
  end
96
96
  end
97
+
98
+ describe '#perform_out_of_channel_notifications' do
99
+ let(:notification) { double }
100
+
101
+ before do
102
+ allow(Bullet).to receive(:for_each_active_notifier_with_notification).and_yield(notification)
103
+ allow(notification).to receive(:notify_out_of_channel)
104
+ end
105
+
106
+ context 'when called with no args' do
107
+ it 'should notification.url is nil' do
108
+ expect(notification).to receive(:url=).with(nil)
109
+ Bullet.perform_out_of_channel_notifications
110
+ end
111
+ end
112
+
113
+ context 'when called with Rack environment hash' do
114
+ let(:env) {
115
+ {
116
+ 'PATH_INFO' => '/path',
117
+ 'QUERY_STRING' => 'foo=bar',
118
+ }
119
+ }
120
+
121
+ context "when env['REQUEST_URI'] is nil" do
122
+ before { env['REQUEST_URI'] = nil }
123
+
124
+ it 'should notification.url is built' do
125
+ expect(notification).to receive(:url=).with('/path?foo=bar')
126
+ Bullet.perform_out_of_channel_notifications(env)
127
+ end
128
+ end
129
+
130
+ context "when env['REQUEST_URI'] is present" do
131
+ before { env['REQUEST_URI'] = 'http://example.com/path' }
132
+
133
+ it "should notification.url is env['REQUEST_URI']" do
134
+ expect(notification).to receive(:url=).with(env['REQUEST_URI'])
135
+ Bullet.perform_out_of_channel_notifications(env)
136
+ end
137
+ end
138
+ end
139
+ end
97
140
  end
@@ -13,6 +13,16 @@ if !mongoid? && active_record3?
13
13
  expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(Post, :comments)
14
14
  end
15
15
 
16
+ it "should detect non preload post => comments for find_by_sql" do
17
+ Post.find_by_sql("SELECT * FROM posts").each do |post|
18
+ post.comments.map(&:name)
19
+ end
20
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
21
+ expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
22
+
23
+ expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(Post, :comments)
24
+ end
25
+
16
26
  it "should detect preload with post => comments" do
17
27
  Post.includes(:comments).each do |post|
18
28
  post.comments.map(&:name)
@@ -13,6 +13,16 @@ if !mongoid? && active_record4?
13
13
  expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(Post, :comments)
14
14
  end
15
15
 
16
+ it "should detect non preload post => comments for find_by_sql" do
17
+ Post.find_by_sql("SELECT * FROM posts").each do |post|
18
+ post.comments.map(&:name)
19
+ end
20
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
21
+ expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
22
+
23
+ expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(Post, :comments)
24
+ end
25
+
16
26
  it "should detect preload with post => comments" do
17
27
  Post.includes(:comments).each do |post|
18
28
  post.comments.map(&:name)
@@ -13,6 +13,16 @@ if !mongoid? && active_record5?
13
13
  expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(Post, :comments)
14
14
  end
15
15
 
16
+ it "should detect non preload post => comments for find_by_sql" do
17
+ Post.find_by_sql("SELECT * FROM posts").each do |post|
18
+ post.comments.map(&:name)
19
+ end
20
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
21
+ expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
22
+
23
+ expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(Post, :comments)
24
+ end
25
+
16
26
  it "should detect preload with post => comments" do
17
27
  Post.includes(:comments).each do |post|
18
28
  post.comments.map(&:name)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bullet
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.2.0
4
+ version: 5.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Richard Huang
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-07-26 00:00:00.000000000 Z
11
+ date: 2016-08-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport