crashlog 0.0.1 → 0.0.2

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.
Files changed (77) hide show
  1. data/.gitignore +2 -1
  2. data/.rspec +1 -0
  3. data/.travis.yml +11 -0
  4. data/Gemfile +10 -0
  5. data/Gemfile.lock +161 -0
  6. data/INSTALL +22 -0
  7. data/README.md +14 -4
  8. data/Rakefile +13 -0
  9. data/crashlog.gemspec +12 -3
  10. data/generators/crashlog/templates/initializer.rb +6 -0
  11. data/install.rb +2 -0
  12. data/lib/crash_log/backtrace/line.rb +105 -0
  13. data/lib/crash_log/backtrace/line_cache.rb +23 -0
  14. data/lib/crash_log/backtrace.rb +66 -0
  15. data/lib/crash_log/configuration.bak.rb +199 -0
  16. data/lib/crash_log/configuration.rb +188 -0
  17. data/lib/crash_log/logging.rb +51 -0
  18. data/lib/crash_log/payload.rb +157 -0
  19. data/lib/crash_log/rack.rb +47 -0
  20. data/lib/crash_log/rails/action_controller_rescue.rb +32 -0
  21. data/lib/crash_log/rails/controller_methods.rb +45 -0
  22. data/lib/crash_log/rails/middleware/debug_exception_catcher.rb +43 -0
  23. data/lib/crash_log/rails.rb +32 -0
  24. data/lib/crash_log/railtie.rb +41 -0
  25. data/lib/crash_log/reporter.rb +105 -0
  26. data/lib/crash_log/system_information.rb +64 -0
  27. data/lib/crash_log/templates/payload.rabl +7 -0
  28. data/lib/crash_log/version.rb +1 -1
  29. data/lib/crash_log.rb +118 -0
  30. data/lib/faraday/request/hmac_authentication.rb +73 -0
  31. data/lib/rails/generators/crashlog/crashlog_generator.rb +42 -0
  32. data/rails/init.rb +1 -0
  33. data/spec/crash_log/backtrace_spec.rb +79 -0
  34. data/spec/crash_log/initializer_spec.rb +53 -0
  35. data/spec/crash_log/payload_spec.rb +124 -0
  36. data/spec/crash_log/reporter_spec.rb +179 -0
  37. data/spec/crash_log_spec.rb +153 -0
  38. data/spec/dummy/README.rdoc +261 -0
  39. data/spec/dummy/Rakefile +7 -0
  40. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  41. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  42. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  43. data/spec/dummy/app/controllers/break_controller.rb +10 -0
  44. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  45. data/spec/dummy/app/mailers/.gitkeep +0 -0
  46. data/spec/dummy/app/models/.gitkeep +0 -0
  47. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  48. data/spec/dummy/config/application.rb +59 -0
  49. data/spec/dummy/config/boot.rb +10 -0
  50. data/spec/dummy/config/database.yml +44 -0
  51. data/spec/dummy/config/environment.rb +5 -0
  52. data/spec/dummy/config/environments/development.rb +37 -0
  53. data/spec/dummy/config/environments/production.rb +67 -0
  54. data/spec/dummy/config/environments/test.rb +37 -0
  55. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  56. data/spec/dummy/config/initializers/crashlog.rb +6 -0
  57. data/spec/dummy/config/initializers/inflections.rb +15 -0
  58. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  59. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  60. data/spec/dummy/config/initializers/session_store.rb +8 -0
  61. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  62. data/spec/dummy/config/routes.rb +6 -0
  63. data/spec/dummy/config.ru +4 -0
  64. data/spec/dummy/lib/assets/.gitkeep +0 -0
  65. data/spec/dummy/log/.gitkeep +0 -0
  66. data/spec/dummy/public/404.html +26 -0
  67. data/spec/dummy/public/422.html +26 -0
  68. data/spec/dummy/public/500.html +25 -0
  69. data/spec/dummy/public/favicon.ico +0 -0
  70. data/spec/dummy/script/rails +6 -0
  71. data/spec/requests/rack_spec.rb +29 -0
  72. data/spec/requests/rails_controller_rescue_spec.rb +46 -0
  73. data/spec/spec_helper.rb +23 -0
  74. data/spec/support/doing.rb +1 -0
  75. data/spec/support/dummy_app.rb +13 -0
  76. data/spec/support/hash_ext.rb +7 -0
  77. metadata +197 -7
@@ -0,0 +1,45 @@
1
+ module CrashLog
2
+ module Rails
3
+ module ControllerMethods
4
+
5
+ def crash_log_context
6
+ { :parameters => crash_log_filter_if_filtering(params.to_hash),
7
+ :session_data => crash_log_filter_if_filtering(crash_log_session_data),
8
+ :controller => params[:controller],
9
+ :action => params[:action],
10
+ :url => crash_log_request_url,
11
+ :cgi_data => crash_log_filter_if_filtering(request.env)
12
+ }
13
+ end
14
+
15
+ private
16
+ def notify_crashlog(exception, custom_data = nil)
17
+ request_data = crash_log_context
18
+ #request_data[:meta_data][:custom] = custom_data if custom_data
19
+ CrashLog.notify(exception, request_data)
20
+ end
21
+
22
+ alias_method :notify_airbrake, :notify_crashlog
23
+
24
+ def crash_log_session_data
25
+ if session.respond_to?(:to_hash)
26
+ session.to_hash
27
+ else
28
+ session.data
29
+ end
30
+ end
31
+
32
+ def crash_log_request_url
33
+ url = "#{request.protocol}#{request.host}"
34
+
35
+ unless [80, 443].include?(request.port)
36
+ url << ":#{request.port}"
37
+ end
38
+
39
+ url << request.fullpath
40
+ url
41
+ end
42
+
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,43 @@
1
+ module CrashLog
2
+ module Rails
3
+ module Middleware
4
+ module DebugExceptionCatcher
5
+
6
+ def self.included(base)
7
+ base.send(:alias_method_chain, :render_exception, :crash_log)
8
+ end
9
+
10
+ # Hook into the rails error rendering page to send this exception to
11
+ # CrashLog before rails handlers take over.
12
+ def render_exception_with_crash_log(env, exception)
13
+ controller = env['action_controller.instance']
14
+
15
+ env['crash_log.error_id'] = CrashLog.notify(exception) #,
16
+ # crash_log_context(controller, env))
17
+
18
+ if defined?(controller.rescue_action_in_public_without_crash_log)
19
+ controller.rescue_action_in_public_without_crash_log(exception)
20
+ end
21
+
22
+ rescue Exception => e
23
+ # If it breaks here there is possibly something wrong with us, so
24
+ # instead of crashing again, we'll just pass it on.
25
+ ensure
26
+ render_exception_without_crash_log(env, exception)
27
+ end
28
+
29
+ private
30
+
31
+ def crash_log_context(controller, env)
32
+ # TODO: Replace this with user context lookup
33
+ if controller.respond_to?(:crash_log_request_data)
34
+ controller.crash_log_request_data
35
+ else
36
+ {:rack_env => env}
37
+ end
38
+ end
39
+
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,32 @@
1
+ require "crash_log"
2
+ require "crash_log/rails/controller_methods"
3
+ require "crash_log/rails/action_controller_rescue"
4
+
5
+ # Rails 2.x support
6
+ module CrashLog
7
+ module Rails
8
+ def self.initialize
9
+ if defined?(ActionController::Base)
10
+ ActionController::Base.send(:include, CrashLog::Rails::ActionControllerRescue)
11
+ ActionController::Base.send(:include, CrashLog::Rails::ControllerMethods)
12
+ end
13
+
14
+ # Try to find where to log to
15
+ rails_logger = nil
16
+ if defined?(::Rails.logger)
17
+ rails_logger = ::Rails.logger
18
+ elsif defined?(RAILS_DEFAULT_LOGGER)
19
+ rails_logger = RAILS_DEFAULT_LOGGER
20
+ end
21
+
22
+ CrashLog.configure do |config|
23
+ config.logger = rails_logger
24
+ config.release_stage = RAILS_ENV if defined?(RAILS_ENV)
25
+ config.project_root = RAILS_ROOT if defined?(RAILS_ROOT)
26
+ config.framework = "Rails: #{::Rails::VERSION::STRING}" if defined?(::Rails::VERSION)
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ CrashLog::Rails.initialize
@@ -0,0 +1,41 @@
1
+ require 'crash_log'
2
+ require 'rails'
3
+
4
+ # Rails 3.x support
5
+ module CrashLog
6
+ class Railtie < ::Rails::Railtie
7
+
8
+ config.after_initialize do
9
+ CrashLog.configure do |config|
10
+ config.logger ||= ::Rails.logger
11
+ config.stage ||= ::Rails.env
12
+ config.project_root ||= ::Rails.root
13
+ config.framework = "Rails: #{::Rails::VERSION::STRING}"
14
+ end
15
+
16
+ initializer "crash_log.use_rack_middleware" do |app|
17
+ app.config.middleware.insert 0, "CrashLog::Rack"
18
+ end
19
+
20
+ # Attach our Rails Controller methods
21
+ ActiveSupport.on_load(:action_controller) do
22
+ # Lazily load action_controller methods
23
+ require 'crash_log/rails/controller_methods'
24
+
25
+ include CrashLog::Rails::ControllerMethods
26
+ end
27
+
28
+ if defined?(::ActionDispatch::DebugExceptions)
29
+ # We should catch the exceptions in ActionDispatch::DebugExceptions in Rails 3.2.x.
30
+ require 'crash_log/rails/middleware/debug_exception_catcher'
31
+ ::ActionDispatch::DebugExceptions.__send__(:include, CrashLog::Rails::Middleware::DebugExceptionCatcher)
32
+ elsif defined?(::ActionDispatch::ShowExceptions)
33
+
34
+ # ActionDispatch::DebugExceptions is not defined in Rails 3.0.x and 3.1.x so
35
+ # catch the exceptions in ShowExceptions.
36
+ require 'crash_log/rails/middleware/debug_exception_catcher'
37
+ ::ActionDispatch::ShowExceptions.send(:include, CrashLog::Rails::Middleware::DebugExceptionCatcher)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,105 @@
1
+ require 'faraday'
2
+ require 'faraday/request/hmac_authentication'
3
+ require 'uuid'
4
+ require 'yajl'
5
+
6
+ module CrashLog
7
+ class Reporter
8
+ include Logging
9
+
10
+ attr_reader :host, :port, :scheme, :endpoint, :announce_endpoint
11
+ attr_reader :result, :config, :response
12
+
13
+ def initialize(config)
14
+ @config = config
15
+ @scheme = config.scheme
16
+ @host = config.host
17
+ @port = config.port
18
+ @endpoint = config.endpoint
19
+ @announce_endpoint = config.announce == true ?
20
+ config.announce_endpoint : nil
21
+ end
22
+
23
+ def notify(payload)
24
+ #return if dry_run?
25
+ #MultiJson.use(:yajl)
26
+ response = post(endpoint, MultiJson.encode({:payload => payload}))
27
+ @response = response
28
+ report_result(response.body)
29
+ response.success?
30
+ rescue => e
31
+ log_exception e
32
+ error("Sending exception failed due to a connectivity issue")
33
+ end
34
+
35
+ def announce
36
+ return if dry_run?
37
+ return "Unknown application" unless announce_endpoint
38
+
39
+ response = post(config.announce_endpoint, JSON.dump(identification_hash))
40
+ if response.status == 201
41
+ JSON.load(response.body).symbolize_keys.fetch(:application, 'Default')
42
+ else
43
+ false
44
+ end
45
+ rescue => e
46
+ # We only want to log our mess when testing
47
+ log_exception(e) # if respond_to?(:should)
48
+ error("Failed to announce application launch")
49
+ nil
50
+ end
51
+
52
+ def report_result(body)
53
+ @result = JSON.load(body).symbolize_keys
54
+ end
55
+
56
+ def url
57
+ URI.parse("#{scheme}://#{host}:#{port}")
58
+ end
59
+
60
+ def identification_hash
61
+ {
62
+ :hostname => SystemInformation.hostname,
63
+ :timestamp => Time.now.utc.to_i
64
+ }
65
+ end
66
+
67
+ def print_result
68
+
69
+ end
70
+
71
+ def dry_run?
72
+ config.dry_run == true
73
+ end
74
+
75
+ def post(endpoint, body)
76
+ connection.post do |req|
77
+ req.url(endpoint)
78
+ # req.sign!(config.project_id, config.api_key)
79
+ req.headers['Content-Type'] = 'application/json'
80
+ req.body = body
81
+ end
82
+ end
83
+
84
+ # private
85
+
86
+ def connection
87
+ @connection ||= begin
88
+ Faraday.new(:url => url) do |faraday|
89
+ faraday.request :hmac_authentication, config.project_id, config.api_key, {:service_id => config.service_name}
90
+ faraday.adapter(adapter)
91
+ faraday.request :url_encoded
92
+ # faraday.request :token_auth, config.api_key
93
+ # faraday.response :logger
94
+ # faraday.options[:timeout] = config.http_read_timeout
95
+ # faraday.options[:open_timeout] = config.http_open_timeout
96
+ # faraday.ssl[:verify] = false
97
+ end
98
+ end
99
+ end
100
+
101
+ def adapter
102
+ config.adapter
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,64 @@
1
+ module CrashLog
2
+ class SystemInformation
3
+ class << self
4
+
5
+ def to_hash
6
+ {
7
+ :hostname => hostname,
8
+ :ruby_version => ruby_version,
9
+ :username => username,
10
+ :environment => environment,
11
+ :libraries_loaded => libraries_loaded,
12
+ :platform => platform,
13
+ :application_root => application_root,
14
+ :stage => stage
15
+ }
16
+ end
17
+
18
+ def hostname
19
+ require 'socket' unless defined?(Socket)
20
+ Socket.gethostname
21
+ rescue
22
+ 'UNKNOWN'
23
+ end
24
+
25
+ def ruby_version
26
+ "#{RUBY_VERSION rescue '?.?.?'}-p#{RUBY_PATCHLEVEL rescue '???'} #{RUBY_RELEASE_DATE rescue '????-??-??'} #{RUBY_PLATFORM rescue '????'}"
27
+ end
28
+
29
+ def username
30
+ ENV['LOGNAME'] || ENV['USER'] || ENV['USERNAME'] || ENV['APACHE_RUN_USER'] || 'UNKNOWN'
31
+ end
32
+
33
+ def environment
34
+ if ENV.respond_to?(:to_hash)
35
+ ENV.to_hash.reject do |k, v|
36
+ (k =~ /^HTTP_/) || CrashLog.configuration.environment_filters.include?(k)
37
+ end
38
+ else
39
+ {}
40
+ end
41
+ end
42
+
43
+ def libraries_loaded
44
+ Hash[*Gem.loaded_specs.map {|name, gem_specification|
45
+ [name, gem_specification.version.to_s]
46
+ }.flatten]
47
+ rescue
48
+ {}
49
+ end
50
+
51
+ def platform
52
+ RUBY_PLATFORM rescue '????'
53
+ end
54
+
55
+ def application_root
56
+ CrashLog.configuration.root
57
+ end
58
+
59
+ def stage
60
+ CrashLog.configuration.stage
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,7 @@
1
+ object @payload
2
+
3
+ attribute :notifier
4
+ attribute :event
5
+ attribute :backtrace
6
+ attribute :environment
7
+ attribute :context
@@ -1,3 +1,3 @@
1
1
  module CrashLog
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
data/lib/crash_log.rb CHANGED
@@ -1,5 +1,123 @@
1
+ $: << File.expand_path('..', __FILE__)
2
+
1
3
  require 'crash_log/version'
4
+ begin
5
+ require 'active_support'
6
+ require 'active_support/core_ext'
7
+ rescue LoadError
8
+ require 'activesupport'
9
+ require 'activesupport/core_ext'
10
+ end
11
+ require 'faraday'
12
+ require 'crash_log/railtie' if defined?(Rails::Railtie)
13
+
14
+ require 'crash_log/logging'
2
15
 
3
16
  module CrashLog
17
+ extend Logging::ClassMethods
18
+
19
+ autoload :Backtrace, 'crash_log/backtrace'
20
+ autoload :Configuration, 'crash_log/configuration'
21
+ autoload :Payload, 'crash_log/payload'
22
+ autoload :Rack, 'crash_log/rack'
23
+ autoload :Reporter, 'crash_log/reporter'
24
+ autoload :SystemInformation, 'crash_log/system_information'
25
+
26
+ LOG_PREFIX = '** [CrashLog]'
27
+
28
+ class << self
29
+
30
+ # Sends a notification to CrashLog
31
+ #
32
+ # This is the main entry point into the exception sending stack.
33
+ #
34
+ # Examples:
35
+ #
36
+ # def something_dangerous
37
+ # raise RuntimeError, "This is too dangerous for you"
38
+ # rescue => e
39
+ # CrashLog.notify(e)
40
+ # end
41
+ #
42
+ # You can also include information about the current user and Crashlog
43
+ # will allow you to correlate errors by affected users:
44
+ #
45
+ # def something_dangerous
46
+ # raise RuntimeError, "This is too dangerous for you"
47
+ # rescue => e
48
+ # CrashLog.notify(e, {current_user: current_user})
49
+ # end
50
+ #
51
+ # This will try to serialize the current user by calling `as_crashlog_context` or `as_json`
52
+ # otherwise it will try `to_s`
53
+ #
54
+ # Returns true if successful, otherwise false
55
+ def notify(exception, context = {})
56
+ send_notification(exception, context)
57
+ end
58
+
59
+ # Sends the notice unless it is one of the default ignored exceptions.
60
+ def notify_or_ignore(exception, context = {})
61
+ send_notification(exception, context = {}) unless ignored?(exception)
62
+ end
63
+
64
+ # Print a message at the top of the applciation's logs to say we're ready.
65
+ def report_for_duty!
66
+ application = CrashLog::Reporter.new(configuration).announce
67
+
68
+ if application
69
+ info("Initialized and ready to handle exceptions for #{application}")
70
+ else
71
+ error("Failed to report for duty, your application failed to authenticate correctly with stdin.crashlog.io")
72
+ end
73
+ end
74
+
75
+ # Configure the gem to send notifications, at the very least an api_key is
76
+ # required.
77
+ def configure
78
+ yield(configuration) if block_given?
79
+ if configuration.valid?
80
+ report_for_duty!
81
+ else
82
+ error('Not configured correctly')
83
+ end
84
+ end
85
+
86
+ # The global configuration object.
87
+ def configuration
88
+ @configuration ||= Configuration.new
89
+ end
90
+
91
+ # The default logging device.
92
+ def logger
93
+ self.configuration.logger || Logger.new($stdout)
94
+ end
95
+
96
+ # Is the logger live
97
+ #
98
+ # Returns true if the current stage is included in the release
99
+ # stages config, false otherwise.
100
+ def live?
101
+ configuration.release_stage?
102
+ end
103
+
104
+ # Looks up ignored exceptions
105
+ #
106
+ # Returns true if this exception should be ignored, false otherwise.
107
+ def ignored?(exception)
108
+ configuration.ignored?(exception)
109
+ end
110
+
111
+ private
112
+
113
+ def send_notification(exception, context = {})
114
+ build_payload(exception, context).deliver! if live?
115
+ end
4
116
 
117
+ def build_payload(exception, context = {})
118
+ Payload.build(exception, configuration) do |payload|
119
+ payload.add_context(context)
120
+ end
121
+ end
122
+ end
5
123
  end
@@ -0,0 +1,73 @@
1
+ module Faraday
2
+ class Request
3
+
4
+ require 'crash_log/auth_hmac'
5
+ require 'uri'
6
+
7
+ attr_reader :access_id, :secret
8
+
9
+ # # Sign the request with the specified `access_id` and `secret`.
10
+ # def sign!(access_id, secret)
11
+ # @access_id, @secret = access_id, secret
12
+
13
+ # #self.sign_with = access_id
14
+ # #CrashLog::AuthHMAC.keys[access_id] = secret
15
+ # end
16
+
17
+ class HMACAuthentication < Faraday::Middleware
18
+ # Modified CanonicalString to know how to pull from the Faraday-specific
19
+ # env hash.
20
+ class CanonicalString < CrashLog::AuthHMAC::CanonicalString
21
+ def request_method(request)
22
+ request[:method].to_s.upcase
23
+ end
24
+
25
+ def request_body(request)
26
+ request[:body]
27
+ end
28
+
29
+ # def request_path(request)
30
+ # URI.parse(request[:url]).path
31
+ # end
32
+
33
+ def request_path(request)
34
+ URI.parse(request[:url].to_s).path
35
+ end
36
+
37
+ def headers(request)
38
+ request[:request_headers]
39
+ end
40
+ end
41
+
42
+ KEY = "Authorization".freeze
43
+
44
+ attr_reader :auth, :token, :secret
45
+
46
+ def initialize(app, token, secret, options = {})
47
+ options.merge!(:signature => HMACAuthentication::CanonicalString)
48
+ keys = {token => secret}
49
+ @token, @secret = token, secret
50
+ @auth = CrashLog::AuthHMAC.new(keys, options)
51
+ super(app)
52
+ end
53
+
54
+ # Public
55
+ def call(env)
56
+ env[:request_headers][KEY] ||= hmac_auth_header(env).to_s #if sign_request?
57
+ puts env[:request_headers]
58
+ @app.call(env)
59
+ end
60
+
61
+ def hmac_auth_header(env)
62
+ auth.authorization(env, token, secret)
63
+ end
64
+
65
+ def sign_request?
66
+ !!@token && !!@secret
67
+ end
68
+ end
69
+ end
70
+
71
+ Faraday.register_middleware :request, :hmac_authentication => Faraday::Request::HMACAuthentication
72
+ end
73
+
@@ -0,0 +1,42 @@
1
+ require 'rails/generators'
2
+
3
+ class CrashlogGenerator < Rails::Generators::Base
4
+
5
+ class_option :api_key, :aliases => "-k", :type => :string, :desc => "Your CrashLog API key"
6
+
7
+ def self.source_root
8
+ @_crashlog_source_root ||= File.expand_path("../../../../../generators/crashlog/templates", __FILE__)
9
+ end
10
+
11
+ def install
12
+ ensure_api_key_was_configured
13
+ generate_initializer unless api_key_configured?
14
+ test_crashlog
15
+ end
16
+
17
+ private
18
+
19
+ def ensure_api_key_was_configured
20
+ if !options[:api_key] && !api_key_configured?
21
+ puts "Must pass --api-key or create config/initializers/crashlog.rb"
22
+ exit
23
+ end
24
+ end
25
+
26
+ def api_key_expression
27
+ "'#{options[:api_key]}'"
28
+ end
29
+
30
+ def generate_initializer
31
+ template 'initializer.rb', 'config/initializers/crashlog.rb'
32
+ end
33
+
34
+ def api_key_configured?
35
+ File.exists?('config/initializers/crashlog.rb')
36
+ end
37
+
38
+ def test_crashlog
39
+ puts run("rake crashlog:test")
40
+ end
41
+ end
42
+
data/rails/init.rb ADDED
@@ -0,0 +1 @@
1
+ require "crash_log/rails"
@@ -0,0 +1,79 @@
1
+ require 'spec_helper'
2
+
3
+ describe CrashLog::Backtrace do
4
+ let(:raised_error) do
5
+ begin
6
+ raise RuntimeError, "This broke"
7
+ rescue RuntimeError => e
8
+ e
9
+ end
10
+ end
11
+
12
+ describe '.parse' do
13
+ it 'accepts a standed caller array' do
14
+ CrashLog::Backtrace.parse(caller).to_a.size.should == caller.size
15
+ end
16
+
17
+ it 'accepts a ruby exception backtrace' do
18
+ CrashLog::Backtrace.parse(raised_error.backtrace).to_a.size.should ==
19
+ raised_error.backtrace.size
20
+ end
21
+ end
22
+
23
+ describe 'lines' do
24
+ it 'are converted into a parsed Line object' do
25
+ CrashLog::Backtrace.parse(raised_error.backtrace).lines.first.should be_a(CrashLog::Backtrace::Line)
26
+ end
27
+
28
+ it 'responds to to_hash' do
29
+ CrashLog::Backtrace.parse(raised_error.backtrace).lines.first.to_hash.should have_keys(:number, :method, :file)
30
+ end
31
+
32
+ it 'captures line context' do
33
+ line = CrashLog::Backtrace.parse(raised_error.backtrace).lines.first
34
+ line.context_line.should match /raise RuntimeError/
35
+ end
36
+
37
+ it 'captures pre_context' do
38
+ line = CrashLog::Backtrace.parse(raised_error.backtrace).lines.first
39
+ line.pre_context.should == [
40
+ "require \\'spec_helper\\'",
41
+ "",
42
+ "describe CrashLog::Backtrace do",
43
+ " let(:raised_error) do",
44
+ " begin"
45
+ ]
46
+ end
47
+
48
+ it 'captures pre_context' do
49
+ line = CrashLog::Backtrace.parse(raised_error.backtrace).lines.first
50
+ line.post_context.should == [
51
+ " rescue RuntimeError => e",
52
+ " e",
53
+ " end",
54
+ " end",
55
+ ""
56
+ ]
57
+ end
58
+ end
59
+
60
+ describe 'filters' do
61
+ it 'should replace project root with [PROJECT_ROOT]' do
62
+ CrashLog.configuration.project_root = File.expand_path('../../', __FILE__)
63
+ filters = CrashLog.configuration.backtrace_filters
64
+ backtrace = CrashLog::Backtrace.parse(raised_error.backtrace, :filters => filters)
65
+
66
+ backtrace.lines.first.file.should match /\[PROJECT_ROOT\]/
67
+ backtrace.lines.first.file.should == '[PROJECT_ROOT]/crash_log/backtrace_spec.rb'
68
+ end
69
+
70
+ it 'should not replace project root if it is not set' do
71
+ CrashLog.configuration.project_root = nil
72
+
73
+ filter = CrashLog::Configuration::DEFAULT_BACKTRACE_FILTERS
74
+ backtrace = CrashLog::Backtrace.parse(raised_error.backtrace, :filters => filter)
75
+
76
+ backtrace.lines.first.file.should_not match /\[PROJECT_ROOT\]/
77
+ end
78
+ end
79
+ end