Skip to content

Commit 9911bc3

Browse files
author
Jon de Andres
committed
Add Rollbar.js integration
Users can now use the gem to configure the Rollbar JS notifier using the same initializer. The gem will inject the Rollbar JS snippet and the configuration to the returned response. In order to configure the Rollbar.js using the gem, users can follo the next: js_config = { accessToken: ""POST_CLIENT_ITEM_ACCESS_TOKEN", captureUncaught: true, payload: { environment: "production" } } Rollbar.configure do |config| # common gem configuration config.js_enabled = true config.js_options = js_config end The options passed to `#js_options=` are the same than the used for the Rollbar JS notifier.
1 parent 369812b commit 9911bc3

File tree

20 files changed

+426
-4
lines changed

20 files changed

+426
-4
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,4 @@ test/version_tmp
2020
tmp
2121
*.swp
2222
.idea/
23+
gemfiles/vendor

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[submodule "rollbar.js"]
2+
path = rollbar.js
3+
url = https://github.com/rollbar/rollbar.js.git

Rakefile

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@ desc 'Run specs'
1111
task :default do
1212
ENV['LOCAL'] = '1'
1313
Rake::Task[:spec].invoke
14-
14+
1515
Rake::Task[:spec].reenable
16-
16+
1717
ENV['LOCAL'] = '0'
1818
Rake::Task[:spec].invoke
19-
end
19+
end
20+
21+
Dir.glob('lib/tasks/*.rake').each { |r| load r }

data/rollbar.snippet.js

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/rollbar.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
require 'rollbar/version'
1313
require 'rollbar/json'
14+
require 'rollbar/js'
1415
require 'rollbar/configuration'
1516
require 'rollbar/encoding'
1617
require 'rollbar/logger_proxy'
@@ -781,6 +782,8 @@ def safely?
781782
end
782783

783784
def require_hooks
785+
::Rollbar::Js.prepare if configuration.js_enabled
786+
784787
return if configuration.disable_monkey_patch
785788
wrap_delayed_worker
786789

lib/rollbar/configuration.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ class Configuration
3232
attr_accessor :report_dj_data
3333
attr_accessor :request_timeout
3434
attr_accessor :root
35+
attr_accessor :js_options
36+
attr_accessor :js_enabled
3537
attr_accessor :safely
3638
attr_accessor :scrub_fields
3739
attr_accessor :scrub_user
@@ -84,6 +86,8 @@ def initialize
8486
@populate_empty_backtraces = false
8587
@report_dj_data = true
8688
@request_timeout = 3
89+
@js_enabled = true
90+
@js_options = {}
8791
@scrub_fields = [:passwd, :password, :password_confirmation, :secret,
8892
:confirm_password, :password_confirmation, :secret_token,
8993
:api_key, :access_token ]

lib/rollbar/js.rb

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
require "rollbar/js/version"
2+
3+
module Rollbar
4+
module Js
5+
extend self
6+
7+
attr_reader :framework
8+
attr_reader :framework_loader
9+
10+
def prepare
11+
@framework ||= detect_framework
12+
@framework_loader ||= load_framework_class.new
13+
14+
@framework_loader.prepare
15+
end
16+
17+
private
18+
19+
def detect_framework
20+
case
21+
when defined?(::Rails::VERSION)
22+
:rails
23+
end
24+
end
25+
26+
def load_framework_class
27+
require "rollbar/js/frameworks/#{framework}"
28+
29+
Rollbar::Js::Frameworks.const_get(framework.to_s.capitalize)
30+
end
31+
end
32+
end

lib/rollbar/js/frameworks.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
module Rollbar
2+
module Js
3+
module Frameworks
4+
end
5+
end
6+
end

lib/rollbar/js/frameworks/rails.rb

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
module Rollbar
2+
module Js
3+
module Frameworks
4+
class Rails
5+
attr_accessor :prepared
6+
7+
alias prepared? prepared
8+
9+
def prepare
10+
return if prepared?
11+
12+
require 'rollbar/js/middleware'
13+
14+
config = {
15+
:options => Rollbar.configuration.js_options,
16+
:enabled => Rollbar.configuration.js_enabled
17+
}
18+
rails_config.middleware.use(::Rollbar::Js::Middleware, config)
19+
20+
self.prepared = true
21+
end
22+
23+
def rails_config
24+
::Rails.configuration
25+
end
26+
end
27+
end
28+
end
29+
end

lib/rollbar/js/middleware.rb

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
require 'rack'
2+
require 'rack/response'
3+
4+
5+
module Rollbar
6+
module Js
7+
class Middleware
8+
attr_reader :app
9+
attr_reader :config
10+
11+
JS_IS_INJECTED_KEY = 'rollbar.js_is_injected'
12+
SNIPPET = File.read(File.expand_path('../../../../data/rollbar.snippet.js', __FILE__))
13+
14+
def initialize(app, config)
15+
@app = app
16+
@config = config
17+
end
18+
19+
def call(env)
20+
result = app.call(env)
21+
22+
status = result[0]
23+
headers = result[1]
24+
response = result[2]
25+
26+
return result unless should_add_js?(env, status, headers)
27+
28+
if response_string = add_js(response)
29+
env[JS_IS_INJECTED_KEY] = true
30+
response = ::Rack::Response.new(response_string, result[0], result[1])
31+
32+
response.finish
33+
else
34+
result
35+
end
36+
end
37+
38+
private
39+
40+
def enabled?
41+
!!config[:enabled]
42+
end
43+
44+
def should_add_js?(env, status, headers)
45+
enabled? &&
46+
status == 200 &&
47+
!env[JS_IS_INJECTED_KEY] &&
48+
html?(headers) &&
49+
!attachment?(headers) &&
50+
!streaming?(env)
51+
end
52+
53+
def html?(headers)
54+
headers['Content-Type'] && headers['Content-Type'].include?('text/html')
55+
end
56+
57+
def attachment?(headers)
58+
headers['Content-Disposition'].to_s.include?('attachment')
59+
end
60+
61+
def streaming?(env)
62+
return false unless defined?(ActionController::Live)
63+
64+
env['action_controller.instance'].class.included_modules.include?(ActionController::Live)
65+
end
66+
67+
def add_js(response)
68+
body = join_body(response)
69+
close_old_response(response)
70+
71+
return nil unless body
72+
73+
head_close = find_end_of_head_open(body)
74+
return nil unless head_close
75+
76+
if head_close
77+
body = body[0..head_close] <<
78+
config_js_tag <<
79+
snippet_js_tag <<
80+
body[head_close..-1]
81+
end
82+
83+
body
84+
rescue => e
85+
Rollbar.log_error("[Rollbar] Rollbar.js could not be added because #{e} exception")
86+
nil
87+
end
88+
89+
def find_end_of_head_open(body)
90+
head_open = body.index('<head')
91+
body.index('>', head_open) + 1 if head_open
92+
end
93+
94+
def join_body(response)
95+
source = nil
96+
response.each { |fragment| source ? (source << fragment.to_s) : (source = fragment.to_s)}
97+
source
98+
end
99+
100+
def close_old_response(response)
101+
response.close if response.respond_to?(:close)
102+
end
103+
104+
def calculate_content_length(source)
105+
if source.respond_to?(:bytesize)
106+
source.bytesize
107+
else
108+
source.length
109+
end
110+
end
111+
112+
def config_js_tag
113+
script_tag("var _rollbarConfig = #{config[:options].to_json};")
114+
end
115+
116+
def snippet_js_tag
117+
script_tag(js_snippet)
118+
end
119+
120+
def js_snippet
121+
@js_snippet ||= SNIPPET
122+
end
123+
124+
def script_tag(content)
125+
html_safe_if_needed("\n<script type=\"text/javascript\">#{content}</script>")
126+
end
127+
128+
def html_safe_if_needed(string)
129+
string = string.html_safe if string.respond_to?(:html_safe)
130+
string
131+
end
132+
end
133+
end
134+
end

0 commit comments

Comments
 (0)