gbuesing-toadhopper 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ /pkg
2
+ /.yardoc
3
+ /vendor
4
+ /bin
5
+ /doc
6
+ /toadhopper.gemspec
data/.yardopts ADDED
@@ -0,0 +1,5 @@
1
+ --no-private
2
+ -r
3
+ README.md
4
+ -
5
+ LICENSE
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2009 Tim Lucas
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,37 @@
1
+ A base library for [Hoptoad](http://www.hoptoadapp.com/) error reporting.
2
+
3
+ Toadhopper can be used to report plain old Ruby exceptions, or to build a framework-specific gem such as [toadhopper-sinatra](http://github.com/toolmantim/toadhopper-sinatra).
4
+
5
+ begin
6
+ raise "Kaboom!"
7
+ rescue => e
8
+ require 'toadhopper'
9
+ ToadHopper("YOURAPIKEY").post!(e)
10
+ end
11
+
12
+ You can install it via rubygems:
13
+
14
+ gem install toadhopper
15
+
16
+ ## Development
17
+
18
+ Firstly, `gem install bundler`, then:
19
+
20
+ % git clone git://github.com/toolmantim/toadhopper.git
21
+ % cd toadhopper
22
+ % gem bundle
23
+ % bin/rake test
24
+
25
+ If you set a `HOPTOAD_API_KEY` environment variable it'll test actually posting to the Hoptoad API. For example:
26
+
27
+ % bin/rake test HOPTOAD_API_KEY=abc123
28
+
29
+ To generate the docs:
30
+
31
+ % bin/yardoc
32
+
33
+ ## Contributors
34
+
35
+ * [Tim Lucas](http://github.com/toolmantim)
36
+ * [Samuel Tesla](http://github.com/stesla)
37
+ * [atmos](http://github.com/atmos)
data/Rakefile ADDED
@@ -0,0 +1,22 @@
1
+ require 'rake/testtask'
2
+
3
+ Rake::TestTask.new do |t|
4
+ t.libs << "test"
5
+ t.test_files = FileList['test/test*.rb']
6
+ t.verbose = true
7
+ end
8
+ task :default => :test
9
+
10
+ begin
11
+ require 'jeweler'
12
+ Jeweler::Tasks.new do |gemspec|
13
+ gemspec.name = "gbuesing-toadhopper"
14
+ gemspec.summary = "Post error notifications to Hoptoad"
15
+ gemspec.description = "Fork of toolmantim-toadhopper, with Haml and Nokogiri dependencies removed"
16
+ gemspec.email = "gbuesing@gmail.com"
17
+ gemspec.homepage = "http://github.com/gbuesing/toadhopper"
18
+ gemspec.authors = ["Geoff Buesing"]
19
+ end
20
+ rescue LoadError
21
+ puts "Jeweler not available. Install it with: gem install jeweler"
22
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,55 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{gbuesing-toadhopper}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Geoff Buesing"]
12
+ s.date = %q{2009-12-29}
13
+ s.description = %q{Fork of toolmantim-toadhopper, with Haml and Nokogiri dependencies removed}
14
+ s.email = %q{gbuesing@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.md"
18
+ ]
19
+ s.files = [
20
+ ".gitignore",
21
+ ".yardopts",
22
+ "LICENSE",
23
+ "README.md",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "gbuesing-toadhopper.gemspec",
27
+ "lib/notice.haml",
28
+ "lib/notice.xml.erb",
29
+ "lib/toadhopper.rb",
30
+ "test/helper.rb",
31
+ "test/test_filters.rb",
32
+ "test/test_posting.rb"
33
+ ]
34
+ s.homepage = %q{http://github.com/gbuesing/toadhopper}
35
+ s.rdoc_options = ["--charset=UTF-8"]
36
+ s.require_paths = ["lib"]
37
+ s.rubygems_version = %q{1.3.5}
38
+ s.summary = %q{Post error notifications to Hoptoad}
39
+ s.test_files = [
40
+ "test/helper.rb",
41
+ "test/test_filters.rb",
42
+ "test/test_posting.rb"
43
+ ]
44
+
45
+ if s.respond_to? :specification_version then
46
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
47
+ s.specification_version = 3
48
+
49
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
50
+ else
51
+ end
52
+ else
53
+ end
54
+ end
55
+
data/lib/notice.haml ADDED
@@ -0,0 +1,34 @@
1
+ !!!XML
2
+ %notice{:version => '2.0.0'}
3
+ %api-key= api_key
4
+ %notifier
5
+ %name= notifier_name
6
+ %version= notifier_version
7
+ %url= notifier_url
8
+ %error
9
+ %class= error.class.name
10
+ %message= "#{error.class.name}: #{error.message}"
11
+ %backtrace
12
+ - backtrace.each do |line|
13
+ %line{:method => line.method, :file => line.file, :number => line.number}/
14
+ %request
15
+ %url= url
16
+ %component= component
17
+ - if action && !action.blank?
18
+ %action= action
19
+ - if request && request.params.any?
20
+ %params
21
+ - request.params.each do |key,value|
22
+ %var{:key => key}= value
23
+ - if session && session.any?
24
+ %session
25
+ - session.each do |key,value|
26
+ %var{:key => key}= value
27
+ - if environment && environment.any?
28
+ %cgi-data
29
+ - environment.each do |key,value|
30
+ %var{:key => key}= value
31
+
32
+ %server-environment
33
+ %project-root= project_root
34
+ %environment-name= framework_env
@@ -0,0 +1,50 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <notice version="2.0.0">
3
+ <api-key><%=h defaults[:api_key] %></api-key>
4
+ <notifier>
5
+ <name><%=h defaults[:notifier_name] %></name>
6
+ <version><%=h defaults[:notifier_version] %></version>
7
+ <url><%=h defaults[:notifier_url] %></url>
8
+ </notifier>
9
+ <error>
10
+ <class><%=h defaults[:error].class.name %></class>
11
+ <message><%=h defaults[:error].class.name %>: <%=h defaults[:error].message %></message>
12
+ <backtrace>
13
+ <% defaults[:backtrace].each do |line| %>
14
+ <line method="<%=h line.method %>" file="<%=h line.file %>" number="<%=h line.number %>"/>
15
+ <% end %>
16
+ </backtrace>
17
+ </error>
18
+ <request>
19
+ <url><%=h defaults[:url] %></url>
20
+ <component><%=h defaults[:component] %></component>
21
+ <% if defaults[:action] %>
22
+ <component><%=h defaults[:action] %></component>
23
+ <% end %>
24
+ <% if defaults[:request] && defaults[:request].params.any? %>
25
+ <params>
26
+ <% defaults[:request].params.each do |key,value| %>
27
+ <var key="<%=h key %>"><%=h value %></var>
28
+ <% end %>
29
+ </params>
30
+ <% end %>
31
+ <% if defaults[:session] && defaults[:session].any? %>
32
+ <session>
33
+ <% defaults[:session].each do |key,value| %>
34
+ <var key="<%=h key %>"><%=h value %></var>
35
+ <% end %>
36
+ </session>
37
+ <% end %>
38
+ <% if defaults[:environment] && defaults[:environment].any? %>
39
+ <cgi-data>
40
+ <% defaults[:environment].each do |key,value| %>
41
+ <var key="<%=h key %>"><%=h value %></var>
42
+ <% end %>
43
+ </cgi-data>
44
+ <% end %>
45
+ </request>
46
+ <server-environment>
47
+ <project-root><%=h defaults[:project_root] %></project-root>
48
+ <environment-name><%=h defaults[:framework_env] %></environment-name>
49
+ </server-environment>
50
+ </notice>
data/lib/toadhopper.rb ADDED
@@ -0,0 +1,136 @@
1
+ require 'net/http'
2
+ require 'erb'
3
+
4
+ # Posts errors to the Hoptoad API
5
+ class ToadHopper
6
+ VERSION = "0.9.3"
7
+ include ERB::Util
8
+
9
+ # Hoptoad API response
10
+ class Response < Struct.new(:status, :body, :errors); end
11
+
12
+ attr_reader :api_key
13
+
14
+ def initialize(api_key)
15
+ @api_key = api_key
16
+ end
17
+
18
+ # Sets patterns to +[FILTER]+ out sensitive data such as +/password/+, +/email/+ and +/credit_card_number/+
19
+ def filters=(*filters)
20
+ @filters = filters.flatten
21
+ end
22
+
23
+ # @private
24
+ def filters
25
+ [@filters].flatten.compact
26
+ end
27
+
28
+ # Posts an exception to hoptoad.
29
+ #
30
+ # @param [Exception] error the error to post
31
+ #
32
+ # @param [Hash] options
33
+ # @option options [String] url The url for the request, required to post but not useful in a console environment
34
+ # @option options [String] component Normally this is your Controller name in an MVC framework
35
+ # @option options [String] action Normally the action for your request in an MVC framework
36
+ # @option options [#params] request An object that response to #params and returns a hash
37
+ # @option options [String] notifier_name Say you're a different notifier than ToadHopper
38
+ # @option options [String] notifier_version Specify the version of your custom notifier
39
+ # @option options [String] notifier_url Specify the project URL of your custom notifier
40
+ # @option options [Hash] session A hash of the user session in a web request
41
+ # @option options [String] framework_env The framework environment your app is running under
42
+ # @option options [Array] backtrace Normally not needed, parsed automatically from the provided exception parameter
43
+ # @option options [Hash] environment You MUST scrub your environment if you plan to use this, please do not use it though. :)
44
+ # @option options [String] project_root The root directory of your app
45
+ #
46
+ # @param [Hash] http_headers extra HTTP headers to be sent in the post to the API
47
+ #
48
+ # @example
49
+ # ToadHopper('apikey').post! error,
50
+ # {:action => 'show', :controller => 'Users'},
51
+ # {'X-Hoptoad-Client-Name' => 'My Awesome Notifier'}
52
+ #
53
+ # @return [Response]
54
+ def post!(error, options={}, http_headers={})
55
+ options[:notifier_name] ||= 'ToadHopper'
56
+ post_document(document_for(error, options), {'X-Hoptoad-Client-Name' => options[:notifier_name]})
57
+ end
58
+
59
+ # @private
60
+ def post_document(document, headers={})
61
+ uri = URI.parse("http://hoptoadapp.com:80/notifier_api/v2/notices")
62
+ Net::HTTP.start(uri.host, uri.port) do |http|
63
+ http.read_timeout = 5 # seconds
64
+ http.open_timeout = 2 # seconds
65
+ begin
66
+ response = http.post uri.path,
67
+ document,
68
+ {'Content-type' => 'text/xml', 'Accept' => 'text/xml, application/xml'}.merge(headers)
69
+ Response.new response.code.to_i,
70
+ response.body
71
+ rescue TimeoutError => e
72
+ Response.new(500, '', ['Timeout error'])
73
+ end
74
+ end
75
+ end
76
+
77
+ # @private
78
+ def document_for(exception, options={})
79
+ defaults = {
80
+ :error => exception,
81
+ :api_key => api_key,
82
+ :environment => clean(ENV.to_hash),
83
+ :backtrace => exception.backtrace.map {|l| backtrace_line(l)},
84
+ :url => 'http://localhost/',
85
+ :component => 'http://localhost/',
86
+ :action => nil,
87
+ :request => nil,
88
+ :notifier_version => VERSION,
89
+ :notifier_url => 'http://github.com/toolmantim/toadhopper',
90
+ :session => {},
91
+ :framework_env => ENV['RACK_ENV'] || 'development',
92
+ :project_root => Dir.pwd
93
+ }.merge(options)
94
+
95
+ ERB.new(notice_template).result(binding)
96
+ end
97
+
98
+ # @private
99
+ def backtrace_line(line)
100
+ Struct.new(:file, :number, :method).new(*line.match(%r{^([^:]+):(\d+)(?::in `([^']+)')?$}).captures)
101
+ end
102
+
103
+ # @private
104
+ def notice_template
105
+ File.read(::File.join(::File.dirname(__FILE__), 'notice.xml.erb'))
106
+ end
107
+
108
+ # @private
109
+ def clean(hash)
110
+ hash.inject({}) do |acc, (k, v)|
111
+ acc[k] = (v.is_a?(Hash) ? clean(v) : filtered_value(k,v)) if serializable?(v)
112
+ acc
113
+ end
114
+ end
115
+
116
+ # @private
117
+ def filtered_value(key, value)
118
+ if filters.any? {|f| key.to_s =~ Regexp.new(f)}
119
+ "[FILTERED]"
120
+ else
121
+ value
122
+ end
123
+ end
124
+
125
+ # @private
126
+ def serializable?(value)
127
+ [Fixnum, Array, String, Hash, Bignum].any? {|c| value.is_a?(c)}
128
+ end
129
+ end
130
+
131
+ # Convenience method for creating ToadHoppers
132
+ #
133
+ # @return [ToadHopper]
134
+ def ToadHopper(api_key)
135
+ ToadHopper.new(api_key)
136
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,3 @@
1
+ require 'test/unit'
2
+
3
+ require File.join(File.dirname(__FILE__), "..", "lib", "toadhopper")
@@ -0,0 +1,30 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'helper'))
2
+
3
+ class ToadHopper::TestFilters < Test::Unit::TestCase
4
+ def toadhopper
5
+ @toadhopper ||= ToadHopper("test api key")
6
+ end
7
+
8
+ def test_no_filters
9
+ assert_equal( {:id => "myid", :password => "mypassword"},
10
+ toadhopper.clean(:id => "myid", :password => "mypassword"))
11
+ end
12
+
13
+ def test_string_filter
14
+ toadhopper.filters = "pass"
15
+ assert_equal( {:id => "myid", :password => "[FILTERED]"},
16
+ toadhopper.clean(:id => "myid", :password => "mypassword"))
17
+ end
18
+
19
+ def test_regex_filter
20
+ toadhopper.filters = /pas{2}/
21
+ assert_equal( {:id => "myid", :password => "[FILTERED]"},
22
+ toadhopper.clean(:id => "myid", :password => "mypassword"))
23
+ end
24
+
25
+ def test_multiple_filters
26
+ toadhopper.filters = "email", /pas{2}/
27
+ assert_equal( {:id => "myid", :email => "[FILTERED]", :password => "[FILTERED]"},
28
+ toadhopper.clean(:id => "myid", :email => "myemail", :password => "mypassword"))
29
+ end
30
+ end
@@ -0,0 +1,21 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'helper'))
2
+
3
+ class ToadHopper::TestPosting < Test::Unit::TestCase
4
+ def test_posting
5
+ toadhopper = ToadHopper("abc123")
6
+ error = begin; raise "Kaboom!"; rescue => e; e end
7
+
8
+ response = toadhopper.post!(error)
9
+ assert_equal 422, response.status
10
+ end
11
+
12
+ if ENV['HOPTOAD_API_KEY']
13
+ def test_posting_integration
14
+ toadhopper = ToadHopper(ENV['HOPTOAD_API_KEY'])
15
+ error = begin; raise "Kaboom!"; rescue => e; e end
16
+
17
+ response = toadhopper.post!(error)
18
+ assert_equal 200, response.status
19
+ end
20
+ end
21
+ end
metadata ADDED
@@ -0,0 +1,70 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gbuesing-toadhopper
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Geoff Buesing
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-12-29 00:00:00 -06:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Fork of toolmantim-toadhopper, with Haml and Nokogiri dependencies removed
17
+ email: gbuesing@gmail.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - LICENSE
24
+ - README.md
25
+ files:
26
+ - .gitignore
27
+ - .yardopts
28
+ - LICENSE
29
+ - README.md
30
+ - Rakefile
31
+ - VERSION
32
+ - gbuesing-toadhopper.gemspec
33
+ - lib/notice.haml
34
+ - lib/notice.xml.erb
35
+ - lib/toadhopper.rb
36
+ - test/helper.rb
37
+ - test/test_filters.rb
38
+ - test/test_posting.rb
39
+ has_rdoc: true
40
+ homepage: http://github.com/gbuesing/toadhopper
41
+ licenses: []
42
+
43
+ post_install_message:
44
+ rdoc_options:
45
+ - --charset=UTF-8
46
+ require_paths:
47
+ - lib
48
+ required_ruby_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: "0"
53
+ version:
54
+ required_rubygems_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: "0"
59
+ version:
60
+ requirements: []
61
+
62
+ rubyforge_project:
63
+ rubygems_version: 1.3.5
64
+ signing_key:
65
+ specification_version: 3
66
+ summary: Post error notifications to Hoptoad
67
+ test_files:
68
+ - test/helper.rb
69
+ - test/test_filters.rb
70
+ - test/test_posting.rb