chordate-ruby 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,6 @@
1
+ .idea
2
+ .idea/*
3
+ /.bundle
4
+ *.rdb
5
+ Gemfile.lock
6
+ pkg/*
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm use ruby-1.9.3-p327@chordate-ruby --create
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'http://rubygems.org'
2
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Brian Norton
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a
4
+ copy of this software and associated documentation files (the "Software"),
5
+ to deal in the Software without restriction, including without limitation
6
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
7
+ and/or sell copies of the Software, and to permit persons to whom the
8
+ Software is furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
17
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19
+
20
+ The above License is for PERSONAL USE ONLY, when used in COMMERCIAL or CORPORATE
21
+ software release (YES EVEN FOR AN INTERNAL BOARDS AS INTENDED),
22
+ the COMM-LICENSE applies to that and all COMMERCIAL scenarios.
@@ -0,0 +1 @@
1
+ Official Ruby Gem for the Chordate Error Reporting Service.
@@ -0,0 +1,22 @@
1
+ require File.expand_path('../lib/chordate-ruby/version', __FILE__)
2
+
3
+ Gem::Specification.new do |gem|
4
+ gem.authors = %w(bnorton)
5
+ gem.email = 'brian.nort@gmail.com'
6
+ gem.summary = ''
7
+ gem.description = ''
8
+ gem.homepage = 'http://github.com/chordate/chordate-ruby'
9
+ gem.license = 'MIT'
10
+ gem.require_paths = %w(lib)
11
+
12
+ gem.version = Chordate::Version::STRING
13
+ gem.name = 'chordate-ruby'
14
+
15
+ gem.files = `git ls-files`.split("\n")
16
+ gem.test_files = `git ls-files -- spec/*`.split("\n")
17
+
18
+ gem.add_dependency 'typhoeus', '> 0.5.0'
19
+ gem.add_dependency 'celluloid'
20
+ gem.add_development_dependency 'rspec'
21
+ gem.add_development_dependency 'webmock'
22
+ end
@@ -0,0 +1,13 @@
1
+ require 'chordate-ruby/chordate'
2
+
3
+ ['typhoeus',
4
+ 'celluloid',
5
+ 'chordate-ruby/version',
6
+ 'chordate-ruby/http',
7
+ 'chordate-ruby/error',
8
+ 'chordate-ruby/runner',
9
+ ].each do |file|
10
+ require file
11
+ end
12
+
13
+ Celluloid.logger = nil
@@ -0,0 +1,45 @@
1
+ require 'chordate-ruby/config'
2
+ require 'chordate-ruby/runner/event'
3
+ require 'chordate-ruby/error'
4
+
5
+ module Chordate
6
+ class << self
7
+ def configure
8
+ (@config ||= Chordate::Config.new).tap do |config|
9
+ yield config
10
+ end
11
+ end
12
+
13
+ def config
14
+ @config
15
+ end
16
+
17
+ def run!
18
+ unless running?
19
+ (@event_runner ||= Runner::Event.new).tap do |runner|
20
+ runner.start
21
+ end
22
+
23
+ @running = true
24
+ end
25
+ end
26
+
27
+ def running?
28
+ !!@running
29
+ end
30
+
31
+ def error(*args)
32
+ @event_runner.async.push(
33
+ Chordate::Error.new(*args).as_json
34
+ )
35
+ end
36
+
37
+ private
38
+
39
+ def clear
40
+ @event_runner && @event_runner.terminate
41
+
42
+ @running = @event_runner = nil
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,43 @@
1
+ module Chordate
2
+ class Config
3
+ def initialize
4
+ @config = {
5
+ 'env' => rails(:env) || 'default',
6
+ 'proto' => 'https://',
7
+ 'host' => "chordate.io",
8
+ 'server' => {
9
+ :root => rails(:root) || `pwd`.chomp,
10
+ :hostname => `hostname`.chomp,
11
+ :gem_version => Chordate::Version::STRING
12
+ }
13
+ }
14
+ end
15
+
16
+ def [](key)
17
+ @config[key.to_s]
18
+ end
19
+
20
+ def token=(token)
21
+ token = token.split(':')
22
+
23
+ (@config['token'] = token[1]).tap do
24
+ @config['base_url'] ||= "/v1/applications/#{token[0]}"
25
+ end
26
+ end
27
+
28
+ def method_missing(meth, *args)
29
+ case meth
30
+ when /=$/
31
+ @config[meth.to_s[0..-2]] = args.first
32
+ else
33
+ @config[meth.to_s]
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ def rails(method)
40
+ defined?(Rails) ? Rails.send(method).to_s : nil
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,30 @@
1
+ module Chordate
2
+ class Error
3
+ def initialize(*args)
4
+ @ex = args.shift
5
+
6
+ @json = {
7
+ :env => Chordate.config.env,
8
+ :generated_at => Time.now,
9
+ :klass => @ex.class.name,
10
+ :message => @ex.message,
11
+ :backtrace => @ex.backtrace,
12
+ :server => Chordate.config.server
13
+ }
14
+
15
+ merge!(args.shift)
16
+ end
17
+
18
+ def merge!(other)
19
+ other.each do |(k, v)|
20
+ v.nil? ? @json.delete(k) : @json[k] = v
21
+ end
22
+
23
+ self
24
+ end
25
+
26
+ def as_json(*)
27
+ @json
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,15 @@
1
+ module Chordate
2
+ class HTTP
3
+ class << self
4
+ def post(path, body = {})
5
+ Typhoeus.post(url_for(path), :body => body.merge(:token => Chordate.config.token))
6
+ end
7
+
8
+ private
9
+
10
+ def url_for(path)
11
+ "#{Chordate.config.proto}#{Chordate.config.host}#{Chordate.config.base_url}/#{path}.json"
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1 @@
1
+ require 'chordate-ruby/runner/event'
@@ -0,0 +1,40 @@
1
+ require 'celluloid'
2
+
3
+ module Chordate
4
+ module Runner
5
+ class Event
6
+ include Celluloid
7
+
8
+ attr_reader :errors
9
+
10
+ def initialize
11
+ @errors = []
12
+ @exit = false
13
+ end
14
+
15
+ def start
16
+ if errors.any?
17
+ HTTP.post(:events, :batch => errors)
18
+
19
+ @errors = []
20
+ end
21
+
22
+ return if exit?
23
+
24
+ after(5) { start }
25
+ end
26
+
27
+ def push(e)
28
+ @errors << e
29
+ end
30
+
31
+ def exit
32
+ @exit = true
33
+ end
34
+
35
+ def exit?
36
+ @exit
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,10 @@
1
+ module Chordate
2
+ module Version
3
+ MAJOR = 0
4
+ MINOR = 1
5
+ PATCH = 0
6
+ BUILD = nil
7
+
8
+ STRING = [MAJOR, MINOR, PATCH, BUILD].compact.join('.')
9
+ end
10
+ end
@@ -0,0 +1,97 @@
1
+ require "spec_helper"
2
+
3
+ describe Chordate do
4
+ describe '.configure' do
5
+ it 'should yield a Config object' do
6
+ Chordate.configure {|c| c.class.should == Chordate::Config }
7
+ end
8
+
9
+ it 'should cache the object' do
10
+ configs = []
11
+ Chordate.configure {|c| configs << c }
12
+ Chordate.configure {|c| configs << c }
13
+
14
+ configs[0].should == configs[1]
15
+ end
16
+ end
17
+
18
+ describe '.config' do
19
+ before do
20
+ Chordate.configure {|c| c.foo = 'bar' }
21
+ end
22
+
23
+ it 'should be the config object' do
24
+ Chordate.config.class.should == Chordate::Config
25
+ end
26
+
27
+ it 'should have the options' do
28
+ Chordate.config.foo.should == 'bar'
29
+ end
30
+ end
31
+
32
+ describe '.run!' do
33
+ before do
34
+ @event_runner = mock(Chordate::Runner::Event, :start => nil, :terminate => nil)
35
+ Chordate::Runner::Event.stub(:new).and_return(@event_runner)
36
+ end
37
+
38
+ after do
39
+ Chordate.send(:clear)
40
+ end
41
+
42
+ it 'should build the runner' do
43
+ Chordate::Runner::Event.should_receive(:new).and_return(@event_runner)
44
+
45
+ Chordate.run!
46
+ end
47
+
48
+ it 'should start the runner' do
49
+ @event_runner.should_receive(:start)
50
+
51
+ Chordate.run!
52
+ end
53
+
54
+ it "should be running" do
55
+ Chordate.run!
56
+
57
+ Chordate.should be_running
58
+ end
59
+ end
60
+
61
+ describe '.error' do
62
+ let(:exception) { make_exception }
63
+ let(:options) { {} }
64
+
65
+ def error
66
+ Chordate.error(exception, options)
67
+ end
68
+
69
+ before do
70
+ @async = mock(Celluloid::ActorProxy, :push => nil)
71
+ @runner = mock(Chordate::Runner::Event, :start => nil, :async => @async, :terminate => nil)
72
+ Chordate::Runner::Event.stub(:new).and_return(@runner)
73
+
74
+ Chordate.run!
75
+
76
+ @json = mock("JSON")
77
+ @error = mock(Chordate::Error, :as_json => @json)
78
+ Chordate::Error.stub(:new).and_return(@error)
79
+ end
80
+
81
+ after do
82
+ Chordate.send(:clear)
83
+ end
84
+
85
+ it 'should init an error' do
86
+ Chordate::Error.should_receive(:new).with(exception, options).and_return(@error)
87
+
88
+ error
89
+ end
90
+
91
+ it 'should push the error to the runner' do
92
+ @async.should_receive(:push).with(@json)
93
+
94
+ error
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,57 @@
1
+ require 'spec_helper'
2
+
3
+ describe Chordate::Config do
4
+ [[:a_key, '123:abcdef']].each do |(method, value)|
5
+ describe "##{method}" do
6
+ before do
7
+ subject.send("#{method}=", value)
8
+ end
9
+
10
+ it 'should store the given value' do
11
+ subject[method].should == value
12
+ end
13
+
14
+ it "should have the value at ##{method}" do
15
+ subject.send(method).should == value
16
+ end
17
+ end
18
+ end
19
+
20
+ describe '#token=' do
21
+ before do
22
+ subject.token = '789:the-token'
23
+ end
24
+
25
+ it 'should have the token' do
26
+ subject.token.should == 'the-token'
27
+ end
28
+
29
+ it 'should have the id in the base url' do
30
+ subject.base_url.should match(/applications\/789$/)
31
+ end
32
+ end
33
+
34
+ describe 'defaults' do
35
+ subject { Chordate.config }
36
+
37
+ it 'should have the gem version' do
38
+ subject.server[:gem_version].should == Chordate::Version::STRING
39
+ end
40
+
41
+ it 'should have the hostname' do
42
+ subject.server[:hostname].should == `hostname`.chomp
43
+ end
44
+
45
+ it 'should have the root' do
46
+ subject.server[:root].should == `pwd`.chomp
47
+ end
48
+
49
+ it 'should have the environment' do
50
+ subject.env.should == 'the-environment'
51
+ end
52
+
53
+ it 'should have the base_url' do
54
+ subject.base_url.should == '/v1/applications/123'
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,75 @@
1
+ require 'spec_helper'
2
+
3
+ describe Chordate::Error do
4
+ describe '#as_json' do
5
+ let(:options) { {} }
6
+
7
+ describe "for an exception" do
8
+ let(:exception) { make_exception }
9
+
10
+ def json
11
+ Chordate::Error.new(exception, options).as_json
12
+ end
13
+
14
+ it 'should extract the error class' do
15
+ json[:klass].should == 'RuntimeError'
16
+ end
17
+
18
+ it 'should extract the error message' do
19
+ json[:message].should == 'BOOM!'
20
+ end
21
+
22
+ it 'should extract the error backtrace' do
23
+ json[:backtrace].should == exception.backtrace
24
+ end
25
+
26
+ describe 'server' do
27
+ it 'should have the gem version' do
28
+ json[:server][:gem_version].should == Chordate::Version::STRING
29
+ end
30
+
31
+ it 'should have the hostname' do
32
+ json[:server][:hostname].should == Chordate.config.server[:hostname]
33
+ end
34
+
35
+ it 'should have the app root' do
36
+ json[:server][:root].should == Chordate.config.server[:root]
37
+ end
38
+ end
39
+
40
+ describe 'when given options' do
41
+ before do
42
+ options[:extra] = { :data => 'attr' }
43
+ end
44
+
45
+ it 'should have the options' do
46
+ json[:extra][:data].should == 'attr'
47
+ end
48
+ end
49
+ end
50
+ end
51
+
52
+ describe "merge!" do
53
+ let(:options) { { :key => 'value', :other => 3, :env => nil } }
54
+ let(:exception) { make_exception }
55
+
56
+ subject { Chordate::Error.new(exception, {}) }
57
+
58
+ def merge!
59
+ subject.merge!(options)
60
+ end
61
+
62
+ it 'should return itself' do
63
+ merge!.should == subject
64
+ end
65
+
66
+ it 'should add to the keys' do
67
+ merge!.as_json[:key].should == 'value'
68
+ merge!.as_json[:other].should == 3
69
+ end
70
+
71
+ it 'should remove keys with nil values' do
72
+ merge!.as_json.keys.should_not include(:env)
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+
3
+ describe Chordate::HTTP do
4
+ describe '.post' do
5
+ let(:body) { { :key => 'value' } }
6
+
7
+ def post
8
+ Chordate::HTTP.post(:something_special, body)
9
+ end
10
+
11
+ before do
12
+ Chordate::HTTP.rspec_reset
13
+ end
14
+
15
+ it 'should post to the given path' do
16
+ Typhoeus.should_receive(:post).with('https://chordate.io/v1/applications/123/something_special.json', anything)
17
+
18
+ post
19
+ end
20
+
21
+ it 'should pass the given body' do
22
+ Typhoeus.should_receive(:post) do |_, options|
23
+ options[:body][:key].should == 'value'
24
+ end
25
+
26
+ post
27
+ end
28
+
29
+ describe 'defaults' do
30
+ it 'should send the token' do
31
+ Typhoeus.should_receive(:post) do |_, options|
32
+ options[:body][:token].should == Chordate.config.token
33
+ end
34
+
35
+ post
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,45 @@
1
+ require 'spec_helper'
2
+
3
+ describe Chordate::Runner::Event do
4
+ describe '#start' do
5
+ def start
6
+ subject.exit; subject.start
7
+ end
8
+
9
+ describe 'when there are waiting errors' do
10
+ let(:errors) do
11
+ [
12
+ { :klass => 'RuntimeError' },
13
+ { :klass => 'OtherError' }
14
+ ]
15
+ end
16
+
17
+ before do
18
+ subject.push(errors.first)
19
+ subject.push(errors.last)
20
+ end
21
+
22
+ it 'should have the errors' do
23
+ subject.errors.should == errors
24
+ end
25
+
26
+ it 'should post to events' do
27
+ Chordate::HTTP.should_receive(:post).with(:events, anything)
28
+
29
+ start
30
+ end
31
+
32
+ it 'should process the errors' do
33
+ Chordate::HTTP.should_receive(:post).with(anything, :batch => errors)
34
+
35
+ start
36
+ end
37
+
38
+ it 'should clear the errors' do
39
+ start
40
+
41
+ subject.errors.should == []
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,38 @@
1
+ require 'rspec'
2
+ require 'webmock'
3
+ require 'chordate-ruby'
4
+
5
+ RSpec.configure do |config|
6
+ config.treat_symbols_as_metadata_keys_with_true_values = true
7
+ config.run_all_when_everything_filtered = true
8
+
9
+ config.order = 'random'
10
+
11
+ config.before :all do
12
+ WebMock.disable_net_connect!
13
+ end
14
+
15
+ config.before :each do
16
+ Chordate::HTTP.stub(:post)
17
+ end
18
+ end
19
+
20
+ module Rails
21
+ def self.root
22
+ `pwd`.chomp
23
+ end
24
+
25
+ def self.env
26
+ 'the-environment'
27
+ end
28
+ end
29
+
30
+ Chordate.configure do |config|
31
+ config.token = '123:abcdef'
32
+ end
33
+
34
+ def make_exception(type = RuntimeError, message = 'BOOM!')
35
+ raise type.new message
36
+ rescue => e
37
+ e
38
+ end
metadata ADDED
@@ -0,0 +1,136 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: chordate-ruby
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - bnorton
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-01-30 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: typhoeus
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>'
20
+ - !ruby/object:Gem::Version
21
+ version: 0.5.0
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.5.0
30
+ - !ruby/object:Gem::Dependency
31
+ name: celluloid
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
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: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rspec
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: webmock
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ description: ''
79
+ email: brian.nort@gmail.com
80
+ executables: []
81
+ extensions: []
82
+ extra_rdoc_files: []
83
+ files:
84
+ - .gitignore
85
+ - .rspec
86
+ - .rvmrc
87
+ - Gemfile
88
+ - LICENSE.md
89
+ - README.md
90
+ - chordate-ruby.gemspec
91
+ - lib/chordate-ruby.rb
92
+ - lib/chordate-ruby/chordate.rb
93
+ - lib/chordate-ruby/config.rb
94
+ - lib/chordate-ruby/error.rb
95
+ - lib/chordate-ruby/http.rb
96
+ - lib/chordate-ruby/runner.rb
97
+ - lib/chordate-ruby/runner/event.rb
98
+ - lib/chordate-ruby/version.rb
99
+ - spec/lib/chordate_spec.rb
100
+ - spec/lib/config_spec.rb
101
+ - spec/lib/error_spec.rb
102
+ - spec/lib/http_spec.rb
103
+ - spec/lib/runner/event_spec.rb
104
+ - spec/spec_helper.rb
105
+ homepage: http://github.com/chordate/chordate-ruby
106
+ licenses:
107
+ - MIT
108
+ post_install_message:
109
+ rdoc_options: []
110
+ require_paths:
111
+ - lib
112
+ required_ruby_version: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ required_rubygems_version: !ruby/object:Gem::Requirement
119
+ none: false
120
+ requirements:
121
+ - - ! '>='
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ requirements: []
125
+ rubyforge_project:
126
+ rubygems_version: 1.8.25
127
+ signing_key:
128
+ specification_version: 3
129
+ summary: ''
130
+ test_files:
131
+ - spec/lib/chordate_spec.rb
132
+ - spec/lib/config_spec.rb
133
+ - spec/lib/error_spec.rb
134
+ - spec/lib/http_spec.rb
135
+ - spec/lib/runner/event_spec.rb
136
+ - spec/spec_helper.rb