Commit 8bf98094f5b7cd7538e04e49082497c0bf64a895
1 parent
9f0ec712
Exists in
master
and in
1 other branch
Updating hoptoad
Showing
44 changed files
with
4344 additions
and
0 deletions
Show diff stats
... | ... | @@ -0,0 +1,12 @@ |
1 | +# Don't load anything when running the gems:* tasks. | |
2 | +# Otherwise, hoptoad_notifier will be considered a framework gem. | |
3 | +# https://thoughtbot.lighthouseapp.com/projects/14221/tickets/629 | |
4 | +unless ARGV.any? {|a| a =~ /^gems/} | |
5 | + | |
6 | + Dir[File.join(RAILS_ROOT, 'vendor', 'gems', 'hoptoad_notifier-*')].each do |vendored_notifier| | |
7 | + $: << File.join(vendored_notifier, 'lib') | |
8 | + end | |
9 | + | |
10 | + require 'hoptoad_notifier/tasks' | |
11 | + | |
12 | +end | ... | ... |
... | ... | @@ -0,0 +1,184 @@ |
1 | +--- !ruby/object:Gem::Specification | |
2 | +name: hoptoad_notifier | |
3 | +version: !ruby/object:Gem::Version | |
4 | + prerelease: false | |
5 | + segments: | |
6 | + - 2 | |
7 | + - 2 | |
8 | + - 2 | |
9 | + version: 2.2.2 | |
10 | +platform: ruby | |
11 | +authors: | |
12 | +- thoughtbot, inc | |
13 | +autorequire: | |
14 | +bindir: bin | |
15 | +cert_chain: [] | |
16 | + | |
17 | +date: 2010-03-10 00:00:00 -05:00 | |
18 | +default_executable: | |
19 | +dependencies: | |
20 | +- !ruby/object:Gem::Dependency | |
21 | + name: activesupport | |
22 | + prerelease: false | |
23 | + requirement: &id001 !ruby/object:Gem::Requirement | |
24 | + requirements: | |
25 | + - - ">=" | |
26 | + - !ruby/object:Gem::Version | |
27 | + segments: | |
28 | + - 0 | |
29 | + version: "0" | |
30 | + type: :runtime | |
31 | + version_requirements: *id001 | |
32 | +- !ruby/object:Gem::Dependency | |
33 | + name: activerecord | |
34 | + prerelease: false | |
35 | + requirement: &id002 !ruby/object:Gem::Requirement | |
36 | + requirements: | |
37 | + - - ">=" | |
38 | + - !ruby/object:Gem::Version | |
39 | + segments: | |
40 | + - 0 | |
41 | + version: "0" | |
42 | + type: :development | |
43 | + version_requirements: *id002 | |
44 | +- !ruby/object:Gem::Dependency | |
45 | + name: actionpack | |
46 | + prerelease: false | |
47 | + requirement: &id003 !ruby/object:Gem::Requirement | |
48 | + requirements: | |
49 | + - - ">=" | |
50 | + - !ruby/object:Gem::Version | |
51 | + segments: | |
52 | + - 0 | |
53 | + version: "0" | |
54 | + type: :development | |
55 | + version_requirements: *id003 | |
56 | +- !ruby/object:Gem::Dependency | |
57 | + name: jferris-mocha | |
58 | + prerelease: false | |
59 | + requirement: &id004 !ruby/object:Gem::Requirement | |
60 | + requirements: | |
61 | + - - ">=" | |
62 | + - !ruby/object:Gem::Version | |
63 | + segments: | |
64 | + - 0 | |
65 | + version: "0" | |
66 | + type: :development | |
67 | + version_requirements: *id004 | |
68 | +- !ruby/object:Gem::Dependency | |
69 | + name: nokogiri | |
70 | + prerelease: false | |
71 | + requirement: &id005 !ruby/object:Gem::Requirement | |
72 | + requirements: | |
73 | + - - ">=" | |
74 | + - !ruby/object:Gem::Version | |
75 | + segments: | |
76 | + - 0 | |
77 | + version: "0" | |
78 | + type: :development | |
79 | + version_requirements: *id005 | |
80 | +- !ruby/object:Gem::Dependency | |
81 | + name: shoulda | |
82 | + prerelease: false | |
83 | + requirement: &id006 !ruby/object:Gem::Requirement | |
84 | + requirements: | |
85 | + - - ">=" | |
86 | + - !ruby/object:Gem::Version | |
87 | + segments: | |
88 | + - 0 | |
89 | + version: "0" | |
90 | + type: :development | |
91 | + version_requirements: *id006 | |
92 | +description: | |
93 | +email: support@hoptoadapp.com | |
94 | +executables: [] | |
95 | + | |
96 | +extensions: [] | |
97 | + | |
98 | +extra_rdoc_files: | |
99 | +- README.rdoc | |
100 | +files: | |
101 | +- CHANGELOG | |
102 | +- INSTALL | |
103 | +- MIT-LICENSE | |
104 | +- Rakefile | |
105 | +- README.rdoc | |
106 | +- SUPPORTED_RAILS_VERSIONS | |
107 | +- TESTING.rdoc | |
108 | +- generators/hoptoad/hoptoad_generator.rb | |
109 | +- generators/hoptoad/lib/insert_commands.rb | |
110 | +- generators/hoptoad/lib/rake_commands.rb | |
111 | +- generators/hoptoad/templates/capistrano_hook.rb | |
112 | +- generators/hoptoad/templates/hoptoad_notifier_tasks.rake | |
113 | +- generators/hoptoad/templates/initializer.rb | |
114 | +- lib/hoptoad_notifier/backtrace.rb | |
115 | +- lib/hoptoad_notifier/capistrano.rb | |
116 | +- lib/hoptoad_notifier/configuration.rb | |
117 | +- lib/hoptoad_notifier/notice.rb | |
118 | +- lib/hoptoad_notifier/rack.rb | |
119 | +- lib/hoptoad_notifier/rails/action_controller_catcher.rb | |
120 | +- lib/hoptoad_notifier/rails/controller_methods.rb | |
121 | +- lib/hoptoad_notifier/rails/error_lookup.rb | |
122 | +- lib/hoptoad_notifier/rails.rb | |
123 | +- lib/hoptoad_notifier/sender.rb | |
124 | +- lib/hoptoad_notifier/tasks.rb | |
125 | +- lib/hoptoad_notifier/version.rb | |
126 | +- lib/hoptoad_notifier.rb | |
127 | +- lib/hoptoad_tasks.rb | |
128 | +- test/backtrace_test.rb | |
129 | +- test/catcher_test.rb | |
130 | +- test/configuration_test.rb | |
131 | +- test/helper.rb | |
132 | +- test/hoptoad_tasks_test.rb | |
133 | +- test/logger_test.rb | |
134 | +- test/notice_test.rb | |
135 | +- test/notifier_test.rb | |
136 | +- test/rack_test.rb | |
137 | +- test/rails_initializer_test.rb | |
138 | +- test/sender_test.rb | |
139 | +- rails/init.rb | |
140 | +- script/integration_test.rb | |
141 | +- lib/templates/rescue.erb | |
142 | +has_rdoc: true | |
143 | +homepage: http://www.hoptoadapp.com | |
144 | +licenses: [] | |
145 | + | |
146 | +post_install_message: | |
147 | +rdoc_options: | |
148 | +- --line-numbers | |
149 | +- --main | |
150 | +- README.rdoc | |
151 | +require_paths: | |
152 | +- lib | |
153 | +required_ruby_version: !ruby/object:Gem::Requirement | |
154 | + requirements: | |
155 | + - - ">=" | |
156 | + - !ruby/object:Gem::Version | |
157 | + segments: | |
158 | + - 0 | |
159 | + version: "0" | |
160 | +required_rubygems_version: !ruby/object:Gem::Requirement | |
161 | + requirements: | |
162 | + - - ">=" | |
163 | + - !ruby/object:Gem::Version | |
164 | + segments: | |
165 | + - 0 | |
166 | + version: "0" | |
167 | +requirements: [] | |
168 | + | |
169 | +rubyforge_project: | |
170 | +rubygems_version: 1.3.6 | |
171 | +signing_key: | |
172 | +specification_version: 3 | |
173 | +summary: Send your application errors to our hosted service and reclaim your inbox. | |
174 | +test_files: | |
175 | +- test/backtrace_test.rb | |
176 | +- test/catcher_test.rb | |
177 | +- test/configuration_test.rb | |
178 | +- test/hoptoad_tasks_test.rb | |
179 | +- test/logger_test.rb | |
180 | +- test/notice_test.rb | |
181 | +- test/notifier_test.rb | |
182 | +- test/rack_test.rb | |
183 | +- test/rails_initializer_test.rb | |
184 | +- test/sender_test.rb | ... | ... |
... | ... | @@ -0,0 +1,75 @@ |
1 | +Version 2.2.2 - 2010-03-10 | |
2 | +=============================================================================== | |
3 | + | |
4 | +Chad Pytel (1): | |
5 | + document proxy support | |
6 | + | |
7 | +Joe Ferris (8): | |
8 | + Added upgrade instructions to the README | |
9 | + Give a clearer error message when generating a Rails app fails | |
10 | + Fail loudly when a gem can't be vendored | |
11 | + Debugging rubygems issues | |
12 | + Explicitly specify the gem paths | |
13 | + Less noisy | |
14 | + Restore gem path after vendoring | |
15 | + Fixed a typo | |
16 | + | |
17 | +Jon Yurek (1): | |
18 | + Added notice about removing hoptoad rake tasks to upgrade gem | |
19 | + | |
20 | +Mike Burns (1): | |
21 | + Remove stray file with notes in it | |
22 | + | |
23 | + | |
24 | +Version 2.2.1 - 2010-03-10 | |
25 | +=============================================================================== | |
26 | + | |
27 | +Jason Morrison (3): | |
28 | + LH-629 Ensure notifier is not considered a framework gem | |
29 | + Removing things-the-generator-must-do file | |
30 | + Add rake cucumber:wip:rails* tasks for work-in-progress features | |
31 | + | |
32 | + | |
33 | +Version 2.2.0 - 2010-02-18 | |
34 | +=============================================================================== | |
35 | + | |
36 | +Bumping the version from 2.1.4 to 2.2.0 since adding Rack support warrants a minor version. | |
37 | + | |
38 | +Jason Morrison (1): | |
39 | + Stringify array elements when making assertions about Notice XML for 1.9 compatibility | |
40 | + | |
41 | + | |
42 | +Version 2.1.4 - 2010-02-12 | |
43 | +=============================================================================== | |
44 | + | |
45 | +Chad Pytel (2): | |
46 | + add more info to README for 1.2.6 | |
47 | + fix gem unpack line for 1.2.6 | |
48 | + | |
49 | +Jason Morrison (2): | |
50 | + Adding additional instructions for Rails 1.2.6 | |
51 | + Typesetting in README.rdoc | |
52 | + | |
53 | +Joe Ferris (11): | |
54 | + Separating Rails functionality out more | |
55 | + Initial Rack middleware | |
56 | + Extract request info from rack env | |
57 | + Added integration tests for rescuing | |
58 | + Fixed reporting of Rails version | |
59 | + Small refactoring | |
60 | + Automatically add Rack middleware for Rails apps that support it (catches exceptions from Metal) | |
61 | + Added an integration test and docs for rack apps | |
62 | + Added integration/readme coverage of Sinatra apps | |
63 | + Added docs to HoptoadNotifier::Rack | |
64 | + Require rack in tests for older versions of Rails; use active_support instead of activesupport | |
65 | + | |
66 | +Nick Quaranto (3): | |
67 | + Fixing the capistrano hook bit in the readme | |
68 | + Adding changeling:minor and changeling:patch to automate notifier releases | |
69 | + Adding rake changeling:push | |
70 | + | |
71 | + | |
72 | + | |
73 | + | |
74 | + | |
75 | + | ... | ... |
... | ... | @@ -0,0 +1,25 @@ |
1 | +=== Configuration | |
2 | + | |
3 | +You should have something like this in config/initializers/hoptoad.rb. | |
4 | + | |
5 | + HoptoadNotifier.configure do |config| | |
6 | + config.api_key = '1234567890abcdef' | |
7 | + end | |
8 | + | |
9 | +(Please note that this configuration should be in a global configuration, and | |
10 | +is *not* environment-specific. Hoptoad is smart enough to know what errors are | |
11 | +caused by what environments, so your staging errors don't get mixed in with | |
12 | +your production errors.) | |
13 | + | |
14 | +You can test that Hoptoad is working in your production environment by using | |
15 | +this rake task (from RAILS_ROOT): | |
16 | + | |
17 | + rake hoptoad:test | |
18 | + | |
19 | +If everything is configured properly, that task will send a notice to Hoptoad | |
20 | +which will be visible immediately. | |
21 | + | |
22 | +NOTE FOR RAILS 1.2.* USERS: | |
23 | + | |
24 | +You will need to copy the hoptoad_notifier_tasks.rake file into your | |
25 | +RAILS_ROOT/lib/tasks directory in order for the rake hoptoad:test task to work. | ... | ... |
... | ... | @@ -0,0 +1,22 @@ |
1 | +Copyright (c) 2007, Tammer Saleh, Thoughtbot, Inc. | |
2 | + | |
3 | +Permission is hereby granted, free of charge, to any person | |
4 | +obtaining a copy of this software and associated documentation | |
5 | +files (the "Software"), to deal in the Software without | |
6 | +restriction, including without limitation the rights to use, | |
7 | +copy, modify, merge, publish, distribute, sublicense, and/or sell | |
8 | +copies of the Software, and to permit persons to whom the | |
9 | +Software is furnished to do so, subject to the following | |
10 | +conditions: | |
11 | + | |
12 | +The above copyright notice and this permission notice shall be | |
13 | +included in all copies or substantial portions of the Software. | |
14 | + | |
15 | +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
16 | +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES | |
17 | +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
18 | +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT | |
19 | +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | |
20 | +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
21 | +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
22 | +OTHER DEALINGS IN THE SOFTWARE. | ... | ... |
... | ... | @@ -0,0 +1,356 @@ |
1 | += HoptoadNotifier | |
2 | + | |
3 | +This is the notifier gem for integrating apps with Hoptoad. | |
4 | + | |
5 | +When an uncaught exception occurs, HoptoadNotifier will POST the relevant data | |
6 | +to the Hoptoad server specified in your environment. | |
7 | + | |
8 | +== Help | |
9 | + | |
10 | +* {IRC}[irc://irc.freenode.net/thoughtbot] | |
11 | +* {mailing list}[http://groups.google.com/group/hoptoad-notifier-dev] | |
12 | + | |
13 | +== Rails Installation | |
14 | + | |
15 | +=== Remove exception_notifier | |
16 | + | |
17 | +in your ApplicationController, REMOVE this line: | |
18 | + | |
19 | + include ExceptionNotifiable | |
20 | + | |
21 | +In your config/environment* files, remove all references to ExceptionNotifier | |
22 | + | |
23 | +Remove the vendor/plugins/exception_notifier directory. | |
24 | + | |
25 | +=== Remove hoptoad_notifier plugin | |
26 | + | |
27 | +Remove the vendor/plugins/hoptoad_notifier directory before installing the gem, or run: | |
28 | + | |
29 | + script/plugin remove hoptoad_notifier | |
30 | + | |
31 | +=== Rails 2.x | |
32 | + | |
33 | +Add the hoptoad_notifier gem to your app. In config/environment.rb: | |
34 | + | |
35 | + config.gem 'hoptoad_notifier' | |
36 | + | |
37 | +Then from your project's RAILS_ROOT, run: | |
38 | + | |
39 | + rake gems:install | |
40 | + script/generate hoptoad --api-key your_key_here | |
41 | + | |
42 | +Once installed, you should vendor the hoptoad_notifier gem. | |
43 | + | |
44 | + rake gems:unpack GEM=hoptoad_notifier | |
45 | + | |
46 | +As always, if you choose not to vendor the hoptoad_notifier gem, make sure | |
47 | +every server you deploy to has the gem installed or your application won't start. | |
48 | + | |
49 | +=== Rails 1.2.6 | |
50 | + | |
51 | +Install the hoptoad_notifier gem: | |
52 | + | |
53 | + gem install hoptoad_notifier | |
54 | + | |
55 | +Once installed, you should vendor the hoptoad_notifier gem: | |
56 | + | |
57 | + mkdir vendor/gems | |
58 | + cd vendor/gems | |
59 | + gem unpack hoptoad_notifier | |
60 | + | |
61 | +And then add the following to the Rails::Initializer.run do |config| | |
62 | +block in environment.rb so that the vendored gem is loaded. | |
63 | + | |
64 | + # Add the vendor/gems/*/lib directories to the LOAD_PATH | |
65 | + config.load_paths += Dir.glob(File.join(RAILS_ROOT, 'vendor', 'gems', '*', 'lib')) | |
66 | + | |
67 | +Next add something like this at the bottom of your config/environment.rb: | |
68 | + | |
69 | + require 'hoptoad_notifier' | |
70 | + require 'hoptoad_notifier/rails' | |
71 | + HoptoadNotifier.configure do |config| | |
72 | + config.api_key = 'your_key_here' | |
73 | + end | |
74 | + | |
75 | +You will also need to copy the hoptoad_notifier_tasks.rake file into your | |
76 | +RAILS_ROOT/lib/tasks directory in order for the rake hoptoad:test task to work: | |
77 | + | |
78 | + cp vendor/gems/hoptoad_notifier-*/generators/hoptoad/templates/hoptoad_notifier_tasks.rake lib/tasks | |
79 | + | |
80 | +As always, if you choose not to vendor the hoptoad_notifier gem, make sure | |
81 | +every server you deploy to has the gem installed or your application won't start. | |
82 | + | |
83 | +=== Upgrading From Earlier Versions of Hoptoad | |
84 | + | |
85 | +If you're currently using the plugin version (if you have a | |
86 | +vendor/plugins/hoptoad_notifier directory, you are), you'll need to perform a | |
87 | +few extra steps when upgrading to the gem version. | |
88 | + | |
89 | +Add the hoptoad_notifier gem to your app. In config/environment.rb: | |
90 | + | |
91 | + config.gem 'hoptoad_notifier' | |
92 | + | |
93 | +Remove the plugin: | |
94 | + | |
95 | + rm -rf vendor/plugins/hoptoad_notifier | |
96 | + | |
97 | +Before running the hoptoad generator, you need to find your project's API key. | |
98 | +Log in to your account at hoptoadapp.com, and click on the "Projects" button. | |
99 | +Then, find your project in the list, and click on its name. In the left-hand | |
100 | +column, you'll see an "Edit this project" button. Click on that to get your | |
101 | +project's API. (If you accidentally use your personal API auth_token, you won't | |
102 | +be able to install the gem.) | |
103 | + | |
104 | +Then from your project's RAILS_ROOT, run: | |
105 | + | |
106 | + rake gems:install | |
107 | + script/generate hoptoad --api-key your_key_here | |
108 | + | |
109 | +Once installed, you should vendor the hoptoad_notifier gem. | |
110 | + | |
111 | + rake gems:unpack GEM=hoptoad_notifier | |
112 | + | |
113 | +As always, if you choose not to vendor the hoptoad_notifier gem, make sure | |
114 | +every server you deploy to has the gem installed or your application won't | |
115 | +start. | |
116 | + | |
117 | +== Upgrading from Earlier Versions of the Hoptoad Gem (with config.gem) | |
118 | + | |
119 | +If you're currently using the gem version of the hoptoad_notifier and have | |
120 | +a version of Rails that uses config.gem (in the 2.x series), there is | |
121 | +a step or two that you need to do to upgrade. First, you need to remove | |
122 | +the old version of the gem from vendor/gems: | |
123 | + | |
124 | + rm -rf vendor/gems/hoptoad_notifier-X.X.X | |
125 | + | |
126 | +Then you must remove the hoptoad_notifier_tasks.rake file from lib: | |
127 | + | |
128 | + rm lib/tasks/hoptoad_notifier_tasks.rake | |
129 | + | |
130 | +You can them continue to install normally. If you don't remove the rake file, | |
131 | +you will be unable to unpack this gem (Rails will think it's part of the | |
132 | +framework). | |
133 | + | |
134 | +=== Testing it out | |
135 | + | |
136 | +You can test that Hoptoad is working in your production environment by using | |
137 | +this rake task (from RAILS_ROOT): | |
138 | + | |
139 | + rake hoptoad:test | |
140 | + | |
141 | +If everything is configured properly, that task will send a notice to Hoptoad | |
142 | +which will be visible immediately. | |
143 | + | |
144 | +== Rack | |
145 | + | |
146 | +In order to use hoptoad_notifier in a non-Rails rack app, just load the | |
147 | +hoptoad_notifier, configure your API key, and use the HoptoadNotifier::Rack | |
148 | +middleware: | |
149 | + | |
150 | + require 'rack' | |
151 | + require 'hoptoad_notifier' | |
152 | + | |
153 | + HoptoadNotifier.configure do |config| | |
154 | + config.api_key = 'my_api_key' | |
155 | + end | |
156 | + | |
157 | + app = Rack::Builder.app do | |
158 | + use HoptoadNotifier::Rack | |
159 | + run lambda { |env| raise "Rack down" } | |
160 | + end | |
161 | + | |
162 | +== Sinatra | |
163 | + | |
164 | +Using hoptoad_notifier in a Sinatra app is just like a Rack app, but you have | |
165 | +to disable Sinatra's error rescuing functionality: | |
166 | + | |
167 | + require 'sinatra/base' | |
168 | + require 'hoptoad_notifier' | |
169 | + | |
170 | + HoptoadNotifier.configure do |config| | |
171 | + config.api_key = 'my_api_key' | |
172 | + end | |
173 | + | |
174 | + class MyApp < Sinatra::Default | |
175 | + use HoptoadNotifier::Rack | |
176 | + enable :raise_errors | |
177 | + | |
178 | + get "/" do | |
179 | + raise "Sinatra has left the building" | |
180 | + end | |
181 | + end | |
182 | + | |
183 | +== Usage | |
184 | + | |
185 | +For the most part, Hoptoad works for itself. Once you've included the notifier | |
186 | +in your ApplicationController (which is now done automatically by the gem), | |
187 | +all errors will be rescued by the #rescue_action_in_public provided by the gem. | |
188 | + | |
189 | +If you want to log arbitrary things which you've rescued yourself from a | |
190 | +controller, you can do something like this: | |
191 | + | |
192 | + ... | |
193 | + rescue => ex | |
194 | + notify_hoptoad(ex) | |
195 | + flash[:failure] = 'Encryptions could not be rerouted, try again.' | |
196 | + end | |
197 | + ... | |
198 | + | |
199 | +The #notify_hoptoad call will send the notice over to Hoptoad for later | |
200 | +analysis. While in your controllers you use the notify_hoptoad method, anywhere | |
201 | +else in your code, use HoptoadNotifier.notify. | |
202 | + | |
203 | +To perform custom error processing after Hoptoad has been notified, define the | |
204 | +instance method #rescue_action_in_public_without_hoptoad(exception) in your | |
205 | +controller. | |
206 | + | |
207 | +== Tracking deployments in Hoptoad | |
208 | + | |
209 | +Paying Hoptoad plans support the ability to track deployments of your application in Hoptoad. | |
210 | +By notifying Hoptoad of your application deployments, all errors are resolved when a deploy occurs, | |
211 | +so that you'll be notified again about any errors that reoccur after a deployment. | |
212 | + | |
213 | +Additionally, it's possible to review the errors in Hoptoad that occurred before and after a deploy. | |
214 | + | |
215 | +When Hoptoad is installed as a gem, you need to add | |
216 | + | |
217 | + require 'hoptoad_notifier/capistrano' | |
218 | + | |
219 | +to your deploy.rb | |
220 | + | |
221 | +== Going beyond exceptions | |
222 | + | |
223 | +You can also pass a hash to notify_hoptoad method and store whatever you want, | |
224 | +not just an exception. And you can also use it anywhere, not just in | |
225 | +controllers: | |
226 | + | |
227 | + begin | |
228 | + params = { | |
229 | + # params that you pass to a method that can throw an exception | |
230 | + } | |
231 | + my_unpredicable_method(params) | |
232 | + rescue => e | |
233 | + HoptoadNotifier.notify( | |
234 | + :error_class => "Special Error", | |
235 | + :error_message => "Special Error: #{e.message}", | |
236 | + :parameters => params | |
237 | + ) | |
238 | + end | |
239 | + | |
240 | +While in your controllers you use the notify_hoptoad method, anywhere else in | |
241 | +your code, use HoptoadNotifier.notify. Hoptoad will get all the information | |
242 | +about the error itself. As for a hash, these are the keys you should pass: | |
243 | + | |
244 | +* :error_class - Use this to group similar errors together. When Hoptoad catches an exception it sends the class name of that exception object. | |
245 | +* :error_message - This is the title of the error you see in the errors list. For exceptions it is "#{exception.class.name}: #{exception.message}" | |
246 | +* :parameters - While there are several ways to send additional data to Hoptoad, passing a Hash as :parameters as in the example above is the most common use case. When Hoptoad catches an exception in a controller, the actual HTTP client request parameters are sent using this key. | |
247 | + | |
248 | +Hoptoad merges the hash you pass with these default options: | |
249 | + | |
250 | + { | |
251 | + :api_key => HoptoadNotifier.api_key, | |
252 | + :error_message => 'Notification', | |
253 | + :backtrace => caller, | |
254 | + :parameters => {}, | |
255 | + :session => {} | |
256 | + } | |
257 | + | |
258 | +You can override any of those parameters. | |
259 | + | |
260 | +== Filtering | |
261 | + | |
262 | +You can specify a whitelist of errors, that Hoptoad will not report on. Use | |
263 | +this feature when you are so apathetic to certain errors that you don't want | |
264 | +them even logged. | |
265 | + | |
266 | +This filter will only be applied to automatic notifications, not manual | |
267 | +notifications (when #notify is called directly). | |
268 | + | |
269 | +Hoptoad ignores the following exceptions by default: | |
270 | + | |
271 | + ActiveRecord::RecordNotFound | |
272 | + ActionController::RoutingError | |
273 | + ActionController::InvalidAuthenticityToken | |
274 | + ActionController::UnknownAction | |
275 | + CGI::Session::CookieStore::TamperedWithCookie | |
276 | + | |
277 | +To ignore errors in addition to those, specify their names in your Hoptoad | |
278 | +configuration block. | |
279 | + | |
280 | + HoptoadNotifier.configure do |config| | |
281 | + config.api_key = '1234567890abcdef' | |
282 | + config.ignore << ActiveRecord::IgnoreThisError | |
283 | + end | |
284 | + | |
285 | +To ignore *only* certain errors (and override the defaults), use the | |
286 | +#ignore_only attribute. | |
287 | + | |
288 | + HoptoadNotifier.configure do |config| | |
289 | + config.api_key = '1234567890abcdef' | |
290 | + config.ignore_only = [ActiveRecord::IgnoreThisError] | |
291 | + end | |
292 | + | |
293 | +To ignore certain user agents, add in the #ignore_user_agent attribute as a | |
294 | +string or regexp: | |
295 | + | |
296 | + HoptoadNotifier.configure do |config| | |
297 | + config.api_key = '1234567890abcdef' | |
298 | + config.ignore_user_agent << /Ignored/ | |
299 | + config.ignore_user_agent << 'IgnoredUserAgent' | |
300 | + end | |
301 | + | |
302 | +To ignore exceptions based on other conditions, use #ignore_by_filter: | |
303 | + | |
304 | + HoptoadNotifier.configure do |config| | |
305 | + config.api_key = '1234567890abcdef' | |
306 | + config.ignore_by_filter do |exception_data| | |
307 | + true if exception_data[:error_class] == "RuntimeError" | |
308 | + end | |
309 | + end | |
310 | + | |
311 | +To replace sensitive information sent to the Hoptoad service with [FILTERED] use #params_filters: | |
312 | + | |
313 | + HoptoadNotifier.configure do |config| | |
314 | + config.api_key = '1234567890abcdef' | |
315 | + config.params_filters << "credit_card_number" | |
316 | + end | |
317 | + | |
318 | +Note that, when rescuing exceptions within an ActionController method, | |
319 | +hoptoad_notifier will reuse filters specified by #filter_params_logging. | |
320 | + | |
321 | +== Testing | |
322 | + | |
323 | +When you run your tests, you might notice that the Hoptoad service is recording | |
324 | +notices generated using #notify when you don't expect it to. You can | |
325 | +use code like this in your test_helper.rb to redefine that method so those | |
326 | +errors are not reported while running tests. | |
327 | + | |
328 | + module HoptoadNotifier | |
329 | + def self.notify(thing) | |
330 | + # do nothing. | |
331 | + end | |
332 | + end | |
333 | + | |
334 | +== Proxy Support | |
335 | + | |
336 | +The notifier supports using a proxy, if your server is not able to directly reach the Hoptoad servers. To configure the proxy settings, added the following information to your Hoptoad configuration block. | |
337 | + | |
338 | + HoptoadNotifier.configure do |config| | |
339 | + config.proxy_host = ... | |
340 | + config.proxy_port = ... | |
341 | + config.proxy_user = ... | |
342 | + config.proxy_pass = ... | |
343 | + | |
344 | +== Supported Rails versions | |
345 | + | |
346 | +See SUPPORTED_RAILS_VERSIONS for a list of official supported versions of | |
347 | +Rails. | |
348 | + | |
349 | +Please open up a support ticket on Tender ( http://help.hoptoadapp.com ) if | |
350 | +you're using a version of Rails that is not listed above and the notifier is | |
351 | +not working properly. | |
352 | + | |
353 | +== Thanks | |
354 | + | |
355 | +Thanks to Eugene Bolshakov for the excellent write-up on GOING BEYOND | |
356 | +EXCEPTIONS, which we have included above. | ... | ... |
... | ... | @@ -0,0 +1,217 @@ |
1 | +require 'rake' | |
2 | +require 'rake/testtask' | |
3 | +require 'rake/rdoctask' | |
4 | +require 'rake/gempackagetask' | |
5 | +require 'cucumber/rake/task' | |
6 | + | |
7 | +desc 'Default: run unit tests.' | |
8 | +task :default => [:test, :cucumber] | |
9 | + | |
10 | +desc 'Test the hoptoad_notifier gem.' | |
11 | +Rake::TestTask.new(:test) do |t| | |
12 | + t.libs << 'lib' | |
13 | + t.pattern = 'test/**/*_test.rb' | |
14 | + t.verbose = true | |
15 | +end | |
16 | + | |
17 | +desc 'Run ginger tests' | |
18 | +task :ginger do | |
19 | + $LOAD_PATH << File.join(*%w[vendor ginger lib]) | |
20 | + ARGV.clear | |
21 | + ARGV << 'test' | |
22 | + load File.join(*%w[vendor ginger bin ginger]) | |
23 | +end | |
24 | + | |
25 | +namespace :changeling do | |
26 | + desc "Bumps the version by a minor or patch version, depending on what was passed in." | |
27 | + task :bump, :part do |t, args| | |
28 | + # Thanks, Jeweler! | |
29 | + if HoptoadNotifier::VERSION =~ /^(\d+)\.(\d+)\.(\d+)(?:\.(.*?))?$/ | |
30 | + major = $1.to_i | |
31 | + minor = $2.to_i | |
32 | + patch = $3.to_i | |
33 | + build = $4 | |
34 | + else | |
35 | + abort | |
36 | + end | |
37 | + | |
38 | + case args[:part] | |
39 | + when /minor/ | |
40 | + minor += 1 | |
41 | + patch = 0 | |
42 | + when /patch/ | |
43 | + patch += 1 | |
44 | + else | |
45 | + abort | |
46 | + end | |
47 | + | |
48 | + version = [major, minor, patch, build].compact.join('.') | |
49 | + | |
50 | + File.open(File.join("lib", "hoptoad_notifier", "version.rb"), "w") do |f| | |
51 | + f.write <<EOF | |
52 | +module HoptoadNotifier | |
53 | + VERSION = "#{version}".freeze | |
54 | +end | |
55 | +EOF | |
56 | + end | |
57 | + end | |
58 | + | |
59 | + desc "Writes out the new CHANGELOG and prepares the release" | |
60 | + task :change do | |
61 | + load 'lib/hoptoad_notifier/version.rb' | |
62 | + file = "CHANGELOG" | |
63 | + old = File.read(file) | |
64 | + version = HoptoadNotifier::VERSION | |
65 | + message = "Bumping to version #{version}" | |
66 | + | |
67 | + File.open(file, "w") do |f| | |
68 | + f.write <<EOF | |
69 | +Version #{version} - #{Date.today} | |
70 | +=============================================================================== | |
71 | + | |
72 | +#{`git log $(git tag | tail -1)..HEAD | git shortlog`} | |
73 | +#{old} | |
74 | +EOF | |
75 | + end | |
76 | + | |
77 | + exec ["#{ENV["EDITOR"]} #{file}", | |
78 | + "git commit -aqm '#{message}'", | |
79 | + "git tag -a -m '#{message}' v#{version}", | |
80 | + "echo '\n\n\033[32mMarked v#{version} /' `git show-ref -s refs/heads/master` 'for release. Run: rake changeling:push\033[0m\n\n'"].join(' && ') | |
81 | + end | |
82 | + | |
83 | + desc "Bump by a minor version (1.2.3 => 1.3.0)" | |
84 | + task :minor do |t| | |
85 | + Rake::Task['changeling:bump'].invoke(t.name) | |
86 | + Rake::Task['changeling:change'].invoke | |
87 | + end | |
88 | + | |
89 | + desc "Bump by a patch version, (1.2.3 => 1.2.4)" | |
90 | + task :patch do |t| | |
91 | + Rake::Task['changeling:bump'].invoke(t.name) | |
92 | + Rake::Task['changeling:change'].invoke | |
93 | + end | |
94 | + | |
95 | + desc "Push the latest version and tags" | |
96 | + task :push do |t| | |
97 | + system("git push origin master") | |
98 | + system("git push origin $(git tag | tail -1)") | |
99 | + end | |
100 | +end | |
101 | + | |
102 | +begin | |
103 | + require 'yard' | |
104 | + YARD::Rake::YardocTask.new do |t| | |
105 | + t.files = ['lib/**/*.rb', 'TESTING.rdoc'] | |
106 | + end | |
107 | +rescue LoadError | |
108 | +end | |
109 | + | |
110 | +GEM_ROOT = File.dirname(__FILE__).freeze | |
111 | +VERSION_FILE = File.join(GEM_ROOT, 'lib', 'hoptoad_notifier', 'version') | |
112 | + | |
113 | +require VERSION_FILE | |
114 | + | |
115 | +gemspec = Gem::Specification.new do |s| | |
116 | + s.name = %q{hoptoad_notifier} | |
117 | + s.version = HoptoadNotifier::VERSION | |
118 | + s.summary = %q{Send your application errors to our hosted service and reclaim your inbox.} | |
119 | + | |
120 | + s.files = FileList['[A-Z]*', 'generators/**/*.*', 'lib/**/*.rb', | |
121 | + 'test/**/*.rb', 'rails/**/*.rb', 'script/*', | |
122 | + 'lib/templates/*.erb'] | |
123 | + s.require_path = 'lib' | |
124 | + s.test_files = Dir[*['test/**/*_test.rb']] | |
125 | + | |
126 | + s.has_rdoc = true | |
127 | + s.extra_rdoc_files = ["README.rdoc"] | |
128 | + s.rdoc_options = ['--line-numbers', "--main", "README.rdoc"] | |
129 | + | |
130 | + s.add_runtime_dependency("activesupport") | |
131 | + s.add_development_dependency("activerecord") | |
132 | + s.add_development_dependency("actionpack") | |
133 | + s.add_development_dependency("jferris-mocha") | |
134 | + s.add_development_dependency("nokogiri") | |
135 | + s.add_development_dependency("shoulda") | |
136 | + | |
137 | + s.authors = ["thoughtbot, inc"] | |
138 | + s.email = %q{support@hoptoadapp.com} | |
139 | + s.homepage = "http://www.hoptoadapp.com" | |
140 | + | |
141 | + s.platform = Gem::Platform::RUBY | |
142 | +end | |
143 | + | |
144 | +Rake::GemPackageTask.new gemspec do |pkg| | |
145 | + pkg.need_tar = true | |
146 | + pkg.need_zip = true | |
147 | +end | |
148 | + | |
149 | +desc "Clean files generated by rake tasks" | |
150 | +task :clobber => [:clobber_rdoc, :clobber_package] | |
151 | + | |
152 | +desc "Generate a gemspec file" | |
153 | +task :gemspec do | |
154 | + File.open("#{gemspec.name}.gemspec", 'w') do |f| | |
155 | + f.write gemspec.to_ruby | |
156 | + end | |
157 | +end | |
158 | + | |
159 | +LOCAL_GEM_ROOT = File.join(GEM_ROOT, 'tmp', 'local_gems').freeze | |
160 | +RAILS_VERSIONS = IO.read('SUPPORTED_RAILS_VERSIONS').strip.split("\n") | |
161 | +LOCAL_GEMS = [['sham_rack', nil], ['capistrano', nil], ['sqlite3-ruby', nil], ['sinatra', nil]] + | |
162 | + RAILS_VERSIONS.collect { |version| ['rails', version] } | |
163 | + | |
164 | +task :vendor_test_gems do | |
165 | + old_gem_path = ENV['GEM_PATH'] | |
166 | + old_gem_home = ENV['GEM_HOME'] | |
167 | + ENV['GEM_PATH'] = LOCAL_GEM_ROOT | |
168 | + ENV['GEM_HOME'] = LOCAL_GEM_ROOT | |
169 | + LOCAL_GEMS.each do |gem_name, version| | |
170 | + gem_file_pattern = [gem_name, version || '*'].compact.join('-') | |
171 | + version_option = version ? "-v #{version}" : '' | |
172 | + pattern = File.join(LOCAL_GEM_ROOT, 'gems', "#{gem_file_pattern}") | |
173 | + existing = Dir.glob(pattern).first | |
174 | + unless existing | |
175 | + command = "gem install -i #{LOCAL_GEM_ROOT} --no-ri --no-rdoc --backtrace #{version_option} #{gem_name}" | |
176 | + puts "Vendoring #{gem_file_pattern}..." | |
177 | + unless system("#{command} 2>&1") | |
178 | + puts "Command failed: #{command}" | |
179 | + exit(1) | |
180 | + end | |
181 | + end | |
182 | + end | |
183 | + ENV['GEM_PATH'] = old_gem_path | |
184 | + ENV['GEM_HOME'] = old_gem_home | |
185 | +end | |
186 | + | |
187 | +Cucumber::Rake::Task.new(:cucumber) do |t| | |
188 | + t.fork = true | |
189 | + t.cucumber_opts = ['--format', (ENV['CUCUMBER_FORMAT'] || 'progress')] | |
190 | +end | |
191 | + | |
192 | +task :cucumber => [:gemspec, :vendor_test_gems] | |
193 | + | |
194 | +def define_rails_cucumber_tasks(additional_cucumber_args = '') | |
195 | + namespace :rails do | |
196 | + RAILS_VERSIONS.each do |version| | |
197 | + desc "Test integration of the gem with Rails #{version}" | |
198 | + task version do | |
199 | + puts "Testing Rails #{version}" | |
200 | + ENV['RAILS_VERSION'] = version | |
201 | + system("cucumber --format progress #{additional_cucumber_args} features/rails.feature") | |
202 | + end | |
203 | + end | |
204 | + | |
205 | + desc "Test integration of the gem with all Rails versions" | |
206 | + task :all => RAILS_VERSIONS | |
207 | + end | |
208 | +end | |
209 | + | |
210 | +namespace :cucumber do | |
211 | + namespace :wip do | |
212 | + define_rails_cucumber_tasks('--tags @wip') | |
213 | + end | |
214 | + | |
215 | + define_rails_cucumber_tasks | |
216 | +end | |
217 | + | ... | ... |
vendor/gems/hoptoad_notifier-2.2.2/SUPPORTED_RAILS_VERSIONS
0 → 100644
... | ... | @@ -0,0 +1,8 @@ |
1 | += For Maintainers: | |
2 | + | |
3 | +When developing the Hoptoad Notifier, be sure to use the integration test | |
4 | +against an existing project on staging before pushing to master. | |
5 | + | |
6 | ++./script/integration_test.rb <test project's api key> <staging server hostname>+ | |
7 | + | |
8 | ++./script/integration_test.rb <test project's api key> <staging server hostname> secure+ | ... | ... |
vendor/gems/hoptoad_notifier-2.2.2/generators/hoptoad/hoptoad_generator.rb
0 → 100644
... | ... | @@ -0,0 +1,54 @@ |
1 | +require File.expand_path(File.dirname(__FILE__) + "/lib/insert_commands.rb") | |
2 | +require File.expand_path(File.dirname(__FILE__) + "/lib/rake_commands.rb") | |
3 | + | |
4 | +class HoptoadGenerator < Rails::Generator::Base | |
5 | + def add_options!(opt) | |
6 | + opt.on('-k', '--api-key=key', String, "Your Hoptoad API key") {|v| options[:api_key] = v} | |
7 | + end | |
8 | + | |
9 | + def manifest | |
10 | + if !api_key_configured? && !options[:api_key] | |
11 | + puts "Must pass --api-key or create config/initializers/hoptoad.rb" | |
12 | + exit | |
13 | + end | |
14 | + if plugin_is_present? | |
15 | + puts "You must first remove the hoptoad_notifier plugin. Please run: script/plugin remove hoptoad_notifier" | |
16 | + exit | |
17 | + end | |
18 | + record do |m| | |
19 | + m.directory 'lib/tasks' | |
20 | + m.file 'hoptoad_notifier_tasks.rake', 'lib/tasks/hoptoad_notifier_tasks.rake' | |
21 | + if File.exists?('config/deploy.rb') | |
22 | + m.append_to 'config/deploy.rb', capistrano_hook | |
23 | + end | |
24 | + if options[:api_key] | |
25 | + if use_initializer? | |
26 | + m.template 'initializer.rb', 'config/initializers/hoptoad.rb', | |
27 | + :assigns => {:api_key => options[:api_key]} | |
28 | + else | |
29 | + m.template 'initializer.rb', 'config/hoptoad.rb', | |
30 | + :assigns => {:api_key => options[:api_key]} | |
31 | + m.append_to 'config/environment.rb', "require 'config/hoptoad'" | |
32 | + end | |
33 | + end | |
34 | + m.rake "hoptoad:test", :generate_only => true | |
35 | + end | |
36 | + end | |
37 | + | |
38 | + def use_initializer? | |
39 | + Rails::VERSION::MAJOR > 1 | |
40 | + end | |
41 | + | |
42 | + def api_key_configured? | |
43 | + File.exists?('config/initializers/hoptoad.rb') || | |
44 | + system("grep HoptoadNotifier config/environment.rb") | |
45 | + end | |
46 | + | |
47 | + def capistrano_hook | |
48 | + IO.read(source_path('capistrano_hook.rb')) | |
49 | + end | |
50 | + | |
51 | + def plugin_is_present? | |
52 | + File.exists?('vendor/plugins/hoptoad_notifier') | |
53 | + end | |
54 | +end | ... | ... |
vendor/gems/hoptoad_notifier-2.2.2/generators/hoptoad/lib/insert_commands.rb
0 → 100644
... | ... | @@ -0,0 +1,34 @@ |
1 | +# Mostly pinched from http://github.com/ryanb/nifty-generators/tree/master | |
2 | + | |
3 | +Rails::Generator::Commands::Base.class_eval do | |
4 | + def file_contains?(relative_destination, line) | |
5 | + File.read(destination_path(relative_destination)).include?(line) | |
6 | + end | |
7 | +end | |
8 | + | |
9 | +Rails::Generator::Commands::Create.class_eval do | |
10 | + def append_to(file, line) | |
11 | + logger.insert "#{line} appended to #{file}" | |
12 | + unless options[:pretend] || file_contains?(file, line) | |
13 | + File.open(file, "a") do |file| | |
14 | + file.puts | |
15 | + file.puts line | |
16 | + end | |
17 | + end | |
18 | + end | |
19 | +end | |
20 | + | |
21 | +Rails::Generator::Commands::Destroy.class_eval do | |
22 | + def append_to(file, line) | |
23 | + logger.remove "#{line} removed from #{file}" | |
24 | + unless options[:pretend] | |
25 | + gsub_file file, "\n#{line}", '' | |
26 | + end | |
27 | + end | |
28 | +end | |
29 | + | |
30 | +Rails::Generator::Commands::List.class_eval do | |
31 | + def append_to(file, line) | |
32 | + logger.insert "#{line} appended to #{file}" | |
33 | + end | |
34 | +end | ... | ... |
vendor/gems/hoptoad_notifier-2.2.2/generators/hoptoad/lib/rake_commands.rb
0 → 100644
... | ... | @@ -0,0 +1,24 @@ |
1 | +Rails::Generator::Commands::Create.class_eval do | |
2 | + def rake(cmd, opts = {}) | |
3 | + logger.rake "rake #{cmd}" | |
4 | + unless system("rake #{cmd}") | |
5 | + logger.rake "#{cmd} failed. Rolling back" | |
6 | + command(:destroy).invoke! | |
7 | + end | |
8 | + end | |
9 | +end | |
10 | + | |
11 | +Rails::Generator::Commands::Destroy.class_eval do | |
12 | + def rake(cmd, opts = {}) | |
13 | + unless opts[:generate_only] | |
14 | + logger.rake "rake #{cmd}" | |
15 | + system "rake #{cmd}" | |
16 | + end | |
17 | + end | |
18 | +end | |
19 | + | |
20 | +Rails::Generator::Commands::List.class_eval do | |
21 | + def rake(cmd, opts = {}) | |
22 | + logger.rake "rake #{cmd}" | |
23 | + end | |
24 | +end | ... | ... |
vendor/gems/hoptoad_notifier-2.2.2/generators/hoptoad/templates/capistrano_hook.rb
0 → 100644
vendor/gems/hoptoad_notifier-2.2.2/generators/hoptoad/templates/hoptoad_notifier_tasks.rake
0 → 100644
... | ... | @@ -0,0 +1,12 @@ |
1 | +# Don't load anything when running the gems:* tasks. | |
2 | +# Otherwise, hoptoad_notifier will be considered a framework gem. | |
3 | +# https://thoughtbot.lighthouseapp.com/projects/14221/tickets/629 | |
4 | +unless ARGV.any? {|a| a =~ /^gems/} | |
5 | + | |
6 | + Dir[File.join(RAILS_ROOT, 'vendor', 'gems', 'hoptoad_notifier-*')].each do |vendored_notifier| | |
7 | + $: << File.join(vendored_notifier, 'lib') | |
8 | + end | |
9 | + | |
10 | + require 'hoptoad_notifier/tasks' | |
11 | + | |
12 | +end | ... | ... |
vendor/gems/hoptoad_notifier-2.2.2/generators/hoptoad/templates/initializer.rb
0 → 100644
vendor/gems/hoptoad_notifier-2.2.2/lib/hoptoad_notifier.rb
0 → 100644
... | ... | @@ -0,0 +1,146 @@ |
1 | +require 'net/http' | |
2 | +require 'net/https' | |
3 | +require 'rubygems' | |
4 | +require 'active_support' | |
5 | +require 'hoptoad_notifier/version' | |
6 | +require 'hoptoad_notifier/configuration' | |
7 | +require 'hoptoad_notifier/notice' | |
8 | +require 'hoptoad_notifier/sender' | |
9 | +require 'hoptoad_notifier/backtrace' | |
10 | +require 'hoptoad_notifier/rack' | |
11 | + | |
12 | +# Gem for applications to automatically post errors to the Hoptoad of their choice. | |
13 | +module HoptoadNotifier | |
14 | + | |
15 | + API_VERSION = "2.0" | |
16 | + LOG_PREFIX = "** [Hoptoad] " | |
17 | + | |
18 | + HEADERS = { | |
19 | + 'Content-type' => 'text/xml', | |
20 | + 'Accept' => 'text/xml, application/xml' | |
21 | + } | |
22 | + | |
23 | + class << self | |
24 | + # The sender object is responsible for delivering formatted data to the Hoptoad server. | |
25 | + # Must respond to #send_to_hoptoad. See HoptoadNotifier::Sender. | |
26 | + attr_accessor :sender | |
27 | + | |
28 | + # A Hoptoad configuration object. Must act like a hash and return sensible | |
29 | + # values for all Hoptoad configuration options. See HoptoadNotifier::Configuration. | |
30 | + attr_accessor :configuration | |
31 | + | |
32 | + # Tell the log that the Notifier is good to go | |
33 | + def report_ready | |
34 | + write_verbose_log("Notifier #{VERSION} ready to catch errors") | |
35 | + end | |
36 | + | |
37 | + # Prints out the environment info to the log for debugging help | |
38 | + def report_environment_info | |
39 | + write_verbose_log("Environment Info: #{environment_info}") | |
40 | + end | |
41 | + | |
42 | + # Prints out the response body from Hoptoad for debugging help | |
43 | + def report_response_body(response) | |
44 | + write_verbose_log("Response from Hoptoad: \n#{response}") | |
45 | + end | |
46 | + | |
47 | + # Returns the Ruby version, Rails version, and current Rails environment | |
48 | + def environment_info | |
49 | + info = "[Ruby: #{RUBY_VERSION}]" | |
50 | + info << " [#{configuration.framework}]" | |
51 | + info << " [Env: #{configuration.environment_name}]" | |
52 | + end | |
53 | + | |
54 | + # Writes out the given message to the #logger | |
55 | + def write_verbose_log(message) | |
56 | + logger.info LOG_PREFIX + message if logger | |
57 | + end | |
58 | + | |
59 | + # Look for the Rails logger currently defined | |
60 | + def logger | |
61 | + self.configuration.logger | |
62 | + end | |
63 | + | |
64 | + # Call this method to modify defaults in your initializers. | |
65 | + # | |
66 | + # @example | |
67 | + # HoptoadNotifier.configure do |config| | |
68 | + # config.api_key = '1234567890abcdef' | |
69 | + # config.secure = false | |
70 | + # end | |
71 | + def configure(silent = false) | |
72 | + self.configuration ||= Configuration.new | |
73 | + yield(configuration) | |
74 | + self.sender = Sender.new(configuration) | |
75 | + report_ready unless silent | |
76 | + end | |
77 | + | |
78 | + # Sends an exception manually using this method, even when you are not in a controller. | |
79 | + # | |
80 | + # @param [Exception] exception The exception you want to notify Hoptoad about. | |
81 | + # @param [Hash] opts Data that will be sent to Hoptoad. | |
82 | + # | |
83 | + # @option opts [String] :api_key The API key for this project. The API key is a unique identifier that Hoptoad uses for identification. | |
84 | + # @option opts [String] :error_message The error returned by the exception (or the message you want to log). | |
85 | + # @option opts [String] :backtrace A backtrace, usually obtained with +caller+. | |
86 | + # @option opts [String] :request The controller's request object. | |
87 | + # @option opts [String] :session The contents of the user's session. | |
88 | + # @option opts [String] :environment ENV merged with the contents of the request's environment. | |
89 | + def notify(exception, opts = {}) | |
90 | + send_notice(build_notice_for(exception, opts)) | |
91 | + end | |
92 | + | |
93 | + # Sends the notice unless it is one of the default ignored exceptions | |
94 | + # @see HoptoadNotifier.notify | |
95 | + def notify_or_ignore(exception, opts = {}) | |
96 | + notice = build_notice_for(exception, opts) | |
97 | + send_notice(notice) unless notice.ignore? | |
98 | + end | |
99 | + | |
100 | + def build_lookup_hash_for(exception, options = {}) | |
101 | + notice = build_notice_for(exception, options) | |
102 | + | |
103 | + result = {} | |
104 | + result[:action] = notice.action rescue nil | |
105 | + result[:component] = notice.component rescue nil | |
106 | + result[:error_class] = notice.error_class if notice.error_class | |
107 | + result[:environment_name] = 'production' | |
108 | + | |
109 | + unless notice.backtrace.lines.empty? | |
110 | + result[:file] = notice.backtrace.lines.first.file | |
111 | + result[:line_number] = notice.backtrace.lines.first.number | |
112 | + end | |
113 | + | |
114 | + result | |
115 | + end | |
116 | + | |
117 | + private | |
118 | + | |
119 | + def send_notice(notice) | |
120 | + if configuration.public? | |
121 | + sender.send_to_hoptoad(notice.to_xml) | |
122 | + end | |
123 | + end | |
124 | + | |
125 | + def build_notice_for(exception, opts = {}) | |
126 | + exception = unwrap_exception(exception) | |
127 | + if exception.respond_to?(:to_hash) | |
128 | + opts = opts.merge(exception) | |
129 | + else | |
130 | + opts = opts.merge(:exception => exception) | |
131 | + end | |
132 | + Notice.new(configuration.merge(opts)) | |
133 | + end | |
134 | + | |
135 | + def unwrap_exception(exception) | |
136 | + if exception.respond_to?(:original_exception) | |
137 | + exception.original_exception | |
138 | + elsif exception.respond_to?(:continued_exception) | |
139 | + exception.continued_exception | |
140 | + else | |
141 | + exception | |
142 | + end | |
143 | + end | |
144 | + end | |
145 | +end | |
146 | + | ... | ... |
vendor/gems/hoptoad_notifier-2.2.2/lib/hoptoad_notifier/backtrace.rb
0 → 100644
... | ... | @@ -0,0 +1,99 @@ |
1 | +module HoptoadNotifier | |
2 | + # Front end to parsing the backtrace for each notice | |
3 | + class Backtrace | |
4 | + | |
5 | + # Handles backtrace parsing line by line | |
6 | + class Line | |
7 | + | |
8 | + INPUT_FORMAT = %r{^([^:]+):(\d+)(?::in `([^']+)')?$}.freeze | |
9 | + | |
10 | + # The file portion of the line (such as app/models/user.rb) | |
11 | + attr_reader :file | |
12 | + | |
13 | + # The line number portion of the line | |
14 | + attr_reader :number | |
15 | + | |
16 | + # The method of the line (such as index) | |
17 | + attr_reader :method | |
18 | + | |
19 | + # Parses a single line of a given backtrace | |
20 | + # @param [String] unparsed_line The raw line from +caller+ or some backtrace | |
21 | + # @return [Line] The parsed backtrace line | |
22 | + def self.parse(unparsed_line) | |
23 | + _, file, number, method = unparsed_line.match(INPUT_FORMAT).to_a | |
24 | + new(file, number, method) | |
25 | + end | |
26 | + | |
27 | + def initialize(file, number, method) | |
28 | + self.file = file | |
29 | + self.number = number | |
30 | + self.method = method | |
31 | + end | |
32 | + | |
33 | + # Reconstructs the line in a readable fashion | |
34 | + def to_s | |
35 | + "#{file}:#{number}:in `#{method}'" | |
36 | + end | |
37 | + | |
38 | + def ==(other) | |
39 | + to_s == other.to_s | |
40 | + end | |
41 | + | |
42 | + def inspect | |
43 | + "<Line:#{to_s}>" | |
44 | + end | |
45 | + | |
46 | + private | |
47 | + | |
48 | + attr_writer :file, :number, :method | |
49 | + end | |
50 | + | |
51 | + # holder for an Array of Backtrace::Line instances | |
52 | + attr_reader :lines | |
53 | + | |
54 | + def self.parse(ruby_backtrace, opts = {}) | |
55 | + ruby_lines = split_multiline_backtrace(ruby_backtrace) | |
56 | + | |
57 | + filters = opts[:filters] || [] | |
58 | + filtered_lines = ruby_lines.to_a.map do |line| | |
59 | + filters.inject(line) do |line, proc| | |
60 | + proc.call(line) | |
61 | + end | |
62 | + end.compact | |
63 | + | |
64 | + lines = filtered_lines.collect do |unparsed_line| | |
65 | + Line.parse(unparsed_line) | |
66 | + end | |
67 | + | |
68 | + instance = new(lines) | |
69 | + end | |
70 | + | |
71 | + def initialize(lines) | |
72 | + self.lines = lines | |
73 | + end | |
74 | + | |
75 | + def inspect | |
76 | + "<Backtrace: " + lines.collect { |line| line.inspect }.join(", ") + ">" | |
77 | + end | |
78 | + | |
79 | + def ==(other) | |
80 | + if other.respond_to?(:lines) | |
81 | + lines == other.lines | |
82 | + else | |
83 | + false | |
84 | + end | |
85 | + end | |
86 | + | |
87 | + private | |
88 | + | |
89 | + attr_writer :lines | |
90 | + | |
91 | + def self.split_multiline_backtrace(backtrace) | |
92 | + if backtrace.to_a.size == 1 | |
93 | + backtrace.to_a.first.split(/\n\s*/) | |
94 | + else | |
95 | + backtrace | |
96 | + end | |
97 | + end | |
98 | + end | |
99 | +end | ... | ... |
vendor/gems/hoptoad_notifier-2.2.2/lib/hoptoad_notifier/capistrano.rb
0 → 100644
... | ... | @@ -0,0 +1,20 @@ |
1 | +# Defines deploy:notify_hoptoad which will send information about the deploy to Hoptoad. | |
2 | + | |
3 | +Capistrano::Configuration.instance(:must_exist).load do | |
4 | + after "deploy", "deploy:notify_hoptoad" | |
5 | + after "deploy:migrations", "deploy:notify_hoptoad" | |
6 | + | |
7 | + namespace :deploy do | |
8 | + desc "Notify Hoptoad of the deployment" | |
9 | + task :notify_hoptoad, :except => { :no_release => true } do | |
10 | + rails_env = fetch(:hoptoad_env, fetch(:rails_env, "production")) | |
11 | + local_user = ENV['USER'] || ENV['USERNAME'] | |
12 | + executable = RUBY_PLATFORM.downcase.include?('mswin') ? 'rake.bat' : 'rake' | |
13 | + notify_command = "#{executable} hoptoad:deploy TO=#{rails_env} REVISION=#{current_revision} REPO=#{repository} USER=#{local_user}" | |
14 | + notify_command << " API_KEY=#{ENV['API_KEY']}" if ENV['API_KEY'] | |
15 | + puts "Notifying Hoptoad of Deploy (#{notify_command})" | |
16 | + `#{notify_command}` | |
17 | + puts "Hoptoad Notification Complete." | |
18 | + end | |
19 | + end | |
20 | +end | ... | ... |
vendor/gems/hoptoad_notifier-2.2.2/lib/hoptoad_notifier/configuration.rb
0 → 100644
... | ... | @@ -0,0 +1,232 @@ |
1 | +module HoptoadNotifier | |
2 | + # Used to set up and modify settings for the notifier. | |
3 | + class Configuration | |
4 | + | |
5 | + OPTIONS = [:api_key, :backtrace_filters, :development_environments, | |
6 | + :development_lookup, :environment_name, :host, | |
7 | + :http_open_timeout, :http_read_timeout, :ignore, :ignore_by_filters, | |
8 | + :ignore_user_agent, :notifier_name, :notifier_url, :notifier_version, | |
9 | + :params_filters, :project_root, :port, :protocol, :proxy_host, | |
10 | + :proxy_pass, :proxy_port, :proxy_user, :secure, :framework].freeze | |
11 | + | |
12 | + # The API key for your project, found on the project edit form. | |
13 | + attr_accessor :api_key | |
14 | + | |
15 | + # The host to connect to (defaults to hoptoadapp.com). | |
16 | + attr_accessor :host | |
17 | + | |
18 | + # The port on which your Hoptoad server runs (defaults to 443 for secure | |
19 | + # connections, 80 for insecure connections). | |
20 | + attr_accessor :port | |
21 | + | |
22 | + # +true+ for https connections, +false+ for http connections. | |
23 | + attr_accessor :secure | |
24 | + | |
25 | + # The HTTP open timeout in seconds (defaults to 2). | |
26 | + attr_accessor :http_open_timeout | |
27 | + | |
28 | + # The HTTP read timeout in seconds (defaults to 5). | |
29 | + attr_accessor :http_read_timeout | |
30 | + | |
31 | + # The hostname of your proxy server (if using a proxy) | |
32 | + attr_accessor :proxy_host | |
33 | + | |
34 | + # The port of your proxy server (if using a proxy) | |
35 | + attr_accessor :proxy_port | |
36 | + | |
37 | + # The username to use when logging into your proxy server (if using a proxy) | |
38 | + attr_accessor :proxy_user | |
39 | + | |
40 | + # The password to use when logging into your proxy server (if using a proxy) | |
41 | + attr_accessor :proxy_pass | |
42 | + | |
43 | + # A list of parameters that should be filtered out of what is sent to Hoptoad. | |
44 | + # By default, all "password" attributes will have their contents replaced. | |
45 | + attr_reader :params_filters | |
46 | + | |
47 | + # A list of filters for cleaning and pruning the backtrace. See #filter_backtrace. | |
48 | + attr_reader :backtrace_filters | |
49 | + | |
50 | + # A list of filters for ignoring exceptions. See #ignore_by_filter. | |
51 | + attr_reader :ignore_by_filters | |
52 | + | |
53 | + # A list of exception classes to ignore. The array can be appended to. | |
54 | + attr_reader :ignore | |
55 | + | |
56 | + # A list of user agents that are being ignored. The array can be appended to. | |
57 | + attr_reader :ignore_user_agent | |
58 | + | |
59 | + # A list of environments in which notifications should not be sent. | |
60 | + attr_accessor :development_environments | |
61 | + | |
62 | + # +true+ if you want to check for production errors matching development errors, +false+ otherwise. | |
63 | + attr_accessor :development_lookup | |
64 | + | |
65 | + # The name of the environment the application is running in | |
66 | + attr_accessor :environment_name | |
67 | + | |
68 | + # The path to the project in which the error occurred, such as the RAILS_ROOT | |
69 | + attr_accessor :project_root | |
70 | + | |
71 | + # The name of the notifier library being used to send notifications (such as "Hoptoad Notifier") | |
72 | + attr_accessor :notifier_name | |
73 | + | |
74 | + # The version of the notifier library being used to send notifications (such as "1.0.2") | |
75 | + attr_accessor :notifier_version | |
76 | + | |
77 | + # The url of the notifier library being used to send notifications | |
78 | + attr_accessor :notifier_url | |
79 | + | |
80 | + # The logger used by HoptoadNotifier | |
81 | + attr_accessor :logger | |
82 | + | |
83 | + # The framework HoptoadNotifier is configured to use | |
84 | + attr_accessor :framework | |
85 | + | |
86 | + DEFAULT_PARAMS_FILTERS = %w(password password_confirmation).freeze | |
87 | + | |
88 | + DEFAULT_BACKTRACE_FILTERS = [ | |
89 | + lambda { |line| | |
90 | + if defined?(HoptoadNotifier.configuration.project_root) && HoptoadNotifier.configuration.project_root.to_s != '' | |
91 | + line.gsub(/#{HoptoadNotifier.configuration.project_root}/, "[PROJECT_ROOT]") | |
92 | + else | |
93 | + line | |
94 | + end | |
95 | + }, | |
96 | + lambda { |line| line.gsub(/^\.\//, "") }, | |
97 | + lambda { |line| | |
98 | + if defined?(Gem) | |
99 | + Gem.path.inject(line) do |line, path| | |
100 | + line.gsub(/#{path}/, "[GEM_ROOT]") | |
101 | + end | |
102 | + end | |
103 | + }, | |
104 | + lambda { |line| line if line !~ %r{lib/hoptoad_notifier} } | |
105 | + ].freeze | |
106 | + | |
107 | + IGNORE_DEFAULT = ['ActiveRecord::RecordNotFound', | |
108 | + 'ActionController::RoutingError', | |
109 | + 'ActionController::InvalidAuthenticityToken', | |
110 | + 'CGI::Session::CookieStore::TamperedWithCookie', | |
111 | + 'ActionController::UnknownAction'] | |
112 | + | |
113 | + alias_method :secure?, :secure | |
114 | + | |
115 | + def initialize | |
116 | + @secure = false | |
117 | + @host = 'hoptoadapp.com' | |
118 | + @http_open_timeout = 2 | |
119 | + @http_read_timeout = 5 | |
120 | + @params_filters = DEFAULT_PARAMS_FILTERS.dup | |
121 | + @backtrace_filters = DEFAULT_BACKTRACE_FILTERS.dup | |
122 | + @ignore_by_filters = [] | |
123 | + @ignore = IGNORE_DEFAULT.dup | |
124 | + @ignore_user_agent = [] | |
125 | + @development_environments = %w(development test cucumber) | |
126 | + @development_lookup = true | |
127 | + @notifier_name = 'Hoptoad Notifier' | |
128 | + @notifier_version = VERSION | |
129 | + @notifier_url = 'http://hoptoadapp.com' | |
130 | + @framework = 'Standalone' | |
131 | + end | |
132 | + | |
133 | + # Takes a block and adds it to the list of backtrace filters. When the filters | |
134 | + # run, the block will be handed each line of the backtrace and can modify | |
135 | + # it as necessary. | |
136 | + # | |
137 | + # @example | |
138 | + # config.filter_bracktrace do |line| | |
139 | + # line.gsub(/^#{Rails.root}/, "[RAILS_ROOT]") | |
140 | + # end | |
141 | + # | |
142 | + # @param [Proc] block The new backtrace filter. | |
143 | + # @yieldparam [String] line A line in the backtrace. | |
144 | + def filter_backtrace(&block) | |
145 | + self.backtrace_filters << block | |
146 | + end | |
147 | + | |
148 | + # Takes a block and adds it to the list of ignore filters. | |
149 | + # When the filters run, the block will be handed the exception. | |
150 | + # @example | |
151 | + # config.ignore_by_filter do |exception_data| | |
152 | + # true if exception_data[:error_class] == "RuntimeError" | |
153 | + # end | |
154 | + # | |
155 | + # @param [Proc] block The new ignore filter | |
156 | + # @yieldparam [Hash] data The exception data given to +HoptoadNotifier.notify+ | |
157 | + # @yieldreturn [Boolean] If the block returns true the exception will be ignored, otherwise it will be processed by hoptoad. | |
158 | + def ignore_by_filter(&block) | |
159 | + self.ignore_by_filters << block | |
160 | + end | |
161 | + | |
162 | + # Overrides the list of default ignored errors. | |
163 | + # | |
164 | + # @param [Array<Exception>] names A list of exceptions to ignore. | |
165 | + def ignore_only=(names) | |
166 | + @ignore = [names].flatten | |
167 | + end | |
168 | + | |
169 | + # Overrides the list of default ignored user agents | |
170 | + # | |
171 | + # @param [Array<String>] A list of user agents to ignore | |
172 | + def ignore_user_agent_only=(names) | |
173 | + @ignore_user_agent = [names].flatten | |
174 | + end | |
175 | + | |
176 | + # Allows config options to be read like a hash | |
177 | + # | |
178 | + # @param [Symbol] option Key for a given attribute | |
179 | + def [](option) | |
180 | + send(option) | |
181 | + end | |
182 | + | |
183 | + # Returns a hash of all configurable options | |
184 | + def to_hash | |
185 | + OPTIONS.inject({}) do |hash, option| | |
186 | + hash.merge(option.to_sym => send(option)) | |
187 | + end | |
188 | + end | |
189 | + | |
190 | + # Returns a hash of all configurable options merged with +hash+ | |
191 | + # | |
192 | + # @param [Hash] hash A set of configuration options that will take precedence over the defaults | |
193 | + def merge(hash) | |
194 | + to_hash.merge(hash) | |
195 | + end | |
196 | + | |
197 | + # Determines if the notifier will send notices. | |
198 | + # @return [Boolean] Returns +false+ if in a development environment, +true+ otherwise. | |
199 | + def public? | |
200 | + !development_environments.include?(environment_name) | |
201 | + end | |
202 | + | |
203 | + def port | |
204 | + @port || default_port | |
205 | + end | |
206 | + | |
207 | + def protocol | |
208 | + if secure? | |
209 | + 'https' | |
210 | + else | |
211 | + 'http' | |
212 | + end | |
213 | + end | |
214 | + | |
215 | + def environment_filters | |
216 | + warn 'config.environment_filters has been deprecated and has no effect.' | |
217 | + [] | |
218 | + end | |
219 | + | |
220 | + private | |
221 | + | |
222 | + def default_port | |
223 | + if secure? | |
224 | + 443 | |
225 | + else | |
226 | + 80 | |
227 | + end | |
228 | + end | |
229 | + | |
230 | + end | |
231 | + | |
232 | +end | ... | ... |
vendor/gems/hoptoad_notifier-2.2.2/lib/hoptoad_notifier/notice.rb
0 → 100644
... | ... | @@ -0,0 +1,305 @@ |
1 | +module HoptoadNotifier | |
2 | + class Notice | |
3 | + | |
4 | + # The exception that caused this notice, if any | |
5 | + attr_reader :exception | |
6 | + | |
7 | + # The API key for the project to which this notice should be sent | |
8 | + attr_reader :api_key | |
9 | + | |
10 | + # The backtrace from the given exception or hash. | |
11 | + attr_reader :backtrace | |
12 | + | |
13 | + # The name of the class of error (such as RuntimeError) | |
14 | + attr_reader :error_class | |
15 | + | |
16 | + # The name of the server environment (such as "production") | |
17 | + attr_reader :environment_name | |
18 | + | |
19 | + # CGI variables such as HTTP_METHOD | |
20 | + attr_reader :cgi_data | |
21 | + | |
22 | + # The message from the exception, or a general description of the error | |
23 | + attr_reader :error_message | |
24 | + | |
25 | + # See Configuration#backtrace_filters | |
26 | + attr_reader :backtrace_filters | |
27 | + | |
28 | + # See Configuration#params_filters | |
29 | + attr_reader :params_filters | |
30 | + | |
31 | + # A hash of parameters from the query string or post body. | |
32 | + attr_reader :parameters | |
33 | + alias_method :params, :parameters | |
34 | + | |
35 | + # The component (if any) which was used in this request (usually the controller) | |
36 | + attr_reader :component | |
37 | + alias_method :controller, :component | |
38 | + | |
39 | + # The action (if any) that was called in this request | |
40 | + attr_reader :action | |
41 | + | |
42 | + # A hash of session data from the request | |
43 | + attr_reader :session_data | |
44 | + | |
45 | + # The path to the project that caused the error (usually RAILS_ROOT) | |
46 | + attr_reader :project_root | |
47 | + | |
48 | + # The URL at which the error occurred (if any) | |
49 | + attr_reader :url | |
50 | + | |
51 | + # See Configuration#ignore | |
52 | + attr_reader :ignore | |
53 | + | |
54 | + # See Configuration#ignore_by_filters | |
55 | + attr_reader :ignore_by_filters | |
56 | + | |
57 | + # The name of the notifier library sending this notice, such as "Hoptoad Notifier" | |
58 | + attr_reader :notifier_name | |
59 | + | |
60 | + # The version number of the notifier library sending this notice, such as "2.1.3" | |
61 | + attr_reader :notifier_version | |
62 | + | |
63 | + # A URL for more information about the notifier library sending this notice | |
64 | + attr_reader :notifier_url | |
65 | + | |
66 | + def initialize(args) | |
67 | + self.args = args | |
68 | + self.exception = args[:exception] | |
69 | + self.api_key = args[:api_key] | |
70 | + self.project_root = args[:project_root] | |
71 | + self.url = args[:url] || rack_env(:url) | |
72 | + | |
73 | + self.notifier_name = args[:notifier_name] | |
74 | + self.notifier_version = args[:notifier_version] | |
75 | + self.notifier_url = args[:notifier_url] | |
76 | + | |
77 | + self.ignore = args[:ignore] || [] | |
78 | + self.ignore_by_filters = args[:ignore_by_filters] || [] | |
79 | + self.backtrace_filters = args[:backtrace_filters] || [] | |
80 | + self.params_filters = args[:params_filters] || [] | |
81 | + self.parameters = args[:parameters] || rack_env(:params) || {} | |
82 | + self.component = args[:component] || args[:controller] | |
83 | + self.action = args[:action] | |
84 | + | |
85 | + self.environment_name = args[:environment_name] | |
86 | + self.cgi_data = args[:cgi_data] || args[:rack_env] | |
87 | + self.backtrace = Backtrace.parse(exception_attribute(:backtrace, caller), :filters => self.backtrace_filters) | |
88 | + self.error_class = exception_attribute(:error_class) {|exception| exception.class.name } | |
89 | + self.error_message = exception_attribute(:error_message, 'Notification') do |exception| | |
90 | + "#{exception.class.name}: #{exception.message}" | |
91 | + end | |
92 | + | |
93 | + find_session_data | |
94 | + clean_params | |
95 | + end | |
96 | + | |
97 | + # Converts the given notice to XML | |
98 | + def to_xml | |
99 | + builder = Builder::XmlMarkup.new | |
100 | + builder.instruct! | |
101 | + xml = builder.notice(:version => HoptoadNotifier::API_VERSION) do |notice| | |
102 | + notice.tag!("api-key", api_key) | |
103 | + notice.notifier do |notifier| | |
104 | + notifier.name(notifier_name) | |
105 | + notifier.version(notifier_version) | |
106 | + notifier.url(notifier_url) | |
107 | + end | |
108 | + notice.error do |error| | |
109 | + error.tag!('class', error_class) | |
110 | + error.message(error_message) | |
111 | + error.backtrace do |backtrace| | |
112 | + self.backtrace.lines.each do |line| | |
113 | + backtrace.line(:number => line.number, | |
114 | + :file => line.file, | |
115 | + :method => line.method) | |
116 | + end | |
117 | + end | |
118 | + end | |
119 | + if url || | |
120 | + controller || | |
121 | + action || | |
122 | + !parameters.blank? || | |
123 | + !cgi_data.blank? || | |
124 | + !session_data.blank? | |
125 | + notice.request do |request| | |
126 | + request.url(url) | |
127 | + request.component(controller) | |
128 | + request.action(action) | |
129 | + unless parameters.blank? | |
130 | + request.params do |params| | |
131 | + xml_vars_for(params, parameters) | |
132 | + end | |
133 | + end | |
134 | + unless session_data.blank? | |
135 | + request.session do |session| | |
136 | + xml_vars_for(session, session_data) | |
137 | + end | |
138 | + end | |
139 | + unless cgi_data.blank? | |
140 | + request.tag!("cgi-data") do |cgi_datum| | |
141 | + xml_vars_for(cgi_datum, cgi_data) | |
142 | + end | |
143 | + end | |
144 | + end | |
145 | + end | |
146 | + notice.tag!("server-environment") do |env| | |
147 | + env.tag!("project-root", project_root) | |
148 | + env.tag!("environment-name", environment_name) | |
149 | + end | |
150 | + end | |
151 | + xml.to_s | |
152 | + end | |
153 | + | |
154 | + # Determines if this notice should be ignored | |
155 | + def ignore? | |
156 | + ignored_class_names.include?(error_class) || | |
157 | + ignore_by_filters.any? {|filter| filter.call(self) } | |
158 | + end | |
159 | + | |
160 | + # Allows properties to be accessed using a hash-like syntax | |
161 | + # | |
162 | + # @example | |
163 | + # notice[:error_message] | |
164 | + # @param [String] method The given key for an attribute | |
165 | + # @return The attribute value, or self if given +:request+ | |
166 | + def [](method) | |
167 | + case method | |
168 | + when :request | |
169 | + self | |
170 | + else | |
171 | + send(method) | |
172 | + end | |
173 | + end | |
174 | + | |
175 | + private | |
176 | + | |
177 | + attr_writer :exception, :api_key, :backtrace, :error_class, :error_message, | |
178 | + :backtrace_filters, :parameters, :params_filters, | |
179 | + :environment_filters, :session_data, :project_root, :url, :ignore, | |
180 | + :ignore_by_filters, :notifier_name, :notifier_url, :notifier_version, | |
181 | + :component, :action, :cgi_data, :environment_name | |
182 | + | |
183 | + # Arguments given in the initializer | |
184 | + attr_accessor :args | |
185 | + | |
186 | + # Gets a property named +attribute+ of an exception, either from an actual | |
187 | + # exception or a hash. | |
188 | + # | |
189 | + # If an exception is available, #from_exception will be used. Otherwise, | |
190 | + # a key named +attribute+ will be used from the #args. | |
191 | + # | |
192 | + # If no exception or hash key is available, +default+ will be used. | |
193 | + def exception_attribute(attribute, default = nil, &block) | |
194 | + (exception && from_exception(attribute, &block)) || args[attribute] || default | |
195 | + end | |
196 | + | |
197 | + # Gets a property named +attribute+ from an exception. | |
198 | + # | |
199 | + # If a block is given, it will be used when getting the property from an | |
200 | + # exception. The block should accept and exception and return the value for | |
201 | + # the property. | |
202 | + # | |
203 | + # If no block is given, a method with the same name as +attribute+ will be | |
204 | + # invoked for the value. | |
205 | + def from_exception(attribute) | |
206 | + if block_given? | |
207 | + yield(exception) | |
208 | + else | |
209 | + exception.send(attribute) | |
210 | + end | |
211 | + end | |
212 | + | |
213 | + # Removes non-serializable data from the given attribute. | |
214 | + # See #clean_unserializable_data | |
215 | + def clean_unserializable_data_from(attribute) | |
216 | + self.send(:"#{attribute}=", clean_unserializable_data(send(attribute))) | |
217 | + end | |
218 | + | |
219 | + # Removes non-serializable data. Allowed data types are strings, arrays, | |
220 | + # and hashes. All other types are converted to strings. | |
221 | + # TODO: move this onto Hash | |
222 | + def clean_unserializable_data(data) | |
223 | + if data.respond_to?(:to_hash) | |
224 | + data.to_hash.inject({}) do |result, (key, value)| | |
225 | + result.merge(key => clean_unserializable_data(value)) | |
226 | + end | |
227 | + elsif data.respond_to?(:to_ary) | |
228 | + data.collect do |value| | |
229 | + clean_unserializable_data(value) | |
230 | + end | |
231 | + else | |
232 | + data.to_s | |
233 | + end | |
234 | + end | |
235 | + | |
236 | + # Replaces the contents of params that match params_filters. | |
237 | + # TODO: extract this to a different class | |
238 | + def clean_params | |
239 | + clean_unserializable_data_from(:parameters) | |
240 | + filter(parameters) | |
241 | + if cgi_data | |
242 | + clean_unserializable_data_from(:cgi_data) | |
243 | + filter(cgi_data) | |
244 | + end | |
245 | + if session_data | |
246 | + clean_unserializable_data_from(:session_data) | |
247 | + end | |
248 | + end | |
249 | + | |
250 | + def filter(hash) | |
251 | + if params_filters | |
252 | + hash.each do |key, value| | |
253 | + if filter_key?(key) | |
254 | + hash[key] = "[FILTERED]" | |
255 | + elsif value.respond_to?(:to_hash) | |
256 | + filter(hash[key]) | |
257 | + end | |
258 | + end | |
259 | + end | |
260 | + end | |
261 | + | |
262 | + def filter_key?(key) | |
263 | + params_filters.any? do |filter| | |
264 | + key.to_s.include?(filter) | |
265 | + end | |
266 | + end | |
267 | + | |
268 | + def find_session_data | |
269 | + self.session_data = args[:session_data] || args[:session] || {} | |
270 | + self.session_data = session_data[:data] if session_data[:data] | |
271 | + end | |
272 | + | |
273 | + # Converts the mixed class instances and class names into just names | |
274 | + # TODO: move this into Configuration or another class | |
275 | + def ignored_class_names | |
276 | + ignore.collect do |string_or_class| | |
277 | + if string_or_class.respond_to?(:name) | |
278 | + string_or_class.name | |
279 | + else | |
280 | + string_or_class | |
281 | + end | |
282 | + end | |
283 | + end | |
284 | + | |
285 | + def xml_vars_for(builder, hash) | |
286 | + hash.each do |key, value| | |
287 | + if value.respond_to?(:to_hash) | |
288 | + builder.var(:key => key){|b| xml_vars_for(b, value.to_hash) } | |
289 | + else | |
290 | + builder.var(value.to_s, :key => key) | |
291 | + end | |
292 | + end | |
293 | + end | |
294 | + | |
295 | + def rack_env(method) | |
296 | + rack_request.send(method) if rack_request | |
297 | + end | |
298 | + | |
299 | + def rack_request | |
300 | + @rack_request ||= if args[:rack_env] | |
301 | + ::Rack::Request.new(args[:rack_env]) | |
302 | + end | |
303 | + end | |
304 | + end | |
305 | +end | ... | ... |
vendor/gems/hoptoad_notifier-2.2.2/lib/hoptoad_notifier/rack.rb
0 → 100644
... | ... | @@ -0,0 +1,40 @@ |
1 | +module HoptoadNotifier | |
2 | + # Middleware for Rack applications. Any errors raised by the upstream | |
3 | + # application will be delivered to Hoptoad and re-raised. | |
4 | + # | |
5 | + # Synopsis: | |
6 | + # | |
7 | + # require 'rack' | |
8 | + # require 'hoptoad_notifier' | |
9 | + # | |
10 | + # HoptoadNotifier.configure do |config| | |
11 | + # config.api_key = 'my_api_key' | |
12 | + # end | |
13 | + # | |
14 | + # app = Rack::Builder.app do | |
15 | + # use HoptoadNotifier::Rack | |
16 | + # run lambda { |env| raise "Rack down" } | |
17 | + # end | |
18 | + # | |
19 | + # Use a standard HoptoadNotifier.configure call to configure your api key. | |
20 | + class Rack | |
21 | + def initialize(app) | |
22 | + @app = app | |
23 | + end | |
24 | + | |
25 | + def call(env) | |
26 | + begin | |
27 | + response = @app.call(env) | |
28 | + rescue Exception => raised | |
29 | + HoptoadNotifier.notify_or_ignore(raised, :rack_env => env) | |
30 | + raise | |
31 | + end | |
32 | + | |
33 | + if env['rack.exception'] | |
34 | + HoptoadNotifier.notify_or_ignore(env['rack.exception'], :rack_env => env) | |
35 | + end | |
36 | + | |
37 | + response | |
38 | + end | |
39 | + end | |
40 | +end | ... | ... |
vendor/gems/hoptoad_notifier-2.2.2/lib/hoptoad_notifier/rails.rb
0 → 100644
... | ... | @@ -0,0 +1,37 @@ |
1 | +require 'hoptoad_notifier' | |
2 | +require 'hoptoad_notifier/rails/controller_methods' | |
3 | +require 'hoptoad_notifier/rails/action_controller_catcher' | |
4 | +require 'hoptoad_notifier/rails/error_lookup' | |
5 | + | |
6 | +module HoptoadNotifier | |
7 | + module Rails | |
8 | + def self.initialize | |
9 | + if defined?(ActionController::Base) | |
10 | + ActionController::Base.send(:include, HoptoadNotifier::Rails::ActionControllerCatcher) | |
11 | + ActionController::Base.send(:include, HoptoadNotifier::Rails::ErrorLookup) | |
12 | + ActionController::Base.send(:include, HoptoadNotifier::Rails::ControllerMethods) | |
13 | + end | |
14 | + | |
15 | + rails_logger = if defined?(::Rails.logger) | |
16 | + ::Rails.logger | |
17 | + elsif defined?(RAILS_DEFAULT_LOGGER) | |
18 | + RAILS_DEFAULT_LOGGER | |
19 | + end | |
20 | + | |
21 | + if defined?(::Rails.configuration) && ::Rails.configuration.respond_to?(:middleware) | |
22 | + ::Rails.configuration.middleware.insert_after 'ActionController::Failsafe', | |
23 | + HoptoadNotifier::Rack | |
24 | + end | |
25 | + | |
26 | + HoptoadNotifier.configure(true) do |config| | |
27 | + config.logger = rails_logger | |
28 | + config.environment_name = RAILS_ENV if defined?(RAILS_ENV) | |
29 | + config.project_root = RAILS_ROOT if defined?(RAILS_ROOT) | |
30 | + config.framework = "Rails: #{::Rails::VERSION::STRING}" if defined?(::Rails::VERSION) | |
31 | + end | |
32 | + end | |
33 | + end | |
34 | +end | |
35 | + | |
36 | +HoptoadNotifier::Rails.initialize | |
37 | + | ... | ... |
vendor/gems/hoptoad_notifier-2.2.2/lib/hoptoad_notifier/rails/action_controller_catcher.rb
0 → 100644
... | ... | @@ -0,0 +1,29 @@ |
1 | +module HoptoadNotifier | |
2 | + module Rails | |
3 | + module ActionControllerCatcher | |
4 | + | |
5 | + # Sets up an alias chain to catch exceptions when Rails does | |
6 | + def self.included(base) #:nodoc: | |
7 | + base.send(:alias_method, :rescue_action_in_public_without_hoptoad, :rescue_action_in_public) | |
8 | + base.send(:alias_method, :rescue_action_in_public, :rescue_action_in_public_with_hoptoad) | |
9 | + end | |
10 | + | |
11 | + private | |
12 | + | |
13 | + # Overrides the rescue_action method in ActionController::Base, but does not inhibit | |
14 | + # any custom processing that is defined with Rails 2's exception helpers. | |
15 | + def rescue_action_in_public_with_hoptoad(exception) | |
16 | + unless hoptoad_ignore_user_agent? | |
17 | + HoptoadNotifier.notify_or_ignore(exception, hoptoad_request_data) | |
18 | + end | |
19 | + rescue_action_in_public_without_hoptoad(exception) | |
20 | + end | |
21 | + | |
22 | + def hoptoad_ignore_user_agent? #:nodoc: | |
23 | + # Rails 1.2.6 doesn't have request.user_agent, so check for it here | |
24 | + user_agent = request.respond_to?(:user_agent) ? request.user_agent : request.env["HTTP_USER_AGENT"] | |
25 | + HoptoadNotifier.configuration.ignore_user_agent.flatten.any? { |ua| ua === user_agent } | |
26 | + end | |
27 | + end | |
28 | + end | |
29 | +end | ... | ... |
vendor/gems/hoptoad_notifier-2.2.2/lib/hoptoad_notifier/rails/controller_methods.rb
0 → 100644
... | ... | @@ -0,0 +1,59 @@ |
1 | +module HoptoadNotifier | |
2 | + module Rails | |
3 | + module ControllerMethods | |
4 | + private | |
5 | + | |
6 | + # This method should be used for sending manual notifications while you are still | |
7 | + # inside the controller. Otherwise it works like HoptoadNotifier.notify. | |
8 | + def notify_hoptoad(hash_or_exception) | |
9 | + unless consider_all_requests_local || local_request? | |
10 | + HoptoadNotifier.notify(hash_or_exception, hoptoad_request_data) | |
11 | + end | |
12 | + end | |
13 | + | |
14 | + def hoptoad_ignore_user_agent? #:nodoc: | |
15 | + # Rails 1.2.6 doesn't have request.user_agent, so check for it here | |
16 | + user_agent = request.respond_to?(:user_agent) ? request.user_agent : request.env["HTTP_USER_AGENT"] | |
17 | + HoptoadNotifier.configuration.ignore_user_agent.flatten.any? { |ua| ua === user_agent } | |
18 | + end | |
19 | + | |
20 | + def hoptoad_request_data | |
21 | + { :parameters => hoptoad_filter_if_filtering(params.to_hash), | |
22 | + :session_data => hoptoad_session_data, | |
23 | + :controller => params[:controller], | |
24 | + :action => params[:action], | |
25 | + :url => hoptoad_request_url, | |
26 | + :cgi_data => hoptoad_filter_if_filtering(request.env), | |
27 | + :environment_vars => hoptoad_filter_if_filtering(ENV) } | |
28 | + end | |
29 | + | |
30 | + def hoptoad_filter_if_filtering(hash) | |
31 | + if respond_to?(:filter_parameters) | |
32 | + filter_parameters(hash) rescue hash | |
33 | + else | |
34 | + hash | |
35 | + end | |
36 | + end | |
37 | + | |
38 | + def hoptoad_session_data | |
39 | + if session.respond_to?(:to_hash) | |
40 | + session.to_hash | |
41 | + else | |
42 | + session.data | |
43 | + end | |
44 | + end | |
45 | + | |
46 | + def hoptoad_request_url | |
47 | + url = "#{request.protocol}#{request.host}" | |
48 | + | |
49 | + unless [80, 443].include?(request.port) | |
50 | + url << ":#{request.port}" | |
51 | + end | |
52 | + | |
53 | + url << request.request_uri | |
54 | + url | |
55 | + end | |
56 | + end | |
57 | + end | |
58 | +end | |
59 | + | ... | ... |
vendor/gems/hoptoad_notifier-2.2.2/lib/hoptoad_notifier/rails/error_lookup.rb
0 → 100644
... | ... | @@ -0,0 +1,33 @@ |
1 | +module HoptoadNotifier | |
2 | + module Rails | |
3 | + module ErrorLookup | |
4 | + | |
5 | + # Sets up an alias chain to catch exceptions when Rails does | |
6 | + def self.included(base) #:nodoc: | |
7 | + base.send(:alias_method, :rescue_action_locally_without_hoptoad, :rescue_action_locally) | |
8 | + base.send(:alias_method, :rescue_action_locally, :rescue_action_locally_with_hoptoad) | |
9 | + end | |
10 | + | |
11 | + private | |
12 | + | |
13 | + def rescue_action_locally_with_hoptoad(exception) | |
14 | + result = rescue_action_locally_without_hoptoad(exception) | |
15 | + | |
16 | + if HoptoadNotifier.configuration.development_lookup | |
17 | + path = File.join(File.dirname(__FILE__), '..', '..', 'templates', 'rescue.erb') | |
18 | + notice = HoptoadNotifier.build_lookup_hash_for(exception, hoptoad_request_data) | |
19 | + | |
20 | + result << @template.render( | |
21 | + :file => path, | |
22 | + :use_full_path => false, | |
23 | + :locals => { :host => HoptoadNotifier.configuration.host, | |
24 | + :api_key => HoptoadNotifier.configuration.api_key, | |
25 | + :notice => notice }) | |
26 | + end | |
27 | + | |
28 | + result | |
29 | + end | |
30 | + end | |
31 | + end | |
32 | +end | |
33 | + | ... | ... |
vendor/gems/hoptoad_notifier-2.2.2/lib/hoptoad_notifier/sender.rb
0 → 100644
... | ... | @@ -0,0 +1,63 @@ |
1 | +module HoptoadNotifier | |
2 | + # Sends out the notice to Hoptoad | |
3 | + class Sender | |
4 | + | |
5 | + NOTICES_URI = '/notifier_api/v2/notices/'.freeze | |
6 | + | |
7 | + def initialize(options = {}) | |
8 | + [:proxy_host, :proxy_port, :proxy_user, :proxy_pass, :protocol, | |
9 | + :host, :port, :secure, :http_open_timeout, :http_read_timeout].each do |option| | |
10 | + instance_variable_set("@#{option}", options[option]) | |
11 | + end | |
12 | + end | |
13 | + | |
14 | + # Sends the notice data off to Hoptoad for processing. | |
15 | + # | |
16 | + # @param [String] data The XML notice to be sent off | |
17 | + def send_to_hoptoad(data) | |
18 | + logger.debug { "Sending request to #{url.to_s}:\n#{data}" } if logger | |
19 | + | |
20 | + http = | |
21 | + Net::HTTP::Proxy(proxy_host, proxy_port, proxy_user, proxy_pass). | |
22 | + new(url.host, url.port) | |
23 | + | |
24 | + http.read_timeout = http_read_timeout | |
25 | + http.open_timeout = http_open_timeout | |
26 | + http.use_ssl = secure | |
27 | + | |
28 | + response = begin | |
29 | + http.post(url.path, data, HEADERS) | |
30 | + rescue TimeoutError => e | |
31 | + log :error, "Timeout while contacting the Hoptoad server." | |
32 | + nil | |
33 | + end | |
34 | + | |
35 | + case response | |
36 | + when Net::HTTPSuccess then | |
37 | + log :info, "Success: #{response.class}", response | |
38 | + else | |
39 | + log :error, "Failure: #{response.class}", response | |
40 | + end | |
41 | + end | |
42 | + | |
43 | + private | |
44 | + | |
45 | + attr_reader :proxy_host, :proxy_port, :proxy_user, :proxy_pass, :protocol, | |
46 | + :host, :port, :secure, :http_open_timeout, :http_read_timeout | |
47 | + | |
48 | + def url | |
49 | + URI.parse("#{protocol}://#{host}:#{port}").merge(NOTICES_URI) | |
50 | + end | |
51 | + | |
52 | + def log(level, message, response = nil) | |
53 | + logger.send level, LOG_PREFIX + message if logger | |
54 | + HoptoadNotifier.report_environment_info | |
55 | + HoptoadNotifier.report_response_body(response.body) if response && response.respond_to?(:body) | |
56 | + end | |
57 | + | |
58 | + def logger | |
59 | + HoptoadNotifier.logger | |
60 | + end | |
61 | + | |
62 | + end | |
63 | +end | ... | ... |
vendor/gems/hoptoad_notifier-2.2.2/lib/hoptoad_notifier/tasks.rb
0 → 100644
... | ... | @@ -0,0 +1,97 @@ |
1 | +require 'hoptoad_notifier' | |
2 | + | |
3 | +namespace :hoptoad do | |
4 | + desc "Notify Hoptoad of a new deploy." | |
5 | + task :deploy => :environment do | |
6 | + require 'hoptoad_tasks' | |
7 | + HoptoadTasks.deploy(:rails_env => ENV['TO'], | |
8 | + :scm_revision => ENV['REVISION'], | |
9 | + :scm_repository => ENV['REPO'], | |
10 | + :local_username => ENV['USER'], | |
11 | + :api_key => ENV['API_KEY']) | |
12 | + end | |
13 | + | |
14 | + task :log_stdout do | |
15 | + require 'logger' | |
16 | + RAILS_DEFAULT_LOGGER = Logger.new(STDOUT) | |
17 | + end | |
18 | + | |
19 | + desc "Verify your gem installation by sending a test exception to the hoptoad service" | |
20 | + task :test => ['hoptoad:log_stdout', :environment] do | |
21 | + RAILS_DEFAULT_LOGGER.level = Logger::DEBUG | |
22 | + | |
23 | + require 'action_controller/test_process' | |
24 | + | |
25 | + Dir["app/controllers/application*.rb"].each { |file| require(file) } | |
26 | + | |
27 | + class HoptoadTestingException < RuntimeError; end | |
28 | + | |
29 | + unless HoptoadNotifier.configuration.api_key | |
30 | + puts "Hoptoad needs an API key configured! Check the README to see how to add it." | |
31 | + exit | |
32 | + end | |
33 | + | |
34 | + HoptoadNotifier.configuration.development_environments = [] | |
35 | + | |
36 | + catcher = HoptoadNotifier::Rails::ActionControllerCatcher | |
37 | + in_controller = ApplicationController.included_modules.include?(catcher) | |
38 | + in_base = ActionController::Base.included_modules.include?(catcher) | |
39 | + if !in_controller || !in_base | |
40 | + puts "Rails initialization did not occur" | |
41 | + exit | |
42 | + end | |
43 | + | |
44 | + puts "Configuration:" | |
45 | + HoptoadNotifier.configuration.to_hash.each do |key, value| | |
46 | + puts sprintf("%25s: %s", key.to_s, value.inspect.slice(0, 55)) | |
47 | + end | |
48 | + | |
49 | + unless defined?(ApplicationController) | |
50 | + puts "No ApplicationController found" | |
51 | + exit | |
52 | + end | |
53 | + | |
54 | + puts 'Setting up the Controller.' | |
55 | + class ApplicationController | |
56 | + # This is to bypass any filters that may prevent access to the action. | |
57 | + prepend_before_filter :test_hoptoad | |
58 | + def test_hoptoad | |
59 | + puts "Raising '#{exception_class.name}' to simulate application failure." | |
60 | + raise exception_class.new, 'Testing hoptoad via "rake hoptoad:test". If you can see this, it works.' | |
61 | + end | |
62 | + | |
63 | + def rescue_action(exception) | |
64 | + rescue_action_in_public exception | |
65 | + end | |
66 | + | |
67 | + # Ensure we actually have an action to go to. | |
68 | + def verify; end | |
69 | + | |
70 | + def consider_all_requests_local | |
71 | + false | |
72 | + end | |
73 | + | |
74 | + def local_request? | |
75 | + false | |
76 | + end | |
77 | + | |
78 | + def exception_class | |
79 | + exception_name = ENV['EXCEPTION'] || "HoptoadTestingException" | |
80 | + Object.const_get(exception_name) | |
81 | + rescue | |
82 | + Object.const_set(exception_name, Class.new(Exception)) | |
83 | + end | |
84 | + | |
85 | + def logger | |
86 | + nil | |
87 | + end | |
88 | + end | |
89 | + class HoptoadVerificationController < ApplicationController; end | |
90 | + | |
91 | + puts 'Processing request.' | |
92 | + request = ActionController::TestRequest.new | |
93 | + response = ActionController::TestResponse.new | |
94 | + HoptoadVerificationController.new.process(request, response) | |
95 | + end | |
96 | +end | |
97 | + | ... | ... |
vendor/gems/hoptoad_notifier-2.2.2/lib/hoptoad_notifier/version.rb
0 → 100644
... | ... | @@ -0,0 +1,37 @@ |
1 | +require 'net/http' | |
2 | +require 'uri' | |
3 | +require 'active_support' | |
4 | + | |
5 | +# Capistrano tasks for notifying Hoptoad of deploys | |
6 | +module HoptoadTasks | |
7 | + | |
8 | + # Alerts Hoptoad of a deploy. | |
9 | + # | |
10 | + # @param [Hash] opts Data about the deploy that is set to Hoptoad | |
11 | + # | |
12 | + # @option opts [String] :rails_env Environment of the deploy (production, staging) | |
13 | + # @option opts [String] :scm_revision The given revision/sha that is being deployed | |
14 | + # @option opts [String] :scm_repository Address of your repository to help with code lookups | |
15 | + # @option opts [String] :local_username Who is deploying | |
16 | + def self.deploy(opts = {}) | |
17 | + if HoptoadNotifier.configuration.api_key.blank? | |
18 | + puts "I don't seem to be configured with an API key. Please check your configuration." | |
19 | + return false | |
20 | + end | |
21 | + | |
22 | + if opts[:rails_env].blank? | |
23 | + puts "I don't know to which Rails environment you are deploying (use the TO=production option)." | |
24 | + return false | |
25 | + end | |
26 | + | |
27 | + params = {'api_key' => opts.delete(:api_key) || | |
28 | + HoptoadNotifier.configuration.api_key} | |
29 | + opts.each {|k,v| params["deploy[#{k}]"] = v } | |
30 | + | |
31 | + url = URI.parse("http://#{HoptoadNotifier.configuration.host || 'hoptoadapp.com'}/deploys.txt") | |
32 | + response = Net::HTTP.post_form(url, params) | |
33 | + puts response.body | |
34 | + return Net::HTTPSuccess === response | |
35 | + end | |
36 | +end | |
37 | + | ... | ... |
vendor/gems/hoptoad_notifier-2.2.2/lib/templates/rescue.erb
0 → 100644
... | ... | @@ -0,0 +1,91 @@ |
1 | +<script type="text/javascript"> | |
2 | +var Hoptoad = { | |
3 | + host : <%= host.to_json %>, | |
4 | + api_key : <%= api_key.to_json %>, | |
5 | + notice : <%= notice.to_json %>, | |
6 | + message : 'This error exists in production!', | |
7 | + | |
8 | + initialize: function() { | |
9 | + if (this.initialized) { | |
10 | + return; | |
11 | + } else { | |
12 | + this.initialized = true; | |
13 | + } | |
14 | + | |
15 | + var data = []; | |
16 | + | |
17 | + for (var key in this.notice) { | |
18 | + data[data.length] = 'notice[' + key + ']=' + this.notice[key]; | |
19 | + } | |
20 | + | |
21 | + data[data.length] = 'notice[api_key]=' + this.api_key; | |
22 | + data[data.length] = 'callback=Hoptoad.onSuccess'; | |
23 | + data[data.length] = '_=' + (new Date()).getTime(); | |
24 | + | |
25 | + var head = document.getElementsByTagName('head')[0]; | |
26 | + var done = false; | |
27 | + | |
28 | + var | |
29 | + script = document.createElement('script'); | |
30 | + script.src = 'http://' + this.host + '/notices_api/v1/notices/exist?' + | |
31 | + data.join('&'); | |
32 | + script.type = 'text/javascript'; | |
33 | + script.onload = script.onreadystatechange = function(){ | |
34 | + if (!done && (!this.readyState || | |
35 | + this.readyState == 'loaded' || this.readyState == 'complete')) { | |
36 | + | |
37 | + done = true; | |
38 | + | |
39 | + // Handle memory leak in IE. (via jQuery) | |
40 | + script.onload = script.onreadystatechange = null; | |
41 | + head.removeChild(script); | |
42 | + } | |
43 | + }; | |
44 | + | |
45 | + head.appendChild(script); | |
46 | + }, | |
47 | + | |
48 | + onSuccess: function(error) { | |
49 | + var body = document.getElementsByTagName('body')[0]; | |
50 | + var text = document.createTextNode(this.message); | |
51 | + var element = document.createElement('a'); | |
52 | + | |
53 | + element.id = 'hoptoad'; | |
54 | + element.href = 'http://' + error.subdomain + '.' + this.host + | |
55 | + '/projects/' + error.project_id + '/errors/' + error.id; | |
56 | + element.appendChild(text); | |
57 | + | |
58 | + body.insertBefore(element, body.firstChild); | |
59 | + | |
60 | + var h1 = document.getElementsByTagName('h1')[0]; | |
61 | + var pre = document.getElementsByTagName('pre')[0]; | |
62 | + var wrapper = document.createElement('div'); | |
63 | + | |
64 | + wrapper.id = 'wrapper'; | |
65 | + wrapper.appendChild(h1); | |
66 | + wrapper.appendChild(pre); | |
67 | + | |
68 | + body.insertBefore(wrapper, body.children[1]); | |
69 | + } | |
70 | +}; | |
71 | + | |
72 | +window.onload = function() { | |
73 | + Hoptoad.initialize.apply(Hoptoad); | |
74 | +}; | |
75 | +</script> | |
76 | + | |
77 | +<style type="text/css"> | |
78 | +#hoptoad { | |
79 | + background: #FFF url(http://hoptoadapp.com/images/fell-off-the-toad.gif) no-repeat top right; | |
80 | + color: #F00; | |
81 | + padding: 45px 101px 45px 12px; | |
82 | + font-size: 14px; | |
83 | + font-weight: bold; | |
84 | + display: block; | |
85 | + float: right; | |
86 | +} | |
87 | + | |
88 | +#wrapper { | |
89 | + padding-right: 360px; | |
90 | +} | |
91 | +</style> | ... | ... |
... | ... | @@ -0,0 +1 @@ |
1 | +require 'hoptoad_notifier/rails' | ... | ... |
vendor/gems/hoptoad_notifier-2.2.2/script/integration_test.rb
0 → 100755
... | ... | @@ -0,0 +1,38 @@ |
1 | +#!/usr/bin/env ruby | |
2 | + | |
3 | +require 'logger' | |
4 | +require 'fileutils' | |
5 | + | |
6 | +RAILS_ENV = "production" | |
7 | +RAILS_ROOT = FileUtils.pwd | |
8 | +RAILS_DEFAULT_LOGGER = Logger.new(STDOUT) | |
9 | + | |
10 | +$: << File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib')) | |
11 | +require 'hoptoad_notifier' | |
12 | +require 'rails/init' | |
13 | + | |
14 | +fail "Please supply an API Key as the first argument" if ARGV.empty? | |
15 | + | |
16 | +host = ARGV[1] | |
17 | +host ||= "hoptoadapp.com" | |
18 | + | |
19 | +secure = (ARGV[2] == "secure") | |
20 | + | |
21 | +exception = begin | |
22 | + raise "Testing hoptoad notifier with secure = #{secure}. If you can see this, it works." | |
23 | + rescue => foo | |
24 | + foo | |
25 | + end | |
26 | + | |
27 | +HoptoadNotifier.configure do |config| | |
28 | + config.secure = secure | |
29 | + config.host = host | |
30 | + config.api_key = ARGV.first | |
31 | +end | |
32 | +puts "Configuration:" | |
33 | +HoptoadNotifier.configuration.to_hash.each do |key, value| | |
34 | + puts sprintf("%25s: %s", key.to_s, value.inspect.slice(0, 55)) | |
35 | +end | |
36 | +puts "Sending #{secure ? "" : "in"}secure notification to project with key #{ARGV.first}" | |
37 | +HoptoadNotifier.notify(exception) | |
38 | + | ... | ... |
vendor/gems/hoptoad_notifier-2.2.2/test/backtrace_test.rb
0 → 100644
... | ... | @@ -0,0 +1,118 @@ |
1 | +require File.dirname(__FILE__) + '/helper' | |
2 | + | |
3 | +class BacktraceTest < Test::Unit::TestCase | |
4 | + | |
5 | + should "parse a backtrace into lines" do | |
6 | + array = [ | |
7 | + "app/models/user.rb:13:in `magic'", | |
8 | + "app/controllers/users_controller.rb:8:in `index'" | |
9 | + ] | |
10 | + | |
11 | + backtrace = HoptoadNotifier::Backtrace.parse(array) | |
12 | + | |
13 | + line = backtrace.lines.first | |
14 | + assert_equal '13', line.number | |
15 | + assert_equal 'app/models/user.rb', line.file | |
16 | + assert_equal 'magic', line.method | |
17 | + | |
18 | + line = backtrace.lines.last | |
19 | + assert_equal '8', line.number | |
20 | + assert_equal 'app/controllers/users_controller.rb', line.file | |
21 | + assert_equal 'index', line.method | |
22 | + end | |
23 | + | |
24 | + should "be equal with equal lines" do | |
25 | + one = build_backtrace_array | |
26 | + two = one.dup | |
27 | + assert_equal one, two | |
28 | + | |
29 | + assert_equal HoptoadNotifier::Backtrace.parse(one), HoptoadNotifier::Backtrace.parse(two) | |
30 | + end | |
31 | + | |
32 | + should "parse massive one-line exceptions into multiple lines" do | |
33 | + original_backtrace = HoptoadNotifier::Backtrace. | |
34 | + parse(["one:1:in `one'\n two:2:in `two'\n three:3:in `three`"]) | |
35 | + expected_backtrace = HoptoadNotifier::Backtrace. | |
36 | + parse(["one:1:in `one'", "two:2:in `two'", "three:3:in `three`"]) | |
37 | + | |
38 | + assert_equal expected_backtrace, original_backtrace | |
39 | + end | |
40 | + | |
41 | + context "with a project root" do | |
42 | + setup do | |
43 | + @project_root = '/some/path' | |
44 | + HoptoadNotifier.configure {|config| config.project_root = @project_root } | |
45 | + end | |
46 | + | |
47 | + teardown do | |
48 | + reset_config | |
49 | + end | |
50 | + | |
51 | + should "filter out the project root" do | |
52 | + backtrace_with_root = HoptoadNotifier::Backtrace.parse( | |
53 | + ["#{@project_root}/app/models/user.rb:7:in `latest'", | |
54 | + "#{@project_root}/app/controllers/users_controller.rb:13:in `index'", | |
55 | + "/lib/something.rb:41:in `open'"], | |
56 | + :filters => default_filters) | |
57 | + backtrace_without_root = HoptoadNotifier::Backtrace.parse( | |
58 | + ["[PROJECT_ROOT]/app/models/user.rb:7:in `latest'", | |
59 | + "[PROJECT_ROOT]/app/controllers/users_controller.rb:13:in `index'", | |
60 | + "/lib/something.rb:41:in `open'"]) | |
61 | + | |
62 | + assert_equal backtrace_without_root, backtrace_with_root | |
63 | + end | |
64 | + end | |
65 | + | |
66 | + context "with a blank project root" do | |
67 | + setup do | |
68 | + HoptoadNotifier.configure {|config| config.project_root = '' } | |
69 | + end | |
70 | + | |
71 | + teardown do | |
72 | + reset_config | |
73 | + end | |
74 | + | |
75 | + should "not filter line numbers with respect to any project root" do | |
76 | + backtrace = ["/app/models/user.rb:7:in `latest'", | |
77 | + "/app/controllers/users_controller.rb:13:in `index'", | |
78 | + "/lib/something.rb:41:in `open'"] | |
79 | + | |
80 | + backtrace_with_root = | |
81 | + HoptoadNotifier::Backtrace.parse(backtrace, :filters => default_filters) | |
82 | + | |
83 | + backtrace_without_root = | |
84 | + HoptoadNotifier::Backtrace.parse(backtrace) | |
85 | + | |
86 | + assert_equal backtrace_without_root, backtrace_with_root | |
87 | + end | |
88 | + end | |
89 | + | |
90 | + should "remove notifier trace" do | |
91 | + inside_notifier = ['lib/hoptoad_notifier.rb:13:in `voodoo`'] | |
92 | + outside_notifier = ['users_controller:8:in `index`'] | |
93 | + | |
94 | + without_inside = HoptoadNotifier::Backtrace.parse(outside_notifier) | |
95 | + with_inside = HoptoadNotifier::Backtrace.parse(inside_notifier + outside_notifier, | |
96 | + :filters => default_filters) | |
97 | + | |
98 | + assert_equal without_inside, with_inside | |
99 | + end | |
100 | + | |
101 | + should "run filters on the backtrace" do | |
102 | + filters = [lambda { |line| line.sub('foo', 'bar') }] | |
103 | + input = HoptoadNotifier::Backtrace.parse(["foo:13:in `one'", "baz:14:in `two'"], | |
104 | + :filters => filters) | |
105 | + expected = HoptoadNotifier::Backtrace.parse(["bar:13:in `one'", "baz:14:in `two'"]) | |
106 | + assert_equal expected, input | |
107 | + end | |
108 | + | |
109 | + def build_backtrace_array | |
110 | + ["app/models/user.rb:13:in `magic'", | |
111 | + "app/controllers/users_controller.rb:8:in `index'"] | |
112 | + end | |
113 | + | |
114 | + def default_filters | |
115 | + HoptoadNotifier::Configuration::DEFAULT_BACKTRACE_FILTERS | |
116 | + end | |
117 | + | |
118 | +end | ... | ... |
... | ... | @@ -0,0 +1,324 @@ |
1 | +require File.dirname(__FILE__) + '/helper' | |
2 | + | |
3 | +class ActionControllerCatcherTest < Test::Unit::TestCase | |
4 | + | |
5 | + include DefinesConstants | |
6 | + | |
7 | + def setup | |
8 | + super | |
9 | + reset_config | |
10 | + HoptoadNotifier.sender = CollectingSender.new | |
11 | + define_constant('RAILS_ROOT', '/path/to/rails/root') | |
12 | + end | |
13 | + | |
14 | + def ignore(exception_class) | |
15 | + HoptoadNotifier.configuration.ignore << exception_class | |
16 | + end | |
17 | + | |
18 | + def build_controller_class(&definition) | |
19 | + returning Class.new(ActionController::Base) do |klass| | |
20 | + klass.__send__(:include, HoptoadNotifier::Rails::ActionControllerCatcher) | |
21 | + klass.class_eval(&definition) if definition | |
22 | + define_constant('HoptoadTestController', klass) | |
23 | + end | |
24 | + end | |
25 | + | |
26 | + def assert_sent_hash(hash, xpath) | |
27 | + hash.each do |key, value| | |
28 | + element_xpath = "#{xpath}/var[@key = '#{key}']" | |
29 | + if value.respond_to?(:to_hash) | |
30 | + assert_sent_hash value.to_hash, element_xpath | |
31 | + else | |
32 | + assert_sent_element value, element_xpath | |
33 | + end | |
34 | + end | |
35 | + end | |
36 | + | |
37 | + def assert_sent_element(value, xpath) | |
38 | + assert_valid_node last_sent_notice_document, xpath, stringify_array_elements(value).to_s | |
39 | + end | |
40 | + | |
41 | + def stringify_array_elements(data) | |
42 | + if data.respond_to?(:to_ary) | |
43 | + data.collect do |value| | |
44 | + stringify_array_elements(value) | |
45 | + end | |
46 | + else | |
47 | + data.to_s | |
48 | + end | |
49 | + end | |
50 | + | |
51 | + def assert_sent_request_info_for(request) | |
52 | + params = request.parameters.to_hash | |
53 | + assert_sent_hash params, '/notice/request/params' | |
54 | + assert_sent_element params['controller'], '/notice/request/component' | |
55 | + assert_sent_element params['action'], '/notice/request/action' | |
56 | + assert_sent_element url_from_request(request), '/notice/request/url' | |
57 | + assert_sent_hash request.env, '/notice/request/cgi-data' | |
58 | + end | |
59 | + | |
60 | + def url_from_request(request) | |
61 | + url = "#{request.protocol}#{request.host}" | |
62 | + | |
63 | + unless [80, 443].include?(request.port) | |
64 | + url << ":#{request.port}" | |
65 | + end | |
66 | + | |
67 | + url << request.request_uri | |
68 | + url | |
69 | + end | |
70 | + | |
71 | + def sender | |
72 | + HoptoadNotifier.sender | |
73 | + end | |
74 | + | |
75 | + def last_sent_notice_xml | |
76 | + sender.collected.last | |
77 | + end | |
78 | + | |
79 | + def last_sent_notice_document | |
80 | + assert_not_nil xml = last_sent_notice_xml, "No xml was sent" | |
81 | + Nokogiri::XML.parse(xml) | |
82 | + end | |
83 | + | |
84 | + def process_action(opts = {}, &action) | |
85 | + opts[:request] ||= ActionController::TestRequest.new | |
86 | + opts[:response] ||= ActionController::TestResponse.new | |
87 | + klass = build_controller_class do | |
88 | + cattr_accessor :local | |
89 | + define_method(:index, &action) | |
90 | + def local_request? | |
91 | + local | |
92 | + end | |
93 | + end | |
94 | + if opts[:filters] | |
95 | + klass.filter_parameter_logging *opts[:filters] | |
96 | + end | |
97 | + if opts[:user_agent] | |
98 | + if opts[:request].respond_to?(:user_agent=) | |
99 | + opts[:request].user_agent = opts[:user_agent] | |
100 | + else | |
101 | + opts[:request].env["HTTP_USER_AGENT"] = opts[:user_agent] | |
102 | + end | |
103 | + end | |
104 | + if opts[:port] | |
105 | + opts[:request].port = opts[:port] | |
106 | + end | |
107 | + klass.consider_all_requests_local = opts[:all_local] | |
108 | + klass.local = opts[:local] | |
109 | + controller = klass.new | |
110 | + controller.stubs(:rescue_action_in_public_without_hoptoad) | |
111 | + opts[:request].query_parameters = opts[:request].query_parameters.merge(opts[:params] || {}) | |
112 | + opts[:request].session = ActionController::TestSession.new(opts[:session] || {}) | |
113 | + controller.process(opts[:request], opts[:response]) | |
114 | + controller | |
115 | + end | |
116 | + | |
117 | + def process_action_with_manual_notification(args = {}) | |
118 | + process_action(args) do | |
119 | + notify_hoptoad(:error_message => 'fail') | |
120 | + # Rails will raise a template error if we don't render something | |
121 | + render :nothing => true | |
122 | + end | |
123 | + end | |
124 | + | |
125 | + def process_action_with_automatic_notification(args = {}) | |
126 | + process_action(args) { raise "Hello" } | |
127 | + end | |
128 | + | |
129 | + should "deliver notices from exceptions raised in public requests" do | |
130 | + process_action_with_automatic_notification | |
131 | + assert_caught_and_sent | |
132 | + end | |
133 | + | |
134 | + should "not deliver notices from exceptions in local requests" do | |
135 | + process_action_with_automatic_notification(:local => true) | |
136 | + assert_caught_and_not_sent | |
137 | + end | |
138 | + | |
139 | + should "not deliver notices from exceptions when all requests are local" do | |
140 | + process_action_with_automatic_notification(:all_local => true) | |
141 | + assert_caught_and_not_sent | |
142 | + end | |
143 | + | |
144 | + should "not deliver notices from actions that don't raise" do | |
145 | + controller = process_action { render :text => 'Hello' } | |
146 | + assert_caught_and_not_sent | |
147 | + assert_equal 'Hello', controller.response.body | |
148 | + end | |
149 | + | |
150 | + should "not deliver ignored exceptions raised by actions" do | |
151 | + ignore(RuntimeError) | |
152 | + process_action_with_automatic_notification | |
153 | + assert_caught_and_not_sent | |
154 | + end | |
155 | + | |
156 | + should "deliver ignored exception raised manually" do | |
157 | + ignore(RuntimeError) | |
158 | + process_action_with_manual_notification | |
159 | + assert_caught_and_sent | |
160 | + end | |
161 | + | |
162 | + should "deliver manually sent notices in public requests" do | |
163 | + process_action_with_manual_notification | |
164 | + assert_caught_and_sent | |
165 | + end | |
166 | + | |
167 | + should "not deliver manually sent notices in local requests" do | |
168 | + process_action_with_manual_notification(:local => true) | |
169 | + assert_caught_and_not_sent | |
170 | + end | |
171 | + | |
172 | + should "not deliver manually sent notices when all requests are local" do | |
173 | + process_action_with_manual_notification(:all_local => true) | |
174 | + assert_caught_and_not_sent | |
175 | + end | |
176 | + | |
177 | + should "continue with default behavior after delivering an exception" do | |
178 | + controller = process_action_with_automatic_notification(:public => true) | |
179 | + # TODO: can we test this without stubbing? | |
180 | + assert_received(controller, :rescue_action_in_public_without_hoptoad) | |
181 | + end | |
182 | + | |
183 | + should "not create actions from Hoptoad methods" do | |
184 | + controller = build_controller_class.new | |
185 | + assert_equal [], HoptoadNotifier::Rails::ActionControllerCatcher.instance_methods | |
186 | + end | |
187 | + | |
188 | + should "ignore exceptions when user agent is being ignored by regular expression" do | |
189 | + HoptoadNotifier.configuration.ignore_user_agent_only = [/Ignored/] | |
190 | + process_action_with_automatic_notification(:user_agent => 'ShouldBeIgnored') | |
191 | + assert_caught_and_not_sent | |
192 | + end | |
193 | + | |
194 | + should "ignore exceptions when user agent is being ignored by string" do | |
195 | + HoptoadNotifier.configuration.ignore_user_agent_only = ['IgnoredUserAgent'] | |
196 | + process_action_with_automatic_notification(:user_agent => 'IgnoredUserAgent') | |
197 | + assert_caught_and_not_sent | |
198 | + end | |
199 | + | |
200 | + should "not ignore exceptions when user agent is not being ignored" do | |
201 | + HoptoadNotifier.configuration.ignore_user_agent_only = ['IgnoredUserAgent'] | |
202 | + process_action_with_automatic_notification(:user_agent => 'NonIgnoredAgent') | |
203 | + assert_caught_and_sent | |
204 | + end | |
205 | + | |
206 | + should "send session data for manual notifications" do | |
207 | + data = { 'one' => 'two' } | |
208 | + process_action_with_manual_notification(:session => data) | |
209 | + assert_sent_hash data, "/notice/request/session" | |
210 | + end | |
211 | + | |
212 | + should "send session data for automatic notification" do | |
213 | + data = { 'one' => 'two' } | |
214 | + process_action_with_automatic_notification(:session => data) | |
215 | + assert_sent_hash data, "/notice/request/session" | |
216 | + end | |
217 | + | |
218 | + should "send request data for manual notification" do | |
219 | + params = { 'controller' => "hoptoad_test", 'action' => "index" } | |
220 | + controller = process_action_with_manual_notification(:params => params) | |
221 | + assert_sent_request_info_for controller.request | |
222 | + end | |
223 | + | |
224 | + should "send request data for manual notification with non-standard port" do | |
225 | + params = { 'controller' => "hoptoad_test", 'action' => "index" } | |
226 | + controller = process_action_with_manual_notification(:params => params, :port => 81) | |
227 | + assert_sent_request_info_for controller.request | |
228 | + end | |
229 | + | |
230 | + should "send request data for automatic notification" do | |
231 | + params = { 'controller' => "hoptoad_test", 'action' => "index" } | |
232 | + controller = process_action_with_automatic_notification(:params => params) | |
233 | + assert_sent_request_info_for controller.request | |
234 | + end | |
235 | + | |
236 | + should "send request data for automatic notification with non-standard port" do | |
237 | + params = { 'controller' => "hoptoad_test", 'action' => "index" } | |
238 | + controller = process_action_with_automatic_notification(:params => params, :port => 81) | |
239 | + assert_sent_request_info_for controller.request | |
240 | + end | |
241 | + | |
242 | + should "use standard rails logging filters on params and env" do | |
243 | + filtered_params = { "abc" => "123", | |
244 | + "def" => "456", | |
245 | + "ghi" => "[FILTERED]" } | |
246 | + ENV['ghi'] = 'abc' | |
247 | + filtered_env = { 'ghi' => '[FILTERED]' } | |
248 | + filtered_cgi = { 'REQUEST_METHOD' => '[FILTERED]' } | |
249 | + | |
250 | + process_action_with_automatic_notification(:filters => [:ghi, :request_method], | |
251 | + :params => { "abc" => "123", | |
252 | + "def" => "456", | |
253 | + "ghi" => "789" }) | |
254 | + assert_sent_hash filtered_params, '/notice/request/params' | |
255 | + assert_sent_hash filtered_cgi, '/notice/request/cgi-data' | |
256 | + end | |
257 | + | |
258 | + context "for a local error with development lookup enabled" do | |
259 | + setup do | |
260 | + HoptoadNotifier.configuration.development_lookup = true | |
261 | + HoptoadNotifier.stubs(:build_lookup_hash_for).returns({ :awesome => 2 }) | |
262 | + | |
263 | + @controller = process_action_with_automatic_notification(:local => true) | |
264 | + @response = @controller.response | |
265 | + end | |
266 | + | |
267 | + should "append custom CSS and JS to response body for a local error" do | |
268 | + assert_match /text\/css/, @response.body | |
269 | + assert_match /text\/javascript/, @response.body | |
270 | + end | |
271 | + | |
272 | + should "contain host, API key and notice JSON" do | |
273 | + assert_match HoptoadNotifier.configuration.host.to_json, @response.body | |
274 | + assert_match HoptoadNotifier.configuration.api_key.to_json, @response.body | |
275 | + assert_match ({ :awesome => 2 }).to_json, @response.body | |
276 | + end | |
277 | + end | |
278 | + | |
279 | + context "for a local error with development lookup disabled" do | |
280 | + setup do | |
281 | + HoptoadNotifier.configuration.development_lookup = false | |
282 | + | |
283 | + @controller = process_action_with_automatic_notification(:local => true) | |
284 | + @response = @controller.response | |
285 | + end | |
286 | + | |
287 | + should "not append custom CSS and JS to response for a local error" do | |
288 | + assert_no_match /text\/css/, @response.body | |
289 | + assert_no_match /text\/javascript/, @response.body | |
290 | + end | |
291 | + end | |
292 | + | |
293 | + should "call session.to_hash if available" do | |
294 | + hash_data = {:key => :value} | |
295 | + | |
296 | + session = ActionController::TestSession.new | |
297 | + ActionController::TestSession.stubs(:new).returns(session) | |
298 | + session.stubs(:to_hash).returns(hash_data) | |
299 | + | |
300 | + process_action_with_automatic_notification | |
301 | + assert_received(session, :to_hash) | |
302 | + assert_received(session, :data) { |expect| expect.never } | |
303 | + assert_caught_and_sent | |
304 | + end | |
305 | + | |
306 | + should "call session.data if session.to_hash is undefined" do | |
307 | + hash_data = {:key => :value} | |
308 | + | |
309 | + session = ActionController::TestSession.new | |
310 | + ActionController::TestSession.stubs(:new).returns(session) | |
311 | + session.stubs(:data).returns(hash_data) | |
312 | + if session.respond_to?(:to_hash) | |
313 | + class << session | |
314 | + undef to_hash | |
315 | + end | |
316 | + end | |
317 | + | |
318 | + process_action_with_automatic_notification | |
319 | + assert_received(session, :to_hash) { |expect| expect.never } | |
320 | + assert_received(session, :data) { |expect| expect.at_least_once } | |
321 | + assert_caught_and_sent | |
322 | + end | |
323 | + | |
324 | +end | ... | ... |
vendor/gems/hoptoad_notifier-2.2.2/test/configuration_test.rb
0 → 100644
... | ... | @@ -0,0 +1,208 @@ |
1 | +require File.dirname(__FILE__) + '/helper' | |
2 | + | |
3 | +class ConfigurationTest < Test::Unit::TestCase | |
4 | + | |
5 | + include DefinesConstants | |
6 | + | |
7 | + should "provide default values" do | |
8 | + assert_config_default :proxy_host, nil | |
9 | + assert_config_default :proxy_port, nil | |
10 | + assert_config_default :proxy_user, nil | |
11 | + assert_config_default :proxy_pass, nil | |
12 | + assert_config_default :project_root, nil | |
13 | + assert_config_default :environment_name, nil | |
14 | + assert_config_default :logger, nil | |
15 | + assert_config_default :notifier_version, HoptoadNotifier::VERSION | |
16 | + assert_config_default :notifier_name, 'Hoptoad Notifier' | |
17 | + assert_config_default :notifier_url, 'http://hoptoadapp.com' | |
18 | + assert_config_default :secure, false | |
19 | + assert_config_default :host, 'hoptoadapp.com' | |
20 | + assert_config_default :http_open_timeout, 2 | |
21 | + assert_config_default :http_read_timeout, 5 | |
22 | + assert_config_default :ignore_by_filters, [] | |
23 | + assert_config_default :ignore_user_agent, [] | |
24 | + assert_config_default :params_filters, | |
25 | + HoptoadNotifier::Configuration::DEFAULT_PARAMS_FILTERS | |
26 | + assert_config_default :backtrace_filters, | |
27 | + HoptoadNotifier::Configuration::DEFAULT_BACKTRACE_FILTERS | |
28 | + assert_config_default :ignore, | |
29 | + HoptoadNotifier::Configuration::IGNORE_DEFAULT | |
30 | + assert_config_default :development_lookup, true | |
31 | + assert_config_default :framework, 'Standalone' | |
32 | + end | |
33 | + | |
34 | + should "provide default values for secure connections" do | |
35 | + config = HoptoadNotifier::Configuration.new | |
36 | + config.secure = true | |
37 | + assert_equal 443, config.port | |
38 | + assert_equal 'https', config.protocol | |
39 | + end | |
40 | + | |
41 | + should "provide default values for insecure connections" do | |
42 | + config = HoptoadNotifier::Configuration.new | |
43 | + config.secure = false | |
44 | + assert_equal 80, config.port | |
45 | + assert_equal 'http', config.protocol | |
46 | + end | |
47 | + | |
48 | + should "not cache inferred ports" do | |
49 | + config = HoptoadNotifier::Configuration.new | |
50 | + config.secure = false | |
51 | + config.port | |
52 | + config.secure = true | |
53 | + assert_equal 443, config.port | |
54 | + end | |
55 | + | |
56 | + should "allow values to be overwritten" do | |
57 | + assert_config_overridable :proxy_host | |
58 | + assert_config_overridable :proxy_port | |
59 | + assert_config_overridable :proxy_user | |
60 | + assert_config_overridable :proxy_pass | |
61 | + assert_config_overridable :secure | |
62 | + assert_config_overridable :host | |
63 | + assert_config_overridable :port | |
64 | + assert_config_overridable :http_open_timeout | |
65 | + assert_config_overridable :http_read_timeout | |
66 | + assert_config_overridable :project_root | |
67 | + assert_config_overridable :notifier_version | |
68 | + assert_config_overridable :notifier_name | |
69 | + assert_config_overridable :notifier_url | |
70 | + assert_config_overridable :environment_name | |
71 | + assert_config_overridable :development_lookup | |
72 | + assert_config_overridable :logger | |
73 | + end | |
74 | + | |
75 | + should "have an api key" do | |
76 | + assert_config_overridable :api_key | |
77 | + end | |
78 | + | |
79 | + should "act like a hash" do | |
80 | + config = HoptoadNotifier::Configuration.new | |
81 | + hash = config.to_hash | |
82 | + [:api_key, :backtrace_filters, :development_environments, | |
83 | + :environment_name, :host, :http_open_timeout, | |
84 | + :http_read_timeout, :ignore, :ignore_by_filters, :ignore_user_agent, | |
85 | + :notifier_name, :notifier_url, :notifier_version, :params_filters, | |
86 | + :project_root, :port, :protocol, :proxy_host, :proxy_pass, :proxy_port, | |
87 | + :proxy_user, :secure, :development_lookup].each do |option| | |
88 | + assert_equal config[option], hash[option], "Wrong value for #{option}" | |
89 | + end | |
90 | + end | |
91 | + | |
92 | + should "be mergable" do | |
93 | + config = HoptoadNotifier::Configuration.new | |
94 | + hash = config.to_hash | |
95 | + assert_equal hash.merge(:key => 'value'), config.merge(:key => 'value') | |
96 | + end | |
97 | + | |
98 | + should "allow param filters to be appended" do | |
99 | + assert_appends_value :params_filters | |
100 | + end | |
101 | + | |
102 | + should "warn when attempting to read environment filters" do | |
103 | + config = HoptoadNotifier::Configuration.new | |
104 | + config. | |
105 | + expects(:warn). | |
106 | + with(regexp_matches(/deprecated/i)) | |
107 | + assert_equal [], config.environment_filters | |
108 | + end | |
109 | + | |
110 | + should "allow ignored user agents to be appended" do | |
111 | + assert_appends_value :ignore_user_agent | |
112 | + end | |
113 | + | |
114 | + should "allow backtrace filters to be appended" do | |
115 | + assert_appends_value(:backtrace_filters) do |config| | |
116 | + new_filter = lambda {} | |
117 | + config.filter_backtrace(&new_filter) | |
118 | + new_filter | |
119 | + end | |
120 | + end | |
121 | + | |
122 | + should "allow ignore by filters to be appended" do | |
123 | + assert_appends_value(:ignore_by_filters) do |config| | |
124 | + new_filter = lambda {} | |
125 | + config.ignore_by_filter(&new_filter) | |
126 | + new_filter | |
127 | + end | |
128 | + end | |
129 | + | |
130 | + should "allow ignored exceptions to be appended" do | |
131 | + config = HoptoadNotifier::Configuration.new | |
132 | + original_filters = config.ignore.dup | |
133 | + new_filter = 'hello' | |
134 | + config.ignore << new_filter | |
135 | + assert_same_elements original_filters + [new_filter], config.ignore | |
136 | + end | |
137 | + | |
138 | + should "allow ignored exceptions to be replaced" do | |
139 | + assert_replaces(:ignore, :ignore_only=) | |
140 | + end | |
141 | + | |
142 | + should "allow ignored user agents to be replaced" do | |
143 | + assert_replaces(:ignore_user_agent, :ignore_user_agent_only=) | |
144 | + end | |
145 | + | |
146 | + should "use development and test as development environments by default" do | |
147 | + config = HoptoadNotifier::Configuration.new | |
148 | + assert_same_elements %w(development test cucumber), config.development_environments | |
149 | + end | |
150 | + | |
151 | + should "be public in a public environment" do | |
152 | + config = HoptoadNotifier::Configuration.new | |
153 | + config.development_environments = %w(development) | |
154 | + config.environment_name = 'production' | |
155 | + assert config.public? | |
156 | + end | |
157 | + | |
158 | + should "not be public in a development environment" do | |
159 | + config = HoptoadNotifier::Configuration.new | |
160 | + config.development_environments = %w(staging) | |
161 | + config.environment_name = 'staging' | |
162 | + assert !config.public? | |
163 | + end | |
164 | + | |
165 | + should "be public without an environment name" do | |
166 | + config = HoptoadNotifier::Configuration.new | |
167 | + assert config.public? | |
168 | + end | |
169 | + | |
170 | + should "use the assigned logger if set" do | |
171 | + config = HoptoadNotifier::Configuration.new | |
172 | + config.logger = "CUSTOM LOGGER" | |
173 | + assert_equal "CUSTOM LOGGER", config.logger | |
174 | + end | |
175 | + | |
176 | + def assert_config_default(option, default_value, config = nil) | |
177 | + config ||= HoptoadNotifier::Configuration.new | |
178 | + assert_equal default_value, config.send(option) | |
179 | + end | |
180 | + | |
181 | + def assert_config_overridable(option, value = 'a value') | |
182 | + config = HoptoadNotifier::Configuration.new | |
183 | + config.send(:"#{option}=", value) | |
184 | + assert_equal value, config.send(option) | |
185 | + end | |
186 | + | |
187 | + def assert_appends_value(option, &block) | |
188 | + config = HoptoadNotifier::Configuration.new | |
189 | + original_values = config.send(option).dup | |
190 | + block ||= lambda do |config| | |
191 | + new_value = 'hello' | |
192 | + config.send(option) << new_value | |
193 | + new_value | |
194 | + end | |
195 | + new_value = block.call(config) | |
196 | + assert_same_elements original_values + [new_value], config.send(option) | |
197 | + end | |
198 | + | |
199 | + def assert_replaces(option, setter) | |
200 | + config = HoptoadNotifier::Configuration.new | |
201 | + new_value = 'hello' | |
202 | + config.send(setter, [new_value]) | |
203 | + assert_equal [new_value], config.send(option) | |
204 | + config.send(setter, new_value) | |
205 | + assert_equal [new_value], config.send(option) | |
206 | + end | |
207 | + | |
208 | +end | ... | ... |
... | ... | @@ -0,0 +1,239 @@ |
1 | +require 'test/unit' | |
2 | +require 'rubygems' | |
3 | + | |
4 | +gem 'jferris-mocha', '>= 0.9.5.0.1241126838' | |
5 | + | |
6 | +$LOAD_PATH << File.join(File.dirname(__FILE__), *%w[.. vendor ginger lib]) | |
7 | +$LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "..", "lib")) | |
8 | + | |
9 | +require 'shoulda' | |
10 | +require 'mocha' | |
11 | + | |
12 | +require 'ginger' | |
13 | + | |
14 | +require 'action_controller' | |
15 | +require 'action_controller/test_process' | |
16 | +require 'active_record' | |
17 | +require 'active_record/base' | |
18 | +require 'active_support' | |
19 | +require 'nokogiri' | |
20 | +require 'rack' | |
21 | + | |
22 | +require "hoptoad_notifier" | |
23 | + | |
24 | +begin require 'redgreen'; rescue LoadError; end | |
25 | + | |
26 | +module TestMethods | |
27 | + def rescue_action e | |
28 | + raise e | |
29 | + end | |
30 | + | |
31 | + def do_raise | |
32 | + raise "Hoptoad" | |
33 | + end | |
34 | + | |
35 | + def do_not_raise | |
36 | + render :text => "Success" | |
37 | + end | |
38 | + | |
39 | + def do_raise_ignored | |
40 | + raise ActiveRecord::RecordNotFound.new("404") | |
41 | + end | |
42 | + | |
43 | + def do_raise_not_ignored | |
44 | + raise ActiveRecord::StatementInvalid.new("Statement invalid") | |
45 | + end | |
46 | + | |
47 | + def manual_notify | |
48 | + notify_hoptoad(Exception.new) | |
49 | + render :text => "Success" | |
50 | + end | |
51 | + | |
52 | + def manual_notify_ignored | |
53 | + notify_hoptoad(ActiveRecord::RecordNotFound.new("404")) | |
54 | + render :text => "Success" | |
55 | + end | |
56 | +end | |
57 | + | |
58 | +class HoptoadController < ActionController::Base | |
59 | + include TestMethods | |
60 | +end | |
61 | + | |
62 | +class Test::Unit::TestCase | |
63 | + def request(action = nil, method = :get, user_agent = nil, params = {}) | |
64 | + @request = ActionController::TestRequest.new | |
65 | + @request.action = action ? action.to_s : "" | |
66 | + | |
67 | + if user_agent | |
68 | + if @request.respond_to?(:user_agent=) | |
69 | + @request.user_agent = user_agent | |
70 | + else | |
71 | + @request.env["HTTP_USER_AGENT"] = user_agent | |
72 | + end | |
73 | + end | |
74 | + @request.query_parameters = @request.query_parameters.merge(params) | |
75 | + @response = ActionController::TestResponse.new | |
76 | + @controller.process(@request, @response) | |
77 | + end | |
78 | + | |
79 | + # Borrowed from ActiveSupport 2.3.2 | |
80 | + def assert_difference(expression, difference = 1, message = nil, &block) | |
81 | + b = block.send(:binding) | |
82 | + exps = Array.wrap(expression) | |
83 | + before = exps.map { |e| eval(e, b) } | |
84 | + | |
85 | + yield | |
86 | + | |
87 | + exps.each_with_index do |e, i| | |
88 | + error = "#{e.inspect} didn't change by #{difference}" | |
89 | + error = "#{message}.\n#{error}" if message | |
90 | + assert_equal(before[i] + difference, eval(e, b), error) | |
91 | + end | |
92 | + end | |
93 | + | |
94 | + def assert_no_difference(expression, message = nil, &block) | |
95 | + assert_difference expression, 0, message, &block | |
96 | + end | |
97 | + | |
98 | + def stub_sender | |
99 | + stub('sender', :send_to_hoptoad => nil) | |
100 | + end | |
101 | + | |
102 | + def stub_sender! | |
103 | + HoptoadNotifier.sender = stub_sender | |
104 | + end | |
105 | + | |
106 | + def stub_notice | |
107 | + stub('notice', :to_xml => 'some yaml', :ignore? => false) | |
108 | + end | |
109 | + | |
110 | + def stub_notice! | |
111 | + returning stub_notice do |notice| | |
112 | + HoptoadNotifier::Notice.stubs(:new => notice) | |
113 | + end | |
114 | + end | |
115 | + | |
116 | + def create_dummy | |
117 | + HoptoadNotifier::DummySender.new | |
118 | + end | |
119 | + | |
120 | + def reset_config | |
121 | + HoptoadNotifier.configuration = nil | |
122 | + HoptoadNotifier.configure do |config| | |
123 | + config.api_key = 'abc123' | |
124 | + end | |
125 | + end | |
126 | + | |
127 | + def clear_backtrace_filters | |
128 | + HoptoadNotifier.configuration.backtrace_filters.clear | |
129 | + end | |
130 | + | |
131 | + def build_exception | |
132 | + raise | |
133 | + rescue => caught_exception | |
134 | + caught_exception | |
135 | + end | |
136 | + | |
137 | + def build_notice_data(exception = nil) | |
138 | + exception ||= build_exception | |
139 | + { | |
140 | + :api_key => 'abc123', | |
141 | + :error_class => exception.class.name, | |
142 | + :error_message => "#{exception.class.name}: #{exception.message}", | |
143 | + :backtrace => exception.backtrace, | |
144 | + :environment => { 'PATH' => '/bin', 'REQUEST_URI' => '/users/1' }, | |
145 | + :request => { | |
146 | + :params => { 'controller' => 'users', 'action' => 'show', 'id' => '1' }, | |
147 | + :rails_root => '/path/to/application', | |
148 | + :url => "http://test.host/users/1" | |
149 | + }, | |
150 | + :session => { | |
151 | + :key => '123abc', | |
152 | + :data => { 'user_id' => '5', 'flash' => { 'notice' => 'Logged in successfully' } } | |
153 | + } | |
154 | + } | |
155 | + end | |
156 | + | |
157 | + def assert_caught_and_sent | |
158 | + assert !HoptoadNotifier.sender.collected.empty? | |
159 | + end | |
160 | + | |
161 | + def assert_caught_and_not_sent | |
162 | + assert HoptoadNotifier.sender.collected.empty? | |
163 | + end | |
164 | + | |
165 | + def assert_array_starts_with(expected, actual) | |
166 | + assert_respond_to actual, :to_ary | |
167 | + array = actual.to_ary.reverse | |
168 | + expected.reverse.each_with_index do |value, i| | |
169 | + assert_equal value, array[i] | |
170 | + end | |
171 | + end | |
172 | + | |
173 | + def assert_valid_node(document, xpath, content) | |
174 | + nodes = document.xpath(xpath) | |
175 | + assert nodes.any?{|node| node.content == content }, | |
176 | + "Expected xpath #{xpath} to have content #{content}, " + | |
177 | + "but found #{nodes.map { |n| n.content }} in #{nodes.size} matching nodes." + | |
178 | + "Document:\n#{document.to_s}" | |
179 | + end | |
180 | +end | |
181 | + | |
182 | +module DefinesConstants | |
183 | + def setup | |
184 | + @defined_constants = [] | |
185 | + end | |
186 | + | |
187 | + def teardown | |
188 | + @defined_constants.each do |constant| | |
189 | + Object.__send__(:remove_const, constant) | |
190 | + end | |
191 | + end | |
192 | + | |
193 | + def define_constant(name, value) | |
194 | + Object.const_set(name, value) | |
195 | + @defined_constants << name | |
196 | + end | |
197 | +end | |
198 | + | |
199 | +# Also stolen from AS 2.3.2 | |
200 | +class Array | |
201 | + # Wraps the object in an Array unless it's an Array. Converts the | |
202 | + # object to an Array using #to_ary if it implements that. | |
203 | + def self.wrap(object) | |
204 | + case object | |
205 | + when nil | |
206 | + [] | |
207 | + when self | |
208 | + object | |
209 | + else | |
210 | + if object.respond_to?(:to_ary) | |
211 | + object.to_ary | |
212 | + else | |
213 | + [object] | |
214 | + end | |
215 | + end | |
216 | + end | |
217 | + | |
218 | +end | |
219 | + | |
220 | +class CollectingSender | |
221 | + attr_reader :collected | |
222 | + | |
223 | + def initialize | |
224 | + @collected = [] | |
225 | + end | |
226 | + | |
227 | + def send_to_hoptoad(data) | |
228 | + @collected << data | |
229 | + end | |
230 | +end | |
231 | + | |
232 | +class FakeLogger | |
233 | + def info(*args); end | |
234 | + def debug(*args); end | |
235 | + def warn(*args); end | |
236 | + def error(*args); end | |
237 | + def fatal(*args); end | |
238 | +end | |
239 | + | ... | ... |
vendor/gems/hoptoad_notifier-2.2.2/test/hoptoad_tasks_test.rb
0 → 100644
... | ... | @@ -0,0 +1,138 @@ |
1 | +require File.dirname(__FILE__) + '/helper' | |
2 | +require 'rubygems' | |
3 | + | |
4 | +require File.dirname(__FILE__) + '/../lib/hoptoad_tasks' | |
5 | +require 'fakeweb' | |
6 | + | |
7 | +FakeWeb.allow_net_connect = false | |
8 | + | |
9 | +class HoptoadTasksTest < Test::Unit::TestCase | |
10 | + def successful_response(body = "") | |
11 | + response = Net::HTTPSuccess.new('1.2', '200', 'OK') | |
12 | + response.stubs(:body).returns(body) | |
13 | + return response | |
14 | + end | |
15 | + | |
16 | + def unsuccessful_response(body = "") | |
17 | + response = Net::HTTPClientError.new('1.2', '200', 'OK') | |
18 | + response.stubs(:body).returns(body) | |
19 | + return response | |
20 | + end | |
21 | + | |
22 | + context "being quiet" do | |
23 | + setup { HoptoadTasks.stubs(:puts) } | |
24 | + | |
25 | + context "in a configured project" do | |
26 | + setup { HoptoadNotifier.configure { |config| config.api_key = "1234123412341234" } } | |
27 | + | |
28 | + context "on deploy({})" do | |
29 | + setup { @output = HoptoadTasks.deploy({}) } | |
30 | + | |
31 | + before_should "complain about missing rails env" do | |
32 | + HoptoadTasks.expects(:puts).with(regexp_matches(/rails environment/i)) | |
33 | + end | |
34 | + | |
35 | + should "return false" do | |
36 | + assert !@output | |
37 | + end | |
38 | + end | |
39 | + | |
40 | + context "given valid options" do | |
41 | + setup { @options = {:rails_env => "staging"} } | |
42 | + | |
43 | + context "on deploy(options)" do | |
44 | + setup { @output = HoptoadTasks.deploy(@options) } | |
45 | + | |
46 | + before_should "post to http://hoptoadapp.com/deploys.txt" do | |
47 | + URI.stubs(:parse).with('http://hoptoadapp.com/deploys.txt').returns(:uri) | |
48 | + Net::HTTP.expects(:post_form).with(:uri, kind_of(Hash)).returns(successful_response) | |
49 | + end | |
50 | + | |
51 | + before_should "use the project api key" do | |
52 | + Net::HTTP.expects(:post_form). | |
53 | + with(kind_of(URI), has_entries('api_key' => "1234123412341234")). | |
54 | + returns(successful_response) | |
55 | + end | |
56 | + | |
57 | + before_should "use send the rails_env param" do | |
58 | + Net::HTTP.expects(:post_form). | |
59 | + with(kind_of(URI), has_entries("deploy[rails_env]" => "staging")). | |
60 | + returns(successful_response) | |
61 | + end | |
62 | + | |
63 | + [:local_username, :scm_repository, :scm_revision].each do |key| | |
64 | + before_should "use send the #{key} param if it's passed in." do | |
65 | + @options[key] = "value" | |
66 | + Net::HTTP.expects(:post_form). | |
67 | + with(kind_of(URI), has_entries("deploy[#{key}]" => "value")). | |
68 | + returns(successful_response) | |
69 | + end | |
70 | + end | |
71 | + | |
72 | + before_should "use the :api_key param if it's passed in." do | |
73 | + @options[:api_key] = "value" | |
74 | + Net::HTTP.expects(:post_form). | |
75 | + with(kind_of(URI), has_entries("api_key" => "value")). | |
76 | + returns(successful_response) | |
77 | + end | |
78 | + | |
79 | + before_should "puts the response body on success" do | |
80 | + HoptoadTasks.expects(:puts).with("body") | |
81 | + Net::HTTP.expects(:post_form).with(any_parameters).returns(successful_response('body')) | |
82 | + end | |
83 | + | |
84 | + before_should "puts the response body on failure" do | |
85 | + HoptoadTasks.expects(:puts).with("body") | |
86 | + Net::HTTP.expects(:post_form).with(any_parameters).returns(unsuccessful_response('body')) | |
87 | + end | |
88 | + | |
89 | + should "return false on failure", :before => lambda { | |
90 | + Net::HTTP.expects(:post_form).with(any_parameters).returns(unsuccessful_response('body')) | |
91 | + } do | |
92 | + assert !@output | |
93 | + end | |
94 | + | |
95 | + should "return true on success", :before => lambda { | |
96 | + Net::HTTP.expects(:post_form).with(any_parameters).returns(successful_response('body')) | |
97 | + } do | |
98 | + assert @output | |
99 | + end | |
100 | + end | |
101 | + end | |
102 | + end | |
103 | + | |
104 | + context "in a configured project with custom host" do | |
105 | + setup do | |
106 | + HoptoadNotifier.configure do |config| | |
107 | + config.api_key = "1234123412341234" | |
108 | + config.host = "custom.host" | |
109 | + end | |
110 | + end | |
111 | + | |
112 | + context "on deploy(:rails_env => 'staging')" do | |
113 | + setup { @output = HoptoadTasks.deploy(:rails_env => "staging") } | |
114 | + | |
115 | + before_should "post to the custom host" do | |
116 | + URI.stubs(:parse).with('http://custom.host/deploys.txt').returns(:uri) | |
117 | + Net::HTTP.expects(:post_form).with(:uri, kind_of(Hash)).returns(successful_response) | |
118 | + end | |
119 | + end | |
120 | + end | |
121 | + | |
122 | + context "when not configured" do | |
123 | + setup { HoptoadNotifier.configure { |config| config.api_key = "" } } | |
124 | + | |
125 | + context "on deploy(:rails_env => 'staging')" do | |
126 | + setup { @output = HoptoadTasks.deploy(:rails_env => "staging") } | |
127 | + | |
128 | + before_should "complain about missing api key" do | |
129 | + HoptoadTasks.expects(:puts).with(regexp_matches(/api key/i)) | |
130 | + end | |
131 | + | |
132 | + should "return false" do | |
133 | + assert !@output | |
134 | + end | |
135 | + end | |
136 | + end | |
137 | + end | |
138 | +end | ... | ... |
... | ... | @@ -0,0 +1,85 @@ |
1 | +require File.dirname(__FILE__) + '/helper' | |
2 | + | |
3 | +class LoggerTest < Test::Unit::TestCase | |
4 | + def stub_http(response, body = nil) | |
5 | + response.stubs(:body => body) if body | |
6 | + @http = stub(:post => response, | |
7 | + :read_timeout= => nil, | |
8 | + :open_timeout= => nil, | |
9 | + :use_ssl= => nil) | |
10 | + Net::HTTP.stubs(:new).returns(@http) | |
11 | + end | |
12 | + | |
13 | + def send_notice | |
14 | + HoptoadNotifier.sender.send_to_hoptoad('data') | |
15 | + end | |
16 | + | |
17 | + def stub_verbose_log | |
18 | + HoptoadNotifier.stubs(:write_verbose_log) | |
19 | + end | |
20 | + | |
21 | + def assert_logged(expected) | |
22 | + assert_received(HoptoadNotifier, :write_verbose_log) do |expect| | |
23 | + expect.with {|actual| actual =~ expected } | |
24 | + end | |
25 | + end | |
26 | + | |
27 | + def assert_not_logged(expected) | |
28 | + assert_received(HoptoadNotifier, :write_verbose_log) do |expect| | |
29 | + expect.with {|actual| actual =~ expected }.never | |
30 | + end | |
31 | + end | |
32 | + | |
33 | + def configure | |
34 | + HoptoadNotifier.configure { |config| } | |
35 | + end | |
36 | + | |
37 | + should "report that notifier is ready when configured" do | |
38 | + stub_verbose_log | |
39 | + configure | |
40 | + assert_logged /Notifier (.*) ready/ | |
41 | + end | |
42 | + | |
43 | + should "not report that notifier is ready when internally configured" do | |
44 | + stub_verbose_log | |
45 | + HoptoadNotifier.configure(true) { |config| } | |
46 | + assert_not_logged /.*/ | |
47 | + end | |
48 | + | |
49 | + should "print environment info a successful notification without a body" do | |
50 | + reset_config | |
51 | + stub_verbose_log | |
52 | + stub_http(Net::HTTPSuccess) | |
53 | + send_notice | |
54 | + assert_logged /Environment Info:/ | |
55 | + assert_not_logged /Response from Hoptoad:/ | |
56 | + end | |
57 | + | |
58 | + should "print environment info on a failed notification without a body" do | |
59 | + reset_config | |
60 | + stub_verbose_log | |
61 | + stub_http(Net::HTTPError) | |
62 | + send_notice | |
63 | + assert_logged /Environment Info:/ | |
64 | + assert_not_logged /Response from Hoptoad:/ | |
65 | + end | |
66 | + | |
67 | + should "print environment info and response on a success with a body" do | |
68 | + reset_config | |
69 | + stub_verbose_log | |
70 | + stub_http(Net::HTTPSuccess, 'test') | |
71 | + send_notice | |
72 | + assert_logged /Environment Info:/ | |
73 | + assert_logged /Response from Hoptoad:/ | |
74 | + end | |
75 | + | |
76 | + should "print environment info and response on a failure with a body" do | |
77 | + reset_config | |
78 | + stub_verbose_log | |
79 | + stub_http(Net::HTTPError, 'test') | |
80 | + send_notice | |
81 | + assert_logged /Environment Info:/ | |
82 | + assert_logged /Response from Hoptoad:/ | |
83 | + end | |
84 | + | |
85 | +end | ... | ... |
... | ... | @@ -0,0 +1,417 @@ |
1 | +require File.dirname(__FILE__) + '/helper' | |
2 | + | |
3 | +class NoticeTest < Test::Unit::TestCase | |
4 | + | |
5 | + include DefinesConstants | |
6 | + | |
7 | + def configure | |
8 | + returning HoptoadNotifier::Configuration.new do |config| | |
9 | + config.api_key = 'abc123def456' | |
10 | + end | |
11 | + end | |
12 | + | |
13 | + def build_notice(args = {}) | |
14 | + configuration = args.delete(:configuration) || configure | |
15 | + HoptoadNotifier::Notice.new(configuration.merge(args)) | |
16 | + end | |
17 | + | |
18 | + def stub_request(attrs = {}) | |
19 | + stub('request', { :parameters => { 'one' => 'two' }, | |
20 | + :protocol => 'http', | |
21 | + :host => 'some.host', | |
22 | + :request_uri => '/some/uri', | |
23 | + :session => { :to_hash => { 'a' => 'b' } }, | |
24 | + :env => { 'three' => 'four' } }.update(attrs)) | |
25 | + end | |
26 | + | |
27 | + should "set the api key" do | |
28 | + api_key = 'key' | |
29 | + notice = build_notice(:api_key => api_key) | |
30 | + assert_equal api_key, notice.api_key | |
31 | + end | |
32 | + | |
33 | + should "accept a project root" do | |
34 | + project_root = '/path/to/project' | |
35 | + notice = build_notice(:project_root => project_root) | |
36 | + assert_equal project_root, notice.project_root | |
37 | + end | |
38 | + | |
39 | + should "accept a component" do | |
40 | + assert_equal 'users_controller', build_notice(:component => 'users_controller').controller | |
41 | + end | |
42 | + | |
43 | + should "alias the component as controller" do | |
44 | + assert_equal 'users_controller', build_notice(:controller => 'users_controller').component | |
45 | + assert_equal 'users_controller', build_notice(:component => 'users_controller').controller | |
46 | + end | |
47 | + | |
48 | + should "accept a action" do | |
49 | + assert_equal 'index', build_notice(:action => 'index').action | |
50 | + end | |
51 | + | |
52 | + should "accept a url" do | |
53 | + url = 'http://some.host/uri' | |
54 | + notice = build_notice(:url => url) | |
55 | + assert_equal url, notice.url | |
56 | + end | |
57 | + | |
58 | + should "accept a backtrace from an exception or hash" do | |
59 | + array = ["user.rb:34:in `crazy'"] | |
60 | + exception = build_exception | |
61 | + exception.set_backtrace array | |
62 | + backtrace = HoptoadNotifier::Backtrace.parse(array) | |
63 | + notice_from_exception = build_notice(:exception => exception) | |
64 | + | |
65 | + | |
66 | + assert_equal backtrace, | |
67 | + notice_from_exception.backtrace, | |
68 | + "backtrace was not correctly set from an exception" | |
69 | + | |
70 | + notice_from_hash = build_notice(:backtrace => array) | |
71 | + assert_equal backtrace, | |
72 | + notice_from_hash.backtrace, | |
73 | + "backtrace was not correctly set from a hash" | |
74 | + end | |
75 | + | |
76 | + should "pass its backtrace filters for parsing" do | |
77 | + backtrace_array = ['my/file/backtrace:3'] | |
78 | + exception = build_exception | |
79 | + exception.set_backtrace(backtrace_array) | |
80 | + HoptoadNotifier::Backtrace.expects(:parse).with(backtrace_array, {:filters => 'foo'}) | |
81 | + | |
82 | + notice = HoptoadNotifier::Notice.new({:exception => exception, :backtrace_filters => 'foo'}) | |
83 | + end | |
84 | + | |
85 | + should "set the error class from an exception or hash" do | |
86 | + assert_accepts_exception_attribute :error_class do |exception| | |
87 | + exception.class.name | |
88 | + end | |
89 | + end | |
90 | + | |
91 | + should "set the error message from an exception or hash" do | |
92 | + assert_accepts_exception_attribute :error_message do |exception| | |
93 | + "#{exception.class.name}: #{exception.message}" | |
94 | + end | |
95 | + end | |
96 | + | |
97 | + should "accept parameters from a request or hash" do | |
98 | + parameters = { 'one' => 'two' } | |
99 | + notice_from_hash = build_notice(:parameters => parameters) | |
100 | + assert_equal notice_from_hash.parameters, parameters | |
101 | + end | |
102 | + | |
103 | + should "accept session data from a session[:data] hash" do | |
104 | + data = { 'one' => 'two' } | |
105 | + notice = build_notice(:session => { :data => data }) | |
106 | + assert_equal data, notice.session_data | |
107 | + end | |
108 | + | |
109 | + should "accept session data from a session_data hash" do | |
110 | + data = { 'one' => 'two' } | |
111 | + notice = build_notice(:session_data => data) | |
112 | + assert_equal data, notice.session_data | |
113 | + end | |
114 | + | |
115 | + should "accept an environment name" do | |
116 | + assert_equal 'development', build_notice(:environment_name => 'development').environment_name | |
117 | + end | |
118 | + | |
119 | + should "accept CGI data from a hash" do | |
120 | + data = { 'string' => 'value' } | |
121 | + notice = build_notice(:cgi_data => data) | |
122 | + assert_equal data, notice.cgi_data, "should take CGI data from a hash" | |
123 | + end | |
124 | + | |
125 | + should "accept notifier information" do | |
126 | + params = { :notifier_name => 'a name for a notifier', | |
127 | + :notifier_version => '1.0.5', | |
128 | + :notifier_url => 'http://notifiers.r.us/download' } | |
129 | + notice = build_notice(params) | |
130 | + assert_equal params[:notifier_name], notice.notifier_name | |
131 | + assert_equal params[:notifier_version], notice.notifier_version | |
132 | + assert_equal params[:notifier_url], notice.notifier_url | |
133 | + end | |
134 | + | |
135 | + should "set sensible defaults without an exception" do | |
136 | + backtrace = HoptoadNotifier::Backtrace.parse(build_backtrace_array) | |
137 | + notice = build_notice(:backtrace => build_backtrace_array) | |
138 | + | |
139 | + assert_equal 'Notification', notice.error_message | |
140 | + assert_array_starts_with backtrace.lines, notice.backtrace.lines | |
141 | + assert_equal({}, notice.parameters) | |
142 | + assert_equal({}, notice.session_data) | |
143 | + end | |
144 | + | |
145 | + should "use the caller as the backtrace for an exception without a backtrace" do | |
146 | + filters = HoptoadNotifier::Configuration.new.backtrace_filters | |
147 | + backtrace = HoptoadNotifier::Backtrace.parse(caller, :filters => filters) | |
148 | + notice = build_notice(:exception => StandardError.new('error'), :backtrace => nil) | |
149 | + | |
150 | + assert_array_starts_with backtrace.lines, notice.backtrace.lines | |
151 | + end | |
152 | + | |
153 | + should "convert unserializable objects to strings" do | |
154 | + assert_serializes_hash(:parameters) | |
155 | + assert_serializes_hash(:cgi_data) | |
156 | + assert_serializes_hash(:session_data) | |
157 | + end | |
158 | + | |
159 | + should "filter parameters" do | |
160 | + assert_filters_hash(:parameters) | |
161 | + end | |
162 | + | |
163 | + should "filter cgi data" do | |
164 | + assert_filters_hash(:cgi_data) | |
165 | + end | |
166 | + | |
167 | + context "a Notice turned into XML" do | |
168 | + setup do | |
169 | + HoptoadNotifier.configure do |config| | |
170 | + config.api_key = "1234567890" | |
171 | + end | |
172 | + | |
173 | + @exception = build_exception | |
174 | + | |
175 | + @notice = build_notice({ | |
176 | + :notifier_name => 'a name', | |
177 | + :notifier_version => '1.2.3', | |
178 | + :notifier_url => 'http://some.url/path', | |
179 | + :exception => @exception, | |
180 | + :controller => "controller", | |
181 | + :action => "action", | |
182 | + :url => "http://url.com", | |
183 | + :parameters => { "paramskey" => "paramsvalue", | |
184 | + "nestparentkey" => { "nestkey" => "nestvalue" } }, | |
185 | + :session_data => { "sessionkey" => "sessionvalue" }, | |
186 | + :cgi_data => { "cgikey" => "cgivalue" }, | |
187 | + :project_root => "RAILS_ROOT", | |
188 | + :environment_name => "RAILS_ENV" | |
189 | + }) | |
190 | + | |
191 | + @xml = @notice.to_xml | |
192 | + | |
193 | + @document = Nokogiri::XML::Document.parse(@xml) | |
194 | + end | |
195 | + | |
196 | + should "validate against the XML schema" do | |
197 | + assert_valid_notice_document @document | |
198 | + end | |
199 | + | |
200 | + should "serialize a Notice to XML when sent #to_xml" do | |
201 | + assert_valid_node(@document, "//api-key", @notice.api_key) | |
202 | + | |
203 | + assert_valid_node(@document, "//notifier/name", @notice.notifier_name) | |
204 | + assert_valid_node(@document, "//notifier/version", @notice.notifier_version) | |
205 | + assert_valid_node(@document, "//notifier/url", @notice.notifier_url) | |
206 | + | |
207 | + assert_valid_node(@document, "//error/class", @notice.error_class) | |
208 | + assert_valid_node(@document, "//error/message", @notice.error_message) | |
209 | + | |
210 | + assert_valid_node(@document, "//error/backtrace/line/@number", @notice.backtrace.lines.first.number) | |
211 | + assert_valid_node(@document, "//error/backtrace/line/@file", @notice.backtrace.lines.first.file) | |
212 | + assert_valid_node(@document, "//error/backtrace/line/@method", @notice.backtrace.lines.first.method) | |
213 | + | |
214 | + assert_valid_node(@document, "//request/url", @notice.url) | |
215 | + assert_valid_node(@document, "//request/component", @notice.controller) | |
216 | + assert_valid_node(@document, "//request/action", @notice.action) | |
217 | + | |
218 | + assert_valid_node(@document, "//request/params/var/@key", "paramskey") | |
219 | + assert_valid_node(@document, "//request/params/var", "paramsvalue") | |
220 | + assert_valid_node(@document, "//request/params/var/@key", "nestparentkey") | |
221 | + assert_valid_node(@document, "//request/params/var/var/@key", "nestkey") | |
222 | + assert_valid_node(@document, "//request/params/var/var", "nestvalue") | |
223 | + assert_valid_node(@document, "//request/session/var/@key", "sessionkey") | |
224 | + assert_valid_node(@document, "//request/session/var", "sessionvalue") | |
225 | + assert_valid_node(@document, "//request/cgi-data/var/@key", "cgikey") | |
226 | + assert_valid_node(@document, "//request/cgi-data/var", "cgivalue") | |
227 | + | |
228 | + assert_valid_node(@document, "//server-environment/project-root", "RAILS_ROOT") | |
229 | + assert_valid_node(@document, "//server-environment/environment-name", "RAILS_ENV") | |
230 | + end | |
231 | + end | |
232 | + | |
233 | + should "not send empty request data" do | |
234 | + notice = build_notice | |
235 | + assert_nil notice.url | |
236 | + assert_nil notice.controller | |
237 | + assert_nil notice.action | |
238 | + | |
239 | + xml = notice.to_xml | |
240 | + document = Nokogiri::XML.parse(xml) | |
241 | + assert_nil document.at('//request/url') | |
242 | + assert_nil document.at('//request/component') | |
243 | + assert_nil document.at('//request/action') | |
244 | + | |
245 | + assert_valid_notice_document document | |
246 | + end | |
247 | + | |
248 | + %w(url controller action).each do |var| | |
249 | + should "send a request if #{var} is present" do | |
250 | + notice = build_notice(var.to_sym => 'value') | |
251 | + xml = notice.to_xml | |
252 | + document = Nokogiri::XML.parse(xml) | |
253 | + assert_not_nil document.at('//request') | |
254 | + end | |
255 | + end | |
256 | + | |
257 | + %w(parameters cgi_data session_data).each do |var| | |
258 | + should "send a request if #{var} is present" do | |
259 | + notice = build_notice(var.to_sym => { 'key' => 'value' }) | |
260 | + xml = notice.to_xml | |
261 | + document = Nokogiri::XML.parse(xml) | |
262 | + assert_not_nil document.at('//request') | |
263 | + end | |
264 | + end | |
265 | + | |
266 | + should "not ignore an exception not matching ignore filters" do | |
267 | + notice = build_notice(:error_class => 'ArgumentError', | |
268 | + :ignore => ['Argument'], | |
269 | + :ignore_by_filters => [lambda { |notice| false }]) | |
270 | + assert !notice.ignore? | |
271 | + end | |
272 | + | |
273 | + should "ignore an exception with a matching error class" do | |
274 | + notice = build_notice(:error_class => 'ArgumentError', | |
275 | + :ignore => [ArgumentError]) | |
276 | + assert notice.ignore? | |
277 | + end | |
278 | + | |
279 | + should "ignore an exception with a matching error class name" do | |
280 | + notice = build_notice(:error_class => 'ArgumentError', | |
281 | + :ignore => ['ArgumentError']) | |
282 | + assert notice.ignore? | |
283 | + end | |
284 | + | |
285 | + should "ignore an exception with a matching filter" do | |
286 | + filter = lambda {|notice| notice.error_class == 'ArgumentError' } | |
287 | + notice = build_notice(:error_class => 'ArgumentError', | |
288 | + :ignore_by_filters => [filter]) | |
289 | + assert notice.ignore? | |
290 | + end | |
291 | + | |
292 | + should "not raise without an ignore list" do | |
293 | + notice = build_notice(:ignore => nil, :ignore_by_filters => nil) | |
294 | + assert_nothing_raised do | |
295 | + notice.ignore? | |
296 | + end | |
297 | + end | |
298 | + | |
299 | + should "ignore RecordNotFound error by default" do | |
300 | + notice = build_notice(:error_class => 'ActiveRecord::RecordNotFound') | |
301 | + assert notice.ignore? | |
302 | + end | |
303 | + | |
304 | + should "ignore RoutingError error by default" do | |
305 | + notice = build_notice(:error_class => 'ActionController::RoutingError') | |
306 | + assert notice.ignore? | |
307 | + end | |
308 | + | |
309 | + should "ignore InvalidAuthenticityToken error by default" do | |
310 | + notice = build_notice(:error_class => 'ActionController::InvalidAuthenticityToken') | |
311 | + assert notice.ignore? | |
312 | + end | |
313 | + | |
314 | + should "ignore TamperedWithCookie error by default" do | |
315 | + notice = build_notice(:error_class => 'CGI::Session::CookieStore::TamperedWithCookie') | |
316 | + assert notice.ignore? | |
317 | + end | |
318 | + | |
319 | + should "ignore UnknownAction error by default" do | |
320 | + notice = build_notice(:error_class => 'ActionController::UnknownAction') | |
321 | + assert notice.ignore? | |
322 | + end | |
323 | + | |
324 | + should "act like a hash" do | |
325 | + notice = build_notice(:error_message => 'some message') | |
326 | + assert_equal notice.error_message, notice[:error_message] | |
327 | + end | |
328 | + | |
329 | + should "return params on notice[:request][:params]" do | |
330 | + params = { 'one' => 'two' } | |
331 | + notice = build_notice(:parameters => params) | |
332 | + assert_equal params, notice[:request][:params] | |
333 | + end | |
334 | + | |
335 | + should "ensure #to_hash is called on objects that support it" do | |
336 | + assert_nothing_raised do | |
337 | + build_notice(:session => { :object => stub(:to_hash => {}) }) | |
338 | + end | |
339 | + end | |
340 | + | |
341 | + should "extract data from a rack environment hash" do | |
342 | + # TODO: extract session data | |
343 | + # TODO: extract controller | |
344 | + # TODO: extract action | |
345 | + url = "https://subdomain.happylane.com:100/test/file.rb?var=value&var2=value2" | |
346 | + parameters = { 'var' => 'value', 'var2' => 'value2' } | |
347 | + env = Rack::MockRequest.env_for(url) | |
348 | + | |
349 | + notice = build_notice(:rack_env => env) | |
350 | + | |
351 | + assert_equal url, notice.url | |
352 | + assert_equal parameters, notice.parameters | |
353 | + assert_equal 'GET', notice.cgi_data['REQUEST_METHOD'] | |
354 | + end | |
355 | + | |
356 | + def assert_accepts_exception_attribute(attribute, args = {}, &block) | |
357 | + exception = build_exception | |
358 | + block ||= lambda { exception.send(attribute) } | |
359 | + value = block.call(exception) | |
360 | + | |
361 | + notice_from_exception = build_notice(args.merge(:exception => exception)) | |
362 | + | |
363 | + assert_equal notice_from_exception.send(attribute), | |
364 | + value, | |
365 | + "#{attribute} was not correctly set from an exception" | |
366 | + | |
367 | + notice_from_hash = build_notice(args.merge(attribute => value)) | |
368 | + assert_equal notice_from_hash.send(attribute), | |
369 | + value, | |
370 | + "#{attribute} was not correctly set from a hash" | |
371 | + end | |
372 | + | |
373 | + def assert_serializes_hash(attribute) | |
374 | + [File.open(__FILE__), Proc.new { puts "boo!" }, Module.new].each do |object| | |
375 | + hash = { | |
376 | + :strange_object => object, | |
377 | + :sub_hash => { | |
378 | + :sub_object => object | |
379 | + }, | |
380 | + :array => [object] | |
381 | + } | |
382 | + notice = build_notice(attribute => hash) | |
383 | + hash = notice.send(attribute) | |
384 | + assert_equal object.to_s, hash[:strange_object], "objects should be serialized" | |
385 | + assert_kind_of Hash, hash[:sub_hash], "subhashes should be kept" | |
386 | + assert_equal object.to_s, hash[:sub_hash][:sub_object], "subhash members should be serialized" | |
387 | + assert_kind_of Array, hash[:array], "arrays should be kept" | |
388 | + assert_equal object.to_s, hash[:array].first, "array members should be serialized" | |
389 | + end | |
390 | + end | |
391 | + | |
392 | + def assert_valid_notice_document(document) | |
393 | + xsd_path = File.join(File.dirname(__FILE__), "hoptoad_2_0.xsd") | |
394 | + schema = Nokogiri::XML::Schema.new(IO.read(xsd_path)) | |
395 | + errors = schema.validate(document) | |
396 | + assert errors.empty?, errors.collect{|e| e.message }.join | |
397 | + end | |
398 | + | |
399 | + def assert_filters_hash(attribute) | |
400 | + filters = %w(abc def) | |
401 | + original = { 'abc' => "123", 'def' => "456", 'ghi' => "789", 'nested' => { 'abc' => '100' } } | |
402 | + filtered = { 'abc' => "[FILTERED]", | |
403 | + 'def' => "[FILTERED]", | |
404 | + 'ghi' => "789", | |
405 | + 'nested' => { 'abc' => '[FILTERED]' } } | |
406 | + | |
407 | + notice = build_notice(:params_filters => filters, attribute => original) | |
408 | + | |
409 | + assert_equal(filtered, | |
410 | + notice.send(attribute)) | |
411 | + end | |
412 | + | |
413 | + def build_backtrace_array | |
414 | + ["app/models/user.rb:13:in `magic'", | |
415 | + "app/controllers/users_controller.rb:8:in `index'"] | |
416 | + end | |
417 | +end | ... | ... |
vendor/gems/hoptoad_notifier-2.2.2/test/notifier_test.rb
0 → 100644
... | ... | @@ -0,0 +1,222 @@ |
1 | +require File.dirname(__FILE__) + '/helper' | |
2 | + | |
3 | +class NotifierTest < Test::Unit::TestCase | |
4 | + | |
5 | + class OriginalException < Exception | |
6 | + end | |
7 | + | |
8 | + class ContinuedException < Exception | |
9 | + end | |
10 | + | |
11 | + include DefinesConstants | |
12 | + | |
13 | + def setup | |
14 | + super | |
15 | + reset_config | |
16 | + end | |
17 | + | |
18 | + def assert_sent(notice, notice_args) | |
19 | + assert_received(HoptoadNotifier::Notice, :new) {|expect| expect.with(has_entries(notice_args)) } | |
20 | + assert_received(notice, :to_xml) | |
21 | + assert_received(HoptoadNotifier.sender, :send_to_hoptoad) {|expect| expect.with(notice.to_xml) } | |
22 | + end | |
23 | + | |
24 | + def set_public_env | |
25 | + HoptoadNotifier.configure { |config| config.environment_name = 'production' } | |
26 | + end | |
27 | + | |
28 | + def set_development_env | |
29 | + HoptoadNotifier.configure { |config| config.environment_name = 'development' } | |
30 | + end | |
31 | + | |
32 | + should "yield and save a configuration when configuring" do | |
33 | + yielded_configuration = nil | |
34 | + HoptoadNotifier.configure do |config| | |
35 | + yielded_configuration = config | |
36 | + end | |
37 | + | |
38 | + assert_kind_of HoptoadNotifier::Configuration, yielded_configuration | |
39 | + assert_equal yielded_configuration, HoptoadNotifier.configuration | |
40 | + end | |
41 | + | |
42 | + should "not remove existing config options when configuring twice" do | |
43 | + first_config = nil | |
44 | + HoptoadNotifier.configure do |config| | |
45 | + first_config = config | |
46 | + end | |
47 | + HoptoadNotifier.configure do |config| | |
48 | + assert_equal first_config, config | |
49 | + end | |
50 | + end | |
51 | + | |
52 | + should "configure the sender" do | |
53 | + sender = stub_sender | |
54 | + HoptoadNotifier::Sender.stubs(:new => sender) | |
55 | + configuration = nil | |
56 | + | |
57 | + HoptoadNotifier.configure { |yielded_config| configuration = yielded_config } | |
58 | + | |
59 | + assert_received(HoptoadNotifier::Sender, :new) { |expect| expect.with(configuration) } | |
60 | + assert_equal sender, HoptoadNotifier.sender | |
61 | + end | |
62 | + | |
63 | + should "create and send a notice for an exception" do | |
64 | + set_public_env | |
65 | + exception = build_exception | |
66 | + stub_sender! | |
67 | + notice = stub_notice! | |
68 | + | |
69 | + HoptoadNotifier.notify(exception) | |
70 | + | |
71 | + assert_sent notice, :exception => exception | |
72 | + end | |
73 | + | |
74 | + should "create and send a notice for a hash" do | |
75 | + set_public_env | |
76 | + notice = stub_notice! | |
77 | + notice_args = { :error_message => 'uh oh' } | |
78 | + stub_sender! | |
79 | + | |
80 | + HoptoadNotifier.notify(notice_args) | |
81 | + | |
82 | + assert_sent(notice, notice_args) | |
83 | + end | |
84 | + | |
85 | + should "create and sent a notice for an exception and hash" do | |
86 | + set_public_env | |
87 | + exception = build_exception | |
88 | + notice = stub_notice! | |
89 | + notice_args = { :error_message => 'uh oh' } | |
90 | + stub_sender! | |
91 | + | |
92 | + HoptoadNotifier.notify(exception, notice_args) | |
93 | + | |
94 | + assert_sent(notice, notice_args.merge(:exception => exception)) | |
95 | + end | |
96 | + | |
97 | + should "not create a notice in a development environment" do | |
98 | + set_development_env | |
99 | + sender = stub_sender! | |
100 | + | |
101 | + HoptoadNotifier.notify(build_exception) | |
102 | + HoptoadNotifier.notify_or_ignore(build_exception) | |
103 | + | |
104 | + assert_received(sender, :send_to_hoptoad) {|expect| expect.never } | |
105 | + end | |
106 | + | |
107 | + should "not deliver an ignored exception when notifying implicitly" do | |
108 | + set_public_env | |
109 | + exception = build_exception | |
110 | + sender = stub_sender! | |
111 | + notice = stub_notice! | |
112 | + notice.stubs(:ignore? => true) | |
113 | + | |
114 | + HoptoadNotifier.notify_or_ignore(exception) | |
115 | + | |
116 | + assert_received(sender, :send_to_hoptoad) {|expect| expect.never } | |
117 | + end | |
118 | + | |
119 | + should "deliver an ignored exception when notifying manually" do | |
120 | + set_public_env | |
121 | + exception = build_exception | |
122 | + sender = stub_sender! | |
123 | + notice = stub_notice! | |
124 | + notice.stubs(:ignore? => true) | |
125 | + | |
126 | + HoptoadNotifier.notify(exception) | |
127 | + | |
128 | + assert_sent(notice, :exception => exception) | |
129 | + end | |
130 | + | |
131 | + should "pass config to created notices" do | |
132 | + exception = build_exception | |
133 | + config_opts = { 'one' => 'two', 'three' => 'four' } | |
134 | + notice = stub_notice! | |
135 | + stub_sender! | |
136 | + HoptoadNotifier.configuration = stub('config', :merge => config_opts, :public? => true) | |
137 | + | |
138 | + HoptoadNotifier.notify(exception) | |
139 | + | |
140 | + assert_received(HoptoadNotifier::Notice, :new) do |expect| | |
141 | + expect.with(has_entries(config_opts)) | |
142 | + end | |
143 | + end | |
144 | + | |
145 | + context "building notice JSON for an exception" do | |
146 | + setup do | |
147 | + @params = { :controller => "users", :action => "create" } | |
148 | + @exception = build_exception | |
149 | + @hash = HoptoadNotifier.build_lookup_hash_for(@exception, @params) | |
150 | + end | |
151 | + | |
152 | + should "set action" do | |
153 | + assert_equal @params[:action], @hash[:action] | |
154 | + end | |
155 | + | |
156 | + should "set controller" do | |
157 | + assert_equal @params[:controller], @hash[:component] | |
158 | + end | |
159 | + | |
160 | + should "set line number" do | |
161 | + assert @hash[:line_number] =~ /\d+/ | |
162 | + end | |
163 | + | |
164 | + should "set file" do | |
165 | + assert_match /test\/helper\.rb$/, @hash[:file] | |
166 | + end | |
167 | + | |
168 | + should "set rails_env to production" do | |
169 | + assert_equal 'production', @hash[:environment_name] | |
170 | + end | |
171 | + | |
172 | + should "set error class" do | |
173 | + assert_equal 'RuntimeError', @hash[:error_class] | |
174 | + end | |
175 | + | |
176 | + should "not set file or line number with no backtrace" do | |
177 | + @exception.stubs(:backtrace).returns([]) | |
178 | + | |
179 | + @hash = HoptoadNotifier.build_lookup_hash_for(@exception) | |
180 | + | |
181 | + assert_nil @hash[:line_number] | |
182 | + assert_nil @hash[:file] | |
183 | + end | |
184 | + | |
185 | + should "not set action or controller when not provided" do | |
186 | + @hash = HoptoadNotifier.build_lookup_hash_for(@exception) | |
187 | + | |
188 | + assert_nil @hash[:action] | |
189 | + assert_nil @hash[:controller] | |
190 | + end | |
191 | + | |
192 | + context "when an exception that provides #original_exception is raised" do | |
193 | + setup do | |
194 | + @exception.stubs(:original_exception).returns(begin | |
195 | + raise NotifierTest::OriginalException.new | |
196 | + rescue Exception => e | |
197 | + e | |
198 | + end) | |
199 | + end | |
200 | + | |
201 | + should "unwrap exceptions that provide #original_exception" do | |
202 | + @hash = HoptoadNotifier.build_lookup_hash_for(@exception) | |
203 | + assert_equal "NotifierTest::OriginalException", @hash[:error_class] | |
204 | + end | |
205 | + end | |
206 | + | |
207 | + context "when an exception that provides #continued_exception is raised" do | |
208 | + setup do | |
209 | + @exception.stubs(:continued_exception).returns(begin | |
210 | + raise NotifierTest::ContinuedException.new | |
211 | + rescue Exception => e | |
212 | + e | |
213 | + end) | |
214 | + end | |
215 | + | |
216 | + should "unwrap exceptions that provide #continued_exception" do | |
217 | + @hash = HoptoadNotifier.build_lookup_hash_for(@exception) | |
218 | + assert_equal "NotifierTest::ContinuedException", @hash[:error_class] | |
219 | + end | |
220 | + end | |
221 | + end | |
222 | +end | ... | ... |
... | ... | @@ -0,0 +1,58 @@ |
1 | +require File.dirname(__FILE__) + '/helper' | |
2 | + | |
3 | +class RackTest < Test::Unit::TestCase | |
4 | + | |
5 | + should "call the upstream app with the environment" do | |
6 | + environment = { 'key' => 'value' } | |
7 | + app = lambda { |env| ['response', {}, env] } | |
8 | + stack = HoptoadNotifier::Rack.new(app) | |
9 | + | |
10 | + response = stack.call(environment) | |
11 | + | |
12 | + assert_equal ['response', {}, environment], response | |
13 | + end | |
14 | + | |
15 | + should "deliver an exception raised while calling an upstream app" do | |
16 | + HoptoadNotifier.stubs(:notify_or_ignore) | |
17 | + | |
18 | + exception = build_exception | |
19 | + environment = { 'key' => 'value' } | |
20 | + app = lambda do |env| | |
21 | + raise exception | |
22 | + end | |
23 | + | |
24 | + begin | |
25 | + stack = HoptoadNotifier::Rack.new(app) | |
26 | + stack.call(environment) | |
27 | + rescue Exception => raised | |
28 | + assert_equal exception, raised | |
29 | + else | |
30 | + flunk "Didn't raise an exception" | |
31 | + end | |
32 | + | |
33 | + assert_received(HoptoadNotifier, :notify_or_ignore) do |expect| | |
34 | + expect.with(exception, :rack_env => environment) | |
35 | + end | |
36 | + end | |
37 | + | |
38 | + should "deliver an exception in rack.exception" do | |
39 | + HoptoadNotifier.stubs(:notify_or_ignore) | |
40 | + exception = build_exception | |
41 | + environment = { 'key' => 'value' } | |
42 | + | |
43 | + response = [200, {}, ['okay']] | |
44 | + app = lambda do |env| | |
45 | + env['rack.exception'] = exception | |
46 | + response | |
47 | + end | |
48 | + stack = HoptoadNotifier::Rack.new(app) | |
49 | + | |
50 | + actual_response = stack.call(environment) | |
51 | + | |
52 | + assert_equal response, actual_response | |
53 | + assert_received(HoptoadNotifier, :notify_or_ignore) do |expect| | |
54 | + expect.with(exception, :rack_env => environment) | |
55 | + end | |
56 | + end | |
57 | + | |
58 | +end | ... | ... |
vendor/gems/hoptoad_notifier-2.2.2/test/rails_initializer_test.rb
0 → 100644
... | ... | @@ -0,0 +1,36 @@ |
1 | +require File.dirname(__FILE__) + '/helper' | |
2 | + | |
3 | +require 'hoptoad_notifier/rails' | |
4 | + | |
5 | +class RailsInitializerTest < Test::Unit::TestCase | |
6 | + include DefinesConstants | |
7 | + | |
8 | + should "trigger use of Rails' logger if logger isn't set and Rails' logger exists" do | |
9 | + rails = Module.new do | |
10 | + def self.logger | |
11 | + "RAILS LOGGER" | |
12 | + end | |
13 | + end | |
14 | + define_constant("Rails", rails) | |
15 | + HoptoadNotifier::Rails.initialize | |
16 | + assert_equal "RAILS LOGGER", HoptoadNotifier.logger | |
17 | + end | |
18 | + | |
19 | + should "trigger use of Rails' default logger if logger isn't set and Rails.logger doesn't exist" do | |
20 | + define_constant("RAILS_DEFAULT_LOGGER", "RAILS DEFAULT LOGGER") | |
21 | + | |
22 | + HoptoadNotifier::Rails.initialize | |
23 | + assert_equal "RAILS DEFAULT LOGGER", HoptoadNotifier.logger | |
24 | + end | |
25 | + | |
26 | + should "allow overriding of the logger if already assigned" do | |
27 | + define_constant("RAILS_DEFAULT_LOGGER", "RAILS DEFAULT LOGGER") | |
28 | + HoptoadNotifier::Rails.initialize | |
29 | + | |
30 | + HoptoadNotifier.configure(true) do |config| | |
31 | + config.logger = "OVERRIDDEN LOGGER" | |
32 | + end | |
33 | + | |
34 | + assert_equal "OVERRIDDEN LOGGER", HoptoadNotifier.logger | |
35 | + end | |
36 | +end | ... | ... |
... | ... | @@ -0,0 +1,123 @@ |
1 | +require File.dirname(__FILE__) + '/helper' | |
2 | + | |
3 | +class SenderTest < Test::Unit::TestCase | |
4 | + | |
5 | + def setup | |
6 | + reset_config | |
7 | + end | |
8 | + | |
9 | + def build_sender(opts = {}) | |
10 | + config = HoptoadNotifier::Configuration.new | |
11 | + opts.each {|opt, value| config.send(:"#{opt}=", value) } | |
12 | + HoptoadNotifier::Sender.new(config) | |
13 | + end | |
14 | + | |
15 | + def send_exception(args = {}) | |
16 | + notice = args.delete(:notice) || build_notice_data | |
17 | + sender = args.delete(:sender) || build_sender(args) | |
18 | + sender.send_to_hoptoad(notice) | |
19 | + sender | |
20 | + end | |
21 | + | |
22 | + def stub_http | |
23 | + response = stub(:body => 'body') | |
24 | + http = stub(:post => response, | |
25 | + :read_timeout= => nil, | |
26 | + :open_timeout= => nil, | |
27 | + :use_ssl= => nil) | |
28 | + Net::HTTP.stubs(:new => http) | |
29 | + http | |
30 | + end | |
31 | + | |
32 | + should "post to Hoptoad when using an HTTP proxy" do | |
33 | + response = stub(:body => 'body') | |
34 | + http = stub(:post => response, | |
35 | + :read_timeout= => nil, | |
36 | + :open_timeout= => nil, | |
37 | + :use_ssl= => nil) | |
38 | + proxy = stub(:new => http) | |
39 | + Net::HTTP.stubs(:Proxy => proxy) | |
40 | + | |
41 | + url = "http://hoptoadapp.com:80#{HoptoadNotifier::Sender::NOTICES_URI}" | |
42 | + uri = URI.parse(url) | |
43 | + | |
44 | + proxy_host = 'some.host' | |
45 | + proxy_port = 88 | |
46 | + proxy_user = 'login' | |
47 | + proxy_pass = 'passwd' | |
48 | + | |
49 | + send_exception(:proxy_host => proxy_host, | |
50 | + :proxy_port => proxy_port, | |
51 | + :proxy_user => proxy_user, | |
52 | + :proxy_pass => proxy_pass) | |
53 | + assert_received(http, :post) do |expect| | |
54 | + expect.with(uri.path, anything, HoptoadNotifier::HEADERS) | |
55 | + end | |
56 | + assert_received(Net::HTTP, :Proxy) do |expect| | |
57 | + expect.with(proxy_host, proxy_port, proxy_user, proxy_pass) | |
58 | + end | |
59 | + end | |
60 | + | |
61 | + should "post to the right url for non-ssl" do | |
62 | + http = stub_http | |
63 | + url = "http://hoptoadapp.com:80#{HoptoadNotifier::Sender::NOTICES_URI}" | |
64 | + uri = URI.parse(url) | |
65 | + send_exception(:secure => false) | |
66 | + assert_received(http, :post) {|expect| expect.with(uri.path, anything, HoptoadNotifier::HEADERS) } | |
67 | + end | |
68 | + | |
69 | + should "post to the right path for ssl" do | |
70 | + http = stub_http | |
71 | + send_exception(:secure => true) | |
72 | + assert_received(http, :post) {|expect| expect.with(HoptoadNotifier::Sender::NOTICES_URI, anything, HoptoadNotifier::HEADERS) } | |
73 | + end | |
74 | + | |
75 | + should "default the open timeout to 2 seconds" do | |
76 | + http = stub_http | |
77 | + send_exception | |
78 | + assert_received(http, :open_timeout=) {|expect| expect.with(2) } | |
79 | + end | |
80 | + | |
81 | + should "default the read timeout to 5 seconds" do | |
82 | + http = stub_http | |
83 | + send_exception | |
84 | + assert_received(http, :read_timeout=) {|expect| expect.with(5) } | |
85 | + end | |
86 | + | |
87 | + should "allow override of the open timeout" do | |
88 | + http = stub_http | |
89 | + send_exception(:http_open_timeout => 4) | |
90 | + assert_received(http, :open_timeout=) {|expect| expect.with(4) } | |
91 | + end | |
92 | + | |
93 | + should "allow override of the read timeout" do | |
94 | + http = stub_http | |
95 | + send_exception(:http_read_timeout => 10) | |
96 | + assert_received(http, :read_timeout=) {|expect| expect.with(10) } | |
97 | + end | |
98 | + | |
99 | + should "connect to the right port for ssl" do | |
100 | + stub_http | |
101 | + send_exception(:secure => true) | |
102 | + assert_received(Net::HTTP, :new) {|expect| expect.with("hoptoadapp.com", 443) } | |
103 | + end | |
104 | + | |
105 | + should "connect to the right port for non-ssl" do | |
106 | + stub_http | |
107 | + send_exception(:secure => false) | |
108 | + assert_received(Net::HTTP, :new) {|expect| expect.with("hoptoadapp.com", 80) } | |
109 | + end | |
110 | + | |
111 | + should "use ssl if secure" do | |
112 | + stub_http | |
113 | + send_exception(:secure => true, :host => 'example.org') | |
114 | + assert_received(Net::HTTP, :new) {|expect| expect.with('example.org', 443) } | |
115 | + end | |
116 | + | |
117 | + should "not use ssl if not secure" do | |
118 | + stub_http | |
119 | + send_exception(:secure => false, :host => 'example.org') | |
120 | + assert_received(Net::HTTP, :new) {|expect| expect.with('example.org', 80) } | |
121 | + end | |
122 | + | |
123 | +end | ... | ... |