rollbar 2.7.1 → 2.8.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 +4 -0
- data/.gitmodules +3 -0
- data/.rubocop.yml +21 -0
- data/.travis.yml +1 -4
- data/CHANGELOG.md +25 -0
- data/README.md +146 -4
- data/Rakefile +5 -3
- data/data/rollbar.snippet.js +1 -0
- data/docs/configuration_options.md +281 -0
- data/lib/rollbar.rb +122 -32
- data/lib/rollbar/configuration.rb +18 -0
- data/lib/rollbar/exceptions.rb +3 -0
- data/lib/rollbar/js.rb +32 -0
- data/lib/rollbar/js/frameworks.rb +6 -0
- data/lib/rollbar/js/frameworks/rails.rb +29 -0
- data/lib/rollbar/js/middleware.rb +129 -0
- data/lib/rollbar/js/version.rb +5 -0
- data/lib/rollbar/lazy_store.rb +83 -0
- data/lib/rollbar/tasks/rollbar.cap +1 -1
- data/lib/rollbar/version.rb +1 -1
- data/lib/tasks/tasks.rake +13 -0
- data/rollbar.gemspec +1 -1
- data/spec/dummyapp/app/controllers/home_controller.rb +4 -0
- data/spec/dummyapp/app/views/js/test.html.erb +1 -0
- data/spec/dummyapp/app/views/layouts/simple.html.erb +18 -0
- data/spec/dummyapp/config/initializers/rollbar.rb +5 -2
- data/spec/dummyapp/config/routes.rb +1 -0
- data/spec/rollbar/js/frameworks/rails_spec.rb +19 -0
- data/spec/rollbar/js/middleware_spec.rb +154 -0
- data/spec/rollbar/lazy_store_spec.rb +99 -0
- data/spec/rollbar_spec.rb +272 -11
- data/spec/spec_helper.rb +2 -8
- metadata +26 -4
@@ -0,0 +1,83 @@
|
|
1
|
+
module Rollbar
|
2
|
+
class LazyStore
|
3
|
+
attr_reader :loaded_data
|
4
|
+
private :loaded_data
|
5
|
+
|
6
|
+
attr_reader :raw
|
7
|
+
|
8
|
+
def initialize(initial_data)
|
9
|
+
initial_data ||= {}
|
10
|
+
|
11
|
+
@raw = initial_data
|
12
|
+
@loaded_data = {}
|
13
|
+
end
|
14
|
+
|
15
|
+
def eql?(other)
|
16
|
+
if other.is_a?(self.class)
|
17
|
+
raw.eql?(other.raw)
|
18
|
+
else
|
19
|
+
raw.eql?(other)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def ==(other)
|
24
|
+
if other.is_a?(self.class)
|
25
|
+
raw == other.raw
|
26
|
+
else
|
27
|
+
raw == other
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# With this version of clone we ensure that the loaded_data is empty
|
32
|
+
def clone
|
33
|
+
self.class.new(raw.clone)
|
34
|
+
end
|
35
|
+
|
36
|
+
def [](key)
|
37
|
+
load_value(key)
|
38
|
+
end
|
39
|
+
|
40
|
+
def []=(key, value)
|
41
|
+
raw[key] = value
|
42
|
+
|
43
|
+
loaded_data.delete(key)
|
44
|
+
|
45
|
+
value
|
46
|
+
end
|
47
|
+
|
48
|
+
def data
|
49
|
+
raw.reduce({}) do |acc, (k, _)|
|
50
|
+
acc[k] = self[k]
|
51
|
+
|
52
|
+
acc
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def load_value(key)
|
59
|
+
return loaded_data[key] if loaded_data.key?(key)
|
60
|
+
return unless raw.key?(key)
|
61
|
+
|
62
|
+
value = find_value(key)
|
63
|
+
loaded_data[key] = value
|
64
|
+
|
65
|
+
value
|
66
|
+
end
|
67
|
+
|
68
|
+
def find_value(key)
|
69
|
+
value = raw[key]
|
70
|
+
value.respond_to?(:call) ? value.call : value
|
71
|
+
end
|
72
|
+
|
73
|
+
def method_missing(method_sym, *args, &block)
|
74
|
+
return raw.send(method_sym, *args, &block) if raw.respond_to?(method_sym)
|
75
|
+
|
76
|
+
super
|
77
|
+
end
|
78
|
+
|
79
|
+
def respond_to?(method_sym)
|
80
|
+
super || raw.respond_to?(method_sym)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -6,7 +6,7 @@ namespace :rollbar do
|
|
6
6
|
|
7
7
|
desc 'Send the deployment notification to Rollbar.'
|
8
8
|
task :deploy do
|
9
|
-
on
|
9
|
+
on primary fetch(:rollbar_role) do
|
10
10
|
warn("You need to upgrade capistrano to '>= 3.1' version in order to correctly report deploys to Rollbar. (On 3.0, the reported revision will be incorrect.)") if Capistrano::VERSION =~ /^3\.0/
|
11
11
|
|
12
12
|
uri = URI.parse 'https://api.rollbar.com/api/1/deploy/'
|
data/lib/rollbar/version.rb
CHANGED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
desc 'Update rollbar.js snippet'
|
4
|
+
task :update_snippet do
|
5
|
+
input_path = File.expand_path("../../../rollbar.js/dist/rollbar.snippet.js", __FILE__)
|
6
|
+
output_path = File.expand_path("../../../data/rollbar.snippet.js", __FILE__)
|
7
|
+
output_dir = File.expand_path("../../../data/", __FILE__)
|
8
|
+
|
9
|
+
$stdout.write("Copying #{input_path} to #{output_path}\n")
|
10
|
+
|
11
|
+
FileUtils.mkdir_p(output_dir)
|
12
|
+
FileUtils.copy(input_path, output_path)
|
13
|
+
end
|
data/rollbar.gemspec
CHANGED
@@ -23,7 +23,7 @@ Gem::Specification.new do |gem|
|
|
23
23
|
gem.add_development_dependency 'rspec-rails', '>= 2.14.0'
|
24
24
|
gem.add_development_dependency 'database_cleaner', '~> 1.0.0'
|
25
25
|
gem.add_development_dependency 'girl_friday', '>= 0.11.1'
|
26
|
-
gem.add_development_dependency 'sucker_punch', '
|
26
|
+
gem.add_development_dependency 'sucker_punch', '~> 1.0.0' if RUBY_VERSION != '1.8.7'
|
27
27
|
gem.add_development_dependency 'sidekiq', '>= 2.13.0' if RUBY_VERSION != '1.8.7'
|
28
28
|
gem.add_development_dependency 'genspec', '>= 0.2.8'
|
29
29
|
gem.add_development_dependency 'sinatra'
|
@@ -0,0 +1 @@
|
|
1
|
+
Testing Rollbar JS
|
@@ -0,0 +1,18 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
5
|
+
<title><%= content_for?(:title) ? yield(:title) : "Dummyapp" %></title>
|
6
|
+
<meta name="description" content="<%= content_for?(:description) ? yield(:description) : "Dummyapp" %>">
|
7
|
+
<%= yield(:head) %>
|
8
|
+
</head>
|
9
|
+
<body class="<%= controller_name %> <%= action_name %>">
|
10
|
+
<div id="container" class="container">
|
11
|
+
<div id="main" role="main">
|
12
|
+
<%= yield %>
|
13
|
+
</div>
|
14
|
+
<footer>
|
15
|
+
</footer>
|
16
|
+
</div> <!--! end of #container -->
|
17
|
+
</body>
|
18
|
+
</html>
|
@@ -1,7 +1,10 @@
|
|
1
1
|
Rollbar.configure do |config|
|
2
2
|
config.access_token = 'aaaabbbbccccddddeeeeffff00001111'
|
3
3
|
config.request_timeout = 60
|
4
|
-
|
4
|
+
config.js_enabled = true
|
5
|
+
config.js_options = {
|
6
|
+
:foo => :bar
|
7
|
+
}
|
5
8
|
# By default, Rollbar will try to call the `current_user` controller method
|
6
9
|
# to fetch the logged-in user object, and then call that object's `id`,
|
7
10
|
# `username`, and `email` methods to fetch those properties. To customize:
|
@@ -17,4 +20,4 @@ Rollbar.configure do |config|
|
|
17
20
|
# Valid levels: 'critical', 'error', 'warning', 'info', 'debug', 'ignore'
|
18
21
|
# 'ignore' will cause the exception to not be reported at all.
|
19
22
|
# config.exception_level_filters.merge!('MyCriticalException' => 'critical')
|
20
|
-
end
|
23
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ApplicationController, :type => 'request' do
|
4
|
+
before do
|
5
|
+
Rollbar.configure do |config|
|
6
|
+
config.js_options = { :foo => :bar }
|
7
|
+
config.js_enabled = true
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'renders the snippet and config in the response', :type => 'request' do
|
12
|
+
get '/test_rollbar_js'
|
13
|
+
|
14
|
+
snippet_from_submodule = File.read(File.expand_path('../../../../../rollbar.js/dist/rollbar.snippet.js', __FILE__))
|
15
|
+
|
16
|
+
expect(response.body).to include("var _rollbarConfig = #{Rollbar::configuration.js_options.to_json};")
|
17
|
+
expect(response.body).to include(snippet_from_submodule)
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,154 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'rollbar/js/middleware'
|
3
|
+
|
4
|
+
describe Rollbar::Js::Middleware do
|
5
|
+
subject { described_class.new(app, config) }
|
6
|
+
|
7
|
+
let(:env) { {} }
|
8
|
+
let(:config) { {} }
|
9
|
+
let(:app) do
|
10
|
+
proc do |_|
|
11
|
+
[status, headers, body]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
let(:html) do
|
15
|
+
<<-END
|
16
|
+
<html>
|
17
|
+
<head>
|
18
|
+
<link rel="stylesheet" href="url" type="text/css" media="screen" />
|
19
|
+
<script type="text/javascript" src="foo"></script>
|
20
|
+
</head>
|
21
|
+
<body>
|
22
|
+
<h1>Testing the middleware</h1>
|
23
|
+
</body>
|
24
|
+
</html>
|
25
|
+
END
|
26
|
+
end
|
27
|
+
let(:snippet) { 'THIS IS THE SNIPPET' }
|
28
|
+
let(:content_type) { 'text/html' }
|
29
|
+
|
30
|
+
before do
|
31
|
+
allow(subject).to receive(:js_snippet).and_return(snippet)
|
32
|
+
end
|
33
|
+
|
34
|
+
shared_examples "doesn't add the snippet or config", :add_js => false do
|
35
|
+
it "doesn't add the snippet or config" do
|
36
|
+
res_status, res_headers, response = subject.call(env)
|
37
|
+
new_body = response.join
|
38
|
+
|
39
|
+
expect(new_body).not_to include(snippet)
|
40
|
+
expect(new_body).not_to include(config[:options].to_json)
|
41
|
+
expect(new_body).to be_eql(body.join)
|
42
|
+
expect(res_status).to be_eql(status)
|
43
|
+
expect(res_headers['Content-Type']).to be_eql(content_type)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe '#call' do
|
48
|
+
context 'with enabled config' do
|
49
|
+
let(:config) do
|
50
|
+
{
|
51
|
+
:enabled => true,
|
52
|
+
:options => { :foo => :bar }
|
53
|
+
}
|
54
|
+
end
|
55
|
+
|
56
|
+
context 'having a html 200 response' do
|
57
|
+
let(:body) { [html] }
|
58
|
+
let(:status) { 200 }
|
59
|
+
let(:headers) do
|
60
|
+
{ 'Content-Type' => content_type }
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'adds the config and the snippet to the response' do
|
64
|
+
res_status, res_headers, response = subject.call(env)
|
65
|
+
new_body = response.body.join
|
66
|
+
|
67
|
+
expect(new_body).to include(snippet)
|
68
|
+
expect(new_body).to include(config[:options].to_json)
|
69
|
+
expect(res_status).to be_eql(status)
|
70
|
+
expect(res_headers['Content-Type']).to be_eql(content_type)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
context 'having a html 200 response without head', :add_js => false do
|
75
|
+
let(:body) { ['foobar'] }
|
76
|
+
let(:status) { 200 }
|
77
|
+
let(:headers) do
|
78
|
+
{ 'Content-Type' => content_type }
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
context 'having a html 302 response', :add_js => false do
|
83
|
+
let(:body) { ['foobar'] }
|
84
|
+
let(:status) { 302 }
|
85
|
+
let(:headers) do
|
86
|
+
{ 'Content-Type' => content_type }
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
context 'having the js already injected key in env', :add_js => false do
|
91
|
+
let(:body) { ['foobar'] }
|
92
|
+
let(:status) { 200 }
|
93
|
+
let(:headers) do
|
94
|
+
{ 'Content-Type' => content_type }
|
95
|
+
end
|
96
|
+
let(:env) do
|
97
|
+
{ described_class::JS_IS_INJECTED_KEY => true }
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
context 'having an attachment', :add_js => false do
|
102
|
+
let(:content_type) { 'text/plain' }
|
103
|
+
let(:body) { ['foobar'] }
|
104
|
+
let(:status) { 200 }
|
105
|
+
let(:headers) do
|
106
|
+
{ 'Content-Disposition' => 'attachment',
|
107
|
+
'Content-Type' => content_type
|
108
|
+
}
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
context 'with an exception raised while adding the js', :add_js => false do
|
113
|
+
let(:body) { [html] }
|
114
|
+
let(:status) { 200 }
|
115
|
+
let(:headers) do
|
116
|
+
{ 'Content-Type' => content_type }
|
117
|
+
end
|
118
|
+
|
119
|
+
before do
|
120
|
+
allow(subject).to receive(:add_js).and_raise(StandardError.new)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
context 'having the config disabled', :add_js => false do
|
126
|
+
let(:body) { ['foobar'] }
|
127
|
+
let(:status) { 302 }
|
128
|
+
let(:headers) do
|
129
|
+
{ 'Content-Type' => content_type }
|
130
|
+
end
|
131
|
+
let(:config) do
|
132
|
+
{
|
133
|
+
:enabled => false,
|
134
|
+
:options => { :foo => :bar }
|
135
|
+
}
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
context 'if the app raises' do
|
140
|
+
let(:exception) { StandardError.new }
|
141
|
+
let(:app) do
|
142
|
+
proc do |_|
|
143
|
+
raise exception
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
it 'propagates the exception' do
|
148
|
+
expect do
|
149
|
+
app.call(env)
|
150
|
+
end.to raise_exception(exception)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'rollbar/lazy_store'
|
4
|
+
|
5
|
+
|
6
|
+
describe Rollbar::LazyStore do
|
7
|
+
subject { Rollbar::LazyStore.new(data) }
|
8
|
+
let(:lazy_value) do
|
9
|
+
proc { :bar }
|
10
|
+
end
|
11
|
+
let(:data) do
|
12
|
+
{
|
13
|
+
:somekey => :value,
|
14
|
+
:foo => lazy_value
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
describe '#[]' do
|
19
|
+
it 'gets the regular values' do
|
20
|
+
expect(subject[:somekey]).to be_eql(:value)
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'gets the lazy values and evaluates them just once' do
|
24
|
+
expect(lazy_value).to receive(:call).once.and_call_original
|
25
|
+
|
26
|
+
value1 = subject[:foo]
|
27
|
+
value2 = subject[:foo]
|
28
|
+
|
29
|
+
expect(value1).to be_eql(:bar)
|
30
|
+
expect(value2).to be_eql(:bar)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe '#[]=' do
|
35
|
+
before do
|
36
|
+
# load data in :foo
|
37
|
+
subject[:foo]
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'sets the data and clears the loaded data' do
|
41
|
+
subject[:foo] = 'something-else'
|
42
|
+
|
43
|
+
expect(subject[:foo]).to be_eql('something-else')
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe '#eql?' do
|
48
|
+
context 'passing a Hash' do
|
49
|
+
it 'checks correctly eql?' do
|
50
|
+
expect(subject.eql?(data)).to be(true)
|
51
|
+
expect(subject.eql?({})).to be(false)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context 'passing a LazyStore' do
|
56
|
+
it 'checks correctly eql?' do
|
57
|
+
expect(subject.eql?(Rollbar::LazyStore.new(data))).to be(true)
|
58
|
+
expect(subject.eql?(Rollbar::LazyStore.new({}))).to be(false)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe '#==' do
|
64
|
+
context 'passing a Hash' do
|
65
|
+
it 'checks correctly eql?' do
|
66
|
+
expect(subject == data).to be(true)
|
67
|
+
expect(subject == {}).to be(false)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context 'passing a LazyStore' do
|
72
|
+
it 'checks correctly eql?' do
|
73
|
+
expect(subject == Rollbar::LazyStore.new(data)).to be(true)
|
74
|
+
expect(subject == Rollbar::LazyStore.new({})).to be(false)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe '#data' do
|
80
|
+
it 'returns the data with lazy values loaded' do
|
81
|
+
value = subject.data
|
82
|
+
|
83
|
+
expected_value = {
|
84
|
+
:somekey => :value,
|
85
|
+
:foo => :bar
|
86
|
+
}
|
87
|
+
expect(value).to be_eql(expected_value)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
describe '#clone' do
|
92
|
+
it 'returns a new object, with same data and empty loaded_data' do
|
93
|
+
new_scope = subject.clone
|
94
|
+
|
95
|
+
expect(new_scope.instance_variable_get('@loaded_data')).to be_empty
|
96
|
+
expect(new_scope.raw).to be_eql(subject.raw)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|