brakefast 0.0.1
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 +7 -0
- data/.gitignore +14 -0
- data/.rspec +2 -0
- data/.travis.yml +48 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +21 -0
- data/Guardfile +8 -0
- data/Hacking.md +74 -0
- data/MIT-LICENSE +20 -0
- data/README.md +451 -0
- data/Rakefile +6 -0
- data/brakefast.gemspec +30 -0
- data/lib/brakefast/brakeman.rb +18 -0
- data/lib/brakefast/dependency.rb +7 -0
- data/lib/brakefast/detector/base.rb +58 -0
- data/lib/brakefast/detector/controller_warnings.rb +32 -0
- data/lib/brakefast/detector/generic_warnings.rb +27 -0
- data/lib/brakefast/detector/model_warnings.rb +26 -0
- data/lib/brakefast/detector.rb +8 -0
- data/lib/brakefast/notification/base.rb +61 -0
- data/lib/brakefast/notification/controller_warnings.rb +14 -0
- data/lib/brakefast/notification/generic_warnings.rb +14 -0
- data/lib/brakefast/notification/model_warnings.rb +14 -0
- data/lib/brakefast/notification.rb +10 -0
- data/lib/brakefast/notification_collector.rb +24 -0
- data/lib/brakefast/rack.rb +95 -0
- data/lib/brakefast/version.rb +4 -0
- data/lib/brakefast.rb +178 -0
- data/perf/benchmark.rb +121 -0
- data/rails/init.rb +1 -0
- data/tasks/bullet_tasks.rake +9 -0
- data/test.sh +17 -0
- data/update.sh +15 -0
- metadata +146 -0
data/brakefast.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
lib = File.expand_path('../lib/', __FILE__)
|
2
|
+
$:.unshift lib unless $:.include?(lib)
|
3
|
+
|
4
|
+
require 'brakefast/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "brakefast"
|
8
|
+
spec.version = Brakefast::VERSION
|
9
|
+
spec.platform = Gem::Platform::RUBY
|
10
|
+
spec.authors = ["Sho Hashimoto"]
|
11
|
+
spec.email = ["sho.hsmt@gmail.com"]
|
12
|
+
spec.homepage = "https://github.com/sho-h/brakefast"
|
13
|
+
|
14
|
+
spec.summary = "runtime brakeman notifier like bullet"
|
15
|
+
spec.description = "runtime brakeman notifier like bullet"
|
16
|
+
spec.license = 'MIT'
|
17
|
+
|
18
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
19
|
+
spec.bindir = "exe"
|
20
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
21
|
+
spec.require_paths = ["lib"]
|
22
|
+
|
23
|
+
spec.add_runtime_dependency "uniform_notifier", "~> 1.9.0"
|
24
|
+
spec.add_runtime_dependency "brakeman"
|
25
|
+
|
26
|
+
spec.add_development_dependency "bundler", "~> 1.9"
|
27
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
28
|
+
spec.add_development_dependency "rspec"
|
29
|
+
end
|
30
|
+
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'brakeman'
|
2
|
+
require 'brakefast/detector'
|
3
|
+
|
4
|
+
module Brakefast
|
5
|
+
class Brakeman
|
6
|
+
def self.run(path)
|
7
|
+
tracker = ::Brakeman.run(Rails.root.to_s)
|
8
|
+
report = tracker.report.format(:to_hash)
|
9
|
+
Brakefast::Detector::Base.types.each do |type|
|
10
|
+
next if !Brakefast.public_send("#{type}_enable?")
|
11
|
+
report[type].each do |vulnerability|
|
12
|
+
detector = Brakefast::Detector::Base.create_instance(type, vulnerability)
|
13
|
+
detector.set_detector_module
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module Brakefast
|
4
|
+
module Detector
|
5
|
+
class Base
|
6
|
+
extend Forwardable
|
7
|
+
|
8
|
+
attr_reader :vulnerability
|
9
|
+
def_delegator :@vulnerability, :method, :target_method_name
|
10
|
+
def_delegator :@vulnerability, :message, :message
|
11
|
+
def_delegator :@vulnerability, :file, :file
|
12
|
+
def_delegator :@vulnerability, :line, :line
|
13
|
+
|
14
|
+
@@type2klass = {
|
15
|
+
}
|
16
|
+
|
17
|
+
class << self
|
18
|
+
def create_instance(type, *args)
|
19
|
+
@@type2klass[type].new(*args)
|
20
|
+
end
|
21
|
+
|
22
|
+
def register_detector(type)
|
23
|
+
@@type2klass[type] = self
|
24
|
+
end
|
25
|
+
|
26
|
+
def types
|
27
|
+
@@type2klass.keys
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def initialize(vulnerability)
|
32
|
+
@vulnerability = vulnerability
|
33
|
+
end
|
34
|
+
|
35
|
+
def set_detector_module
|
36
|
+
raise "override me"
|
37
|
+
end
|
38
|
+
|
39
|
+
def target_module_name
|
40
|
+
raise "override me"
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def create_module_name(s)
|
46
|
+
s.to_s.gsub("::", "__") + "BrakefastHook"
|
47
|
+
end
|
48
|
+
|
49
|
+
def create_hook(module_name, hookee_mod)
|
50
|
+
name = create_module_name(module_name)
|
51
|
+
Brakefast.const_set(name, hookee_mod)
|
52
|
+
::Object.const_get(module_name).class_eval %Q{
|
53
|
+
prepend Brakefast::#{name}
|
54
|
+
}
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Brakefast
|
2
|
+
module Detector
|
3
|
+
class ControllerWarnings < Base
|
4
|
+
register_detector :controller_warnings
|
5
|
+
|
6
|
+
def set_detector_module
|
7
|
+
if target_module_name && target_method_name
|
8
|
+
mod = Module.new
|
9
|
+
mod.module_eval %Q{
|
10
|
+
def #{target_method_name}
|
11
|
+
s = "vulnerability found in #{file}:#{line} - #{message}"
|
12
|
+
Thread.current[:brakefast_notifications] << s
|
13
|
+
=begin
|
14
|
+
# for debug
|
15
|
+
File.open("/tmp/1.log", "a") do |f|
|
16
|
+
f.write("vulnerability found in #{e.file}:#{e.line} - #{msg}")
|
17
|
+
end
|
18
|
+
=end
|
19
|
+
super
|
20
|
+
end
|
21
|
+
}
|
22
|
+
|
23
|
+
create_hook(target_module_name, mod)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def target_module_name
|
28
|
+
vulnerability.controller
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Brakefast
|
2
|
+
module Detector
|
3
|
+
class GenericWarnings < Base
|
4
|
+
register_detector :generic_warnings
|
5
|
+
|
6
|
+
def set_detector_module
|
7
|
+
if target_module_name && target_method_name
|
8
|
+
mod = Module.new
|
9
|
+
mod.module_eval %Q{
|
10
|
+
def #{target_method_name}
|
11
|
+
n = Brakefast::Notification::GenericWarnings.new(self, '#{message}',
|
12
|
+
'#{file}','#{line}')
|
13
|
+
Brakefast.notification_collector.add(n)
|
14
|
+
super
|
15
|
+
end
|
16
|
+
}
|
17
|
+
|
18
|
+
create_hook(target_module_name, mod)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def target_module_name
|
23
|
+
vulnerability.class
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Brakefast
|
2
|
+
module Detector
|
3
|
+
class ModelWarnings < Base
|
4
|
+
register_detector :model_warnings
|
5
|
+
|
6
|
+
def set_detector_module
|
7
|
+
if target_module_name && target_method_name
|
8
|
+
mod = Module.new
|
9
|
+
mod.module_eval %Q{
|
10
|
+
def #{target_method_name}
|
11
|
+
s = "vulnerability found in #{file}:#{line} - #{message}"
|
12
|
+
Thread.current[:brakefast_notifications] << s
|
13
|
+
super
|
14
|
+
end
|
15
|
+
}
|
16
|
+
|
17
|
+
create_hook(target_module_name, mod)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def target_module_name
|
22
|
+
vulnerability.model
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
module Brakefast
|
2
|
+
module Detector
|
3
|
+
autoload :Base, 'brakefast/detector/base'
|
4
|
+
autoload :ControllerWarnings, 'brakefast/detector/controller_warnings'
|
5
|
+
autoload :ModelWarnings, 'brakefast/detector/model_warnings'
|
6
|
+
autoload :GenericWarnings, 'brakefast/detector/generic_warnings'
|
7
|
+
end
|
8
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Brakefast
|
2
|
+
module Notification
|
3
|
+
class Base
|
4
|
+
attr_accessor :notifier, :url
|
5
|
+
attr_reader :klass, :message, :path, :line
|
6
|
+
|
7
|
+
def initialize(klass, message, path = nil, line = nil)
|
8
|
+
@klass = klass
|
9
|
+
@message = message
|
10
|
+
@path = path
|
11
|
+
@line = line
|
12
|
+
end
|
13
|
+
|
14
|
+
def title
|
15
|
+
raise NoMethodError.new("no method title defined")
|
16
|
+
end
|
17
|
+
|
18
|
+
def body
|
19
|
+
raise NoMethodError.new("no method body defined")
|
20
|
+
end
|
21
|
+
|
22
|
+
def call_stack_messages
|
23
|
+
""
|
24
|
+
end
|
25
|
+
|
26
|
+
def whoami
|
27
|
+
@user ||= ENV['USER'].presence || (`whoami`.chomp rescue "")
|
28
|
+
if @user.present?
|
29
|
+
"user: #{@user}"
|
30
|
+
else
|
31
|
+
""
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def body_with_caller
|
36
|
+
"#{body}\n#{call_stack_messages}\n"
|
37
|
+
end
|
38
|
+
|
39
|
+
def notify_inline
|
40
|
+
self.notifier.inline_notify(notification_data)
|
41
|
+
end
|
42
|
+
|
43
|
+
def notify_out_of_channel
|
44
|
+
self.notifier.out_of_channel_notify(notification_data)
|
45
|
+
end
|
46
|
+
|
47
|
+
def short_notice
|
48
|
+
[whoami.presence, url, title, body].compact.join(" ")
|
49
|
+
end
|
50
|
+
|
51
|
+
def notification_data
|
52
|
+
{
|
53
|
+
:user => whoami,
|
54
|
+
:url => url,
|
55
|
+
:title => title,
|
56
|
+
:body => body_with_caller,
|
57
|
+
}
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module Brakefast
|
2
|
+
module Notification
|
3
|
+
autoload :Base, 'brakefast/notification/base'
|
4
|
+
autoload :GenericWarnings, 'brakefast/notification/generic_warnings'
|
5
|
+
autoload :ControllerWarnings, 'brakefast/notification/controller_warnings'
|
6
|
+
autoload :ModelWarnings, 'brakefast/notification/model_warnings'
|
7
|
+
|
8
|
+
class UnoptimizedQueryError < StandardError; end
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module Brakefast
|
4
|
+
class NotificationCollector
|
5
|
+
attr_reader :collection
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
reset
|
9
|
+
end
|
10
|
+
|
11
|
+
def reset
|
12
|
+
@collection = Set.new
|
13
|
+
end
|
14
|
+
|
15
|
+
def add(value)
|
16
|
+
@collection << value
|
17
|
+
end
|
18
|
+
|
19
|
+
def notifications_present?
|
20
|
+
!@collection.empty?
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'brakefast/brakeman'
|
2
|
+
|
3
|
+
module Brakefast
|
4
|
+
class Rack
|
5
|
+
include Dependency
|
6
|
+
|
7
|
+
def initialize(app)
|
8
|
+
@app = app
|
9
|
+
@brakeman_run = false
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(env)
|
13
|
+
return @app.call(env) unless Brakefast.enable?
|
14
|
+
if !@brakeman_run
|
15
|
+
Brakefast::Brakeman.run(Rails.root.to_s)
|
16
|
+
@brakeman_run = true
|
17
|
+
end
|
18
|
+
Brakefast.start_request
|
19
|
+
status, headers, response = @app.call(env)
|
20
|
+
|
21
|
+
response_body = nil
|
22
|
+
if Brakefast.notification?
|
23
|
+
if !file?(headers) && !sse?(headers) && !empty?(response) &&
|
24
|
+
status == 200 && !response_body(response).frozen? && html_request?(headers, response)
|
25
|
+
response_body = response_body(response)
|
26
|
+
append_to_html_body(response_body, footer_note) if Brakefast.add_footer
|
27
|
+
append_to_html_body(response_body, Brakefast.gather_inline_notifications)
|
28
|
+
headers['Content-Length'] = response_body.bytesize.to_s
|
29
|
+
end
|
30
|
+
Brakefast.perform_out_of_channel_notifications(env)
|
31
|
+
end
|
32
|
+
[status, headers, response_body ? [response_body] : response]
|
33
|
+
ensure
|
34
|
+
Brakefast.end_request
|
35
|
+
end
|
36
|
+
|
37
|
+
# fix issue if response's body is a Proc
|
38
|
+
def empty?(response)
|
39
|
+
# response may be ["Not Found"], ["Move Permanently"], etc.
|
40
|
+
if rails?
|
41
|
+
(response.is_a?(Array) && response.size <= 1) ||
|
42
|
+
!response.respond_to?(:body) ||
|
43
|
+
!response_body(response).respond_to?(:empty?) ||
|
44
|
+
response_body(response).empty?
|
45
|
+
else
|
46
|
+
body = response_body(response)
|
47
|
+
body.nil? || body.empty?
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def append_to_html_body(response_body, content)
|
52
|
+
if response_body.include?('</body>')
|
53
|
+
position = response_body.rindex('</body>')
|
54
|
+
response_body.insert(position, content)
|
55
|
+
else
|
56
|
+
response_body << content
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def footer_note
|
61
|
+
"<div #{footer_div_attributes}>" + Brakefast.footer_info.uniq.join("<br>") + "</div>"
|
62
|
+
end
|
63
|
+
|
64
|
+
def file?(headers)
|
65
|
+
headers["Content-Transfer-Encoding"] == "binary"
|
66
|
+
end
|
67
|
+
|
68
|
+
def sse?(headers)
|
69
|
+
headers["Content-Type"] == "text/event-stream"
|
70
|
+
end
|
71
|
+
|
72
|
+
def html_request?(headers, response)
|
73
|
+
headers['Content-Type'] && headers['Content-Type'].include?('text/html') && response_body(response).include?("<html")
|
74
|
+
end
|
75
|
+
|
76
|
+
def response_body(response)
|
77
|
+
if rails?
|
78
|
+
Array === response.body ? response.body.first : response.body
|
79
|
+
else
|
80
|
+
response.first
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
def footer_div_attributes
|
86
|
+
<<EOF
|
87
|
+
data-is-brakefast-footer ondblclick="this.parentNode.removeChild(this);" style="position: fixed; bottom: 0pt; left: 0pt; cursor: pointer; border-style: solid; border-color: rgb(153, 153, 153);
|
88
|
+
-moz-border-top-colors: none; -moz-border-right-colors: none; -moz-border-bottom-colors: none;
|
89
|
+
-moz-border-left-colors: none; -moz-border-image: none; border-width: 2pt 2pt 0px 0px;
|
90
|
+
padding: 5px; border-radius: 0pt 10pt 0pt 0px; background: none repeat scroll 0% 0% rgba(200, 200, 200, 0.8);
|
91
|
+
color: rgb(119, 119, 119); font-size: 18px; font-family: 'Arial', sans-serif; z-index:9999;"
|
92
|
+
EOF
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
data/lib/brakefast.rb
ADDED
@@ -0,0 +1,178 @@
|
|
1
|
+
require "active_support/core_ext/module/delegation"
|
2
|
+
require 'set'
|
3
|
+
require 'uniform_notifier'
|
4
|
+
require 'brakefast/dependency'
|
5
|
+
|
6
|
+
module Brakefast
|
7
|
+
extend Dependency
|
8
|
+
|
9
|
+
autoload :Rack, 'brakefast/rack'
|
10
|
+
autoload :Notification, 'brakefast/notification'
|
11
|
+
autoload :Detector, 'brakefast/detector'
|
12
|
+
autoload :NotificationCollector, 'brakefast/notification_collector'
|
13
|
+
|
14
|
+
BRAKEFAST_DEBUG = 'BRAKEFAST_DEBUG'.freeze
|
15
|
+
TRUE = 'true'.freeze
|
16
|
+
|
17
|
+
if defined? Rails::Railtie
|
18
|
+
class BrakefastRailtie < Rails::Railtie
|
19
|
+
initializer "brakefast.configure_rails_initialization" do |app|
|
20
|
+
app.middleware.use Brakefast::Rack
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class << self
|
26
|
+
attr_writer :enable, :errors_enable, :generic_warnings_enable, :controller_warnings_enable, :model_warnings_enable, :stacktrace_includes
|
27
|
+
attr_reader :notification_collector, :whitelist
|
28
|
+
attr_accessor :add_footer
|
29
|
+
|
30
|
+
available_notifiers = UniformNotifier::AVAILABLE_NOTIFIERS.map { |notifier| "#{notifier}=" }
|
31
|
+
available_notifiers << { :to => UniformNotifier }
|
32
|
+
delegate *available_notifiers
|
33
|
+
|
34
|
+
def raise=(should_raise)
|
35
|
+
UniformNotifier.raise=(should_raise ? Notification::UnoptimizedQueryError : false)
|
36
|
+
end
|
37
|
+
|
38
|
+
DETECTORS = [ Brakefast::Detector::ControllerWarnings,
|
39
|
+
Brakefast::Detector::ModelWarnings,
|
40
|
+
Brakefast::Detector::GenericWarnings ]
|
41
|
+
|
42
|
+
def enable=(enable)
|
43
|
+
@enable = @errors_enable = @generic_warnings_enable = @controller_warnings_enable = @model_warnings_enable = enable
|
44
|
+
reset_whitelist if enable?
|
45
|
+
end
|
46
|
+
|
47
|
+
def enable?
|
48
|
+
!!@enable
|
49
|
+
end
|
50
|
+
|
51
|
+
def errors_enable?
|
52
|
+
self.enable? && !!@errors_enable
|
53
|
+
end
|
54
|
+
|
55
|
+
def generic_warnings_enable?
|
56
|
+
self.enable? && !!@generic_warnings_enable
|
57
|
+
end
|
58
|
+
|
59
|
+
def controller_warnings_enable?
|
60
|
+
self.enable? && !!@controller_warnings_enable
|
61
|
+
end
|
62
|
+
|
63
|
+
def model_warnings_enable?
|
64
|
+
self.enable? && !!@model_warnings_enable
|
65
|
+
end
|
66
|
+
|
67
|
+
def stacktrace_includes
|
68
|
+
@stacktrace_includes || []
|
69
|
+
end
|
70
|
+
|
71
|
+
def add_whitelist(options)
|
72
|
+
@whitelist[options[:type]][options[:class_name].classify] ||= []
|
73
|
+
@whitelist[options[:type]][options[:class_name].classify] << options[:association].to_sym
|
74
|
+
end
|
75
|
+
|
76
|
+
def get_whitelist_associations(type, class_name)
|
77
|
+
Array(@whitelist[type][class_name])
|
78
|
+
end
|
79
|
+
|
80
|
+
def reset_whitelist
|
81
|
+
@whitelist = {:n_plus_one_query => {}, :unused_eager_loading => {}, :counter_cache => {}}
|
82
|
+
end
|
83
|
+
|
84
|
+
def brakefast_logger=(active)
|
85
|
+
if active
|
86
|
+
require 'fileutils'
|
87
|
+
root_path = "#{rails? ? Rails.root.to_s : Dir.pwd}"
|
88
|
+
FileUtils.mkdir_p(root_path + '/log')
|
89
|
+
brakefast_log_file = File.open("#{root_path}/log/brakefast.log", 'a+')
|
90
|
+
brakefast_log_file.sync = true
|
91
|
+
UniformNotifier.customized_logger = brakefast_log_file
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def debug(title, message)
|
96
|
+
puts "[Brakefast][#{title}] #{message}" if ENV[BRAKEFAST_DEBUG] == TRUE
|
97
|
+
end
|
98
|
+
|
99
|
+
def start_request
|
100
|
+
Thread.current[:brakefast_start] = true
|
101
|
+
Thread.current[:brakefast_notification_collector] = Brakefast::NotificationCollector.new
|
102
|
+
end
|
103
|
+
|
104
|
+
def end_request
|
105
|
+
Thread.current[:brakefast_start] = nil
|
106
|
+
Thread.current[:brakefast_notification_collector] = nil
|
107
|
+
end
|
108
|
+
|
109
|
+
def start?
|
110
|
+
enable? && Thread.current[:brakefast_start]
|
111
|
+
end
|
112
|
+
|
113
|
+
def notification_collector
|
114
|
+
Thread.current[:brakefast_notification_collector]
|
115
|
+
end
|
116
|
+
|
117
|
+
def notification?
|
118
|
+
return unless start?
|
119
|
+
notification_collector.notifications_present?
|
120
|
+
end
|
121
|
+
|
122
|
+
def gather_inline_notifications
|
123
|
+
responses = []
|
124
|
+
for_each_active_notifier_with_notification do |notification|
|
125
|
+
responses << notification.notify_inline
|
126
|
+
end
|
127
|
+
responses.join( "\n" )
|
128
|
+
end
|
129
|
+
|
130
|
+
def perform_out_of_channel_notifications(env = {})
|
131
|
+
for_each_active_notifier_with_notification do |notification|
|
132
|
+
notification.url = env['REQUEST_URI']
|
133
|
+
notification.notify_out_of_channel
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def footer_info
|
138
|
+
info = []
|
139
|
+
notification_collector.collection.each do |notification|
|
140
|
+
info << notification.short_notice
|
141
|
+
end
|
142
|
+
info
|
143
|
+
end
|
144
|
+
|
145
|
+
def warnings
|
146
|
+
notification_collector.collection.inject({}) do |warnings, notification|
|
147
|
+
warning_type = notification.class.to_s.split(':').last.tableize
|
148
|
+
warnings[warning_type] ||= []
|
149
|
+
warnings[warning_type] << notification
|
150
|
+
warnings
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def profile
|
155
|
+
if Brakefast.enable?
|
156
|
+
begin
|
157
|
+
Brakefast.start_request
|
158
|
+
|
159
|
+
yield
|
160
|
+
|
161
|
+
Brakefast.perform_out_of_channel_notifications if Brakefast.notification?
|
162
|
+
ensure
|
163
|
+
Brakefast.end_request
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
private
|
169
|
+
def for_each_active_notifier_with_notification
|
170
|
+
UniformNotifier.active_notifiers.each do |notifier|
|
171
|
+
notification_collector.collection.each do |notification|
|
172
|
+
notification.notifier = notifier
|
173
|
+
yield notification
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|