rollbar 2.7.1 → 2.8.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|