bullet 4.13.1 → 5.5.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/.gitignore +1 -0
- data/.travis.yml +7 -11
- data/CHANGELOG.md +62 -3
- data/Gemfile.mongoid +0 -2
- data/Gemfile.mongoid-4.0 +1 -5
- data/{Gemfile.mongoid-2.8 → Gemfile.mongoid-5.0} +2 -6
- data/{Gemfile.mongoid-2.5 → Gemfile.mongoid-6.0} +2 -6
- data/Gemfile.rails-4.0 +2 -5
- data/Gemfile.rails-4.1 +2 -5
- data/{Gemfile.rails-3.0 → Gemfile.rails-4.2} +3 -6
- data/{Gemfile.rails-3.1 → Gemfile.rails-5.0} +2 -6
- data/Hacking.md +1 -0
- data/README.md +39 -7
- data/bullet.gemspec +1 -1
- data/lib/bullet/active_record4.rb +109 -33
- data/lib/bullet/active_record41.rb +99 -37
- data/lib/bullet/active_record42.rb +251 -0
- data/lib/bullet/active_record5.rb +243 -0
- data/lib/bullet/dependency.rb +34 -28
- data/lib/bullet/detector/association.rb +16 -16
- data/lib/bullet/detector/counter_cache.rb +13 -13
- data/lib/bullet/detector/n_plus_one_query.rb +30 -35
- data/lib/bullet/detector/unused_eager_loading.rb +12 -11
- data/lib/bullet/ext/object.rb +4 -2
- data/lib/bullet/{mongoid3x.rb → mongoid5x.rb} +3 -3
- data/lib/bullet/{mongoid2x.rb → mongoid6x.rb} +12 -12
- data/lib/bullet/notification/base.rb +9 -13
- data/lib/bullet/notification/n_plus_one_query.rb +8 -6
- data/lib/bullet/notification/unused_eager_loading.rb +18 -1
- data/lib/bullet/rack.rb +28 -16
- data/lib/bullet/stack_trace_filter.rb +34 -0
- data/lib/bullet/version.rb +1 -1
- data/lib/bullet.rb +48 -14
- data/spec/bullet/detector/counter_cache_spec.rb +8 -8
- data/spec/bullet/detector/n_plus_one_query_spec.rb +46 -31
- data/spec/bullet/detector/unused_eager_loading_spec.rb +17 -3
- data/spec/bullet/ext/object_spec.rb +6 -0
- data/spec/bullet/notification/base_spec.rb +4 -29
- data/spec/bullet/notification/n_plus_one_query_spec.rb +4 -4
- data/spec/bullet/notification/unused_eager_loading_spec.rb +3 -3
- data/spec/bullet/rack_spec.rb +38 -7
- data/spec/bullet_spec.rb +93 -0
- data/spec/integration/active_record4/association_spec.rb +112 -4
- data/spec/integration/{active_record3 → active_record5}/association_spec.rb +121 -15
- data/spec/integration/counter_cache_spec.rb +24 -1
- data/spec/models/category.rb +4 -1
- data/spec/models/comment.rb +2 -0
- data/spec/models/post.rb +7 -2
- data/spec/models/reply.rb +3 -0
- data/spec/models/submission.rb +1 -1
- data/spec/spec_helper.rb +3 -5
- data/spec/support/mongo_seed.rb +13 -0
- data/spec/support/sqlite_seed.rb +15 -6
- data/test.sh +4 -10
- data/update.sh +7 -0
- metadata +21 -23
- data/Gemfile.mongoid-2.4 +0 -19
- data/Gemfile.mongoid-2.6 +0 -19
- data/Gemfile.mongoid-2.7 +0 -19
- data/Gemfile.mongoid-3.0 +0 -19
- data/Gemfile.mongoid-3.1 +0 -19
- data/Gemfile.rails-3.2 +0 -19
- data/lib/bullet/active_record3.rb +0 -154
- data/lib/bullet/active_record3x.rb +0 -134
|
@@ -2,11 +2,10 @@ module Bullet
|
|
|
2
2
|
module Mongoid
|
|
3
3
|
def self.enable
|
|
4
4
|
require 'mongoid'
|
|
5
|
-
|
|
6
|
-
::Mongoid::Contexts::Mongo.class_eval do
|
|
5
|
+
::Mongoid::Contextual::Mongo.class_eval do
|
|
7
6
|
alias_method :origin_first, :first
|
|
8
7
|
alias_method :origin_last, :last
|
|
9
|
-
alias_method :
|
|
8
|
+
alias_method :origin_each, :each
|
|
10
9
|
alias_method :origin_eager_load, :eager_load
|
|
11
10
|
|
|
12
11
|
def first
|
|
@@ -21,20 +20,20 @@ module Bullet
|
|
|
21
20
|
result
|
|
22
21
|
end
|
|
23
22
|
|
|
24
|
-
def
|
|
25
|
-
records =
|
|
26
|
-
if records.
|
|
23
|
+
def each(&block)
|
|
24
|
+
records = view.map{ |doc| ::Mongoid::Factory.from_db(klass, doc) }
|
|
25
|
+
if records.length > 1
|
|
27
26
|
Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
|
|
28
27
|
elsif records.size == 1
|
|
29
28
|
Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
|
|
30
29
|
end
|
|
31
|
-
|
|
30
|
+
origin_each(&block)
|
|
32
31
|
end
|
|
33
32
|
|
|
34
33
|
def eager_load(docs)
|
|
35
34
|
associations = criteria.inclusions.map(&:name)
|
|
36
35
|
docs.each do |doc|
|
|
37
|
-
Bullet::Detector::
|
|
36
|
+
Bullet::Detector::NPlusOneQuery.add_object_associations(doc, associations)
|
|
38
37
|
end
|
|
39
38
|
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(docs, associations)
|
|
40
39
|
origin_eager_load(docs)
|
|
@@ -42,13 +41,14 @@ module Bullet
|
|
|
42
41
|
end
|
|
43
42
|
|
|
44
43
|
::Mongoid::Relations::Accessors.class_eval do
|
|
45
|
-
alias_method :
|
|
44
|
+
alias_method :origin_get_relation, :get_relation
|
|
46
45
|
|
|
47
|
-
def
|
|
48
|
-
|
|
46
|
+
def get_relation(name, metadata, object, reload = false)
|
|
47
|
+
result = origin_get_relation(name, metadata, object, reload)
|
|
48
|
+
if metadata.macro !~ /embed/
|
|
49
49
|
Bullet::Detector::NPlusOneQuery.call_association(self, name)
|
|
50
50
|
end
|
|
51
|
-
|
|
51
|
+
result
|
|
52
52
|
end
|
|
53
53
|
end
|
|
54
54
|
end
|
|
@@ -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
|
|
@@ -61,11 +57,11 @@ module Bullet
|
|
|
61
57
|
end
|
|
62
58
|
|
|
63
59
|
def eql?(other)
|
|
64
|
-
klazz_associations_str == other.klazz_associations_str
|
|
60
|
+
self.class == other.class && klazz_associations_str == other.klazz_associations_str
|
|
65
61
|
end
|
|
66
62
|
|
|
67
63
|
def hash
|
|
68
|
-
klazz_associations_str.hash
|
|
64
|
+
[self.class, klazz_associations_str].hash
|
|
69
65
|
end
|
|
70
66
|
|
|
71
67
|
protected
|
|
@@ -74,7 +70,7 @@ module Bullet
|
|
|
74
70
|
end
|
|
75
71
|
|
|
76
72
|
def associations_str
|
|
77
|
-
":
|
|
73
|
+
":includes => #{@associations.map{ |a| a.to_s.to_sym unless a.is_a? Hash }.inspect}"
|
|
78
74
|
end
|
|
79
75
|
end
|
|
80
76
|
end
|
|
@@ -7,21 +7,23 @@ module Bullet
|
|
|
7
7
|
@callers = callers
|
|
8
8
|
end
|
|
9
9
|
|
|
10
|
-
def body_with_caller
|
|
11
|
-
"#{body}\n#{call_stack_messages}"
|
|
12
|
-
end
|
|
13
|
-
|
|
14
10
|
def body
|
|
15
11
|
"#{klazz_associations_str}\n Add to your finder: #{associations_str}"
|
|
16
12
|
end
|
|
17
13
|
|
|
18
14
|
def title
|
|
19
|
-
"
|
|
15
|
+
"USE eager loading #{@path ? "in #{@path}" : 'detected'}"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def notification_data
|
|
19
|
+
super.merge(
|
|
20
|
+
:backtrace => []
|
|
21
|
+
)
|
|
20
22
|
end
|
|
21
23
|
|
|
22
24
|
protected
|
|
23
25
|
def call_stack_messages
|
|
24
|
-
(['
|
|
26
|
+
(['Call stack'] + @callers).join( "\n " )
|
|
25
27
|
end
|
|
26
28
|
end
|
|
27
29
|
end
|
|
@@ -1,13 +1,30 @@
|
|
|
1
1
|
module Bullet
|
|
2
2
|
module Notification
|
|
3
3
|
class UnusedEagerLoading < Base
|
|
4
|
+
def initialize(callers, base_class, associations, path = nil)
|
|
5
|
+
super(base_class, associations, path)
|
|
6
|
+
|
|
7
|
+
@callers = callers
|
|
8
|
+
end
|
|
9
|
+
|
|
4
10
|
def body
|
|
5
11
|
"#{klazz_associations_str}\n Remove from your finder: #{associations_str}"
|
|
6
12
|
end
|
|
7
13
|
|
|
8
14
|
def title
|
|
9
|
-
"
|
|
15
|
+
"AVOID eager loading #{@path ? "in #{@path}" : 'detected'}"
|
|
10
16
|
end
|
|
17
|
+
|
|
18
|
+
def notification_data
|
|
19
|
+
super.merge(
|
|
20
|
+
:backtrace => []
|
|
21
|
+
)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
protected
|
|
25
|
+
def call_stack_messages
|
|
26
|
+
(['Call stack'] + @callers).join( "\n " )
|
|
27
|
+
end
|
|
11
28
|
end
|
|
12
29
|
end
|
|
13
30
|
end
|
data/lib/bullet/rack.rb
CHANGED
|
@@ -10,17 +10,16 @@ module Bullet
|
|
|
10
10
|
return @app.call(env) unless Bullet.enable?
|
|
11
11
|
Bullet.start_request
|
|
12
12
|
status, headers, response = @app.call(env)
|
|
13
|
-
return [status, headers, response] if file?(headers) || sse?(response) || empty?(response)
|
|
14
13
|
|
|
15
14
|
response_body = nil
|
|
16
15
|
if Bullet.notification?
|
|
17
|
-
if
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
if !file?(headers) && !sse?(headers) && !empty?(response) &&
|
|
17
|
+
status == 200 && !response_body(response).frozen? && html_request?(headers, response)
|
|
18
|
+
response_body = response_body(response)
|
|
19
|
+
append_to_html_body(response_body, footer_note) if Bullet.add_footer
|
|
20
|
+
append_to_html_body(response_body, Bullet.gather_inline_notifications)
|
|
20
21
|
headers['Content-Length'] = response_body.bytesize.to_s
|
|
21
22
|
end
|
|
22
|
-
end
|
|
23
|
-
if Bullet.enable? && Bullet.notification?
|
|
24
23
|
Bullet.perform_out_of_channel_notifications(env)
|
|
25
24
|
end
|
|
26
25
|
[status, headers, response_body ? [response_body] : response]
|
|
@@ -42,16 +41,25 @@ module Bullet
|
|
|
42
41
|
end
|
|
43
42
|
end
|
|
44
43
|
|
|
45
|
-
def
|
|
46
|
-
response_body
|
|
44
|
+
def append_to_html_body(response_body, content)
|
|
45
|
+
if response_body.include?('</body>')
|
|
46
|
+
position = response_body.rindex('</body>')
|
|
47
|
+
response_body.insert(position, content)
|
|
48
|
+
else
|
|
49
|
+
response_body << content
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def footer_note
|
|
54
|
+
"<div #{footer_div_attributes}>" + footer_close_button + Bullet.footer_info.uniq.join("<br>") + "</div>"
|
|
47
55
|
end
|
|
48
56
|
|
|
49
57
|
def file?(headers)
|
|
50
58
|
headers["Content-Transfer-Encoding"] == "binary"
|
|
51
59
|
end
|
|
52
60
|
|
|
53
|
-
def sse?(
|
|
54
|
-
|
|
61
|
+
def sse?(headers)
|
|
62
|
+
headers["Content-Type"] == "text/event-stream"
|
|
55
63
|
end
|
|
56
64
|
|
|
57
65
|
def html_request?(headers, response)
|
|
@@ -59,7 +67,7 @@ module Bullet
|
|
|
59
67
|
end
|
|
60
68
|
|
|
61
69
|
def response_body(response)
|
|
62
|
-
if
|
|
70
|
+
if response.respond_to?(:body)
|
|
63
71
|
Array === response.body ? response.body.first : response.body
|
|
64
72
|
else
|
|
65
73
|
response.first
|
|
@@ -67,15 +75,19 @@ module Bullet
|
|
|
67
75
|
end
|
|
68
76
|
|
|
69
77
|
private
|
|
70
|
-
|
|
78
|
+
|
|
79
|
+
def footer_div_attributes
|
|
71
80
|
<<EOF
|
|
72
|
-
style="position: fixed; bottom: 0pt; left: 0pt; cursor: pointer; border-style: solid; border-color: rgb(153, 153, 153);
|
|
81
|
+
data-is-bullet-footer ondblclick="this.parentNode.removeChild(this);" style="position: fixed; bottom: 0pt; left: 0pt; cursor: pointer; border-style: solid; border-color: rgb(153, 153, 153);
|
|
73
82
|
-moz-border-top-colors: none; -moz-border-right-colors: none; -moz-border-bottom-colors: none;
|
|
74
83
|
-moz-border-left-colors: none; -moz-border-image: none; border-width: 2pt 2pt 0px 0px;
|
|
75
|
-
padding: 5px; border-radius: 0pt 10pt 0pt 0px; background: none repeat scroll 0% 0% rgba(200, 200, 200, 0.8);
|
|
76
|
-
color: rgb(119, 119, 119); font-size:
|
|
84
|
+
padding: 3px 5px; border-radius: 0pt 10pt 0pt 0px; background: none repeat scroll 0% 0% rgba(200, 200, 200, 0.8);
|
|
85
|
+
color: rgb(119, 119, 119); font-size: 16px; font-family: 'Arial', sans-serif; z-index:9999;"
|
|
77
86
|
EOF
|
|
78
87
|
end
|
|
88
|
+
|
|
89
|
+
def footer_close_button
|
|
90
|
+
"<span onclick='this.parentNode.remove()' style='position:absolute; right: 10px; top: 0px; font-weight: bold; color: #333;'>×</span>"
|
|
91
|
+
end
|
|
79
92
|
end
|
|
80
93
|
end
|
|
81
|
-
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module Bullet
|
|
2
|
+
module StackTraceFilter
|
|
3
|
+
VENDOR_PATH = "/vendor"
|
|
4
|
+
|
|
5
|
+
def caller_in_project
|
|
6
|
+
app_root = rails? ? Rails.root.to_s : Dir.pwd
|
|
7
|
+
vendor_root = app_root + VENDOR_PATH
|
|
8
|
+
caller.select do |caller_path|
|
|
9
|
+
caller_path.include?(app_root) && !caller_path.include?(vendor_root) ||
|
|
10
|
+
Bullet.stacktrace_includes.any? do |include_pattern|
|
|
11
|
+
case include_pattern
|
|
12
|
+
when String
|
|
13
|
+
caller_path.include?(include_pattern)
|
|
14
|
+
when Regexp
|
|
15
|
+
caller_path =~ include_pattern
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def excluded_stacktrace_path?
|
|
22
|
+
Bullet.stacktrace_excludes.any? do |exclude_pattern|
|
|
23
|
+
caller_in_project.any? do |caller_path|
|
|
24
|
+
case exclude_pattern
|
|
25
|
+
when String
|
|
26
|
+
caller_path.include?(exclude_pattern)
|
|
27
|
+
when Regexp
|
|
28
|
+
caller_path =~ exclude_pattern
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
data/lib/bullet/version.rb
CHANGED
data/lib/bullet.rb
CHANGED
|
@@ -4,6 +4,7 @@ require 'uniform_notifier'
|
|
|
4
4
|
require 'bullet/ext/object'
|
|
5
5
|
require 'bullet/ext/string'
|
|
6
6
|
require 'bullet/dependency'
|
|
7
|
+
require 'bullet/stack_trace_filter'
|
|
7
8
|
|
|
8
9
|
module Bullet
|
|
9
10
|
extend Dependency
|
|
@@ -16,6 +17,9 @@ module Bullet
|
|
|
16
17
|
autoload :Registry, 'bullet/registry'
|
|
17
18
|
autoload :NotificationCollector, 'bullet/notification_collector'
|
|
18
19
|
|
|
20
|
+
BULLET_DEBUG = 'BULLET_DEBUG'.freeze
|
|
21
|
+
TRUE = 'true'.freeze
|
|
22
|
+
|
|
19
23
|
if defined? Rails::Railtie
|
|
20
24
|
class BulletRailtie < Rails::Railtie
|
|
21
25
|
initializer "bullet.configure_rails_initialization" do |app|
|
|
@@ -25,11 +29,13 @@ module Bullet
|
|
|
25
29
|
end
|
|
26
30
|
|
|
27
31
|
class << self
|
|
28
|
-
attr_writer :enable, :n_plus_one_query_enable, :unused_eager_loading_enable, :counter_cache_enable, :stacktrace_includes
|
|
32
|
+
attr_writer :enable, :n_plus_one_query_enable, :unused_eager_loading_enable, :counter_cache_enable, :stacktrace_includes, :stacktrace_excludes
|
|
29
33
|
attr_reader :notification_collector, :whitelist
|
|
30
34
|
attr_accessor :add_footer, :orm_pathches_applied
|
|
31
35
|
|
|
32
|
-
|
|
36
|
+
available_notifiers = UniformNotifier::AVAILABLE_NOTIFIERS.map { |notifier| "#{notifier}=" }
|
|
37
|
+
available_notifiers << { :to => UniformNotifier }
|
|
38
|
+
delegate *available_notifiers
|
|
33
39
|
|
|
34
40
|
def raise=(should_raise)
|
|
35
41
|
UniformNotifier.raise=(should_raise ? Notification::UnoptimizedQueryError : false)
|
|
@@ -71,9 +77,14 @@ module Bullet
|
|
|
71
77
|
@stacktrace_includes || []
|
|
72
78
|
end
|
|
73
79
|
|
|
80
|
+
def stacktrace_excludes
|
|
81
|
+
@stacktrace_excludes || []
|
|
82
|
+
end
|
|
83
|
+
|
|
74
84
|
def add_whitelist(options)
|
|
75
|
-
|
|
76
|
-
@whitelist[options[:type]][options[:class_name]
|
|
85
|
+
reset_whitelist
|
|
86
|
+
@whitelist[options[:type]][options[:class_name]] ||= []
|
|
87
|
+
@whitelist[options[:type]][options[:class_name]] << options[:association].to_sym
|
|
77
88
|
end
|
|
78
89
|
|
|
79
90
|
def get_whitelist_associations(type, class_name)
|
|
@@ -81,7 +92,11 @@ module Bullet
|
|
|
81
92
|
end
|
|
82
93
|
|
|
83
94
|
def reset_whitelist
|
|
84
|
-
@whitelist
|
|
95
|
+
@whitelist ||= {:n_plus_one_query => {}, :unused_eager_loading => {}, :counter_cache => {}}
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def clear_whitelist
|
|
99
|
+
@whitelist = nil
|
|
85
100
|
end
|
|
86
101
|
|
|
87
102
|
def bullet_logger=(active)
|
|
@@ -96,7 +111,7 @@ module Bullet
|
|
|
96
111
|
end
|
|
97
112
|
|
|
98
113
|
def debug(title, message)
|
|
99
|
-
puts "[Bullet][#{title}] #{message}" if ENV[
|
|
114
|
+
puts "[Bullet][#{title}] #{message}" if ENV[BULLET_DEBUG] == TRUE
|
|
100
115
|
end
|
|
101
116
|
|
|
102
117
|
def start_request
|
|
@@ -130,7 +145,7 @@ module Bullet
|
|
|
130
145
|
end
|
|
131
146
|
|
|
132
147
|
def start?
|
|
133
|
-
Thread.current[:bullet_start]
|
|
148
|
+
enable? && Thread.current[:bullet_start]
|
|
134
149
|
end
|
|
135
150
|
|
|
136
151
|
def notification_collector
|
|
@@ -152,15 +167,16 @@ module Bullet
|
|
|
152
167
|
end
|
|
153
168
|
|
|
154
169
|
def perform_out_of_channel_notifications(env = {})
|
|
170
|
+
request_uri = build_request_uri(env)
|
|
155
171
|
for_each_active_notifier_with_notification do |notification|
|
|
156
|
-
notification.url =
|
|
172
|
+
notification.url = request_uri
|
|
157
173
|
notification.notify_out_of_channel
|
|
158
174
|
end
|
|
159
175
|
end
|
|
160
176
|
|
|
161
177
|
def footer_info
|
|
162
178
|
info = []
|
|
163
|
-
|
|
179
|
+
notification_collector.collection.each do |notification|
|
|
164
180
|
info << notification.short_notice
|
|
165
181
|
end
|
|
166
182
|
info
|
|
@@ -176,14 +192,22 @@ module Bullet
|
|
|
176
192
|
end
|
|
177
193
|
|
|
178
194
|
def profile
|
|
179
|
-
|
|
195
|
+
return_value = nil
|
|
196
|
+
if Bullet.enable?
|
|
197
|
+
begin
|
|
198
|
+
Bullet.start_request
|
|
180
199
|
|
|
181
|
-
|
|
200
|
+
return_value = yield
|
|
182
201
|
|
|
183
|
-
|
|
184
|
-
|
|
202
|
+
Bullet.perform_out_of_channel_notifications if Bullet.notification?
|
|
203
|
+
ensure
|
|
204
|
+
Bullet.end_request
|
|
205
|
+
end
|
|
206
|
+
else
|
|
207
|
+
return_value = yield
|
|
185
208
|
end
|
|
186
|
-
|
|
209
|
+
|
|
210
|
+
return_value
|
|
187
211
|
end
|
|
188
212
|
|
|
189
213
|
private
|
|
@@ -195,5 +219,15 @@ module Bullet
|
|
|
195
219
|
end
|
|
196
220
|
end
|
|
197
221
|
end
|
|
222
|
+
|
|
223
|
+
def build_request_uri(env)
|
|
224
|
+
return "#{env['REQUEST_METHOD']} #{env['REQUEST_URI']}" if env['REQUEST_URI']
|
|
225
|
+
|
|
226
|
+
if env['QUERY_STRING'].present?
|
|
227
|
+
"#{env['REQUEST_METHOD']} #{env['PATH_INFO']}?#{env['QUERY_STRING']}"
|
|
228
|
+
else
|
|
229
|
+
"#{env['REQUEST_METHOD']} #{env['PATH_INFO']}"
|
|
230
|
+
end
|
|
231
|
+
end
|
|
198
232
|
end
|
|
199
233
|
end
|
|
@@ -10,13 +10,13 @@ module Bullet
|
|
|
10
10
|
|
|
11
11
|
context ".add_counter_cache" do
|
|
12
12
|
it "should create notification if conditions met" do
|
|
13
|
-
expect(CounterCache).to receive(:conditions_met?).with(@post1
|
|
13
|
+
expect(CounterCache).to receive(:conditions_met?).with(@post1, [:comments]).and_return(true)
|
|
14
14
|
expect(CounterCache).to receive(:create_notification).with("Post", [:comments])
|
|
15
15
|
CounterCache.add_counter_cache(@post1, [:comments])
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
it "should not create notification if conditions not met" do
|
|
19
|
-
expect(CounterCache).to receive(:conditions_met?).with(@post1
|
|
19
|
+
expect(CounterCache).to receive(:conditions_met?).with(@post1, [:comments]).and_return(false)
|
|
20
20
|
expect(CounterCache).to receive(:create_notification).never
|
|
21
21
|
CounterCache.add_counter_cache(@post1, [:comments])
|
|
22
22
|
end
|
|
@@ -25,30 +25,30 @@ module Bullet
|
|
|
25
25
|
context ".add_possible_objects" do
|
|
26
26
|
it "should add possible objects" do
|
|
27
27
|
CounterCache.add_possible_objects([@post1, @post2])
|
|
28
|
-
expect(CounterCache.
|
|
29
|
-
expect(CounterCache.
|
|
28
|
+
expect(CounterCache.possible_objects).to be_include(@post1.bullet_key)
|
|
29
|
+
expect(CounterCache.possible_objects).to be_include(@post2.bullet_key)
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
it "should add impossible object" do
|
|
33
33
|
CounterCache.add_impossible_object(@post1)
|
|
34
|
-
expect(CounterCache.
|
|
34
|
+
expect(CounterCache.impossible_objects).to be_include(@post1.bullet_key)
|
|
35
35
|
end
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
context ".conditions_met?" do
|
|
39
39
|
it "should be true when object is possible, not impossible" do
|
|
40
40
|
CounterCache.add_possible_objects(@post1)
|
|
41
|
-
expect(CounterCache.
|
|
41
|
+
expect(CounterCache.conditions_met?(@post1, :associations)).to eq true
|
|
42
42
|
end
|
|
43
43
|
|
|
44
44
|
it "should be false when object is not possible" do
|
|
45
|
-
expect(CounterCache.
|
|
45
|
+
expect(CounterCache.conditions_met?(@post1, :associations)).to eq false
|
|
46
46
|
end
|
|
47
47
|
|
|
48
48
|
it "should be true when object is possible, and impossible" do
|
|
49
49
|
CounterCache.add_possible_objects(@post1)
|
|
50
50
|
CounterCache.add_impossible_object(@post1)
|
|
51
|
-
expect(CounterCache.
|
|
51
|
+
expect(CounterCache.conditions_met?(@post1, :associations)).to eq false
|
|
52
52
|
end
|
|
53
53
|
end
|
|
54
54
|
end
|
|
@@ -18,73 +18,88 @@ module Bullet
|
|
|
18
18
|
context ".possible?" do
|
|
19
19
|
it "should be true if possible_objects contain" do
|
|
20
20
|
NPlusOneQuery.add_possible_objects(@post)
|
|
21
|
-
expect(NPlusOneQuery.
|
|
21
|
+
expect(NPlusOneQuery.possible?(@post)).to eq true
|
|
22
22
|
end
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
context ".impossible?" do
|
|
26
26
|
it "should be true if impossible_objects contain" do
|
|
27
27
|
NPlusOneQuery.add_impossible_object(@post)
|
|
28
|
-
expect(NPlusOneQuery.
|
|
28
|
+
expect(NPlusOneQuery.impossible?(@post)).to eq true
|
|
29
29
|
end
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
context ".association?" do
|
|
33
33
|
it "should be true if object, associations pair is already existed" do
|
|
34
34
|
NPlusOneQuery.add_object_associations(@post, :association)
|
|
35
|
-
expect(NPlusOneQuery.
|
|
35
|
+
expect(NPlusOneQuery.association?(@post, :association)).to eq true
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
it "should be false if object, association pair is not existed" do
|
|
39
39
|
NPlusOneQuery.add_object_associations(@post, :association1)
|
|
40
|
-
expect(NPlusOneQuery.
|
|
40
|
+
expect(NPlusOneQuery.association?(@post, :associatio2)).to eq false
|
|
41
41
|
end
|
|
42
42
|
end
|
|
43
43
|
|
|
44
44
|
context ".conditions_met?" do
|
|
45
45
|
it "should be true if object is possible, not impossible and object, associations pair is not already existed" do
|
|
46
|
-
allow(NPlusOneQuery).to receive(:possible?).with(@post
|
|
47
|
-
allow(NPlusOneQuery).to receive(:impossible?).with(@post
|
|
48
|
-
allow(NPlusOneQuery).to receive(:association?).with(@post
|
|
49
|
-
expect(NPlusOneQuery.
|
|
46
|
+
allow(NPlusOneQuery).to receive(:possible?).with(@post).and_return(true)
|
|
47
|
+
allow(NPlusOneQuery).to receive(:impossible?).with(@post).and_return(false)
|
|
48
|
+
allow(NPlusOneQuery).to receive(:association?).with(@post, :associations).and_return(false)
|
|
49
|
+
expect(NPlusOneQuery.conditions_met?(@post, :associations)).to eq true
|
|
50
50
|
end
|
|
51
51
|
|
|
52
52
|
it "should be false if object is not possible, not impossible and object, associations pair is not already existed" do
|
|
53
|
-
allow(NPlusOneQuery).to receive(:possible?).with(@post
|
|
54
|
-
allow(NPlusOneQuery).to receive(:impossible?).with(@post
|
|
55
|
-
allow(NPlusOneQuery).to receive(:association?).with(@post
|
|
56
|
-
expect(NPlusOneQuery.
|
|
53
|
+
allow(NPlusOneQuery).to receive(:possible?).with(@post).and_return(false)
|
|
54
|
+
allow(NPlusOneQuery).to receive(:impossible?).with(@post).and_return(false)
|
|
55
|
+
allow(NPlusOneQuery).to receive(:association?).with(@post, :associations).and_return(false)
|
|
56
|
+
expect(NPlusOneQuery.conditions_met?(@post, :associations)).to eq false
|
|
57
57
|
end
|
|
58
58
|
|
|
59
59
|
it "should be false if object is possible, but impossible and object, associations pair is not already existed" do
|
|
60
|
-
allow(NPlusOneQuery).to receive(:possible?).with(@post
|
|
61
|
-
allow(NPlusOneQuery).to receive(:impossible?).with(@post
|
|
62
|
-
allow(NPlusOneQuery).to receive(:association?).with(@post
|
|
63
|
-
expect(NPlusOneQuery.
|
|
60
|
+
allow(NPlusOneQuery).to receive(:possible?).with(@post).and_return(true)
|
|
61
|
+
allow(NPlusOneQuery).to receive(:impossible?).with(@post).and_return(true)
|
|
62
|
+
allow(NPlusOneQuery).to receive(:association?).with(@post, :associations).and_return(false)
|
|
63
|
+
expect(NPlusOneQuery.conditions_met?(@post, :associations)).to eq false
|
|
64
64
|
end
|
|
65
65
|
|
|
66
66
|
it "should be false if object is possible, not impossible and object, associations pair is already existed" do
|
|
67
|
-
allow(NPlusOneQuery).to receive(:possible?).with(@post
|
|
68
|
-
allow(NPlusOneQuery).to receive(:impossible?).with(@post
|
|
69
|
-
allow(NPlusOneQuery).to receive(:association?).with(@post
|
|
70
|
-
expect(NPlusOneQuery.
|
|
67
|
+
allow(NPlusOneQuery).to receive(:possible?).with(@post).and_return(true)
|
|
68
|
+
allow(NPlusOneQuery).to receive(:impossible?).with(@post).and_return(false)
|
|
69
|
+
allow(NPlusOneQuery).to receive(:association?).with(@post, :associations).and_return(true)
|
|
70
|
+
expect(NPlusOneQuery.conditions_met?(@post, :associations)).to eq false
|
|
71
71
|
end
|
|
72
72
|
end
|
|
73
73
|
|
|
74
74
|
context ".call_association" do
|
|
75
75
|
it "should create notification if conditions met" do
|
|
76
|
-
expect(NPlusOneQuery).to receive(:conditions_met?).with(@post
|
|
76
|
+
expect(NPlusOneQuery).to receive(:conditions_met?).with(@post, :association).and_return(true)
|
|
77
77
|
expect(NPlusOneQuery).to receive(:caller_in_project).and_return(["caller"])
|
|
78
78
|
expect(NPlusOneQuery).to receive(:create_notification).with(["caller"], "Post", :association)
|
|
79
79
|
NPlusOneQuery.call_association(@post, :association)
|
|
80
80
|
end
|
|
81
81
|
|
|
82
82
|
it "should not create notification if conditions not met" do
|
|
83
|
-
expect(NPlusOneQuery).to receive(:conditions_met?).with(@post
|
|
83
|
+
expect(NPlusOneQuery).to receive(:conditions_met?).with(@post, :association).and_return(false)
|
|
84
84
|
expect(NPlusOneQuery).not_to receive(:caller_in_project!)
|
|
85
85
|
expect(NPlusOneQuery).not_to receive(:create_notification).with("Post", :association)
|
|
86
86
|
NPlusOneQuery.call_association(@post, :association)
|
|
87
87
|
end
|
|
88
|
+
|
|
89
|
+
context "stacktrace_excludes" do
|
|
90
|
+
before { Bullet.stacktrace_excludes = [ /def/ ] }
|
|
91
|
+
after { Bullet.stacktrace_excludes = nil }
|
|
92
|
+
|
|
93
|
+
it "should not create notification when stacktrace contains paths that are in the exclude list" do
|
|
94
|
+
in_project = File.join(Dir.pwd, 'abc', 'abc.rb')
|
|
95
|
+
included_path = '/ghi/ghi.rb'
|
|
96
|
+
excluded_path = '/def/def.rb'
|
|
97
|
+
|
|
98
|
+
expect(NPlusOneQuery).to receive(:caller).and_return([in_project, included_path, excluded_path])
|
|
99
|
+
expect(NPlusOneQuery).to_not receive(:create_notification)
|
|
100
|
+
NPlusOneQuery.call_association(@post, :association)
|
|
101
|
+
end
|
|
102
|
+
end
|
|
88
103
|
end
|
|
89
104
|
|
|
90
105
|
context ".caller_in_project" do
|
|
@@ -93,23 +108,23 @@ module Bullet
|
|
|
93
108
|
not_in_project = '/def/def.rb'
|
|
94
109
|
|
|
95
110
|
expect(NPlusOneQuery).to receive(:caller).and_return([in_project, not_in_project])
|
|
96
|
-
expect(NPlusOneQuery).to receive(:conditions_met?).with(@post
|
|
111
|
+
expect(NPlusOneQuery).to receive(:conditions_met?).with(@post, :association).and_return(true)
|
|
97
112
|
expect(NPlusOneQuery).to receive(:create_notification).with([in_project], "Post", :association)
|
|
98
113
|
NPlusOneQuery.call_association(@post, :association)
|
|
99
114
|
end
|
|
100
115
|
|
|
101
116
|
context "stacktrace_includes" do
|
|
102
|
-
before { Bullet.stacktrace_includes = [ 'def' ] }
|
|
117
|
+
before { Bullet.stacktrace_includes = [ 'def', /xyz/ ] }
|
|
103
118
|
after { Bullet.stacktrace_includes = nil }
|
|
104
119
|
|
|
105
120
|
it "should include paths that are in the stacktrace_include list" do
|
|
106
121
|
in_project = File.join(Dir.pwd, 'abc', 'abc.rb')
|
|
107
|
-
|
|
122
|
+
included_gems = ['/def/def.rb', 'xyz/xyz.rb']
|
|
108
123
|
excluded_gem = '/ghi/ghi.rb'
|
|
109
124
|
|
|
110
|
-
expect(NPlusOneQuery).to receive(:caller).and_return([in_project,
|
|
111
|
-
expect(NPlusOneQuery).to receive(:conditions_met?).with(@post
|
|
112
|
-
expect(NPlusOneQuery).to receive(:create_notification).with([in_project,
|
|
125
|
+
expect(NPlusOneQuery).to receive(:caller).and_return([in_project, *included_gems, excluded_gem])
|
|
126
|
+
expect(NPlusOneQuery).to receive(:conditions_met?).with(@post, :association).and_return(true)
|
|
127
|
+
expect(NPlusOneQuery).to receive(:create_notification).with([in_project, *included_gems], "Post", :association)
|
|
113
128
|
NPlusOneQuery.call_association(@post, :association)
|
|
114
129
|
end
|
|
115
130
|
end
|
|
@@ -118,8 +133,8 @@ module Bullet
|
|
|
118
133
|
context ".add_possible_objects" do
|
|
119
134
|
it "should add possible objects" do
|
|
120
135
|
NPlusOneQuery.add_possible_objects([@post, @post2])
|
|
121
|
-
expect(NPlusOneQuery.
|
|
122
|
-
expect(NPlusOneQuery.
|
|
136
|
+
expect(NPlusOneQuery.possible_objects).to be_include(@post.bullet_key)
|
|
137
|
+
expect(NPlusOneQuery.possible_objects).to be_include(@post2.bullet_key)
|
|
123
138
|
end
|
|
124
139
|
|
|
125
140
|
it "should not raise error if object is nil" do
|
|
@@ -130,7 +145,7 @@ module Bullet
|
|
|
130
145
|
context ".add_impossible_object" do
|
|
131
146
|
it "should add impossible object" do
|
|
132
147
|
NPlusOneQuery.add_impossible_object(@post)
|
|
133
|
-
expect(NPlusOneQuery.
|
|
148
|
+
expect(NPlusOneQuery.impossible_objects).to be_include(@post.bullet_key)
|
|
134
149
|
end
|
|
135
150
|
end
|
|
136
151
|
end
|