crashlog 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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