radar 0.1.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.
- data/.gitignore +6 -0
- data/.yardopts +2 -0
- data/Gemfile +11 -0
- data/Gemfile.lock +34 -0
- data/README.md +107 -0
- data/Rakefile +17 -0
- data/docs/user_guide.md +197 -0
- data/lib/radar.rb +18 -0
- data/lib/radar/application.rb +96 -0
- data/lib/radar/config.rb +35 -0
- data/lib/radar/data_extensions/host_environment.rb +24 -0
- data/lib/radar/error.rb +6 -0
- data/lib/radar/exception_event.rb +61 -0
- data/lib/radar/reporter.rb +29 -0
- data/lib/radar/reporter/file_reporter.rb +37 -0
- data/lib/radar/support.rb +25 -0
- data/lib/radar/version.rb +3 -0
- data/radar.gemspec +28 -0
- data/test/radar/application_test.rb +105 -0
- data/test/radar/config_test.rb +59 -0
- data/test/radar/data_extensions/host_environment_test.rb +15 -0
- data/test/radar/exception_event_test.rb +48 -0
- data/test/radar/reporter/file_reporter_test.rb +27 -0
- data/test/radar/reporter_test.rb +14 -0
- data/test/radar/support_test.rb +26 -0
- data/test/test_helper.rb +25 -0
- metadata +163 -0
data/.gitignore
ADDED
data/.yardopts
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
source :gemcutter
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in radar.gemspec
|
4
|
+
gemspec
|
5
|
+
|
6
|
+
# Additional gems which I don't really want in the gemspec but
|
7
|
+
# are useful for development
|
8
|
+
group :development do
|
9
|
+
gem "yard", :git => "http://github.com/lsegal/yard.git"
|
10
|
+
gem "bluecloth"
|
11
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
GIT
|
2
|
+
remote: http://github.com/lsegal/yard.git
|
3
|
+
revision: bf84275
|
4
|
+
specs:
|
5
|
+
yard (0.6.0.rc1)
|
6
|
+
|
7
|
+
PATH
|
8
|
+
remote: .
|
9
|
+
specs:
|
10
|
+
radar (0.1.0)
|
11
|
+
json (>= 1.4.6)
|
12
|
+
|
13
|
+
GEM
|
14
|
+
remote: http://rubygems.org/
|
15
|
+
specs:
|
16
|
+
bluecloth (2.0.7)
|
17
|
+
json (1.4.6)
|
18
|
+
mocha (0.9.8)
|
19
|
+
rake
|
20
|
+
rake (0.8.7)
|
21
|
+
shoulda (2.11.3)
|
22
|
+
|
23
|
+
PLATFORMS
|
24
|
+
ruby
|
25
|
+
|
26
|
+
DEPENDENCIES
|
27
|
+
bluecloth
|
28
|
+
bundler (>= 1.0.0.rc.5)
|
29
|
+
json (>= 1.4.6)
|
30
|
+
mocha
|
31
|
+
radar!
|
32
|
+
rake
|
33
|
+
shoulda
|
34
|
+
yard!
|
data/README.md
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
# Radar
|
2
|
+
|
3
|
+
* Source: [http://github.com/mitchellh/radar](http://github.com/mitchellh/radar)
|
4
|
+
* IRC: `#vagrant` on Freenode
|
5
|
+
* User Guide: [http://mitchellh.github.com/radar/file.user_guide.html](http://mitchellh.github.com/radar/file.user_guide.html)
|
6
|
+
* Documentation: [http://mitchellh.github.com/radar](http://mitchellh.github.com/radar)
|
7
|
+
|
8
|
+
Radar is a tool which provides a drop-in solution to catching and reporting
|
9
|
+
errors in your Ruby applications in customizable ways.
|
10
|
+
|
11
|
+
Radar is not a typical exception notifier such as Hoptoad and Exceptional
|
12
|
+
since the former are built with Rails apps in mind, logging to a central
|
13
|
+
server. Instead, Radar was initially built for [Vagrant](http://vagrantup.com),
|
14
|
+
a command line tool. And instead of solely logging to a central server,
|
15
|
+
Radar supports logging in configurable ways (to a file, to a server, to
|
16
|
+
a growl notification, etc.)
|
17
|
+
|
18
|
+
Radar was built out of the need for an easy automated solution to catching
|
19
|
+
exceptions and gathering information, since I noticed that every time a stack
|
20
|
+
trace was outputted for [Vagrant](http://vagrantup.com), I was asking the
|
21
|
+
user all the same questions which could've easily have been gathered automatically.
|
22
|
+
Now, Vagrant users can simply send me the radar output and I don't have to
|
23
|
+
ask for any more information 90% of the time.
|
24
|
+
|
25
|
+
## Quick Start
|
26
|
+
|
27
|
+
gem install radar
|
28
|
+
|
29
|
+
Then just begin logging exceptions in your application:
|
30
|
+
|
31
|
+
r = Radar::Application.new(:my_application)
|
32
|
+
r.config.reporter Radar::Reporter::FileReporter
|
33
|
+
r.report(exception)
|
34
|
+
|
35
|
+
You can also tell Radar to attach itself to Ruby's `at_exit` hook
|
36
|
+
so it can catch application-crashing exceptions automatically:
|
37
|
+
|
38
|
+
r.rescue_at_exit!
|
39
|
+
|
40
|
+
Both of the above methods can be used together, of course.
|
41
|
+
|
42
|
+
Since the above enabled the `FileReporter` for the application, reported
|
43
|
+
exceptions will be stored in a text file on the local filesystem, by default
|
44
|
+
at `~/.radar/errors/my_application`. Sample contents of the exception
|
45
|
+
data generated by default is shown below. Note that you can add your own
|
46
|
+
custom data to this output by using data extensions (covered in the user
|
47
|
+
guide).
|
48
|
+
|
49
|
+
{
|
50
|
+
"application":{
|
51
|
+
"name":"my_application"
|
52
|
+
},
|
53
|
+
"exception":{
|
54
|
+
"klass":"RuntimeError",
|
55
|
+
"message":"This was an example exception",
|
56
|
+
"backtrace":[
|
57
|
+
"test.rb:28:in `<main>'"
|
58
|
+
],
|
59
|
+
"uniqueness_hash":"296d169e7928c4433ccfcf091b4d737aabe83dcb"
|
60
|
+
},
|
61
|
+
"occurred_at":1281894743,
|
62
|
+
"host_environment":{
|
63
|
+
"ruby_version":"1.9.2",
|
64
|
+
"ruby_pl":-1,
|
65
|
+
"ruby_release_date":"2010-07-11",
|
66
|
+
"ruby_platform":"x86_64-darwin10.4.0"
|
67
|
+
}
|
68
|
+
}
|
69
|
+
|
70
|
+
Also, instead of assigning the application to a variable, you may also
|
71
|
+
look it up later anywhere in your application:
|
72
|
+
|
73
|
+
Radar::Application.find(:my_application).report(exception)
|
74
|
+
|
75
|
+
# Or the shorthand:
|
76
|
+
Radar[:my_application].report(exception)
|
77
|
+
|
78
|
+
## Documentation and User Guide
|
79
|
+
|
80
|
+
For more details on configuring Radar, please view the
|
81
|
+
[user guide](http://mitchellh.github.com/radar/file.user_guide.html), which
|
82
|
+
is a detailed overview of Radar and all of its features.
|
83
|
+
|
84
|
+
## Reporting Bugs and Requesting Features
|
85
|
+
|
86
|
+
Please use the [issues section](http://github.com/mitchellh/radar/issues) to report
|
87
|
+
bugs and request features. This section is also used as the TODO area for the
|
88
|
+
Radar gem.
|
89
|
+
|
90
|
+
## Contributing
|
91
|
+
|
92
|
+
To hack on Radar, you'll need [bundler](http://github.com/carlhuda/bundler) which
|
93
|
+
can be installed with a simple `gem install bundler`. Then, do the following:
|
94
|
+
|
95
|
+
bundle install
|
96
|
+
rake
|
97
|
+
|
98
|
+
This will run the test suite, which should come back all green! Then you're
|
99
|
+
good to go.
|
100
|
+
|
101
|
+
The general steps to contributing a new change are:
|
102
|
+
|
103
|
+
1. Fork the repository
|
104
|
+
2. Make your changes, writing tests if necessary
|
105
|
+
3. Open an [issue](http://github.com/mitchellh/radar/issues) with the feature and
|
106
|
+
a link to your fork or a gist with a patch.
|
107
|
+
4. Wait patiently, for I am but one man.
|
data/Rakefile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
require 'rake/testtask'
|
4
|
+
Bundler::GemHelper.install_tasks
|
5
|
+
|
6
|
+
task :default => :test
|
7
|
+
|
8
|
+
Rake::TestTask.new do |t|
|
9
|
+
t.libs << "test"
|
10
|
+
t.pattern = 'test/**/*_test.rb'
|
11
|
+
end
|
12
|
+
|
13
|
+
begin
|
14
|
+
require 'yard'
|
15
|
+
YARD::Rake::YardocTask.new
|
16
|
+
rescue LoadError
|
17
|
+
end
|
data/docs/user_guide.md
ADDED
@@ -0,0 +1,197 @@
|
|
1
|
+
# Radar User's Guide
|
2
|
+
|
3
|
+
## Overview
|
4
|
+
|
5
|
+
Radar is a tool which provides a drop-in solution to catching and reporting
|
6
|
+
errors in your Ruby application via customizable mediums.
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
Install via RubyGems:
|
11
|
+
|
12
|
+
gem install radar
|
13
|
+
|
14
|
+
Or specify in your own library or application's `Gemfile` or `gemspec` so
|
15
|
+
that the gem is included properly.
|
16
|
+
|
17
|
+
## Basic Usage
|
18
|
+
|
19
|
+
### Setup
|
20
|
+
|
21
|
+
First, as early as possible in your application so that Radar can begin
|
22
|
+
catching exceptions right away, create a new {Radar::Application}
|
23
|
+
instance for your own app, replacing `my_application` with a unique name for your
|
24
|
+
application.
|
25
|
+
|
26
|
+
Radar::Application.new(:my_application)
|
27
|
+
|
28
|
+
But this alone won't do anything, since you haven't configured any reporters
|
29
|
+
for the application. Reporters are what handle taking an {Radar::ExceptionEvent ExceptionEvent}
|
30
|
+
and doing something with it (such as storing it in a file, reporting it to a
|
31
|
+
remote server, etc.). Radar comes with some built-in reporters. Below, we configure
|
32
|
+
the application to log errors to a file (by default at `~/.radar/errors/my_application`):
|
33
|
+
|
34
|
+
Radar::Application.new(:my_application) do |app|
|
35
|
+
app.config.reporter Radar::Reporter::FileReporter
|
36
|
+
end
|
37
|
+
|
38
|
+
### Reporting Errors
|
39
|
+
|
40
|
+
Once the application is setup, there are two methods to report errors:
|
41
|
+
|
42
|
+
1. Manually call the {Radar::Application#report report} method on the application.
|
43
|
+
2. Tell the application to {Radar::Application#rescue_at_exit! rescue at exit} so
|
44
|
+
Radar automatically catches any exceptions before your application crashes.
|
45
|
+
|
46
|
+
Calling the report method manually:
|
47
|
+
|
48
|
+
app = Radar::Application.new(:my_application)
|
49
|
+
app.report(exception)
|
50
|
+
|
51
|
+
The use case for this is in a `rescue` block somewhere, and forces Radar
|
52
|
+
to report the given exception.
|
53
|
+
|
54
|
+
Telling Radar to catch exceptions on exit is equally simple, and can be
|
55
|
+
used in conjunction with the above method as well:
|
56
|
+
|
57
|
+
app = Radar::Application.new(:my_application)
|
58
|
+
app.rescue_at_exit!
|
59
|
+
|
60
|
+
Now, whenever your application is about to crash (an exception not caught by
|
61
|
+
a `rescue`), Radar will catch your exception and report it just prior to
|
62
|
+
crashing.
|
63
|
+
|
64
|
+
# Features
|
65
|
+
|
66
|
+
## Reporters
|
67
|
+
|
68
|
+
On its own, Radar does nothing but catch and shuttle exceptions to reporters. Without
|
69
|
+
reporters, Radar is basically useless! A reporter is a class which takes an
|
70
|
+
{Radar::ExceptionEvent} and does something with it. Its easier to get a better idea
|
71
|
+
of what this means with a few examples:
|
72
|
+
|
73
|
+
* `FileReporter` - Stores the data from an exception on the filesystem for each
|
74
|
+
exception.
|
75
|
+
* `ServerReporter` - Transfers information about an exception to some remote
|
76
|
+
server.
|
77
|
+
|
78
|
+
### Enabling and Configuring a Reporter
|
79
|
+
|
80
|
+
Reporters are enabled using the appilication configuration:
|
81
|
+
|
82
|
+
Radar::Application.new(:my_application) do |app|
|
83
|
+
app.config.reporter FileReporter
|
84
|
+
end
|
85
|
+
|
86
|
+
And can be configured by passing a block to the reporter, which is yielded with
|
87
|
+
the instance of that reporter:
|
88
|
+
|
89
|
+
Radar::Application.new(:my_application) do |app|
|
90
|
+
app.config.reporter FileReporter do |reporter|
|
91
|
+
reporter.storage_directory = "~/.radar/exceptions"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
Radar also allows multiple reporters to be used, which are then called
|
96
|
+
in the order they are defined when an exception occurs:
|
97
|
+
|
98
|
+
Radar::Application.new(:my_application) do |app|
|
99
|
+
app.config.reporter FileReporter
|
100
|
+
app.config.reporter AnotherReporter
|
101
|
+
end
|
102
|
+
|
103
|
+
### Built-in Reporters
|
104
|
+
|
105
|
+
#### FileReporter
|
106
|
+
|
107
|
+
{Radar::Reporter::FileReporter FileReporter} outputs exception information as JSON to a file
|
108
|
+
on the local filesystem. The filename is in the format of `timestamp-uniquehash.txt`,
|
109
|
+
where `timestamp` is the time that the exception occurred and `uniquehash` is the
|
110
|
+
{Radar::ExceptionEvent#uniqueness_hash}.
|
111
|
+
|
112
|
+
The directory where these files will be stored is configurable:
|
113
|
+
|
114
|
+
Radar::Application.new(:my_application) do |app|
|
115
|
+
app.config.reporter Radar::Reporter::FileReporter do |reporter|
|
116
|
+
reporter.output_directory = "~/my_application_errors"
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
You may also use a lambda. Below is also the default value for the FileReporter:
|
121
|
+
|
122
|
+
reporter.output_directory = lambda { |event| "~/.radar/#{event.application.name}" }
|
123
|
+
|
124
|
+
A few notes:
|
125
|
+
|
126
|
+
* The FileReporter does not automatically handle cleaning up old exception files or reporting
|
127
|
+
these files to any remote server. It is up to you, if you wish, to clean up old
|
128
|
+
exception information.
|
129
|
+
* The JSON output is compressed JSON, and is not pretty printed. It is up to you to take the
|
130
|
+
JSON and pretty print it if you wish it to be easily human readable. There are
|
131
|
+
[services](http://jsonformatter.curiousconcept.com/) out there to do this.
|
132
|
+
|
133
|
+
### Custom Reporters
|
134
|
+
|
135
|
+
It is very easy to write custom reporters. A reporter is simply a class which
|
136
|
+
responds to `report` and takes a single {Radar::ExceptionEvent} as a parameter.
|
137
|
+
Below is an example of a reporter which simply prints out that an error
|
138
|
+
occurred:
|
139
|
+
|
140
|
+
class StdoutReporter
|
141
|
+
def report(event)
|
142
|
+
puts "An exception occurred! Message: #{event.exception.message}"
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
And then using that reporter is just as easy:
|
147
|
+
|
148
|
+
Radar::Application.new(:my_application) do |app|
|
149
|
+
app.config.reporter StdoutReporter
|
150
|
+
end
|
151
|
+
|
152
|
+
## Data Extensions
|
153
|
+
|
154
|
+
Data extensions allow you to easily extend the data represented by {Radar::ExceptionEvent#to_hash}.
|
155
|
+
By default, Radar only sends a small amount of information about the host environment and the
|
156
|
+
exception when an exception occurs. Often its more helpful to get more information about the
|
157
|
+
environment to more easily track down a bug. Some examples:
|
158
|
+
|
159
|
+
* HTTP request headers for a web application
|
160
|
+
* Configuration settings for a desktop application
|
161
|
+
|
162
|
+
### Defining Data Extensions
|
163
|
+
|
164
|
+
Data extensions are defined with classes which are expected to be initialized
|
165
|
+
with a reference to a {Radar::ExceptionEvent} and must implement the `to_hash`
|
166
|
+
method. Below is a data extension to add the output of `uname -a` to the event:
|
167
|
+
|
168
|
+
class UnameExtension
|
169
|
+
def initialize(event)
|
170
|
+
@event = event
|
171
|
+
end
|
172
|
+
|
173
|
+
def to_hash
|
174
|
+
{ :uname => `uname -a` }
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
### Enabling Data Extensions
|
179
|
+
|
180
|
+
Data extensions are enabled via the application configuration like most other
|
181
|
+
things:
|
182
|
+
|
183
|
+
Radar::Application.new(:my_application) do |app|
|
184
|
+
app.config.data_extension UnameExtension
|
185
|
+
end
|
186
|
+
|
187
|
+
### Built-In Data Extensions
|
188
|
+
|
189
|
+
By default, {Radar::ExceptionEvent#to_hash} (view the source) returns very little
|
190
|
+
information on its own. To be as general and extensible as possible, even the data
|
191
|
+
such as information about the host are created using built-in data extensions.
|
192
|
+
Some of these are enabled by default, which are designated by the `*` on the name.
|
193
|
+
|
194
|
+
* {Radar::DataExtensions::HostEnvironment HostEnvironment}* - Adds information about the
|
195
|
+
host such as Ruby version and operating system.
|
196
|
+
|
197
|
+
`*`: Enabled by default on every application.
|
data/lib/radar.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'radar/version'
|
2
|
+
require 'radar/error'
|
3
|
+
|
4
|
+
module Radar
|
5
|
+
autoload :Application, 'radar/application'
|
6
|
+
autoload :Config, 'radar/config'
|
7
|
+
autoload :ExceptionEvent, 'radar/exception_event'
|
8
|
+
autoload :Reporter, 'radar/reporter'
|
9
|
+
autoload :Support, 'radar/support'
|
10
|
+
|
11
|
+
module DataExtensions
|
12
|
+
autoload :HostEnvironment, 'radar/data_extensions/host_environment'
|
13
|
+
end
|
14
|
+
|
15
|
+
class Reporter
|
16
|
+
autoload :FileReporter, 'radar/reporter/file_reporter'
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'thread'
|
2
|
+
|
3
|
+
module Radar
|
4
|
+
# A shortcut for {Application.find}.
|
5
|
+
def [](*args)
|
6
|
+
Application.find(*args)
|
7
|
+
end
|
8
|
+
module_function :[]
|
9
|
+
|
10
|
+
# Represents an instance of Radar for a given application. Every
|
11
|
+
# application which uses Radar must instantiate an {Application}.
|
12
|
+
class Application
|
13
|
+
@@registered = {}
|
14
|
+
@@mutex = Mutex.new
|
15
|
+
|
16
|
+
attr_reader :name
|
17
|
+
attr_reader :creation_location
|
18
|
+
|
19
|
+
# Looks up an application which was registered with the given name.
|
20
|
+
#
|
21
|
+
# @param [String] name Application name.
|
22
|
+
# @return [Application]
|
23
|
+
def self.find(name)
|
24
|
+
@@registered[name]
|
25
|
+
end
|
26
|
+
|
27
|
+
# Removes all registered applications. **This is only exposed for testing
|
28
|
+
# purposes.**
|
29
|
+
def self.clear!
|
30
|
+
@@registered.clear
|
31
|
+
end
|
32
|
+
|
33
|
+
# Creates a new application with the given name and registers
|
34
|
+
# it for lookup later. If a block is given, it will be yielded with
|
35
|
+
# the new instantiated {Application} so you can also {#config} it
|
36
|
+
# all in one go.
|
37
|
+
#
|
38
|
+
# @param [String] name Application name. This must be unique for
|
39
|
+
# any given application or an exception will be raised.
|
40
|
+
# @return [Application]
|
41
|
+
def initialize(name, register=true)
|
42
|
+
@@mutex.synchronize do
|
43
|
+
raise ApplicationAlreadyExists.new("'#{name}' already defined at '#{self.class.find(name).creation_location}'") if self.class.find(name)
|
44
|
+
|
45
|
+
@name = name
|
46
|
+
@creation_location = caller.first
|
47
|
+
yield self if block_given?
|
48
|
+
@@registered[name] = self if register
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Configures the application by returning the configuration
|
53
|
+
# object. If a block is given, the configuration object will be
|
54
|
+
# passed into it, allowing for a cleaner way of configuring your
|
55
|
+
# applications.
|
56
|
+
#
|
57
|
+
# $app = Radar::Application.new
|
58
|
+
# $app.config do |config|
|
59
|
+
# config.storage_directory = "foo"
|
60
|
+
# end
|
61
|
+
#
|
62
|
+
# @yield [Config] Configuration object, only if block is given.
|
63
|
+
# @return [Config]
|
64
|
+
def config
|
65
|
+
@_config ||= Config.new
|
66
|
+
yield @_config if block_given?
|
67
|
+
@_config
|
68
|
+
end
|
69
|
+
|
70
|
+
# Reports an exception. This will send the exception on to the
|
71
|
+
# various reporters configured for this application.
|
72
|
+
#
|
73
|
+
# @param [Exception] exception
|
74
|
+
def report(exception)
|
75
|
+
data = ExceptionEvent.new(self, exception)
|
76
|
+
|
77
|
+
# Report the exception to each of the reporters
|
78
|
+
config.reporters.each do |reporter|
|
79
|
+
reporter.report(data)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Hooks this application into the `at_exit` handler so that
|
84
|
+
# application crashing exceptions are properly reported.
|
85
|
+
def rescue_at_exit!
|
86
|
+
at_exit { report($!) if $! }
|
87
|
+
end
|
88
|
+
|
89
|
+
# Converts application to a serialization-friendly hash.
|
90
|
+
#
|
91
|
+
# @return [Hash]
|
92
|
+
def to_hash
|
93
|
+
{ :name => name }
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
data/lib/radar/config.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
module Radar
|
2
|
+
class Config
|
3
|
+
attr_reader :reporters
|
4
|
+
attr_reader :data_extensions
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@reporters = []
|
8
|
+
@data_extensions = [DataExtensions::HostEnvironment]
|
9
|
+
end
|
10
|
+
|
11
|
+
# Add a reporter to an application. If a block is given, it
|
12
|
+
# will be yielded later (since reporters are initialized lazily)
|
13
|
+
# with the instance of the reporter.
|
14
|
+
#
|
15
|
+
# @param [Class] klass A {Reporter} class.
|
16
|
+
def reporter(klass)
|
17
|
+
instance = klass.new
|
18
|
+
yield instance if block_given?
|
19
|
+
@reporters << instance
|
20
|
+
end
|
21
|
+
|
22
|
+
# Adds a data extension to an application. Data extensions allow
|
23
|
+
# extra data to be included into an {ExceptionEvent} (they appear
|
24
|
+
# in the {ExceptionEvent#to_hash} output). For more information,
|
25
|
+
# please read the Radar user guide.
|
26
|
+
#
|
27
|
+
# This method takes a class. This class is expected to be initialized
|
28
|
+
# with an {ExceptionEvent}, and must implement the `to_hash` method.
|
29
|
+
#
|
30
|
+
# @param [Class] klass
|
31
|
+
def data_extension(klass)
|
32
|
+
@data_extensions << klass
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Radar
|
2
|
+
module DataExtensions
|
3
|
+
# Data extension which adds information about the host environment:
|
4
|
+
#
|
5
|
+
# * Operating system
|
6
|
+
# * Ruby version
|
7
|
+
#
|
8
|
+
class HostEnvironment
|
9
|
+
def initialize(event)
|
10
|
+
@event = event
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_hash
|
14
|
+
{ :host_environment => {
|
15
|
+
:ruby_version => (RUBY_VERSION rescue '?'),
|
16
|
+
:ruby_pl => (RUBY_PATCHLEVEL rescue '?'),
|
17
|
+
:ruby_release_date => (RUBY_RELEASE_DATE rescue '?'),
|
18
|
+
:ruby_platform => (RUBY_PLATFORM rescue '?')
|
19
|
+
}
|
20
|
+
}
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/radar/error.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'digest/sha1'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module Radar
|
5
|
+
# Represents the event of an exception being captured. This class
|
6
|
+
# contains references to the {Application} and exception which is
|
7
|
+
# raised.
|
8
|
+
class ExceptionEvent
|
9
|
+
attr_reader :application
|
10
|
+
attr_reader :exception
|
11
|
+
attr_reader :occurred_at
|
12
|
+
|
13
|
+
def initialize(application, exception)
|
14
|
+
@application = application
|
15
|
+
@exception = exception
|
16
|
+
@occurred_at = Time.now
|
17
|
+
end
|
18
|
+
|
19
|
+
# A hash of information about this exception event. This includes
|
20
|
+
# {Application#to_hash} as well as information about the exception.
|
21
|
+
# This also includes any {Config#data_extension data_extensions} if
|
22
|
+
# specified.
|
23
|
+
#
|
24
|
+
# @return [Hash]
|
25
|
+
def to_hash
|
26
|
+
result = { :application => application.to_hash,
|
27
|
+
:exception => {
|
28
|
+
:klass => exception.class.to_s,
|
29
|
+
:message => exception.message,
|
30
|
+
:backtrace => exception.backtrace,
|
31
|
+
:uniqueness_hash => uniqueness_hash
|
32
|
+
},
|
33
|
+
:occurred_at => occurred_at.to_i
|
34
|
+
}
|
35
|
+
|
36
|
+
if !application.config.data_extensions.empty?
|
37
|
+
application.config.data_extensions.each do |extension|
|
38
|
+
Support::Hash.deep_merge!(result, extension.new(self).to_hash)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
result
|
43
|
+
end
|
44
|
+
|
45
|
+
# JSONified {#to_hash} output.
|
46
|
+
#
|
47
|
+
# @return [String]
|
48
|
+
def to_json
|
49
|
+
to_hash.to_json
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns uniqueness hash to test if one event is roughly equivalent to
|
53
|
+
# another. The uniqueness hash is generated by taking the exception
|
54
|
+
# backtrace and class and generating the SHA1 hash of those concatenated.
|
55
|
+
#
|
56
|
+
# @return [String]
|
57
|
+
def uniqueness_hash
|
58
|
+
Digest::SHA1.hexdigest("#{exception.class}-#{exception.backtrace rescue 'blank'}")
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Radar
|
2
|
+
# This class defines the interface for a {Reporter}. A reporter
|
3
|
+
# is a class which takes exception information and "reports" it
|
4
|
+
# in some way. Examples of reporters (note that not all of these
|
5
|
+
# may be implemented; they are ideas):
|
6
|
+
#
|
7
|
+
# - FileReporter - Logs the exception out to a timestamped
|
8
|
+
# file in a specified directory.
|
9
|
+
# - ServerReporter - Logs the exception to a server somewhere for
|
10
|
+
# later retrieval.
|
11
|
+
# - GrowlReporter - Causes a Growl notification to occur, noting
|
12
|
+
# that an exception occurred.
|
13
|
+
#
|
14
|
+
# From the examples above it is clear to see that the reporter doesn't
|
15
|
+
# necessarilly need to store the exception information anywhere, it
|
16
|
+
# is simply required to take some action in the event that an exception
|
17
|
+
# occurred.
|
18
|
+
#
|
19
|
+
# Radar allows for multiple reporters to be used together, so a reporter
|
20
|
+
# doesn't need to do everything. They're meant to be small units of
|
21
|
+
# functionality.
|
22
|
+
#
|
23
|
+
class Reporter
|
24
|
+
# Report the environment.
|
25
|
+
def report(environment)
|
26
|
+
raise "Implement the `report` method in #{self.class}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module Radar
|
4
|
+
class Reporter
|
5
|
+
# Reports exceptions by dumping the JSON data out to a file on the
|
6
|
+
# local filesystem. The reporter is configurable:
|
7
|
+
#
|
8
|
+
# * {#output_directory=}
|
9
|
+
#
|
10
|
+
class FileReporter
|
11
|
+
attr_accessor :output_directory
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@output_directory = lambda { |event| "~/.radar/errors/#{event.application.name}" }
|
15
|
+
end
|
16
|
+
|
17
|
+
def report(event)
|
18
|
+
output_file = File.join(File.expand_path(output_directory(event)), "#{event.occurred_at.to_i}-#{event.uniqueness_hash}.txt")
|
19
|
+
|
20
|
+
# Attempt to make the directory if it doesn't exist
|
21
|
+
FileUtils.mkdir_p File.dirname(output_file)
|
22
|
+
|
23
|
+
# Write out the JSON to the output file
|
24
|
+
File.open(output_file, 'w') { |f| f.write(event.to_json) }
|
25
|
+
end
|
26
|
+
|
27
|
+
# Returns the currently configured output directory. If `event` is given
|
28
|
+
# as a parameter and the currently set directory is a lambda, then the
|
29
|
+
# lambda will be evaluated then returned. If no event is given, the lambda
|
30
|
+
# is returned as-is.
|
31
|
+
def output_directory(event=nil)
|
32
|
+
return @output_directory.call(event) if event && @output_directory.is_a?(Proc)
|
33
|
+
@output_directory
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Radar
|
2
|
+
class Support
|
3
|
+
class Hash
|
4
|
+
#----------------------------------------------------------------------
|
5
|
+
# Deep Merging - Taken from Ruby on Rails ActiveSupport
|
6
|
+
#----------------------------------------------------------------------
|
7
|
+
|
8
|
+
# Returns a new hash with +self+ and +other_hash+ merged recursively.
|
9
|
+
def self.deep_merge(source, other)
|
10
|
+
deep_merge!(source.dup, other)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Returns a new hash with +self+ and +other_hash+ merged recursively.
|
14
|
+
# Modifies the receiver in place.
|
15
|
+
def self.deep_merge!(source, other)
|
16
|
+
other.each_pair do |k,v|
|
17
|
+
tv = source[k]
|
18
|
+
source[k] = tv.is_a?(::Hash) && v.is_a?(::Hash) ? deep_merge(tv, v) : v
|
19
|
+
end
|
20
|
+
|
21
|
+
source
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/radar.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
|
3
|
+
require 'radar/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "radar"
|
7
|
+
s.version = Radar::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Mitchell Hashimoto"]
|
10
|
+
s.email = ["mitchell.hashimoto@gmail.com"]
|
11
|
+
s.homepage = "http://rubygems.org/gems/radar"
|
12
|
+
s.summary = "Easily catch and report errors in your Ruby libraries and applications any way you want!"
|
13
|
+
s.description = "Radar provides a drop-in solution to catching and reporting errors in your libraries and applications."
|
14
|
+
|
15
|
+
s.required_rubygems_version = ">= 1.3.6"
|
16
|
+
s.rubyforge_project = "radar"
|
17
|
+
|
18
|
+
s.add_dependency "json", ">= 1.4.6"
|
19
|
+
|
20
|
+
s.add_development_dependency "bundler", ">= 1.0.0.rc.5"
|
21
|
+
s.add_development_dependency "shoulda"
|
22
|
+
s.add_development_dependency "mocha"
|
23
|
+
s.add_development_dependency "rake"
|
24
|
+
|
25
|
+
s.files = `git ls-files`.split("\n")
|
26
|
+
s.executables = `git ls-files`.split("\n").select{|f| f =~ /^bin/}
|
27
|
+
s.require_path = 'lib'
|
28
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class ApplicationTest < Test::Unit::TestCase
|
4
|
+
context "application class" do
|
5
|
+
setup do
|
6
|
+
@klass = Radar::Application
|
7
|
+
@instance = @klass.new("bar", false)
|
8
|
+
end
|
9
|
+
|
10
|
+
context "initializing" do
|
11
|
+
teardown do
|
12
|
+
@klass.clear!
|
13
|
+
end
|
14
|
+
|
15
|
+
should "be able to create for a name" do
|
16
|
+
instance = @klass.new("foo")
|
17
|
+
assert_equal "foo", instance.name
|
18
|
+
end
|
19
|
+
|
20
|
+
should "be able to lookup after created" do
|
21
|
+
instance = @klass.new("foo")
|
22
|
+
assert_equal instance, @klass.find("foo")
|
23
|
+
end
|
24
|
+
|
25
|
+
should "allow creation of unregistered applications" do
|
26
|
+
instance = @klass.new("foo", false)
|
27
|
+
assert_nil @klass.find("foo")
|
28
|
+
end
|
29
|
+
|
30
|
+
should "raise an exception if duplicate name is used" do
|
31
|
+
assert_raises(Radar::ApplicationAlreadyExists) {
|
32
|
+
@klass.new("foo")
|
33
|
+
@klass.new("foo")
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
should "yield with the instance if a block is given" do
|
38
|
+
@klass.new("foo") do |instance|
|
39
|
+
assert instance.is_a?(@klass)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context "configuration" do
|
45
|
+
setup do
|
46
|
+
@instance.config.reporters.clear
|
47
|
+
|
48
|
+
@reporter = Class.new
|
49
|
+
end
|
50
|
+
|
51
|
+
should "be able to configure an application" do
|
52
|
+
@instance.config.reporter(@reporter)
|
53
|
+
assert !@instance.config.reporters.empty?
|
54
|
+
end
|
55
|
+
|
56
|
+
should "be able to configure using a block" do
|
57
|
+
@instance.config do |config|
|
58
|
+
config.reporter(@reporter)
|
59
|
+
end
|
60
|
+
|
61
|
+
assert !@instance.config.reporters.empty?
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context "reporting" do
|
66
|
+
setup do
|
67
|
+
# The fake reporter class
|
68
|
+
reporter = Class.new do
|
69
|
+
def report(environment)
|
70
|
+
raise "success"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# Setup the application to use the fake reporter
|
75
|
+
@instance.config do |config|
|
76
|
+
config.reporter reporter
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
should "call report on each registered reporter" do
|
81
|
+
assert_raises(RuntimeError) do
|
82
|
+
begin
|
83
|
+
@instance.report(Exception.new)
|
84
|
+
rescue => e
|
85
|
+
assert_equal "success", e.message
|
86
|
+
raise
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
context "to_hash" do
|
93
|
+
setup do
|
94
|
+
@hash = @instance.to_hash
|
95
|
+
end
|
96
|
+
|
97
|
+
should "contain name" do
|
98
|
+
assert_equal @instance.name, @hash[:name]
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# Untested: Application#rescue_at_exit! since I'm not aware of an
|
103
|
+
# [easy] way of testing it without spawning out a separate process.
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class ConfigTest < Test::Unit::TestCase
|
4
|
+
context "configuration" do
|
5
|
+
setup do
|
6
|
+
@klass = Radar::Config
|
7
|
+
@instance = @klass.new
|
8
|
+
end
|
9
|
+
|
10
|
+
context "reporters" do
|
11
|
+
setup do
|
12
|
+
@reporter_klass = Class.new
|
13
|
+
end
|
14
|
+
|
15
|
+
teardown do
|
16
|
+
@instance.reporters.clear
|
17
|
+
end
|
18
|
+
|
19
|
+
should "initially have no reporters" do
|
20
|
+
assert @instance.reporters.empty?
|
21
|
+
end
|
22
|
+
|
23
|
+
should "be able to add reporters" do
|
24
|
+
@instance.reporter @reporter_klass
|
25
|
+
assert !@instance.reporters.empty?
|
26
|
+
assert @instance.reporters.first.is_a?(@reporter_klass)
|
27
|
+
end
|
28
|
+
|
29
|
+
should "yield the reporter instance if a block is given" do
|
30
|
+
@reporter_klass.any_instance.expects(:some_method).once
|
31
|
+
@instance.reporter @reporter_klass do |reporter|
|
32
|
+
reporter.some_method
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context "data extensions" do
|
38
|
+
setup do
|
39
|
+
@extension = Class.new do
|
40
|
+
def initialize(event)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
teardown do
|
46
|
+
@instance.data_extensions.clear
|
47
|
+
end
|
48
|
+
|
49
|
+
should "initially have some data extensions" do
|
50
|
+
assert_equal [Radar::DataExtensions::HostEnvironment], @instance.data_extensions
|
51
|
+
end
|
52
|
+
|
53
|
+
should "be able to add data extensions" do
|
54
|
+
@instance.data_extension @extension
|
55
|
+
assert !@instance.data_extensions.empty?
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class HostEnvironmentDataTest < Test::Unit::TestCase
|
4
|
+
context "host environment class" do
|
5
|
+
setup do
|
6
|
+
@klass = Radar::DataExtensions::HostEnvironment
|
7
|
+
@instance = @klass.new(create_exception_event)
|
8
|
+
end
|
9
|
+
|
10
|
+
should "be able to convert to a hash" do
|
11
|
+
result = @instance.to_hash
|
12
|
+
assert result.is_a?(Hash)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class ExceptionEventTest < Test::Unit::TestCase
|
4
|
+
context "ExceptionEvent class" do
|
5
|
+
setup do
|
6
|
+
@klass = Radar::ExceptionEvent
|
7
|
+
@instance = create_exception_event
|
8
|
+
end
|
9
|
+
|
10
|
+
context "to_hash" do
|
11
|
+
context "data extensions" do
|
12
|
+
setup do
|
13
|
+
@extension = Class.new do
|
14
|
+
def initialize(event); @event = event; end
|
15
|
+
def to_hash; { :exception => { :foo => :bar } }; end
|
16
|
+
end
|
17
|
+
|
18
|
+
@instance.application.config.data_extension @extension
|
19
|
+
@result = @instance.to_hash
|
20
|
+
end
|
21
|
+
|
22
|
+
should "include data extensions if defined" do
|
23
|
+
assert @result[:exception].has_key?(:foo), "instance should have key: foo"
|
24
|
+
assert_equal :bar, @result[:exception][:foo]
|
25
|
+
end
|
26
|
+
|
27
|
+
should "deep merge information" do
|
28
|
+
assert @result[:exception].has_key?(:klass)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context "to_json" do
|
34
|
+
should "just jsonify hash output" do
|
35
|
+
assert_equal @instance.to_hash.to_json, @instance.to_json
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
should "generate a uniqueness hash" do
|
40
|
+
assert @instance.uniqueness_hash, "should have generated a uniqueness hash"
|
41
|
+
end
|
42
|
+
|
43
|
+
should "have a timestamp of when the exception occurred" do
|
44
|
+
assert @instance.occurred_at
|
45
|
+
assert @instance.occurred_at.is_a?(Time)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class FileReporterTest < Test::Unit::TestCase
|
4
|
+
context "file reporter class" do
|
5
|
+
setup do
|
6
|
+
@klass = Radar::Reporter::FileReporter
|
7
|
+
@instance = @klass.new
|
8
|
+
end
|
9
|
+
|
10
|
+
should "allow output directory to be a lambda" do
|
11
|
+
@instance.output_directory = lambda { |event| event.application.name }
|
12
|
+
event = create_exception_event
|
13
|
+
assert_equal event.application.name, @instance.output_directory(event)
|
14
|
+
end
|
15
|
+
|
16
|
+
should "allow output directory to be a string" do
|
17
|
+
value = "value"
|
18
|
+
@instance.output_directory = value
|
19
|
+
assert_equal value, @instance.output_directory
|
20
|
+
end
|
21
|
+
|
22
|
+
should "just return the lambda if no event is given to read" do
|
23
|
+
@instance.output_directory = lambda {}
|
24
|
+
assert @instance.output_directory.is_a?(Proc)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class ReporterTest < Test::Unit::TestCase
|
4
|
+
context "reporter class" do
|
5
|
+
setup do
|
6
|
+
@klass = Radar::Reporter
|
7
|
+
@instance = @klass.new
|
8
|
+
end
|
9
|
+
|
10
|
+
should "raise an exception for an unimplemented reporter" do
|
11
|
+
assert_raises(RuntimeError) { @instance.report(nil) }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class SupportTest < Test::Unit::TestCase
|
4
|
+
context "Support class" do
|
5
|
+
setup do
|
6
|
+
@klass = Radar::Support
|
7
|
+
end
|
8
|
+
|
9
|
+
context "hash" do
|
10
|
+
should "deep merge simple hashes" do
|
11
|
+
result = @klass::Hash.deep_merge({ :a => 1 }, { :b => 2 })
|
12
|
+
assert_equal 1, result[:a]
|
13
|
+
assert_equal 2, result[:b]
|
14
|
+
end
|
15
|
+
|
16
|
+
should "support deep merging" do
|
17
|
+
a = { :a => { :a => 1 } }
|
18
|
+
b = { :a => { :b => 2 } }
|
19
|
+
|
20
|
+
result = @klass::Hash.deep_merge(a, b)
|
21
|
+
assert_equal 1, result[:a][:a]
|
22
|
+
assert_equal 2, result[:a][:b]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
$:.unshift File.expand_path('../../lib', __FILE__)
|
2
|
+
|
3
|
+
require "rubygems"
|
4
|
+
require "bundler/setup"
|
5
|
+
require "test/unit"
|
6
|
+
require "shoulda"
|
7
|
+
require "mocha"
|
8
|
+
require "radar"
|
9
|
+
|
10
|
+
class Test::Unit::TestCase
|
11
|
+
# Returns a real {Radar::ExceptionEvent} object with a newly created
|
12
|
+
# {Radar::Application} and a valid (has a backtrace) exception.
|
13
|
+
def create_exception_event
|
14
|
+
application = Radar::Application.new(:foo, false)
|
15
|
+
exception = nil
|
16
|
+
|
17
|
+
begin
|
18
|
+
raise "Something bad happened!"
|
19
|
+
rescue => e
|
20
|
+
exception = e
|
21
|
+
end
|
22
|
+
|
23
|
+
Radar::ExceptionEvent.new(application, exception)
|
24
|
+
end
|
25
|
+
end
|
metadata
ADDED
@@ -0,0 +1,163 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: radar
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
version: 0.1.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Mitchell Hashimoto
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-08-15 00:00:00 -07:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: json
|
22
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
23
|
+
none: false
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 1
|
29
|
+
- 4
|
30
|
+
- 6
|
31
|
+
version: 1.4.6
|
32
|
+
type: :runtime
|
33
|
+
prerelease: false
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: bundler
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
38
|
+
none: false
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
segments:
|
43
|
+
- 1
|
44
|
+
- 0
|
45
|
+
- 0
|
46
|
+
- rc
|
47
|
+
- 5
|
48
|
+
version: 1.0.0.rc.5
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: *id002
|
52
|
+
- !ruby/object:Gem::Dependency
|
53
|
+
name: shoulda
|
54
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
segments:
|
60
|
+
- 0
|
61
|
+
version: "0"
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: *id003
|
65
|
+
- !ruby/object:Gem::Dependency
|
66
|
+
name: mocha
|
67
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
68
|
+
none: false
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
segments:
|
73
|
+
- 0
|
74
|
+
version: "0"
|
75
|
+
type: :development
|
76
|
+
prerelease: false
|
77
|
+
version_requirements: *id004
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: rake
|
80
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ">="
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
segments:
|
86
|
+
- 0
|
87
|
+
version: "0"
|
88
|
+
type: :development
|
89
|
+
prerelease: false
|
90
|
+
version_requirements: *id005
|
91
|
+
description: Radar provides a drop-in solution to catching and reporting errors in your libraries and applications.
|
92
|
+
email:
|
93
|
+
- mitchell.hashimoto@gmail.com
|
94
|
+
executables: []
|
95
|
+
|
96
|
+
extensions: []
|
97
|
+
|
98
|
+
extra_rdoc_files: []
|
99
|
+
|
100
|
+
files:
|
101
|
+
- .gitignore
|
102
|
+
- .yardopts
|
103
|
+
- Gemfile
|
104
|
+
- Gemfile.lock
|
105
|
+
- README.md
|
106
|
+
- Rakefile
|
107
|
+
- docs/user_guide.md
|
108
|
+
- lib/radar.rb
|
109
|
+
- lib/radar/application.rb
|
110
|
+
- lib/radar/config.rb
|
111
|
+
- lib/radar/data_extensions/host_environment.rb
|
112
|
+
- lib/radar/error.rb
|
113
|
+
- lib/radar/exception_event.rb
|
114
|
+
- lib/radar/reporter.rb
|
115
|
+
- lib/radar/reporter/file_reporter.rb
|
116
|
+
- lib/radar/support.rb
|
117
|
+
- lib/radar/version.rb
|
118
|
+
- radar.gemspec
|
119
|
+
- test/radar/application_test.rb
|
120
|
+
- test/radar/config_test.rb
|
121
|
+
- test/radar/data_extensions/host_environment_test.rb
|
122
|
+
- test/radar/exception_event_test.rb
|
123
|
+
- test/radar/reporter/file_reporter_test.rb
|
124
|
+
- test/radar/reporter_test.rb
|
125
|
+
- test/radar/support_test.rb
|
126
|
+
- test/test_helper.rb
|
127
|
+
has_rdoc: true
|
128
|
+
homepage: http://rubygems.org/gems/radar
|
129
|
+
licenses: []
|
130
|
+
|
131
|
+
post_install_message:
|
132
|
+
rdoc_options: []
|
133
|
+
|
134
|
+
require_paths:
|
135
|
+
- lib
|
136
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
138
|
+
requirements:
|
139
|
+
- - ">="
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
hash: 4451322989948183573
|
142
|
+
segments:
|
143
|
+
- 0
|
144
|
+
version: "0"
|
145
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
146
|
+
none: false
|
147
|
+
requirements:
|
148
|
+
- - ">="
|
149
|
+
- !ruby/object:Gem::Version
|
150
|
+
segments:
|
151
|
+
- 1
|
152
|
+
- 3
|
153
|
+
- 6
|
154
|
+
version: 1.3.6
|
155
|
+
requirements: []
|
156
|
+
|
157
|
+
rubyforge_project: radar
|
158
|
+
rubygems_version: 1.3.7
|
159
|
+
signing_key:
|
160
|
+
specification_version: 3
|
161
|
+
summary: Easily catch and report errors in your Ruby libraries and applications any way you want!
|
162
|
+
test_files: []
|
163
|
+
|