bullet 4.13.1 → 4.14.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 +4 -4
- data/.travis.yml +1 -0
- data/CHANGELOG.md +4 -2
- data/Gemfile.rails-4.0 +1 -1
- data/Gemfile.rails-4.1 +1 -1
- data/Gemfile.rails-4.2 +19 -0
- data/lib/bullet/active_record42.rb +149 -0
- data/lib/bullet/dependency.rb +6 -0
- data/lib/bullet/detector/unused_eager_loading.rb +1 -1
- data/lib/bullet/ext/object.rb +1 -1
- data/lib/bullet/notification/base.rb +6 -10
- data/lib/bullet/notification/n_plus_one_query.rb +0 -4
- data/lib/bullet/rack.rb +3 -4
- data/lib/bullet/version.rb +1 -1
- data/lib/bullet.rb +1 -1
- data/spec/bullet/notification/base_spec.rb +2 -27
- data/spec/bullet/notification/n_plus_one_query_spec.rb +2 -2
- data/test.sh +1 -0
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cb37955eb9271242e6c93d070639487d27c41cc4
|
|
4
|
+
data.tar.gz: 3dcfaee4517be454ae076b8707b479357aef4810
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: aad803335d34af96636af55b2db42e93770c1d989485bad7bcd6a5ac6b78c1cd95d5c133913d2fb606dd9cfedec078785a83d175dac42ebd408595866b791679
|
|
7
|
+
data.tar.gz: e53e36ef7782a6e94079b128eaebecde19501ce26c0204dd35dbc7df53b81313d0f8fa22c0a445c1f10f5f163cf23afcaa29c998279963667ca7b7f34cdc7c66
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
data/Gemfile.rails-4.0
CHANGED
data/Gemfile.rails-4.1
CHANGED
data/Gemfile.rails-4.2
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
source "https://rubygems.org"
|
|
2
|
+
|
|
3
|
+
gemspec
|
|
4
|
+
|
|
5
|
+
gem 'rails', '~> 4.2.0.beta2'
|
|
6
|
+
gem 'sqlite3'
|
|
7
|
+
gem 'activerecord-jdbcsqlite3-adapter', platforms: [:jruby]
|
|
8
|
+
gem 'activerecord-import'
|
|
9
|
+
|
|
10
|
+
gem "rspec"
|
|
11
|
+
gem "guard"
|
|
12
|
+
gem "guard-rspec"
|
|
13
|
+
|
|
14
|
+
gem 'coveralls', require: false
|
|
15
|
+
|
|
16
|
+
platforms :rbx do
|
|
17
|
+
gem 'rubysl', '~> 2.0'
|
|
18
|
+
gem 'rubinius-developer_tools'
|
|
19
|
+
end
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
module Bullet
|
|
2
|
+
module ActiveRecord
|
|
3
|
+
def self.enable
|
|
4
|
+
require 'active_record'
|
|
5
|
+
::ActiveRecord::Relation.class_eval do
|
|
6
|
+
alias_method :origin_to_a, :to_a
|
|
7
|
+
# if select a collection of objects, then these objects have possible to cause N+1 query.
|
|
8
|
+
# if select only one object, then the only one object has impossible to cause N+1 query.
|
|
9
|
+
def to_a
|
|
10
|
+
records = origin_to_a
|
|
11
|
+
if records.first.class.name !~ /^HABTM_/
|
|
12
|
+
if records.size > 1
|
|
13
|
+
Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
|
|
14
|
+
Bullet::Detector::CounterCache.add_possible_objects(records)
|
|
15
|
+
elsif records.size == 1
|
|
16
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
|
|
17
|
+
Bullet::Detector::CounterCache.add_impossible_object(records.first)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
records
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
::ActiveRecord::Associations::Preloader.class_eval do
|
|
25
|
+
alias_method :origin_preloaders_on, :preloaders_on
|
|
26
|
+
|
|
27
|
+
def preloaders_on(association, records, scope)
|
|
28
|
+
records.compact!
|
|
29
|
+
if records.first.class.name !~ /^HABTM_/
|
|
30
|
+
records.each do |record|
|
|
31
|
+
Bullet::Detector::Association.add_object_associations(record, association)
|
|
32
|
+
end
|
|
33
|
+
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, association)
|
|
34
|
+
end
|
|
35
|
+
origin_preloaders_on(association, records, scope)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
::ActiveRecord::FinderMethods.class_eval do
|
|
40
|
+
# add includes in scope
|
|
41
|
+
alias_method :origin_find_with_associations, :find_with_associations
|
|
42
|
+
def find_with_associations
|
|
43
|
+
records = origin_find_with_associations
|
|
44
|
+
associations = (eager_load_values + includes_values).uniq
|
|
45
|
+
records.each do |record|
|
|
46
|
+
Bullet::Detector::Association.add_object_associations(record, associations)
|
|
47
|
+
end
|
|
48
|
+
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, associations)
|
|
49
|
+
records
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
::ActiveRecord::Associations::JoinDependency.class_eval do
|
|
54
|
+
alias_method :origin_instantiate, :instantiate
|
|
55
|
+
alias_method :origin_construct_model, :construct_model
|
|
56
|
+
|
|
57
|
+
def instantiate(result_set, aliases)
|
|
58
|
+
@bullet_eager_loadings = {}
|
|
59
|
+
records = origin_instantiate(result_set, aliases)
|
|
60
|
+
|
|
61
|
+
@bullet_eager_loadings.each do |klazz, eager_loadings_hash|
|
|
62
|
+
objects = eager_loadings_hash.keys
|
|
63
|
+
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(objects, eager_loadings_hash[objects.first].to_a)
|
|
64
|
+
end
|
|
65
|
+
records
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# call join associations
|
|
69
|
+
def construct_model(record, node, row, model_cache, id, aliases)
|
|
70
|
+
result = origin_construct_model(record, node, row, model_cache, id, aliases)
|
|
71
|
+
|
|
72
|
+
associations = node.reflection.name
|
|
73
|
+
Bullet::Detector::Association.add_object_associations(record, associations)
|
|
74
|
+
Bullet::Detector::NPlusOneQuery.call_association(record, associations)
|
|
75
|
+
@bullet_eager_loadings[record.class] ||= {}
|
|
76
|
+
@bullet_eager_loadings[record.class][record] ||= Set.new
|
|
77
|
+
@bullet_eager_loadings[record.class][record] << associations
|
|
78
|
+
|
|
79
|
+
result
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
::ActiveRecord::Associations::CollectionAssociation.class_eval do
|
|
84
|
+
# call one to many associations
|
|
85
|
+
alias_method :origin_load_target, :load_target
|
|
86
|
+
def load_target
|
|
87
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name) unless @inversed
|
|
88
|
+
records = origin_load_target
|
|
89
|
+
|
|
90
|
+
if records.first.class.name !~ /^HABTM_/
|
|
91
|
+
if records.size > 1
|
|
92
|
+
Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
|
|
93
|
+
Bullet::Detector::CounterCache.add_possible_objects(records)
|
|
94
|
+
elsif records.size == 1
|
|
95
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
|
|
96
|
+
Bullet::Detector::CounterCache.add_impossible_object(records.first)
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
records
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
alias_method :origin_empty?, :empty?
|
|
103
|
+
def empty?
|
|
104
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
|
|
105
|
+
origin_empty?
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
alias_method :origin_include?, :include?
|
|
109
|
+
def include?(object)
|
|
110
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
|
|
111
|
+
origin_include?(object)
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
::ActiveRecord::Associations::SingularAssociation.class_eval do
|
|
116
|
+
# call has_one and belongs_to associations
|
|
117
|
+
alias_method :origin_reader, :reader
|
|
118
|
+
def reader(force_reload = false)
|
|
119
|
+
result = origin_reader(force_reload)
|
|
120
|
+
if @owner.class.name !~ /^HABTM_/
|
|
121
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name) unless @inversed
|
|
122
|
+
Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
|
|
123
|
+
end
|
|
124
|
+
result
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
::ActiveRecord::Associations::HasManyAssociation.class_eval do
|
|
129
|
+
alias_method :origin_many_empty?, :empty?
|
|
130
|
+
def empty?
|
|
131
|
+
Thread.current[:bullet_collection_empty] = true
|
|
132
|
+
result = origin_many_empty?
|
|
133
|
+
Thread.current[:bullet_collection_empty] = nil
|
|
134
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
|
|
135
|
+
result
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
alias_method :origin_has_cached_counter?, :has_cached_counter?
|
|
139
|
+
def has_cached_counter?(reflection = reflection())
|
|
140
|
+
result = origin_has_cached_counter?(reflection)
|
|
141
|
+
if !result && !Thread.current[:bullet_collection_empty]
|
|
142
|
+
Bullet::Detector::CounterCache.add_counter_cache(owner, reflection.name)
|
|
143
|
+
end
|
|
144
|
+
result
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
data/lib/bullet/dependency.rb
CHANGED
|
@@ -22,6 +22,8 @@ module Bullet
|
|
|
22
22
|
'active_record4'
|
|
23
23
|
elsif active_record41?
|
|
24
24
|
'active_record41'
|
|
25
|
+
elsif active_record42?
|
|
26
|
+
'active_record42'
|
|
25
27
|
end
|
|
26
28
|
end
|
|
27
29
|
end
|
|
@@ -66,6 +68,10 @@ module Bullet
|
|
|
66
68
|
active_record4? && ::ActiveRecord::VERSION::MINOR == 1
|
|
67
69
|
end
|
|
68
70
|
|
|
71
|
+
def active_record42?
|
|
72
|
+
active_record4? && ::ActiveRecord::VERSION::MINOR == 2
|
|
73
|
+
end
|
|
74
|
+
|
|
69
75
|
def mongoid2x?
|
|
70
76
|
mongoid? && ::Mongoid::VERSION =~ /\A2\.[4-8]/
|
|
71
77
|
end
|
data/lib/bullet/ext/object.rb
CHANGED
|
@@ -18,6 +18,10 @@ module Bullet
|
|
|
18
18
|
raise NoMethodError.new("no method body defined")
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
+
def call_stack_messages
|
|
22
|
+
""
|
|
23
|
+
end
|
|
24
|
+
|
|
21
25
|
def whoami
|
|
22
26
|
@user ||= ENV['USER'].presence || (`whoami`.chomp rescue "")
|
|
23
27
|
if @user.present?
|
|
@@ -28,15 +32,7 @@ module Bullet
|
|
|
28
32
|
end
|
|
29
33
|
|
|
30
34
|
def body_with_caller
|
|
31
|
-
body
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
def standard_notice
|
|
35
|
-
@standard_notifice ||= title + "\n" + body
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
def full_notice
|
|
39
|
-
[whoami.presence, url, title, body_with_caller].compact.join("\n")
|
|
35
|
+
"#{body}\n#{call_stack_messages}\n"
|
|
40
36
|
end
|
|
41
37
|
|
|
42
38
|
def notify_inline
|
|
@@ -48,7 +44,7 @@ module Bullet
|
|
|
48
44
|
end
|
|
49
45
|
|
|
50
46
|
def short_notice
|
|
51
|
-
[whoami.presence, url, title, body].compact.join("
|
|
47
|
+
[whoami.presence, url, title, body].compact.join(" ")
|
|
52
48
|
end
|
|
53
49
|
|
|
54
50
|
def notification_data
|
data/lib/bullet/rack.rb
CHANGED
|
@@ -43,7 +43,7 @@ module Bullet
|
|
|
43
43
|
end
|
|
44
44
|
|
|
45
45
|
def add_footer_note(response_body)
|
|
46
|
-
response_body << "<div #{
|
|
46
|
+
response_body << "<div #{footer_div_attributes}>" + Bullet.footer_info.uniq.join("<br>") + "</div>"
|
|
47
47
|
end
|
|
48
48
|
|
|
49
49
|
def file?(headers)
|
|
@@ -67,9 +67,9 @@ module Bullet
|
|
|
67
67
|
end
|
|
68
68
|
|
|
69
69
|
private
|
|
70
|
-
def
|
|
70
|
+
def footer_div_attributes
|
|
71
71
|
<<EOF
|
|
72
|
-
style="position: fixed; bottom: 0pt; left: 0pt; cursor: pointer; border-style: solid; border-color: rgb(153, 153, 153);
|
|
72
|
+
data-is-bullet-footer style="position: fixed; bottom: 0pt; left: 0pt; cursor: pointer; border-style: solid; border-color: rgb(153, 153, 153);
|
|
73
73
|
-moz-border-top-colors: none; -moz-border-right-colors: none; -moz-border-bottom-colors: none;
|
|
74
74
|
-moz-border-left-colors: none; -moz-border-image: none; border-width: 2pt 2pt 0px 0px;
|
|
75
75
|
padding: 5px; border-radius: 0pt 10pt 0pt 0px; background: none repeat scroll 0% 0% rgba(200, 200, 200, 0.8);
|
|
@@ -78,4 +78,3 @@ EOF
|
|
|
78
78
|
end
|
|
79
79
|
end
|
|
80
80
|
end
|
|
81
|
-
|
data/lib/bullet/version.rb
CHANGED
data/lib/bullet.rb
CHANGED
|
@@ -153,7 +153,7 @@ module Bullet
|
|
|
153
153
|
|
|
154
154
|
def perform_out_of_channel_notifications(env = {})
|
|
155
155
|
for_each_active_notifier_with_notification do |notification|
|
|
156
|
-
notification.url =
|
|
156
|
+
notification.url = env['REQUEST_URI']
|
|
157
157
|
notification.notify_out_of_channel
|
|
158
158
|
end
|
|
159
159
|
end
|
|
@@ -56,33 +56,8 @@ module Bullet
|
|
|
56
56
|
context "#body_with_caller" do
|
|
57
57
|
it "should return body" do
|
|
58
58
|
allow(subject).to receive(:body).and_return("body")
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
context "#standard_notice" do
|
|
64
|
-
it "should return title + body" do
|
|
65
|
-
allow(subject).to receive(:title).and_return("title")
|
|
66
|
-
allow(subject).to receive(:body).and_return("body")
|
|
67
|
-
expect(subject.standard_notice).to eq("title\nbody")
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
context "#full_notice" do
|
|
72
|
-
it "should return whoami + url + title + body_with_caller" do
|
|
73
|
-
allow(subject).to receive(:whoami).and_return("whoami")
|
|
74
|
-
allow(subject).to receive(:url).and_return("url")
|
|
75
|
-
allow(subject).to receive(:title).and_return("title")
|
|
76
|
-
allow(subject).to receive(:body_with_caller).and_return("body_with_caller")
|
|
77
|
-
expect(subject.full_notice).to eq("whoami\nurl\ntitle\nbody_with_caller")
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
it "should return url + title + body_with_caller" do
|
|
81
|
-
allow(subject).to receive(:whoami).and_return("")
|
|
82
|
-
allow(subject).to receive(:url).and_return("url")
|
|
83
|
-
allow(subject).to receive(:title).and_return("title")
|
|
84
|
-
allow(subject).to receive(:body_with_caller).and_return("body_with_caller")
|
|
85
|
-
expect(subject.full_notice).to eq("url\ntitle\nbody_with_caller")
|
|
59
|
+
allow(subject).to receive(:call_stack_messages).and_return("call_stack_messages")
|
|
60
|
+
expect(subject.body_with_caller).to eq("body\ncall_stack_messages\n")
|
|
86
61
|
end
|
|
87
62
|
end
|
|
88
63
|
|
|
@@ -5,8 +5,8 @@ module Bullet
|
|
|
5
5
|
describe NPlusOneQuery do
|
|
6
6
|
subject { NPlusOneQuery.new([["caller1", "caller2"]], Post, [:comments, :votes], "path") }
|
|
7
7
|
|
|
8
|
-
it { expect(subject.body_with_caller).to eq(" Post => [:comments, :votes]\n Add to your finder: :include => [:comments, :votes]\nN+1 Query method call stack\n caller1\n caller2") }
|
|
9
|
-
it { expect([ subject.body_with_caller, subject.body_with_caller]).to eq([ " Post => [:comments, :votes]\n Add to your finder: :include => [:comments, :votes]\nN+1 Query method call stack\n caller1\n caller2", " Post => [:comments, :votes]\n Add to your finder: :include => [:comments, :votes]\nN+1 Query method call stack\n caller1\n caller2" ]) }
|
|
8
|
+
it { expect(subject.body_with_caller).to eq(" Post => [:comments, :votes]\n Add to your finder: :include => [:comments, :votes]\nN+1 Query method call stack\n caller1\n caller2\n") }
|
|
9
|
+
it { expect([ subject.body_with_caller, subject.body_with_caller]).to eq([ " Post => [:comments, :votes]\n Add to your finder: :include => [:comments, :votes]\nN+1 Query method call stack\n caller1\n caller2\n", " Post => [:comments, :votes]\n Add to your finder: :include => [:comments, :votes]\nN+1 Query method call stack\n caller1\n caller2\n" ]) }
|
|
10
10
|
it { expect(subject.body).to eq(" Post => [:comments, :votes]\n Add to your finder: :include => [:comments, :votes]") }
|
|
11
11
|
it { expect(subject.title).to eq("N+1 Query in path") }
|
|
12
12
|
end
|
data/test.sh
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#bundle update rails && bundle exec rspec spec
|
|
2
2
|
#BUNDLE_GEMFILE=Gemfile.mongoid bundle update mongoid && BUNDLE_GEMFILE=Gemfile.mongoid bundle exec rspec spec
|
|
3
|
+
BUNDLE_GEMFILE=Gemfile.rails-4.2 bundle && BUNDLE_GEMFILE=Gemfile.rails-4.2 bundle exec rspec spec
|
|
3
4
|
BUNDLE_GEMFILE=Gemfile.rails-4.1 bundle && BUNDLE_GEMFILE=Gemfile.rails-4.1 bundle exec rspec spec
|
|
4
5
|
BUNDLE_GEMFILE=Gemfile.rails-4.0 bundle && BUNDLE_GEMFILE=Gemfile.rails-4.0 bundle exec rspec spec
|
|
5
6
|
BUNDLE_GEMFILE=Gemfile.rails-3.2 bundle && BUNDLE_GEMFILE=Gemfile.rails-3.2 bundle exec rspec spec
|
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: 4.
|
|
4
|
+
version: 4.14.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: 2014-
|
|
11
|
+
date: 2014-10-03 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activesupport
|
|
@@ -64,6 +64,7 @@ files:
|
|
|
64
64
|
- Gemfile.rails-3.2
|
|
65
65
|
- Gemfile.rails-4.0
|
|
66
66
|
- Gemfile.rails-4.1
|
|
67
|
+
- Gemfile.rails-4.2
|
|
67
68
|
- Guardfile
|
|
68
69
|
- Hacking.md
|
|
69
70
|
- MIT-LICENSE
|
|
@@ -75,6 +76,7 @@ files:
|
|
|
75
76
|
- lib/bullet/active_record3x.rb
|
|
76
77
|
- lib/bullet/active_record4.rb
|
|
77
78
|
- lib/bullet/active_record41.rb
|
|
79
|
+
- lib/bullet/active_record42.rb
|
|
78
80
|
- lib/bullet/dependency.rb
|
|
79
81
|
- lib/bullet/detector.rb
|
|
80
82
|
- lib/bullet/detector/association.rb
|