Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Use exceptional to track errors

  • Loading branch information...
commit c272bb206bd3da19ee301b9893ec51c8c6a7e8fe 1 parent bee656a
@davidsmalley davidsmalley authored
Showing with 981 additions and 0 deletions.
  1. +4 −0 vendor/plugins/exceptional/.gitignore
  2. 0  vendor/plugins/exceptional/History.txt
  3. +20 −0 vendor/plugins/exceptional/Manifest
  4. +13 −0 vendor/plugins/exceptional/README
  5. +40 −0 vendor/plugins/exceptional/Rakefile
  6. +34 −0 vendor/plugins/exceptional/exceptional.gemspec
  7. +34 −0 vendor/plugins/exceptional/exceptional.yml
  8. +20 −0 vendor/plugins/exceptional/init.rb
  9. +19 −0 vendor/plugins/exceptional/install.rb
  10. +235 −0 vendor/plugins/exceptional/lib/exceptional.rb
  11. +56 −0 vendor/plugins/exceptional/lib/exceptional/agent/worker.rb
  12. +86 −0 vendor/plugins/exceptional/lib/exceptional/deployed_environment.rb
  13. +46 −0 vendor/plugins/exceptional/lib/exceptional/exception_data.rb
  14. +20 −0 vendor/plugins/exceptional/lib/exceptional/integration/rails.rb
  15. +53 −0 vendor/plugins/exceptional/lib/exceptional/rails.rb
  16. +9 −0 vendor/plugins/exceptional/lib/exceptional/version.rb
  17. +168 −0 vendor/plugins/exceptional/spec/deployed_environment_spec.rb
  18. +34 −0 vendor/plugins/exceptional/spec/exception_data_spec.rb
  19. +67 −0 vendor/plugins/exceptional/spec/exceptional_spec.rb
  20. +2 −0  vendor/plugins/exceptional/spec/spec_helper.rb
  21. +21 −0 vendor/plugins/exceptional/spec/worker_spec.rb
View
4 vendor/plugins/exceptional/.gitignore
@@ -0,0 +1,4 @@
+.DS_Store
+coverage
+doc
+pkg/*
View
0  vendor/plugins/exceptional/History.txt
No changes.
View
20 vendor/plugins/exceptional/Manifest
@@ -0,0 +1,20 @@
+exceptional.gemspec
+exceptional.yml
+History.txt
+init.rb
+install.rb
+lib/exceptional/agent/worker.rb
+lib/exceptional/deployed_environment.rb
+lib/exceptional/exception_data.rb
+lib/exceptional/integration/rails.rb
+lib/exceptional/rails.rb
+lib/exceptional/version.rb
+lib/exceptional.rb
+Manifest
+Rakefile
+README
+spec/deployed_environment_spec.rb
+spec/exception_data_spec.rb
+spec/exceptional_spec.rb
+spec/spec_helper.rb
+spec/worker_spec.rb
View
13 vendor/plugins/exceptional/README
@@ -0,0 +1,13 @@
+= Exceptional plugin for Ruby on Rails
+
+This plugin posts exception data to Exceptional (http://getexceptional.com). Data about the request, session, environment and a backtrace of the exception is transmitted.
+
+= Exceptional
+
+Exceptional (http://getexceptional.com) is a web-based exception tracking and managing system for Ruby on Rails folks and their apps. It's currently in early, private beta.
+
+= Installation
+
+For installation instructions, follow the steps displayed after adding a new app to your account. Please send any questions or comments to hello@getexceptional.com.
+
+Copyright © 2008 Contrast.
View
40 vendor/plugins/exceptional/Rakefile
@@ -0,0 +1,40 @@
+begin
+ require 'echoe'
+
+ Echoe.new('exceptional', '0.0.1') do |p|
+ p.rubyforge_name = 'exceptional'
+ p.summary = "Exceptional is the core Ruby library for communicating with http://getexceptional.com (hosted error tracking service)"
+ p.description = "Exceptional is the core Ruby library for communicating with http://getexceptional.com (hosted error tracking service)"
+ p.url = "http://getexceptional.com/"
+ p.author = ['David Rice']
+ p.email = "david@contrast.ie"
+ p.dependencies = ["json"]
+ end
+
+rescue LoadError => e
+ puts "You are missing a dependency required for meta-operations on this gem."
+ puts "#{e.to_s.capitalize}."
+end
+
+# add spec tasks, if you have rspec installed
+begin
+ require 'spec/rake/spectask'
+
+ Spec::Rake::SpecTask.new("spec") do |t|
+ t.spec_files = FileList['spec/**/*_spec.rb']
+ t.spec_opts = ['--color']
+ end
+
+ task :test do
+ Rake::Task['spec'].invoke
+ end
+
+ Spec::Rake::SpecTask.new("coverage") do |t|
+ t.spec_files = FileList['spec/**/*_spec.rb']
+ t.spec_opts = ['--color']
+ t.rcov = true
+ t.rcov_opts = ['--exclude', '^spec,/gems/']
+ end
+
+
+end
View
34 vendor/plugins/exceptional/exceptional.gemspec
@@ -0,0 +1,34 @@
+Gem::Specification.new do |s|
+ s.name = "exceptional"
+ s.version = "0.0.1"
+ s.date = "2008-10-13"
+ s.summary = "Exceptional is the core Ruby library for communicating with http://getexceptional.com (hosted error tracking service)"
+ s.email = "david@getexceptional.com"
+ s.homepage = "http://github.com/contrast/exceptional"
+ s.description = "Exceptional is the core Ruby library for communicating with http://getexceptional.com (hosted error tracking service)"
+ s.has_rdoc = true
+ s.authors = ["David Rice", "Paul Campbell"]
+ s.files = ["History.txt",
+ "Manifest",
+ "README",
+ "Rakefile",
+ "exceptional.gemspec",
+ "exceptional.yml",
+ "init.rb",
+ "install.rb",
+ "lib/exceptional/agent/worker.rb",
+ "lib/exceptional/deployed_environment.rb",
+ "lib/exceptional/exception_data.rb",
+ "lib/exceptional/integration/rails.rb",
+ "lib/exceptional/rails.rb",
+ "lib/exceptional/version.rb",
+ "lib/exceptional.rb"]
+ s.test_files = ["spec/deployed_environment_spec.rb",
+ "spec/exception_data_spec.rb",
+ "spec/exceptional_spec.rb",
+ "spec/spec_helper.rb",
+ "spec/worker_spec.rb"]
+ s.rdoc_options = ["--main", "README"]
+ s.extra_rdoc_files = ["History.txt", "Manifest", "README"]
+ s.add_dependency("json", ["> 0.0.0"])
+end
View
34 vendor/plugins/exceptional/exceptional.yml
@@ -0,0 +1,34 @@
+# here are the settings that are common to all environments
+common: &default_settings
+ # You must specify your Exceptional API key here.
+ api-key: PASTE_YOUR_API_KEY_HERE
+ # Exceptional creates a separate log file from your application's logs
+ # available levels are debug, info, warn, error, fatal
+ log-level: info
+ # The exceptional agent sends data via regular http by default
+ # Setting this value to true will send data over SSL, increasing security
+ # There will be an additional CPU overhead in encrypting the data, however
+ # as long as your deployment environment is not Passenger (mod_rails), this
+ # happens in the background so as not to incur a page wait for your users.
+ ssl: false
+
+development:
+ <<: *default_settings
+ # Normally no reason to collect exceptions in development
+ # NOTE: for trial purposes you may want to enable exceptional in development
+ enabled: false
+
+test:
+ <<: *default_settings
+ # No reason to collect exceptions when running tests by default
+ enabled: false
+
+production:
+ <<: *default_settings
+ enabled: true
+
+staging:
+ # It's common development practice to have a staging environment that closely
+ # mirrors production, by default catch errors in this environment too.
+ <<: *default_settings
+ enabled: true
View
20 vendor/plugins/exceptional/init.rb
@@ -0,0 +1,20 @@
+require 'exceptional'
+
+def to_stderr(s)
+ STDERR.puts "** [Exceptional] " + s
+end
+
+config_file = File.join(RAILS_ROOT,"/config/exceptional.yml")
+
+begin
+ Exceptional.application_root = RAILS_ROOT
+ Exceptional.environment = RAILS_ENV
+
+ Exceptional.load_config(config_file)
+ if Exceptional.enabled?
+ Exceptional::Rails.init
+ end
+rescue Exception => e
+ to_stderr e
+ to_stderr "Plugin disabled."
+end
View
19 vendor/plugins/exceptional/install.rb
@@ -0,0 +1,19 @@
+# This is the post install hook for when Exceptional is installed as a plugin.
+require 'ftools'
+
+# puts IO.read(File.join(File.dirname(__FILE__), 'README'))
+
+config_file = File.expand_path("#{File.dirname(__FILE__)}/../../../config/exceptional.yml")
+example_config_file = "#{File.dirname(__FILE__)}/exceptional.yml"
+
+if File::exists? config_file
+ puts "Exceptional config file already exists. Please ensure it is up-to-date with the current format."
+ puts "See #{example_config_file}"
+else
+ puts "Installing default Exceptional config"
+ puts " From #{example_config_file}"
+ puts "For exceptional to work you need to configure your API Key"
+ puts " See #{example_config_file}"
+ puts "If you don't have an API Key, get one at http://getexceptional.com/"
+ File.copy example_config_file, config_file
+end
View
235 vendor/plugins/exceptional/lib/exceptional.rb
@@ -0,0 +1,235 @@
+$:.unshift File.dirname(__FILE__)
+require 'exceptional/rails'
+require 'exceptional/deployed_environment'
+require 'exceptional/agent/worker'
+require 'exceptional/exception_data'
+require 'exceptional/version'
+require 'rubygems'
+
+require 'zlib'
+require 'cgi'
+require 'net/http'
+require 'logger'
+require 'yaml'
+require 'json' unless defined? Rails
+# Hack to force Rails version prior to 2.0 to use quoted JSON as per the JSON standard... (TODO: could be cleaner!)
+ActiveSupport::JSON.unquote_hash_key_identifiers = false if (defined?(ActiveSupport::JSON) && ActiveSupport::JSON.respond_to?(:unquote_hash_key_identifiers))
+
+module Exceptional
+ class LicenseException < StandardError; end
+ class ConfigurationException < StandardError; end
+
+ ::PROTOCOL_VERSION = 3
+ # Defaults for configuration variables
+ ::REMOTE_HOST = "getexceptional.com"
+ ::REMOTE_PORT = 80
+ ::REMOTE_SSL_PORT = 443
+ ::SSL = false
+ ::LOG_LEVEL = 'info'
+ ::LOG_PATH = nil
+ ::WORKER_TIMEOUT = 10 # seconds
+ ::MODE = :direct
+
+ class << self
+ attr_accessor :api_key, :log, :deployed_environment, :log_path, :worker,
+ :worker_thread, :environment, :application_root
+ attr_writer :remote_host, :remote_port, :ssl_enabled, :log_level
+
+ # rescue any exceptions within the given block,
+ # send it to exceptional,
+ # then raise
+ def rescue(&block)
+ begin
+ block.call
+ rescue Exception => e
+ self.catch(e)
+ raise(e)
+ end
+ end
+
+ # parse an exception into an ExceptionData object
+ def parse(exception)
+ exception_data = ExceptionData.new
+ exception_data.exception_backtrace = exception.backtrace
+ exception_data.exception_message = exception.message
+ exception_data.exception_class = exception.class.to_s
+ exception_data
+ end
+
+ # authenticate with getexceptional.com
+ # returns true if the configured api_key is registered and can send data
+ # otherwise false
+ def authenticate
+ begin
+ # TODO No data required to authenticate, send a nil string? hacky
+ # TODO should retry if a http connection failed
+ return @authenticated if @authenticated
+ authenticated = call_remote(:authenticate, "")
+ @authenticated = authenticated =~ /true/
+ rescue
+ @authenticated = false
+ ensure
+ return @authenticated
+ end
+ end
+
+ # post the given exception data to getexceptional.com
+ def post(exception_data)
+ hash = exception_data.to_hash
+ hash[:session].delete("initialization_options")
+ hash[:session].delete("request")
+ call_remote(:errors, hash.to_json)
+ end
+
+ # given a regular ruby Exception class, will parse into an ExceptionData
+ # object and post to getexceptional.com
+ def catch(exception)
+ exception_data = parse(exception)
+ exception_data.controller_name = File.basename($0)
+ post(exception_data)
+ end
+
+ # used with Rails, takes an exception, controller, request and parameters
+ # creates an ExceptionData object
+ # if Exceptional is running in :direct mode, will post to getexceptional.com
+ # if Exceptional is running in :queue mode, the data will be queued and posted later
+ def handle(exception, controller, request, params)
+ log! "Handling #{exception.message}", 'info'
+ e = parse(exception)
+ # Additional data for Rails Exceptions
+ e.framework = "rails"
+ e.controller_name = controller.controller_name
+ e.action_name = controller.action_name
+ e.application_root = self.application_root
+ e.occurred_at = Time.now.strftime("%Y%m%d %H:%M:%S %Z")
+ e.environment = request.env.to_hash
+ e.url = "#{request.protocol}#{request.host}#{request.request_uri}"
+ # Need to remove rack data from environment hash
+ safe_environment = request.env.to_hash
+ safe_environment.delete_if { |k,v| k =~ /rack/ }
+ e.environment = safe_environment
+
+ safe_session = {}
+ request.session.instance_variables.each do |v|
+ next if v =~ /cgi/
+ next if v =~ /db/
+ # remove prepended @'s
+ var = v.sub("@","")
+ safe_session[var] = request.session.instance_variable_get(v)
+ end
+
+ e.session = safe_session
+ e.parameters = params.to_hash
+
+ if mode == :queue
+ worker.add_exception(e)
+ else # :direct mode
+ begin
+ post e
+ rescue Exception => exception
+ log! "Error posting data to Exceptional."
+ log! exception.message
+ log! exception.backtrace.join("\n"), 'debug'
+ end
+ end
+ end
+
+ # TODO these configuration methods & defaults should have their own class
+ def remote_host
+ @remote_host || ::REMOTE_HOST
+ end
+
+ def remote_port
+ @remote_port || default_port
+ end
+
+ def log_level
+ @log_level || ::LOG_LEVEL
+ end
+
+ def mode
+ deployed_environment ? deployed_environment.determine_mode : ::MODE
+ end
+
+ def default_port
+ ssl_enabled? ? ::REMOTE_SSL_PORT : ::REMOTE_PORT
+ end
+
+ def ssl_enabled?
+ @ssl_enabled || ::SSL
+ end
+
+ def enabled?
+ @enabled
+ end
+
+ def log!(msg, level = 'info')
+ to_stderr msg
+ log.send level, msg if log
+ end
+
+ def to_stderr(msg)
+ if deployed_environment && deployed_environment.server != :unknown
+ STDERR.puts "** [Exceptional] " + msg
+ end
+ end
+
+ def log_config_info
+ log! "API Key: #{api_key}", 'debug'
+ log! "Deployed Environment: #{deployed_environment.to_s}", 'debug'
+ log! "Remote Host: #{remote_host}:#{remote_port}", 'debug'
+ log! "Mode: #{mode}", 'debug'
+ log! "Log level: #{log_level}", 'debug'
+ log! "Log path: #{log_path}", 'debug'
+ end
+
+ def load_config(file)
+ begin
+ config = YAML::load(File.open(file))[self.environment]
+ @api_key = config['api-key'] unless config['api-key'].nil?
+ @ssl_enabled = config['ssl'] unless config['ssl'].nil?
+ @log_level = config['log-level'] unless config['log-level'].nil?
+ @enabled = config['enabled'] unless config['enabled'].nil?
+ @remote_port = config['remote-port'].to_i unless config['remote-port'].nil?
+ @remote_host = config['remote-host'] unless config['remote-host'].nil?
+ rescue Exception => e
+ raise ConfigurationException.new("Unable to load configuration file:#{file} for environment:#{environment}")
+ end
+ end
+
+ protected
+
+ def valid_api_key?
+ @api_key && @api_key.length == 40
+ end
+
+
+ def call_remote(method, data)
+ if @api_key.nil?
+ raise LicenseException.new("API Key must be configured")
+ end
+
+ http = Net::HTTP.new(remote_host, remote_port)
+ http.use_ssl = true if ssl_enabled?
+ uri = "/#{method.to_s}?&api_key=#{@api_key}&protocol_version=#{::PROTOCOL_VERSION}"
+ headers = { 'Content-Type' => 'application/x-gzip', 'Accept' => 'application/x-gzip' }
+ compressed_data = CGI::escape(Zlib::Deflate.deflate(data, Zlib::BEST_SPEED))
+ response = http.start do |http|
+ http.post(uri, compressed_data, headers)
+ end
+
+ if response.kind_of? Net::HTTPSuccess
+ return response.body
+ else
+ raise Exception.new("#{response.code}: #{response.message}")
+ end
+
+ rescue Exception => e
+ log! "Error contacting Exceptional: #{e}", 'info'
+ log! e.backtrace.join("\n"), 'debug'
+ raise e
+ end
+
+ end
+
+end
View
56 vendor/plugins/exceptional/lib/exceptional/agent/worker.rb
@@ -0,0 +1,56 @@
+# The Worker is used by Exceptional when started in :queue mode
+# It queues and sends exception data asynchronously.
+
+module Exceptional::Agent
+
+ class Worker
+
+ attr_reader :log, :timeout, :exceptions
+
+ def initialize(log = Logger.new(STDERR))
+ @exceptions = []
+ @timeout = ::WORKER_TIMEOUT
+ @mutex = Mutex.new
+ @log = log
+ @log.info "Started Exceptional Worker."
+ end
+
+ def add_exception(data)
+ @mutex.synchronize do
+ @exceptions << data
+ end
+ end
+
+ def run
+ while(true) do
+ send_any_exception_data
+ end
+ end
+
+ private
+
+ def exception_to_send
+ @mutex.synchronize do
+ return @exceptions.shift
+ end
+ end
+
+
+ def send_any_exception_data
+ if @exceptions.empty?
+ sleep @timeout
+ return
+ end
+
+ data = exception_to_send
+
+ begin
+ Exceptional.post(data)
+ rescue Exception => e
+ @log.error "Error sending exception data: #{e}"
+ @log.debug e.backtrace.join("\n")
+ end
+ end
+
+ end
+end
View
86 vendor/plugins/exceptional/lib/exceptional/deployed_environment.rb
@@ -0,0 +1,86 @@
+# This class is used to determine the server environment
+module Exceptional
+ class DeployedEnvironment
+
+ attr_reader :server, :identifier, :hostname#, :environment
+
+ def initialize
+ @server = :unknown
+ @identifier = nil
+
+ @hostname = determine_host
+ # @environment = Exceptional.environment
+ # @application_root = Exceptional.application_root
+
+ servers = %w[webrick mongrel thin litespeed passenger]
+ while servers.any? && @identifier.nil?
+ send 'is_'+(servers.shift)+'?'
+ end
+
+ end
+
+ def should_start_worker?
+ !identifier.nil?
+ end
+
+ def determine_mode
+ @server == :passenger ? :direct : :queue
+ end
+
+ def to_s
+ "#{@hostname}:#{@identifier} [#{@server}]"
+ end
+
+ def determine_host
+ Socket.gethostname
+ end
+
+ def is_webrick?
+ if defined?(OPTIONS) && defined?(DEFAULT_PORT) && OPTIONS.respond_to?(:fetch)
+ # OPTIONS is set by script/server if launching webrick
+ @identifier = OPTIONS.fetch :port, DEFAULT_PORT
+ @server = :webrick
+ end
+ end
+
+ def is_mongrel?
+ if defined? Mongrel::HttpServer
+ ObjectSpace.each_object(Mongrel::HttpServer) do |mongrel|
+ next unless mongrel.respond_to? :port
+ @server = :mongrel
+ @identifier = mongrel.port
+ end
+ end
+ end
+
+ def is_thin?
+ if defined? Thin::Server
+ ObjectSpace.each_object(Thin::Server) do |thin_server|
+ @server = :thin
+ backend = thin_server.backend
+ if backend.respond_to? :port
+ @identifier = backend.port
+ elsif backend.respond_to? :socket
+ @identifier = backend.socket
+ end
+ end
+ end
+ end
+
+ def is_litespeed?
+ if caller.pop =~ /fcgi-bin\/RailsRunner\.rb/
+ @server = :litespeed
+ @identifier = 'litespeed'
+ end
+ end
+
+ def is_passenger?
+ if defined? Passenger::AbstractServer
+ @server = :passenger
+ @identifier = 'passenger'
+ end
+ end
+
+ end
+end
+
View
46 vendor/plugins/exceptional/lib/exceptional/exception_data.rb
@@ -0,0 +1,46 @@
+# This class encapsulates data about an exception.
+module Exceptional
+ class DataError < StandardError; end
+
+ class ExceptionData
+ ::LANGUAGE = "ruby"
+ ::BASE_ATTRS = [:exception_class, :exception_message, :exception_backtrace]
+ ::OPTIONAL_ATTRS = [:framework, :controller_name, :action_name, :application_root,
+ :url, :occurred_at, :environment, :session, :parameters]
+ ::ACCESSIBLE_ATTRS = ::BASE_ATTRS + ::OPTIONAL_ATTRS
+ ::ATTRS = [:language] + ::ACCESSIBLE_ATTRS
+
+ ::ACCESSIBLE_ATTRS.each do |attribute|
+ attr_accessor attribute
+ end
+ attr_reader :language
+
+ def initialize
+ environment = {}
+ session = {}
+ parameters = {}
+ @language = ::LANGUAGE
+ end
+
+ def valid?
+ ::BASE_ATTRS.each do |method|
+ raise(DataError, "base data #{method} not set") if (self.send(method).nil? || self.send(method).empty?)
+ end
+ end
+
+ def to_hash
+ hash = {}
+ ::ATTRS.each do |attribute|
+ value = send(attribute)
+ hash[attribute] = value unless (value.nil? || value.empty? || attribute.is_a?(TCPSocket) || attribute.is_a?(TCPServer))
+ end
+ hash
+ end
+
+ def to_json
+ self.to_hash.to_json
+ end
+
+ end
+
+end
View
20 vendor/plugins/exceptional/lib/exceptional/integration/rails.rb
@@ -0,0 +1,20 @@
+if defined? ActionController
+
+module ActionController
+ class Base
+ def rescue_action_with_exceptional(exception)
+
+ params_to_send = (respond_to? :filter_parameters) ? filter_parameters(params) : params
+
+ Exceptional.handle(exception, self, request, params_to_send)
+
+ rescue_action_without_exceptional exception
+ end
+
+ alias_method :rescue_action_without_exceptional, :rescue_action
+ alias_method :rescue_action, :rescue_action_with_exceptional
+ protected :rescue_action
+ end
+end
+
+end
View
53 vendor/plugins/exceptional/lib/exceptional/rails.rb
@@ -0,0 +1,53 @@
+module Exceptional
+
+ class Rails
+
+ def self.init
+ Exceptional.deployed_environment = DeployedEnvironment.new
+ # If no server environment detected, don't perform magic.
+ # Means rake, script/console etc perform as expected
+ return false unless Exceptional.deployed_environment.should_start_worker?
+
+ setup_log
+ Exceptional.log_config_info
+
+ if Exceptional.authenticate
+
+ if Exceptional.mode == :queue
+ Exceptional.worker = Agent::Worker.new(Exceptional.log)
+ Exceptional.worker_thread = Thread.new do
+ Exceptional.worker.run
+ end
+ end
+
+ require File.join(File.dirname(__FILE__), 'integration', 'rails')
+
+
+ at_exit do
+ if Exceptional.mode == :queue
+ Exceptional.worker_thread.terminate if Exceptional.worker_thread
+ end
+ end
+ else
+ Exceptional.log! "Plugin not authenticated, check your API Key"
+ Exceptional.log! "Disabling Plugin."
+ end
+ end
+
+ def self.setup_log
+ log_file = "#{Exceptional.application_root}/log/exceptional.log"
+
+ @log = Logger.new log_file
+ @log.level = Logger::INFO
+
+ allowed_log_levels = ['debug', 'info', 'warn', 'error', 'fatal']
+ if Exceptional.log_level && allowed_log_levels.include?(Exceptional.log_level)
+ @log.level = eval("Logger::#{Exceptional.log_level.upcase}")
+ end
+
+ Exceptional.log = @log
+ end
+
+ end
+
+end
View
9 vendor/plugins/exceptional/lib/exceptional/version.rb
@@ -0,0 +1,9 @@
+module Exceptional #:nodoc:
+ module VERSION #:nodoc:
+ MAJOR = 0
+ MINOR = 0
+ TINY = 1
+
+ STRING = [MAJOR, MINOR, TINY].join('.')
+ end
+end
View
168 vendor/plugins/exceptional/spec/deployed_environment_spec.rb
@@ -0,0 +1,168 @@
+require File.dirname(__FILE__) + '/spec_helper'
+
+describe Exceptional::DeployedEnvironment do
+
+ describe "mongrel" do
+
+ before(:all) do
+ class << self
+ module ::Mongrel
+ class HttpServer
+ def port; 3000; end
+ end
+ end
+ end
+
+ Mongrel::HttpServer.new
+ @deployed_environment = Exceptional::DeployedEnvironment.new
+ end
+
+ it "server should be mongrel" do
+ @deployed_environment.server.should == :mongrel
+ end
+
+ it "identifier should be 3000" do
+ @deployed_environment.identifier.should == 3000
+ end
+
+ it "to_s should match" do
+ pending
+ @deployed_environment.to_s.should match(/(:3000 \[:mongrel\])/)
+ end
+
+ it "mode should be queue" do
+ @deployed_environment.determine_mode.should == :queue
+ end
+
+ after(:all) do
+ ObjectSpace.garbage_collect
+ end
+
+ end
+
+ describe "webrick" do
+
+ before(:all) do
+ class MockOptions
+ def fetch(*args)
+ 3001
+ end
+ end
+
+ class << self
+ ::OPTIONS = MockOptions.new
+ ::DEFAULT_PORT = 3000
+ end
+ @deployed_environment = Exceptional::DeployedEnvironment.new
+ end
+
+ it "server should be webrick" do
+ @deployed_environment.server.should == :webrick
+ end
+
+ it "identifier should be 3001" do
+ @deployed_environment.identifier.should == 3001
+ end
+
+ it "mode should be queue" do
+ @deployed_environment.determine_mode.should == :queue
+ end
+
+ after(:all) do
+ ObjectSpace.garbage_collect
+ Object.class_eval { remove_const :OPTIONS }
+ Object.class_eval { remove_const :DEFAULT_PORT }
+ end
+
+ end
+
+ describe "thin" do
+
+ before(:all) do
+ class << self
+ module ::Thin
+ class Server
+ def backend; self; end
+ def socket; "/socket/file.000"; end
+ end
+ end
+ end
+ Thin::Server.new
+
+ @deployed_environment = Exceptional::DeployedEnvironment.new
+ end
+
+ it "server should be thin" do
+ @deployed_environment.server.should == :thin
+ end
+
+ it "identifier should be the socket file" do
+ @deployed_environment.identifier.should == '/socket/file.000'
+ end
+
+ it "mode should be queue" do
+ @deployed_environment.determine_mode.should == :queue
+ end
+
+ after(:all) do
+ ObjectSpace.garbage_collect
+ end
+
+ end
+
+ describe "litespeed" do
+
+ before(:all) do
+ @deployed_environment = Exceptional::DeployedEnvironment.new
+ end
+
+ # Hmmph, how to determine if we're running under litespeed...
+ it "server should be unknown" do
+ @deployed_environment.server.should == :unknown
+ end
+
+ it "identifier should be nil" do
+ @deployed_environment.identifier.should == nil
+ end
+
+ it "mode should be queue" do
+ @deployed_environment.determine_mode.should == :queue
+ end
+
+ after(:all) do
+ ObjectSpace.garbage_collect
+ end
+
+ end
+
+ describe "passenger" do
+
+ before(:all) do
+ class << self
+ module ::Passenger
+ const_set "AbstractServer", 0
+ end
+ end
+ @deployed_environment = Exceptional::DeployedEnvironment.new
+ end
+
+ it "server should be passenger" do
+ @deployed_environment.server.should == :passenger
+ end
+
+ # Would be nicer to identify passenger by some
+ it "identifier should be passenger" do
+ @deployed_environment.identifier.should == 'passenger'
+ end
+
+ it "mode should be queue" do
+ @deployed_environment.determine_mode.should == :direct
+ end
+
+ after(:all) do
+ ObjectSpace.garbage_collect
+ end
+
+ end
+
+end
View
34 vendor/plugins/exceptional/spec/exception_data_spec.rb
@@ -0,0 +1,34 @@
+require File.dirname(__FILE__) + '/spec_helper'
+
+describe Exceptional::ExceptionData do
+
+ describe "with valid base data" do
+
+ before(:each) do
+ @exception_data = Exceptional::ExceptionData.new
+ @exception_data.exception_class = "Error"
+ @exception_data.exception_message = "There was an error"
+ @exception_data.exception_backtrace = "/var/www/app/fail.rb:42 Error('There was an error')"
+ end
+
+ it "language should be ruby" do
+ @exception_data.language.should == "ruby"
+ end
+
+ it "should be valid" do
+ @exception_data.should be_valid
+ end
+
+ it "should convert to hash" do
+ @exception_data.to_hash.should == {:exception_class => "Error", :exception_message => "There was an error",
+ :exception_backtrace => "/var/www/app/fail.rb:42 Error('There was an error')",
+ :language => "ruby"}
+ end
+
+ it "should convert to json" do
+ @exception_data.to_json.class.should == String
+ end
+
+ end
+
+end
View
67 vendor/plugins/exceptional/spec/exceptional_spec.rb
@@ -0,0 +1,67 @@
+require File.dirname(__FILE__) + '/spec_helper'
+
+describe Exceptional do
+
+ describe "with no configuration" do
+ it "should connect to getexceptional.com by default" do
+ Exceptional.remote_host.should == "getexceptional.com"
+ end
+
+ it "should connect to port 80 by default" do
+ Exceptional.remote_port.should == 80
+ end
+
+ it "should parse exception into exception data object" do
+ exception = mock(Exception, :message => "Something bad has happened", :backtrace => ["/app/controllers/buggy_controller.rb:29:in `index'"])
+ exception_data = Exceptional.parse(exception)
+ exception_data.kind_of?(Exceptional::ExceptionData).should be_true
+ exception_data.exception_message.should == exception.message
+ exception_data.exception_backtrace.should == exception.backtrace
+ exception_data.exception_class.should == exception.class.to_s
+ end
+
+ it "should post exception" do
+ exception_data = mock(Exceptional::ExceptionData, :message => "Something bad has happened", :backtrace => ["/app/controllers/buggy_controller.rb:29:in `index'"], :class => Exception)
+ Exceptional.should_receive(:call_remote, :with => [:errors, exception_data])
+ Exceptional.post(exception_data)
+ end
+
+ it "should catch exception" do
+ exception = mock(Exception, :message => "Something bad has happened", :backtrace => ["/app/controllers/buggy_controller.rb:29:in `index'"])
+ exception_data = mock(Exceptional::ExceptionData, :message => "Something bad has happened", :backtrace => ["/app/controllers/buggy_controller.rb:29:in `index'"], :class => Exception)
+ Exceptional.should_receive(:parse, :with => [exception]).and_return(exception_data)
+ Exceptional.should_receive(:post, :with => [exception_data])
+ Exceptional.catch(exception)
+ end
+
+ it "should raise a license exception if api key is not set" do
+ exception_data = mock(Exceptional::ExceptionData, :message => "Something bad has happened", :backtrace => ["/app/controllers/buggy_controller.rb:29:in `index'"], :class => Exception)
+ Exceptional.api_key.should == nil
+ lambda { Exceptional.post(exception_data) }.should raise_error(Exceptional::LicenseException)
+ end
+
+ end
+
+ describe "with a custom host" do
+
+ it "should overwrite default host" do
+ Exceptional.remote_host = "localhost"
+ Exceptional.remote_host.should == "localhost"
+ end
+
+ it "should overwrite default port" do
+ Exceptional.remote_port = 3000
+ Exceptional.remote_port.should == 3000
+ Exceptional.remote_port = nil
+ end
+ end
+
+ describe "with ssl enabled" do
+
+ it "should connect to port 443" do
+ Exceptional.ssl_enabled = true
+ Exceptional.remote_port.should == 443
+ end
+
+ end
+end
View
2  vendor/plugins/exceptional/spec/spec_helper.rb
@@ -0,0 +1,2 @@
+require 'rubygems'
+require File.dirname(__FILE__) + '/../lib/exceptional'
View
21 vendor/plugins/exceptional/spec/worker_spec.rb
@@ -0,0 +1,21 @@
+require File.dirname(__FILE__) + '/spec_helper'
+
+describe Exceptional::Agent::Worker do
+
+ before(:each) do
+ @worker = Exceptional::Agent::Worker.new
+ end
+
+ describe "after initialisation" do
+
+ it "should default worker timeout" do
+ @worker.timeout.should == 10
+ end
+
+ it "should have no exceptions" do
+ @worker.exceptions.should == []
+ end
+
+ end
+
+end
Please sign in to comment.
Something went wrong with that request. Please try again.