captivus 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in captivus.gemspec
4
+ gemspec
5
+
6
+ gem "rspec", "~> 2.11"
7
+ gem "sham_rack", "~> 1.3"
8
+ gem "rack-test", "~> 0.6"
9
+ gem "timecop", '~> 0.5'
10
+ gem "delorean", "~> 2.0.0"
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Austin Schneider
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # Captivus
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'captivus'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install captivus
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/captivus.gemspec ADDED
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'captivus/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "captivus"
8
+ gem.version = Captivus::VERSION
9
+ gem.authors = ["Austin Schneider"]
10
+ gem.email = ["austinthecoder@gmail.com"]
11
+ gem.description = "Captivus client gem"
12
+ gem.summary = "Captivus client gem"
13
+ gem.homepage = "http://captiv.us"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_dependency "faraday", '~> 0.8.4'
21
+ gem.add_dependency "multi_json", "~> 1.3.6"
22
+ gem.add_dependency "captivus-auth_hmac", "0.0.1"
23
+ end
@@ -0,0 +1,21 @@
1
+ module Captivus
2
+ class Backtrace
3
+ class Line
4
+ PATTERN = /^((?:[a-zA-Z]:)?[^:]+):(\d+)(?::in `([^']+)')?$/
5
+
6
+ def initialize(raw_line)
7
+ if match_data = PATTERN.match(raw_line)
8
+ @as_json = {
9
+ :file => match_data[1],
10
+ :number => match_data[2].to_i,
11
+ :method => match_data[3]
12
+ }
13
+ else
14
+ raise ArgumentError, "Unrecognized format"
15
+ end
16
+ end
17
+
18
+ attr_reader :as_json
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,14 @@
1
+ require 'captivus/backtrace/line'
2
+
3
+ module Captivus
4
+ class Backtrace
5
+ def initialize(exception)
6
+ unless exception.respond_to?(:backtrace)
7
+ raise ArgumentError, "#{exception} must respond to `backtrace`"
8
+ end
9
+ @as_json = Array(exception.backtrace).map { |line| Line.new(line).as_json }
10
+ end
11
+
12
+ attr_reader :as_json
13
+ end
14
+ end
@@ -0,0 +1,23 @@
1
+ module Captivus
2
+ class Configuration
3
+ def initialize(attrs = {}, &block)
4
+ defaults = {
5
+ :scheme => 'http',
6
+ :host => 'api.captiv.us'
7
+ }
8
+ configure defaults.merge(attrs), &block
9
+ end
10
+
11
+ attr_accessor :host, :scheme, :api_key, :api_secret_key
12
+
13
+ def configure(attrs = {})
14
+ attrs.each { |attr, value| send "#{attr}=", value }
15
+ yield self if block_given?
16
+ self
17
+ end
18
+
19
+ def ==(other)
20
+ other.is_a?(Captivus::Configuration) && host == other.host
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,20 @@
1
+ module Captivus
2
+ class Payload
3
+ def initialize(exception)
4
+ if exception.respond_to?(:class) && exception.class.respond_to?(:name) && exception.respond_to?(:message)
5
+ @as_json = {
6
+ 'event' => {
7
+ 'type' => exception.class.name,
8
+ 'message' => exception.message,
9
+ 'timestamp' => Time.now.utc.to_s
10
+ },
11
+ 'backtrace' => Backtrace.new(exception).as_json
12
+ }
13
+ else
14
+ raise ArgumentError, "Unexpected exception: #{exception.inspect}"
15
+ end
16
+ end
17
+
18
+ attr_reader :as_json
19
+ end
20
+ end
@@ -0,0 +1,23 @@
1
+ require 'faraday'
2
+ require 'multi_json'
3
+
4
+ module Captivus
5
+ class RackCapturer
6
+ def initialize(app)
7
+ @app = app
8
+ end
9
+
10
+ def call(env)
11
+ begin
12
+ @app.call env
13
+ rescue => exception
14
+ Captivus.notify exception
15
+ raise
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ attr_reader :app
22
+ end
23
+ end
@@ -0,0 +1,3 @@
1
+ module Captivus
2
+ VERSION = "0.0.2"
3
+ end
data/lib/captivus.rb ADDED
@@ -0,0 +1,33 @@
1
+ require "captivus/version"
2
+ require 'captivus/configuration'
3
+ require 'faraday/request/hmac_authentication'
4
+
5
+ module Captivus
6
+ class << self
7
+ def config
8
+ @config ||= Configuration.new
9
+ end
10
+
11
+ def configure(*args, &block)
12
+ config.configure *args, &block
13
+ end
14
+
15
+ def notify(exception)
16
+ connection = Faraday.new(:url => "#{config.scheme}://#{config.host}") do |faraday|
17
+ faraday.request :hmac_authentication, config.api_key, config.api_secret_key, {:service_id => 'Captivus'}
18
+ faraday.adapter :net_http
19
+ end
20
+
21
+ connection.post do |request|
22
+ request.headers['Content-Type'] = 'application/json; charset=UTF-8'
23
+ request.url '/events'
24
+ request.body = MultiJson.dump(Payload.new(exception).as_json)
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+ require 'captivus/auth_hmac'
31
+ require 'captivus/backtrace'
32
+ require 'captivus/payload'
33
+ require 'captivus/rack_capturer'
@@ -0,0 +1,69 @@
1
+ require 'captivus/auth_hmac'
2
+ require 'uri'
3
+ require 'faraday'
4
+
5
+ module Faraday
6
+ class Request
7
+
8
+ attr_reader :access_id, :secret
9
+
10
+ # # Sign the request with the specified `access_id` and `secret`.
11
+ # def sign!(access_id, secret)
12
+ # @access_id, @secret = access_id, secret
13
+
14
+ # #self.sign_with = access_id
15
+ # #Captivus::AuthHMAC.keys[access_id] = secret
16
+ # end
17
+
18
+ class HMACAuthentication < Faraday::Middleware
19
+ # Modified CanonicalString to know how to pull from the Faraday-specific
20
+ # env hash.
21
+ class CanonicalString < Captivus::AuthHMAC::CanonicalString
22
+ def request_method(request)
23
+ request[:method].to_s.upcase
24
+ end
25
+
26
+ def request_body(request)
27
+ request[:body]
28
+ end
29
+
30
+ def request_path(request)
31
+ URI.parse(request[:url].to_s).path
32
+ end
33
+
34
+ def headers(request)
35
+ request[:request_headers]
36
+ end
37
+ end
38
+
39
+ KEY = "Authorization".freeze
40
+
41
+ attr_reader :auth, :token, :secret
42
+
43
+ def initialize(app, token, secret, options = {})
44
+ options.merge!(:signature => HMACAuthentication::CanonicalString)
45
+ keys = {token => secret}
46
+ @token, @secret = token, secret
47
+ @auth = Captivus::AuthHMAC.new(keys, options)
48
+ super(app)
49
+ end
50
+
51
+ # Public
52
+ def call(env)
53
+ env[:request_headers][KEY] ||= hmac_auth_header(env).to_s if sign_request?
54
+ @app.call(env)
55
+ end
56
+
57
+ def hmac_auth_header(env)
58
+ auth.authorization(env, token, secret)
59
+ end
60
+
61
+ def sign_request?
62
+ !!@token && !!@secret
63
+ end
64
+ end
65
+ end
66
+
67
+ Faraday.register_middleware :request, :hmac_authentication => Faraday::Request::HMACAuthentication
68
+ end
69
+
@@ -0,0 +1,53 @@
1
+ require 'spec_helper'
2
+
3
+ describe Captivus::Backtrace::Line do
4
+ def new_line(*args)
5
+ Captivus::Backtrace::Line.new *args
6
+ end
7
+
8
+ describe ".new" do
9
+ it "raises an argument error when the line is not a correctly formatted backtrace line" do
10
+ [
11
+ nil,
12
+ ' ',
13
+ 'foo',
14
+ '/Users/austin/something.rb:10:',
15
+ "/Users/austin/something.rb:10:in 'something'",
16
+ ].each do |line|
17
+ expect { new_line line }.to raise_error(ArgumentError, "Unrecognized format")
18
+ end
19
+ end
20
+ end
21
+
22
+ describe "as_json" do
23
+ context "when the line has the format <file>:<number>" do
24
+ it "returns the parts, minus the method, in a hash" do
25
+ new_line("/Users/austin/dir/run-file.rb:1").as_json.should == {
26
+ :file => '/Users/austin/dir/run-file.rb',
27
+ :number => 1,
28
+ :method => nil
29
+ }
30
+ end
31
+ end
32
+
33
+ context "when the line has the format <file>:<number>:in `<method>'" do
34
+ it "returns the parts in a hash" do
35
+ new_line("/Users/austin/go.rb:10:in `perform'").as_json.should == {
36
+ :file => '/Users/austin/go.rb',
37
+ :number => 10,
38
+ :method => 'perform'
39
+ }
40
+ end
41
+ end
42
+
43
+ context "when the line has the format <drive>:<file>:<number>:in `<method>'" do
44
+ it "returns the parts in a hash" do
45
+ new_line("C:/Users/austin/go.rb:10:in `perform'").as_json.should == {
46
+ :file => 'C:/Users/austin/go.rb',
47
+ :number => 10,
48
+ :method => 'perform'
49
+ }
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+
3
+ describe Captivus::Backtrace do
4
+ def new_backtrace(*args)
5
+ Captivus::Backtrace.new *args
6
+ end
7
+
8
+ describe ".new" do
9
+ context "when the given object doesn't respond to `backtrace`" do
10
+ it "raises an argument error" do
11
+ object = double 'object'
12
+ expect {
13
+ new_backtrace object
14
+ }.to raise_error(ArgumentError, "#{object} must respond to `backtrace`")
15
+ end
16
+ end
17
+ end
18
+
19
+ describe 'as_json' do
20
+ before do
21
+ @object = double 'object', :backtrace => ['/Users/x.rb:1', '/Users/x.rb:2', '/Users/x.rb:3']
22
+ end
23
+
24
+ context "when the object has a backtrace" do
25
+ it 'is an array of the backtrace lines as json' do
26
+ new_backtrace(@object).as_json.should == [
27
+ Captivus::Backtrace::Line.new('/Users/x.rb:1').as_json,
28
+ Captivus::Backtrace::Line.new('/Users/x.rb:2').as_json,
29
+ Captivus::Backtrace::Line.new('/Users/x.rb:3').as_json
30
+ ]
31
+ end
32
+ end
33
+
34
+ context "when the object doesn't have a backtrace" do
35
+ it "is an empty array" do
36
+ @object.stub :backtrace
37
+ new_backtrace(@object).as_json.should == []
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,67 @@
1
+ require 'spec_helper'
2
+
3
+ describe Captivus::Configuration do
4
+ [
5
+ ['scheme', 'http'],
6
+ ['host', 'api.captiv.us'],
7
+ ['api_key', nil],
8
+ ['api_secret_key', nil]
9
+ ].each do |attribute, default_value|
10
+ describe attribute do
11
+ it "defaults to #{default_value.inspect}" do
12
+ expect(eval("Captivus::Configuration.new.#{attribute}")).to eq default_value
13
+ end
14
+
15
+ it "is the given #{attribute.to_sym} option" do
16
+ expect(eval("Captivus::Configuration.new(:#{attribute} => 'x').#{attribute}")).to eq 'x'
17
+ expect(eval("Captivus::Configuration.new(:#{attribute} => ' ').#{attribute}")).to eq ' '
18
+ expect(eval("Captivus::Configuration.new(:#{attribute} => nil).#{attribute}")).to be_nil
19
+ end
20
+ end
21
+ end
22
+
23
+ describe "host" do
24
+ it "defaults to api.captiv.us" do
25
+ expect(Captivus::Configuration.new.host).to eq 'api.captiv.us'
26
+ end
27
+
28
+ it "is the given host option" do
29
+ expect(Captivus::Configuration.new(:host => 'x').host).to eq 'x'
30
+ expect(Captivus::Configuration.new(:host => ' ').host).to eq ' '
31
+ expect(Captivus::Configuration.new(:host => nil).host).to be_nil
32
+ end
33
+ end
34
+
35
+ describe "configure" do
36
+ before { @config = Captivus::Configuration.new }
37
+
38
+ it "sets its attributes from the given hash" do
39
+ @config.configure :host => 'newho.st'
40
+ expect(@config.host).to eq 'newho.st'
41
+ end
42
+
43
+ it "yields itself if given a block" do
44
+ @config.configure do |config|
45
+ expect(config).to eq @config
46
+ end
47
+ end
48
+
49
+ it "returns itself" do
50
+ expect(@config.configure).to equal @config
51
+ end
52
+ end
53
+
54
+ describe "==" do
55
+ it "is true given another configuration with the same attributes" do
56
+ expect(Captivus::Configuration.new(:host => 'a')).to eq Captivus::Configuration.new(:host => 'a')
57
+ end
58
+
59
+ it "is false given another configuration with different attributes" do
60
+ expect(Captivus::Configuration.new).to_not eq Captivus::Configuration.new(:host => 'a')
61
+ end
62
+
63
+ it "is false given a non configuration" do
64
+ expect(Captivus::Configuration.new).to_not eq Object.new
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,47 @@
1
+ require 'spec_helper'
2
+
3
+ describe Captivus::Payload do
4
+ describe ".new" do
5
+ context "when argument doesn't behave like an exception" do
6
+ it "raises an argument error" do
7
+ [
8
+ double('exception'),
9
+ double('exception', :class => double('class')),
10
+ double('exception', :class => double('class', :name => 'X')),
11
+ double('exception', :class => double('class'), :message => 'x')
12
+ ].each do |exception|
13
+ expect {
14
+ Captivus::Payload.new exception
15
+ }.to raise_error(ArgumentError, "Unexpected exception: #{exception.inspect}")
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ describe "as_json" do
22
+ it "returns a hash of event and backtrace info" do
23
+ Timecop.freeze Time.utc(2012, 8, 23, 16, 34, 19)
24
+
25
+ exception = double 'exception',
26
+ :class => double('class', :name => 'ExceptionType'),
27
+ :message => 'The exception message.',
28
+ :backtrace => ['/Users/x.rb:1', '/Users/x.rb:2', '/Users/x.rb:3']
29
+
30
+ Captivus::Payload.new(exception).as_json.tap do |hash|
31
+ expect(hash.keys).to match_array ['event', 'backtrace']
32
+
33
+ hash['event'].tap do |event|
34
+ expect(event.keys).to match_array ['type', 'message', 'timestamp']
35
+
36
+ expect(event['type']).to eq 'ExceptionType'
37
+ expect(event['message']).to eq 'The exception message.'
38
+ expect(event['timestamp']).to eq '2012-08-23 16:34:19 UTC'
39
+ end
40
+
41
+ hash['backtrace'].should == Captivus::Backtrace.new(exception).as_json
42
+ end
43
+
44
+ Timecop.return
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+
3
+ describe Captivus::RackCapturer do
4
+ describe "call" do
5
+ before do
6
+ @app = double 'app'
7
+ @rack_capturer = Captivus::RackCapturer.new @app
8
+ end
9
+
10
+ it "is delegated to the given app, passing the env along" do
11
+ env = double 'env'
12
+ result = double 'result'
13
+ @app.stub(:call).with(env) { result }
14
+ expect(@rack_capturer.call(env)).to eq result
15
+ end
16
+
17
+ context "when calling the app raises an error" do
18
+ before { @app.stub(:call) { raise StandardError, 'Error raised' } }
19
+
20
+ it "raises the error" do
21
+ expect { @rack_capturer.call double('env') }.to raise_error StandardError, "Error raised"
22
+ end
23
+
24
+ it "notifies the Captivus API" do
25
+ Captivus.should_receive(:notify).with kind_of(StandardError)
26
+ @rack_capturer.call double('env') rescue nil
27
+ end
28
+ end
29
+
30
+ context "when calling the app does not raise an error" do
31
+ before { @app.stub :call }
32
+
33
+ it "does not notify the Captivus API" do
34
+ @rack_capturer.call double('env')
35
+ expect(captivus_api.requests.size).to eq 0
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,62 @@
1
+ require 'spec_helper'
2
+
3
+ describe Captivus do
4
+ describe ".configuration" do
5
+ it "is a configuration" do
6
+ expect(Captivus.config).to eq Captivus::Configuration.new
7
+ end
8
+
9
+ it "always returns the same configuration" do
10
+ expect(Captivus.config).to equal Captivus.config
11
+ end
12
+ end
13
+
14
+ describe ".configure" do
15
+ it "is delegated to the config" do
16
+ attrs = {:a => 1, :b => 2}
17
+ block = Proc.new {}
18
+ Captivus.config.should_receive(:configure).with(attrs, &block)
19
+ Captivus.configure attrs, &block
20
+ end
21
+ end
22
+
23
+ describe ".notify" do
24
+ it "sends a notice to the Captivus API" do
25
+ Timecop.freeze Time.utc(2012, 11, 28, 2, 46, 4)
26
+
27
+ exception = StandardError.new('Error raised')
28
+
29
+ Captivus.configure do |config|
30
+ config.api_key = 'api-key'
31
+ config.api_secret_key = 'secret'
32
+ end
33
+
34
+ Captivus.notify exception
35
+
36
+ expect(captivus_api.requests.size).to eq 1
37
+
38
+ headers = {
39
+ "CONTENT_TYPE"=>"application/json; charset=UTF-8",
40
+ "HTTP_ACCEPT" => "*/*",
41
+ "HTTP_USER_AGENT" => "Ruby",
42
+ "HTTP_DATE" => "Wed, 28 Nov 2012 02:46:04 GMT",
43
+ "HTTP_AUTHORIZATION" => "Captivus api-key:IgBW675pSKkCuD4McYnX9QQMI5g=",
44
+ "PATH_INFO" => "/events",
45
+ "QUERY_STRING" => "",
46
+ "REQUEST_METHOD"=>"POST",
47
+ "SCRIPT_NAME" => "",
48
+ "SERVER_NAME" => "api.captiv.us",
49
+ "SERVER_PORT" => "80",
50
+ "rack.url_scheme" => "http"
51
+ }
52
+
53
+ last_request = captivus_api.requests[0]
54
+
55
+ expect(last_request.values_at(*headers.keys)).to eq headers.values
56
+
57
+ expect(last_request['rack.input'].read).to eq MultiJson.dump(Captivus::Payload.new(exception).as_json)
58
+
59
+ Timecop.return
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,93 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Reporting from a rack app' do
4
+ include Rack::Test::Methods
5
+
6
+ before do
7
+ Captivus.configure do |config|
8
+ config.api_key = 'api-key'
9
+ config.api_secret_key = 'secret'
10
+ end
11
+ end
12
+
13
+ context "when installed as middleware in a Rack app" do
14
+ def app
15
+ Rack::Builder.app do
16
+ use Captivus::RackCapturer
17
+ run lambda { |env| raise StandardError, "Error from Rack app" }
18
+ end
19
+ end
20
+
21
+ it "errors are raised as expected" do
22
+ expect { get '/' }.to raise_error StandardError, "Error from Rack app"
23
+ end
24
+
25
+ it "reports errors to the Captivus API" do
26
+ Timecop.freeze Time.utc(2012, 8, 23, 16, 34, 19)
27
+
28
+ get '/' rescue nil
29
+
30
+ expect(captivus_api.requests.size).to eq 1
31
+
32
+ last_request = captivus_api.requests[0]
33
+
34
+ headers = {
35
+ "CONTENT_TYPE"=>"application/json; charset=UTF-8",
36
+ "HTTP_ACCEPT" => "*/*",
37
+ "HTTP_USER_AGENT" => "Ruby",
38
+ "HTTP_DATE" => "Thu, 23 Aug 2012 16:34:19 GMT",
39
+ "HTTP_AUTHORIZATION" => "Captivus api-key:syLWMLH8mJ4jTFGO2lVe+9PfgVo=",
40
+ "PATH_INFO" => "/events",
41
+ "QUERY_STRING" => "",
42
+ "REQUEST_METHOD"=>"POST",
43
+ "SCRIPT_NAME" => "",
44
+ "SERVER_NAME" => "api.captiv.us",
45
+ "SERVER_PORT" => "80",
46
+ "rack.url_scheme" => "http"
47
+ }
48
+
49
+ expect(last_request.values_at(*headers.keys)).to eq headers.values
50
+
51
+ MultiJson.load(last_request['rack.input'].read).tap do |payload|
52
+ expect(payload.keys).to match_array ['event', 'backtrace']
53
+
54
+ payload['event'].tap do |event|
55
+ expect(event.keys).to match_array ['type', 'message', 'timestamp']
56
+
57
+ expect(event['type']).to eq "StandardError"
58
+ expect(event['message']).to eq "Error from Rack app"
59
+ expect(event['timestamp']).to eq "2012-08-23 16:34:19 UTC"
60
+ end
61
+
62
+ expect(payload['backtrace'].size > 10).to be_true
63
+ expect(payload['backtrace'][0..2]).to eq [{
64
+ "file" => "/Users/austin/projects/captivus/gem/spec/integration/events_from_rack_spec.rb",
65
+ "number"=> 17,
66
+ "method" => "block (2 levels) in app"
67
+ }, {
68
+ "file" => "/Users/austin/projects/captivus/gem/lib/captivus/rack_capturer.rb",
69
+ "number"=> 12,
70
+ "method" => "call"
71
+ }, {
72
+ "file" => "/Users/austin/projects/captivus/gem/lib/captivus/rack_capturer.rb",
73
+ "number"=> 12,
74
+ "method" => "call"
75
+ }]
76
+ end
77
+
78
+ Timecop.return
79
+
80
+ # possible things to care about
81
+ # "GATEWAY_INTERFACE"=>"CGI/1.1",
82
+ # "REMOTE_ADDR"=>"127.0.0.1",
83
+ # "REMOTE_HOST"=>"localhost",
84
+ # "REQUEST_URI"=>"http://localhost:9293/events",
85
+ # "SERVER_PROTOCOL"=>"HTTP/1.1",
86
+ # "SERVER_SOFTWARE"=>"WEBrick/1.3.1 (Ruby/1.9.3/2012-04-20)",
87
+ # "HTTP_CONNECTION"=>"close",
88
+ # "HTTP_HOST"=>"localhost:9293",
89
+ # "HTTP_VERSION"=>"HTTP/1.1",
90
+ # "REQUEST_PATH"=>"/events"
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,37 @@
1
+ require 'bundler/setup'
2
+ require 'sham_rack'
3
+ require 'rack/test'
4
+ require 'timecop'
5
+ require "delorean"
6
+ require_relative '../lib/captivus'
7
+
8
+ class CaptivusAPI
9
+ def call(env)
10
+ requests << env
11
+ [200, {}, ['']]
12
+ end
13
+
14
+ def requests
15
+ @requests ||= []
16
+ end
17
+
18
+ def clear_requests!
19
+ @requests = []
20
+ end
21
+ end
22
+
23
+ module Helpers
24
+ def captivus_api
25
+ @captivus_api ||= CaptivusAPI.new
26
+ end
27
+ end
28
+
29
+ RSpec.configure do |config|
30
+ config.include Helpers
31
+ config.include Delorean
32
+
33
+ config.before do
34
+ ShamRack.mount captivus_api, "api.captiv.us"
35
+ captivus_api.clear_requests!
36
+ end
37
+ end
metadata ADDED
@@ -0,0 +1,123 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: captivus
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Austin Schneider
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-10-22 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: faraday
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 0.8.4
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 0.8.4
30
+ - !ruby/object:Gem::Dependency
31
+ name: multi_json
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 1.3.6
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 1.3.6
46
+ - !ruby/object:Gem::Dependency
47
+ name: captivus-auth_hmac
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - '='
52
+ - !ruby/object:Gem::Version
53
+ version: 0.0.1
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - '='
60
+ - !ruby/object:Gem::Version
61
+ version: 0.0.1
62
+ description: Captivus client gem
63
+ email:
64
+ - austinthecoder@gmail.com
65
+ executables: []
66
+ extensions: []
67
+ extra_rdoc_files: []
68
+ files:
69
+ - .gitignore
70
+ - Gemfile
71
+ - LICENSE.txt
72
+ - README.md
73
+ - Rakefile
74
+ - captivus.gemspec
75
+ - lib/captivus.rb
76
+ - lib/captivus/backtrace.rb
77
+ - lib/captivus/backtrace/line.rb
78
+ - lib/captivus/configuration.rb
79
+ - lib/captivus/payload.rb
80
+ - lib/captivus/rack_capturer.rb
81
+ - lib/captivus/version.rb
82
+ - lib/faraday/request/hmac_authentication.rb
83
+ - spec/captivus/backtrace/line_spec.rb
84
+ - spec/captivus/backtrace_spec.rb
85
+ - spec/captivus/configuration_spec.rb
86
+ - spec/captivus/payload_spec.rb
87
+ - spec/captivus/rack_capturer_spec.rb
88
+ - spec/captivus_spec.rb
89
+ - spec/integration/events_from_rack_spec.rb
90
+ - spec/spec_helper.rb
91
+ homepage: http://captiv.us
92
+ licenses: []
93
+ post_install_message:
94
+ rdoc_options: []
95
+ require_paths:
96
+ - lib
97
+ required_ruby_version: !ruby/object:Gem::Requirement
98
+ none: false
99
+ requirements:
100
+ - - ! '>='
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ required_rubygems_version: !ruby/object:Gem::Requirement
104
+ none: false
105
+ requirements:
106
+ - - ! '>='
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ requirements: []
110
+ rubyforge_project:
111
+ rubygems_version: 1.8.24
112
+ signing_key:
113
+ specification_version: 3
114
+ summary: Captivus client gem
115
+ test_files:
116
+ - spec/captivus/backtrace/line_spec.rb
117
+ - spec/captivus/backtrace_spec.rb
118
+ - spec/captivus/configuration_spec.rb
119
+ - spec/captivus/payload_spec.rb
120
+ - spec/captivus/rack_capturer_spec.rb
121
+ - spec/captivus_spec.rb
122
+ - spec/integration/events_from_rack_spec.rb
123
+ - spec/spec_helper.rb