bullet 5.5.0 → 6.1.5
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 +5 -5
- data/.github/workflows/main.yml +66 -0
- data/CHANGELOG.md +83 -1
- data/Gemfile +6 -1
- data/Gemfile.mongoid-7.0 +15 -0
- data/Gemfile.rails-4.0 +1 -1
- data/Gemfile.rails-4.1 +1 -1
- data/Gemfile.rails-4.2 +1 -1
- data/Gemfile.rails-5.0 +2 -2
- data/Gemfile.rails-5.1 +15 -0
- data/Gemfile.rails-5.2 +15 -0
- data/Gemfile.rails-6.0 +15 -0
- data/Gemfile.rails-6.1 +15 -0
- data/Guardfile +2 -2
- data/Hacking.md +1 -1
- data/README.md +58 -37
- data/Rakefile +20 -21
- data/bullet.gemspec +21 -15
- data/lib/bullet/active_job.rb +13 -0
- data/lib/bullet/active_record4.rb +15 -34
- data/lib/bullet/active_record41.rb +14 -31
- data/lib/bullet/active_record42.rb +23 -36
- data/lib/bullet/active_record5.rb +178 -158
- data/lib/bullet/active_record52.rb +251 -0
- data/lib/bullet/active_record60.rb +278 -0
- data/lib/bullet/active_record61.rb +278 -0
- data/lib/bullet/bullet_xhr.js +64 -0
- data/lib/bullet/dependency.rb +60 -32
- data/lib/bullet/detector/association.rb +41 -32
- data/lib/bullet/detector/base.rb +2 -0
- data/lib/bullet/detector/counter_cache.rb +25 -17
- data/lib/bullet/detector/n_plus_one_query.rb +38 -24
- data/lib/bullet/detector/unused_eager_loading.rb +35 -26
- data/lib/bullet/detector.rb +2 -0
- data/lib/bullet/ext/object.rb +9 -5
- data/lib/bullet/ext/string.rb +3 -1
- data/lib/bullet/mongoid4x.rb +9 -8
- data/lib/bullet/mongoid5x.rb +9 -8
- data/lib/bullet/mongoid6x.rb +13 -12
- data/lib/bullet/mongoid7x.rb +57 -0
- data/lib/bullet/notification/base.rb +28 -25
- data/lib/bullet/notification/counter_cache.rb +3 -1
- data/lib/bullet/notification/n_plus_one_query.rb +8 -7
- data/lib/bullet/notification/unused_eager_loading.rb +8 -7
- data/lib/bullet/notification.rb +4 -1
- data/lib/bullet/notification_collector.rb +2 -1
- data/lib/bullet/rack.rb +66 -35
- data/lib/bullet/registry/association.rb +2 -0
- data/lib/bullet/registry/base.rb +2 -0
- data/lib/bullet/registry/object.rb +2 -0
- data/lib/bullet/registry.rb +2 -0
- data/lib/bullet/stack_trace_filter.rb +50 -20
- data/lib/bullet/version.rb +3 -2
- data/lib/bullet.rb +136 -55
- data/lib/generators/bullet/install_generator.rb +48 -0
- data/perf/benchmark.rb +19 -22
- data/rails/init.rb +2 -0
- data/spec/bullet/detector/association_spec.rb +6 -4
- data/spec/bullet/detector/base_spec.rb +2 -0
- data/spec/bullet/detector/counter_cache_spec.rb +17 -15
- data/spec/bullet/detector/n_plus_one_query_spec.rb +75 -46
- data/spec/bullet/detector/unused_eager_loading_spec.rb +45 -26
- data/spec/bullet/ext/object_spec.rb +18 -11
- data/spec/bullet/ext/string_spec.rb +7 -5
- data/spec/bullet/notification/base_spec.rb +38 -39
- data/spec/bullet/notification/counter_cache_spec.rb +5 -3
- data/spec/bullet/notification/n_plus_one_query_spec.rb +20 -5
- data/spec/bullet/notification/unused_eager_loading_spec.rb +9 -3
- data/spec/bullet/notification_collector_spec.rb +12 -10
- data/spec/bullet/rack_spec.rb +200 -48
- data/spec/bullet/registry/association_spec.rb +12 -10
- data/spec/bullet/registry/base_spec.rb +22 -20
- data/spec/bullet/registry/object_spec.rb +6 -4
- data/spec/bullet_spec.rb +64 -33
- data/spec/integration/{active_record4 → active_record}/association_spec.rb +238 -223
- data/spec/integration/counter_cache_spec.rb +26 -44
- data/spec/integration/mongoid/association_spec.rb +53 -65
- data/spec/models/address.rb +2 -0
- data/spec/models/attachment.rb +5 -0
- data/spec/models/author.rb +2 -0
- data/spec/models/base_user.rb +2 -0
- data/spec/models/category.rb +2 -0
- data/spec/models/city.rb +2 -0
- data/spec/models/client.rb +4 -0
- data/spec/models/comment.rb +3 -1
- data/spec/models/company.rb +2 -0
- data/spec/models/country.rb +2 -0
- data/spec/models/deal.rb +5 -0
- data/spec/models/document.rb +4 -2
- data/spec/models/entry.rb +2 -0
- data/spec/models/firm.rb +3 -0
- data/spec/models/folder.rb +2 -0
- data/spec/models/group.rb +4 -0
- data/spec/models/mongoid/address.rb +3 -1
- data/spec/models/mongoid/category.rb +4 -2
- data/spec/models/mongoid/comment.rb +3 -1
- data/spec/models/mongoid/company.rb +3 -1
- data/spec/models/mongoid/entry.rb +3 -1
- data/spec/models/mongoid/post.rb +6 -4
- data/spec/models/mongoid/user.rb +2 -0
- data/spec/models/newspaper.rb +3 -1
- data/spec/models/page.rb +2 -0
- data/spec/models/person.rb +2 -0
- data/spec/models/pet.rb +2 -0
- data/spec/models/post.rb +19 -0
- data/spec/models/relationship.rb +2 -0
- data/spec/models/reply.rb +2 -0
- data/spec/models/student.rb +2 -0
- data/spec/models/submission.rb +3 -0
- data/spec/models/teacher.rb +2 -0
- data/spec/models/user.rb +3 -0
- data/spec/models/writer.rb +2 -0
- data/spec/spec_helper.rb +19 -23
- data/spec/support/bullet_ext.rb +10 -9
- data/spec/support/mongo_seed.rb +38 -57
- data/spec/support/rack_double.rb +13 -19
- data/spec/support/sqlite_seed.rb +104 -76
- data/tasks/bullet_tasks.rake +4 -2
- data/test.sh +5 -0
- data/update.sh +3 -0
- metadata +34 -17
- data/.travis.yml +0 -16
- data/spec/integration/active_record5/association_spec.rb +0 -768
data/lib/bullet/mongoid5x.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Bullet
|
|
2
4
|
module Mongoid
|
|
3
5
|
def self.enable
|
|
@@ -21,20 +23,21 @@ module Bullet
|
|
|
21
23
|
end
|
|
22
24
|
|
|
23
25
|
def each(&block)
|
|
24
|
-
|
|
26
|
+
return to_enum unless block
|
|
27
|
+
|
|
28
|
+
records = []
|
|
29
|
+
origin_each { |record| records << record }
|
|
25
30
|
if records.length > 1
|
|
26
31
|
Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
|
|
27
32
|
elsif records.size == 1
|
|
28
33
|
Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
|
|
29
34
|
end
|
|
30
|
-
|
|
35
|
+
records.each(&block)
|
|
31
36
|
end
|
|
32
37
|
|
|
33
38
|
def eager_load(docs)
|
|
34
39
|
associations = criteria.inclusions.map(&:name)
|
|
35
|
-
docs.each
|
|
36
|
-
Bullet::Detector::NPlusOneQuery.add_object_associations(doc, associations)
|
|
37
|
-
end
|
|
40
|
+
docs.each { |doc| Bullet::Detector::NPlusOneQuery.add_object_associations(doc, associations) }
|
|
38
41
|
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(docs, associations)
|
|
39
42
|
origin_eager_load(docs)
|
|
40
43
|
end
|
|
@@ -45,9 +48,7 @@ module Bullet
|
|
|
45
48
|
|
|
46
49
|
def get_relation(name, metadata, object, reload = false)
|
|
47
50
|
result = origin_get_relation(name, metadata, object, reload)
|
|
48
|
-
if metadata.macro !~ /embed/
|
|
49
|
-
Bullet::Detector::NPlusOneQuery.call_association(self, name)
|
|
50
|
-
end
|
|
51
|
+
Bullet::Detector::NPlusOneQuery.call_association(self, name) if metadata.macro !~ /embed/
|
|
51
52
|
result
|
|
52
53
|
end
|
|
53
54
|
end
|
data/lib/bullet/mongoid6x.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Bullet
|
|
2
4
|
module Mongoid
|
|
3
5
|
def self.enable
|
|
@@ -8,33 +10,34 @@ module Bullet
|
|
|
8
10
|
alias_method :origin_each, :each
|
|
9
11
|
alias_method :origin_eager_load, :eager_load
|
|
10
12
|
|
|
11
|
-
def first
|
|
12
|
-
result = origin_first
|
|
13
|
+
def first(opt = {})
|
|
14
|
+
result = origin_first(opt)
|
|
13
15
|
Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
|
|
14
16
|
result
|
|
15
17
|
end
|
|
16
18
|
|
|
17
|
-
def last
|
|
18
|
-
result = origin_last
|
|
19
|
+
def last(opt = {})
|
|
20
|
+
result = origin_last(opt)
|
|
19
21
|
Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
|
|
20
22
|
result
|
|
21
23
|
end
|
|
22
24
|
|
|
23
25
|
def each(&block)
|
|
24
|
-
|
|
26
|
+
return to_enum unless block
|
|
27
|
+
|
|
28
|
+
records = []
|
|
29
|
+
origin_each { |record| records << record }
|
|
25
30
|
if records.length > 1
|
|
26
31
|
Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
|
|
27
32
|
elsif records.size == 1
|
|
28
33
|
Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
|
|
29
34
|
end
|
|
30
|
-
|
|
35
|
+
records.each(&block)
|
|
31
36
|
end
|
|
32
37
|
|
|
33
38
|
def eager_load(docs)
|
|
34
39
|
associations = criteria.inclusions.map(&:name)
|
|
35
|
-
docs.each
|
|
36
|
-
Bullet::Detector::NPlusOneQuery.add_object_associations(doc, associations)
|
|
37
|
-
end
|
|
40
|
+
docs.each { |doc| Bullet::Detector::NPlusOneQuery.add_object_associations(doc, associations) }
|
|
38
41
|
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(docs, associations)
|
|
39
42
|
origin_eager_load(docs)
|
|
40
43
|
end
|
|
@@ -45,9 +48,7 @@ module Bullet
|
|
|
45
48
|
|
|
46
49
|
def get_relation(name, metadata, object, reload = false)
|
|
47
50
|
result = origin_get_relation(name, metadata, object, reload)
|
|
48
|
-
if metadata.macro !~ /embed/
|
|
49
|
-
Bullet::Detector::NPlusOneQuery.call_association(self, name)
|
|
50
|
-
end
|
|
51
|
+
Bullet::Detector::NPlusOneQuery.call_association(self, name) if metadata.macro !~ /embed/
|
|
51
52
|
result
|
|
52
53
|
end
|
|
53
54
|
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Bullet
|
|
4
|
+
module Mongoid
|
|
5
|
+
def self.enable
|
|
6
|
+
require 'mongoid'
|
|
7
|
+
::Mongoid::Contextual::Mongo.class_eval do
|
|
8
|
+
alias_method :origin_first, :first
|
|
9
|
+
alias_method :origin_last, :last
|
|
10
|
+
alias_method :origin_each, :each
|
|
11
|
+
alias_method :origin_eager_load, :eager_load
|
|
12
|
+
|
|
13
|
+
def first(opts = {})
|
|
14
|
+
result = origin_first(opts)
|
|
15
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
|
|
16
|
+
result
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def last(opts = {})
|
|
20
|
+
result = origin_last(opts)
|
|
21
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
|
|
22
|
+
result
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def each(&block)
|
|
26
|
+
return to_enum unless block
|
|
27
|
+
|
|
28
|
+
records = []
|
|
29
|
+
origin_each { |record| records << record }
|
|
30
|
+
if records.length > 1
|
|
31
|
+
Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
|
|
32
|
+
elsif records.size == 1
|
|
33
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
|
|
34
|
+
end
|
|
35
|
+
records.each(&block)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def eager_load(docs)
|
|
39
|
+
associations = criteria.inclusions.map(&:name)
|
|
40
|
+
docs.each { |doc| Bullet::Detector::NPlusOneQuery.add_object_associations(doc, associations) }
|
|
41
|
+
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(docs, associations)
|
|
42
|
+
origin_eager_load(docs)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
::Mongoid::Association::Accessors.class_eval do
|
|
47
|
+
alias_method :origin_get_relation, :get_relation
|
|
48
|
+
|
|
49
|
+
def get_relation(name, association, object, reload = false)
|
|
50
|
+
result = origin_get_relation(name, association, object, reload)
|
|
51
|
+
Bullet::Detector::NPlusOneQuery.call_association(self, name) unless association.embedded?
|
|
52
|
+
result
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Bullet
|
|
2
4
|
module Notification
|
|
3
5
|
class Base
|
|
@@ -6,29 +8,34 @@ module Bullet
|
|
|
6
8
|
|
|
7
9
|
def initialize(base_class, association_or_associations, path = nil)
|
|
8
10
|
@base_class = base_class
|
|
9
|
-
@associations =
|
|
11
|
+
@associations =
|
|
12
|
+
association_or_associations.is_a?(Array) ? association_or_associations : [association_or_associations]
|
|
10
13
|
@path = path
|
|
11
14
|
end
|
|
12
15
|
|
|
13
16
|
def title
|
|
14
|
-
raise NoMethodError
|
|
17
|
+
raise NoMethodError, 'no method title defined'
|
|
15
18
|
end
|
|
16
19
|
|
|
17
20
|
def body
|
|
18
|
-
raise NoMethodError
|
|
21
|
+
raise NoMethodError, 'no method body defined'
|
|
19
22
|
end
|
|
20
23
|
|
|
21
24
|
def call_stack_messages
|
|
22
|
-
|
|
25
|
+
''
|
|
23
26
|
end
|
|
24
27
|
|
|
25
28
|
def whoami
|
|
26
|
-
@user ||=
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
@user ||=
|
|
30
|
+
ENV['USER'].presence ||
|
|
31
|
+
(
|
|
32
|
+
begin
|
|
33
|
+
`whoami`.chomp
|
|
34
|
+
rescue StandardError
|
|
35
|
+
''
|
|
36
|
+
end
|
|
37
|
+
)
|
|
38
|
+
@user.present? ? "user: #{@user}" : ''
|
|
32
39
|
end
|
|
33
40
|
|
|
34
41
|
def body_with_caller
|
|
@@ -36,24 +43,19 @@ module Bullet
|
|
|
36
43
|
end
|
|
37
44
|
|
|
38
45
|
def notify_inline
|
|
39
|
-
|
|
46
|
+
notifier.inline_notify(notification_data)
|
|
40
47
|
end
|
|
41
48
|
|
|
42
49
|
def notify_out_of_channel
|
|
43
|
-
|
|
50
|
+
notifier.out_of_channel_notify(notification_data)
|
|
44
51
|
end
|
|
45
52
|
|
|
46
53
|
def short_notice
|
|
47
|
-
[whoami.presence, url, title, body].compact.join(
|
|
54
|
+
[whoami.presence, url, title, body].compact.join(' ')
|
|
48
55
|
end
|
|
49
56
|
|
|
50
57
|
def notification_data
|
|
51
|
-
{
|
|
52
|
-
:user => whoami,
|
|
53
|
-
:url => url,
|
|
54
|
-
:title => title,
|
|
55
|
-
:body => body_with_caller,
|
|
56
|
-
}
|
|
58
|
+
{ user: whoami, url: url, title: title, body: body_with_caller }
|
|
57
59
|
end
|
|
58
60
|
|
|
59
61
|
def eql?(other)
|
|
@@ -65,13 +67,14 @@ module Bullet
|
|
|
65
67
|
end
|
|
66
68
|
|
|
67
69
|
protected
|
|
68
|
-
def klazz_associations_str
|
|
69
|
-
" #{@base_class} => [#{@associations.map(&:inspect).join(', ')}]"
|
|
70
|
-
end
|
|
71
70
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
71
|
+
def klazz_associations_str
|
|
72
|
+
" #{@base_class} => [#{@associations.map(&:inspect).join(', ')}]"
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def associations_str
|
|
76
|
+
".includes(#{@associations.map { |a| a.to_s.to_sym }.inspect})"
|
|
77
|
+
end
|
|
75
78
|
end
|
|
76
79
|
end
|
|
77
80
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Bullet
|
|
2
4
|
module Notification
|
|
3
5
|
class NPlusOneQuery < Base
|
|
@@ -8,7 +10,7 @@ module Bullet
|
|
|
8
10
|
end
|
|
9
11
|
|
|
10
12
|
def body
|
|
11
|
-
"#{klazz_associations_str}\n Add to your
|
|
13
|
+
"#{klazz_associations_str}\n Add to your query: #{associations_str}"
|
|
12
14
|
end
|
|
13
15
|
|
|
14
16
|
def title
|
|
@@ -16,15 +18,14 @@ module Bullet
|
|
|
16
18
|
end
|
|
17
19
|
|
|
18
20
|
def notification_data
|
|
19
|
-
super.merge(
|
|
20
|
-
:backtrace => []
|
|
21
|
-
)
|
|
21
|
+
super.merge(backtrace: [])
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
protected
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
|
|
26
|
+
def call_stack_messages
|
|
27
|
+
(['Call stack'] + @callers).join("\n ")
|
|
28
|
+
end
|
|
28
29
|
end
|
|
29
30
|
end
|
|
30
31
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Bullet
|
|
2
4
|
module Notification
|
|
3
5
|
class UnusedEagerLoading < Base
|
|
@@ -8,7 +10,7 @@ module Bullet
|
|
|
8
10
|
end
|
|
9
11
|
|
|
10
12
|
def body
|
|
11
|
-
"#{klazz_associations_str}\n Remove from your
|
|
13
|
+
"#{klazz_associations_str}\n Remove from your query: #{associations_str}"
|
|
12
14
|
end
|
|
13
15
|
|
|
14
16
|
def title
|
|
@@ -16,15 +18,14 @@ module Bullet
|
|
|
16
18
|
end
|
|
17
19
|
|
|
18
20
|
def notification_data
|
|
19
|
-
super.merge(
|
|
20
|
-
:backtrace => []
|
|
21
|
-
)
|
|
21
|
+
super.merge(backtrace: [])
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
protected
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
|
|
26
|
+
def call_stack_messages
|
|
27
|
+
(['Call stack'] + @callers).join("\n ")
|
|
28
|
+
end
|
|
28
29
|
end
|
|
29
30
|
end
|
|
30
31
|
end
|
data/lib/bullet/notification.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Bullet
|
|
2
4
|
module Notification
|
|
3
5
|
autoload :Base, 'bullet/notification/base'
|
|
@@ -5,6 +7,7 @@ module Bullet
|
|
|
5
7
|
autoload :NPlusOneQuery, 'bullet/notification/n_plus_one_query'
|
|
6
8
|
autoload :CounterCache, 'bullet/notification/counter_cache'
|
|
7
9
|
|
|
8
|
-
class UnoptimizedQueryError < StandardError
|
|
10
|
+
class UnoptimizedQueryError < StandardError
|
|
11
|
+
end
|
|
9
12
|
end
|
|
10
13
|
end
|
data/lib/bullet/rack.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Bullet
|
|
2
4
|
class Rack
|
|
3
5
|
include Dependency
|
|
@@ -8,17 +10,26 @@ module Bullet
|
|
|
8
10
|
|
|
9
11
|
def call(env)
|
|
10
12
|
return @app.call(env) unless Bullet.enable?
|
|
13
|
+
|
|
11
14
|
Bullet.start_request
|
|
12
15
|
status, headers, response = @app.call(env)
|
|
13
16
|
|
|
14
17
|
response_body = nil
|
|
18
|
+
|
|
15
19
|
if Bullet.notification?
|
|
16
|
-
if !file?(headers) && !sse?(headers) && !empty?(response) &&
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
if Bullet.inject_into_page? && !file?(headers) && !sse?(headers) && !empty?(response) && status == 200
|
|
21
|
+
if html_request?(headers, response)
|
|
22
|
+
response_body = response_body(response)
|
|
23
|
+
response_body = append_to_html_body(response_body, footer_note) if Bullet.add_footer
|
|
24
|
+
response_body = append_to_html_body(response_body, Bullet.gather_inline_notifications)
|
|
25
|
+
if Bullet.add_footer && !Bullet.skip_http_headers
|
|
26
|
+
response_body = append_to_html_body(response_body, xhr_script)
|
|
27
|
+
end
|
|
28
|
+
headers['Content-Length'] = response_body.bytesize.to_s
|
|
29
|
+
elsif !Bullet.skip_http_headers
|
|
30
|
+
set_header(headers, 'X-bullet-footer-text', Bullet.footer_info.uniq) if Bullet.add_footer
|
|
31
|
+
set_header(headers, 'X-bullet-console-text', Bullet.text_notifications) if Bullet.console_enabled?
|
|
32
|
+
end
|
|
22
33
|
end
|
|
23
34
|
Bullet.perform_out_of_channel_notifications(env)
|
|
24
35
|
end
|
|
@@ -29,65 +40,85 @@ module Bullet
|
|
|
29
40
|
|
|
30
41
|
# fix issue if response's body is a Proc
|
|
31
42
|
def empty?(response)
|
|
32
|
-
# response may be ["Not Found"], ["Move Permanently"], etc
|
|
33
|
-
if
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
!response_body(response).respond_to?(:empty?) ||
|
|
37
|
-
response_body(response).empty?
|
|
38
|
-
else
|
|
39
|
-
body = response_body(response)
|
|
40
|
-
body.nil? || body.empty?
|
|
41
|
-
end
|
|
43
|
+
# response may be ["Not Found"], ["Move Permanently"], etc, but
|
|
44
|
+
# those should not happen if the status is 200
|
|
45
|
+
body = response_body(response)
|
|
46
|
+
body.nil? || body.empty?
|
|
42
47
|
end
|
|
43
48
|
|
|
44
49
|
def append_to_html_body(response_body, content)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
50
|
+
body = response_body.dup
|
|
51
|
+
content = content.html_safe if content.respond_to?(:html_safe)
|
|
52
|
+
if body.include?('</body>')
|
|
53
|
+
position = body.rindex('</body>')
|
|
54
|
+
body.insert(position, content)
|
|
48
55
|
else
|
|
49
|
-
|
|
56
|
+
body << content
|
|
50
57
|
end
|
|
51
58
|
end
|
|
52
59
|
|
|
53
60
|
def footer_note
|
|
54
|
-
"<
|
|
61
|
+
"<details #{details_attributes}><summary #{summary_attributes}>Bullet Warnings</summary><div #{footer_content_attributes}>#{Bullet.footer_info.uniq.join('<br>')}#{footer_console_message}</div></details>"
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def set_header(headers, header_name, header_array)
|
|
65
|
+
# Many proxy applications such as Nginx and AWS ELB limit
|
|
66
|
+
# the size a header to 8KB, so truncate the list of reports to
|
|
67
|
+
# be under that limit
|
|
68
|
+
header_array.pop while header_array.to_json.length > 8 * 1024
|
|
69
|
+
headers[header_name] = header_array.to_json
|
|
55
70
|
end
|
|
56
71
|
|
|
57
72
|
def file?(headers)
|
|
58
|
-
headers[
|
|
73
|
+
headers['Content-Transfer-Encoding'] == 'binary' || headers['Content-Disposition']
|
|
59
74
|
end
|
|
60
75
|
|
|
61
76
|
def sse?(headers)
|
|
62
|
-
headers[
|
|
77
|
+
headers['Content-Type'] == 'text/event-stream'
|
|
63
78
|
end
|
|
64
79
|
|
|
65
80
|
def html_request?(headers, response)
|
|
66
|
-
headers['Content-Type']
|
|
81
|
+
headers['Content-Type']&.include?('text/html') && response_body(response).include?('<html')
|
|
67
82
|
end
|
|
68
83
|
|
|
69
84
|
def response_body(response)
|
|
70
85
|
if response.respond_to?(:body)
|
|
71
86
|
Array === response.body ? response.body.first : response.body
|
|
72
|
-
|
|
87
|
+
elsif response.respond_to?(:first)
|
|
73
88
|
response.first
|
|
74
89
|
end
|
|
75
90
|
end
|
|
76
91
|
|
|
77
92
|
private
|
|
78
93
|
|
|
79
|
-
def
|
|
80
|
-
|
|
81
|
-
data-is-bullet-footer
|
|
82
|
-
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
94
|
+
def details_attributes
|
|
95
|
+
<<~EOF
|
|
96
|
+
id="bullet-footer" data-is-bullet-footer
|
|
97
|
+
style="cursor: pointer; position: fixed; left: 0px; bottom: 0px; z-index: 9999; background: #fdf2f2; color: #9b1c1c; font-size: 12px; border-radius: 0px 8px 0px 0px; border: 1px solid #9b1c1c;"
|
|
98
|
+
EOF
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def summary_attributes
|
|
102
|
+
<<~EOF
|
|
103
|
+
style="font-weight: 600; padding: 2px 8px"
|
|
104
|
+
EOF
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def footer_content_attributes
|
|
108
|
+
<<~EOF
|
|
109
|
+
style="padding: 8px; border-top: 1px solid #9b1c1c;"
|
|
110
|
+
EOF
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def footer_console_message
|
|
114
|
+
if Bullet.console_enabled?
|
|
115
|
+
"<br/><span style='font-style: italic;'>See 'Uniform Notifier' in JS Console for Stacktrace</span>"
|
|
116
|
+
end
|
|
87
117
|
end
|
|
88
118
|
|
|
89
|
-
|
|
90
|
-
|
|
119
|
+
# Make footer work for XHR requests by appending data to the footer
|
|
120
|
+
def xhr_script
|
|
121
|
+
"<script type='text/javascript'>#{File.read("#{__dir__}/bullet_xhr.js")}</script>"
|
|
91
122
|
end
|
|
92
123
|
end
|
|
93
124
|
end
|
data/lib/bullet/registry/base.rb
CHANGED
data/lib/bullet/registry.rb
CHANGED
|
@@ -1,33 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require "bundler"
|
|
3
|
+
|
|
1
4
|
module Bullet
|
|
2
5
|
module StackTraceFilter
|
|
3
|
-
VENDOR_PATH =
|
|
6
|
+
VENDOR_PATH = '/vendor'
|
|
7
|
+
IS_RUBY_19 = Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.0.0')
|
|
4
8
|
|
|
5
9
|
def caller_in_project
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
caller_path
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
when Regexp
|
|
15
|
-
caller_path =~ include_pattern
|
|
16
|
-
end
|
|
17
|
-
end
|
|
10
|
+
vendor_root = Bullet.app_root + VENDOR_PATH
|
|
11
|
+
bundler_path = Bundler.bundle_path.to_s
|
|
12
|
+
select_caller_locations do |location|
|
|
13
|
+
caller_path = location_as_path(location)
|
|
14
|
+
caller_path.include?(Bullet.app_root) && !caller_path.include?(vendor_root) &&
|
|
15
|
+
!caller_path.include?(bundler_path) || Bullet.stacktrace_includes.any? { |include_pattern|
|
|
16
|
+
pattern_matches?(location, include_pattern)
|
|
17
|
+
}
|
|
18
18
|
end
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def excluded_stacktrace_path?
|
|
22
22
|
Bullet.stacktrace_excludes.any? do |exclude_pattern|
|
|
23
|
-
caller_in_project.any?
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
23
|
+
caller_in_project.any? { |location| pattern_matches?(location, exclude_pattern) }
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def pattern_matches?(location, pattern)
|
|
30
|
+
path = location_as_path(location)
|
|
31
|
+
case pattern
|
|
32
|
+
when Array
|
|
33
|
+
pattern_path = pattern.first
|
|
34
|
+
filter = pattern.last
|
|
35
|
+
return false unless pattern_matches?(location, pattern_path)
|
|
36
|
+
|
|
37
|
+
case filter
|
|
38
|
+
when Range
|
|
39
|
+
filter.include?(location.lineno)
|
|
40
|
+
when Integer
|
|
41
|
+
filter == location.lineno
|
|
42
|
+
when String
|
|
43
|
+
filter == location.base_label
|
|
30
44
|
end
|
|
45
|
+
when String
|
|
46
|
+
path.include?(pattern)
|
|
47
|
+
when Regexp
|
|
48
|
+
path =~ pattern
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def location_as_path(location)
|
|
53
|
+
IS_RUBY_19 ? location : location.absolute_path.to_s
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def select_caller_locations
|
|
57
|
+
if IS_RUBY_19
|
|
58
|
+
caller.select { |caller_path| yield caller_path }
|
|
59
|
+
else
|
|
60
|
+
caller_locations.select { |location| yield location }
|
|
31
61
|
end
|
|
32
62
|
end
|
|
33
63
|
end
|
data/lib/bullet/version.rb
CHANGED