hydraulic_brake 0.0.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.
data/.rbenv-version ADDED
@@ -0,0 +1 @@
1
+ 1.9.3-p194
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm use 1.9.3-p194@airbrake --create
data/.yardopts ADDED
@@ -0,0 +1,2 @@
1
+ -
2
+ MIT-LICENSE
data/Gemfile ADDED
@@ -0,0 +1,16 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem "builder"
4
+
5
+ group "development" do
6
+ gem "bourne", ">= 1.0"
7
+ gem "cucumber", "~> 0.10.6"
8
+ gem "fakeweb", "~> 1.3.0"
9
+ gem "jeweler"
10
+ gem "nokogiri", "~> 1.4.3.1"
11
+ gem "rake"
12
+ gem "rspec", "~> 2.6.0"
13
+ gem "shoulda", "~> 2.11.3"
14
+ gem "simplecov"
15
+ gem "yard"
16
+ end
data/INSTALL ADDED
@@ -0,0 +1,20 @@
1
+ === Configuration
2
+
3
+ You should have something like this in config/initializers/hydraulic_brake.rb.
4
+
5
+ HydraulicBrake.configure do |config|
6
+ config.api_key = '1234567890abcdef'
7
+ end
8
+
9
+ (Please note that this configuration should be in a global configuration, and
10
+ is *not* environment-specific. HydraulicBrake is smart enough to know what
11
+ errors are caused by what environments, so your staging errors don't get mixed
12
+ in with your production errors.)
13
+
14
+ You can test that HydraulicBrake is working in your production environment by
15
+ using this rake task:
16
+
17
+ rake hydraulicbrake:test
18
+
19
+ If everything is configured properly, that task will send a notice to Airbrake
20
+ which will be visible immediately.
data/MIT-LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2007 - 2012, Exceptional DBA Airbrake.io
2
+
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following
10
+ conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,103 @@
1
+ HydraulicBrake
2
+ ========
3
+
4
+ This is a replacement notifier gem for the [Airbrake
5
+ gem](https://github.com/airbrake/airbrake) which is used
6
+ for integrating ruby apps with [Airbrake](http://airbrake.io).
7
+
8
+ ### Transitioning from Airbrake to HydraulicBrake
9
+
10
+ HydraulicBrake is a lighter weight alternative to the [Airbrake
11
+ gem](https://github.com/airbrake/airbrake) and takes a different design
12
+ approach. HydraulicBrake doesn't attempt to integrate with any
13
+ frameworks and has few dependencies. HydraulicBrake doesn't change
14
+ anything outside its own namespace. It doesn't do any automatic
15
+ exception handling or configuration.
16
+
17
+ Configuration
18
+ -------------
19
+
20
+ Configure HydraulicBrake when your app starts with a configuration block
21
+ like this one:
22
+
23
+ HydraulicBrake.configure do |config|
24
+ config.host = "api.airbrake.io"
25
+ config.port = "443"
26
+ config.secure = true
27
+ config.environment_name = "staging"
28
+ config.api_key = "<api-key-from-your-airbrake-server>"
29
+ end
30
+
31
+ Usage
32
+ -----
33
+
34
+ Wherever you want to notify an Airbrake server, just call
35
+ HydraulicBrake#notify
36
+
37
+ begin
38
+ params = {
39
+ # params that you pass to a method that can throw an exception
40
+ }
41
+ my_unpredicable_method(params)
42
+ rescue => e
43
+ HydraulicBrake.notify(
44
+ :error_class => "Special Error",
45
+ :error_message => "Special Error: #{e.message}",
46
+ :parameters => params
47
+ )
48
+ end
49
+
50
+ HydraulicBrake merges the hash you pass with these default options:
51
+
52
+ {
53
+ :api_key => HydraulicBrake.api_key,
54
+ :error_message => 'Notification',
55
+ :backtrace => caller,
56
+ :parameters => {},
57
+ :session => {}
58
+ }
59
+
60
+ You can override any of those parameters.
61
+
62
+ Async Notifications
63
+ -------------------
64
+
65
+ HydraulicBrake doesn't provide anything for async notifications, and
66
+ that's a good thing. Just wrap your calls to Airbrake#notify in the
67
+ async library of your choice.
68
+
69
+ Proxy Support
70
+ -------------
71
+
72
+ The notifier supports using a proxy, if your server is not able to
73
+ directly reach the Airbrake servers. To configure the proxy settings,
74
+ added the following information to your HydraulicBrake configuration
75
+ block.
76
+
77
+ HydraulicBrake.configure do |config|
78
+ config.proxy_host = proxy.host.com
79
+ config.proxy_port = 4038
80
+ config.proxy_user = foo # optional
81
+ config.proxy_pass = bar # optional
82
+
83
+ Logging
84
+ ------------
85
+
86
+ HydraulicBrake uses STDOUT by default. If you don't like HydraulicBrake
87
+ scribbling to your standard output, just pass another `Logger` instance
88
+ inside your configuration:
89
+
90
+ HydraulicBrake.configure do |config|
91
+ ...
92
+ config.logger = Logger.new("path/to/your/log/file")
93
+ end
94
+
95
+ Credits
96
+ -------
97
+
98
+ Thank you to all [the airbrake contributors](https://github.com/airbrake/airbrake/contributors)!
99
+
100
+ License
101
+ -------
102
+
103
+ Airbrake is Copyright © 2008-2012 Airbrake. It is free software, and may be redistributed under the terms specified in the MIT-LICENSE file.
data/Rakefile ADDED
@@ -0,0 +1,39 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ gem.name = "hydraulic_brake"
17
+ gem.homepage = "http://github.com/stevecrozz/hydraulic_brake"
18
+ gem.license = "MIT"
19
+ gem.summary = "Simple Airbrake client"
20
+ gem.description = %Q{Sends notifications to an Airbrake server}
21
+ gem.email = "stevecrozz@gmail.com"
22
+ gem.authors = ["Stephen Crosby"]
23
+ gem.require_paths = ["lib"]
24
+ end
25
+ Jeweler::RubygemsDotOrgTasks.new
26
+
27
+ require 'rake/testtask'
28
+ Rake::TestTask.new(:test) do |test|
29
+ test.libs << 'lib' << 'test'
30
+ test.pattern = 'test/*_test.rb'
31
+ test.verbose = true
32
+ end
33
+
34
+ require 'yard'
35
+ YARD::Rake::YardocTask.new do |t|
36
+ t.files = ['lib/**/*.rb']
37
+ end
38
+
39
+ task :default => :test
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.0
@@ -0,0 +1,27 @@
1
+ Feature: Use the Gem to catch errors in a Rake application
2
+ Background:
3
+ Given I have built and installed the "airbrake" gem
4
+
5
+ Scenario: Catching exceptions in Rake
6
+ When I run rake with airbrake
7
+ Then Airbrake should catch the exception
8
+
9
+ Scenario: Falling back to default handler before Airbrake is configured
10
+ When I run rake with airbrake not yet configured
11
+ Then Airbrake should not catch the exception
12
+
13
+ Scenario: Disabling Rake exception catcher
14
+ When I run rake with airbrake disabled
15
+ Then Airbrake should not catch the exception
16
+
17
+ Scenario: Autodetect, running from terminal
18
+ When I run rake with airbrake autodetect from terminal
19
+ Then Airbrake should not catch the exception
20
+
21
+ Scenario: Autodetect, not running from terminal
22
+ When I run rake with airbrake autodetect not from terminal
23
+ Then Airbrake should catch the exception
24
+
25
+ Scenario: Sending the correct component name
26
+ When I run rake with airbrake
27
+ Then Airbrake should send the rake command line as the component name
@@ -0,0 +1,17 @@
1
+ When /I run rake with (.+)/ do |command|
2
+ @rake_command = "rake #{command.gsub(' ','_')}"
3
+ @rake_result = `cd features/support/rake && GEM_HOME=#{BUILT_GEM_ROOT} #{@rake_command} 2>&1`
4
+ end
5
+
6
+ Then /Airbrake should (|not) ?catch the exception/ do |condition|
7
+ if condition=='not'
8
+ @rake_result.should_not =~ /^airbrake/
9
+ else
10
+ @rake_result.should =~ /^airbrake/
11
+ end
12
+ end
13
+
14
+ Then /Airbrake should send the rake command line as the component name/ do
15
+ component = @rake_result.match(/^airbrake (.*)$/)[1]
16
+ component.should == @rake_command
17
+ end
@@ -0,0 +1,35 @@
1
+ RSpec::Matchers.define :have_content do |xpath, content|
2
+ match do |document|
3
+ @elements = document.search(xpath)
4
+
5
+ if @elements.empty?
6
+ false
7
+ else
8
+ element_with_content = document.at("#{xpath}[contains(.,'#{content}')]")
9
+
10
+ if element_with_content.nil?
11
+ @found = @elements.collect { |element| element.content }
12
+
13
+ false
14
+ else
15
+ true
16
+ end
17
+ end
18
+ end
19
+
20
+ failure_message_for_should do |document|
21
+ if @elements.empty?
22
+ "In XML:\n#{document}\nNo element at #{xpath}"
23
+ else
24
+ "In XML:\n#{document}\nGot content #{@found.inspect} at #{xpath} instead of #{content.inspect}"
25
+ end
26
+ end
27
+
28
+ failure_message_for_should_not do |document|
29
+ unless @elements.empty?
30
+ "In XML:\n#{document}\nExpcted no content #{content.inspect} at #{xpath}"
31
+ end
32
+ end
33
+ end
34
+
35
+ World(RSpec::Matchers)
@@ -0,0 +1,61 @@
1
+ # A test harness for RakeHandler
2
+ #
3
+ require 'rake'
4
+ require 'rubygems'
5
+ require 'hydraulic_brake'
6
+ require 'hydraulic_brake/rake_handler'
7
+
8
+ HydraulicBrake.configure do |c|
9
+ end
10
+
11
+ # Should catch exception
12
+ task :hydraulic_brake do
13
+ HydraulicBrake.configuration.rescue_rake_exceptions = true
14
+ stub_tty_output(true)
15
+ raise_exception
16
+ end
17
+
18
+ # Should not catch exception
19
+ task :hydraulic_brake_disabled do
20
+ HydraulicBrake.configuration.rescue_rake_exceptions = false
21
+ stub_tty_output(true)
22
+ raise_exception
23
+ end
24
+
25
+ # Should not catch exception as tty_output is true
26
+ task :hydraulic_brake_autodetect_from_terminal do
27
+ HydraulicBrake.configuration.rescue_rake_exceptions = nil
28
+ stub_tty_output(true)
29
+ raise_exception
30
+ end
31
+
32
+ # Should catch exception as tty_output is false
33
+ task :hydraulic_brake_autodetect_not_from_terminal do
34
+ HydraulicBrake.configuration.rescue_rake_exceptions = nil
35
+ stub_tty_output(false)
36
+ raise_exception
37
+ end
38
+
39
+ task :hydraulic_brake_not_yet_configured do
40
+ HydraulicBrake.configuration.rescue_rake_exceptions = true
41
+ stub_tty_output(true)
42
+ stub_empty_sender
43
+ raise_exception
44
+ end
45
+
46
+ def stub_empty_sender
47
+ HydraulicBrake.sender = nil
48
+ end
49
+
50
+ def stub_tty_output(value)
51
+ Rake.application.instance_eval do
52
+ @tty_output_stub = value
53
+ def tty_output?
54
+ @tty_output_stub
55
+ end
56
+ end
57
+ end
58
+
59
+ def raise_exception
60
+ raise 'TEST'
61
+ end
@@ -0,0 +1,107 @@
1
+ require 'fileutils'
2
+
3
+ Before do
4
+ @terminal = Terminal.new
5
+ end
6
+
7
+ After do |story|
8
+ if story.failed?
9
+ # puts @terminal.output
10
+ end
11
+ end
12
+
13
+ class Terminal
14
+ attr_reader :output, :status
15
+ attr_accessor :environment_variables, :invoke_heroku_rake_tasks_locally
16
+
17
+ def initialize
18
+ @cwd = FileUtils.pwd
19
+ @output = ""
20
+ @status = 0
21
+ @logger = Logger.new(File.join(TEMP_DIR, 'terminal.log'))
22
+
23
+ @invoke_heroku_rake_tasks_locally = false
24
+
25
+ @environment_variables = {
26
+ "GEM_HOME" => LOCAL_GEM_ROOT,
27
+ "GEM_PATH" => "#{LOCAL_GEM_ROOT}:#{BUILT_GEM_ROOT}",
28
+ "PATH" => "#{gem_bin_path}:#{ENV['PATH']}"
29
+ }
30
+ end
31
+
32
+ def cd(directory)
33
+ @cwd = directory
34
+ end
35
+
36
+ def run(command)
37
+ command = optionally_invoke_heroku_rake_tasks_locally(command)
38
+
39
+ output << "#{command}\n"
40
+ FileUtils.cd(@cwd) do
41
+ # The ; forces ruby to shell out so the env settings work right
42
+ cmdline = "#{environment_settings} #{command} 2>&1 ; "
43
+ logger.debug(cmdline)
44
+ result = `#{cmdline}`
45
+ logger.debug(result)
46
+ output << result
47
+ end
48
+ @status = $?
49
+ end
50
+
51
+ def optionally_invoke_heroku_rake_tasks_locally(command)
52
+ if invoke_heroku_rake_tasks_locally
53
+ command.sub(/^heroku /, '')
54
+ else
55
+ command
56
+ end
57
+ end
58
+
59
+ def echo(string)
60
+ logger.debug(string)
61
+ end
62
+
63
+ def flush!
64
+ @output = ""
65
+ end
66
+
67
+ def build_and_install_gem(gemspec)
68
+ pkg_dir = File.join(TEMP_DIR, 'pkg')
69
+ FileUtils.mkdir_p(pkg_dir)
70
+ output = `gem build #{gemspec} 2>&1`
71
+ gem_file = Dir.glob("*.gem").first
72
+ unless gem_file
73
+ raise "Gem didn't build:\n#{output}"
74
+ end
75
+ target = File.join(pkg_dir, gem_file)
76
+ FileUtils.mv(gem_file, target)
77
+ install_gem_to(LOCAL_GEM_ROOT, target)
78
+ end
79
+
80
+ def install_gem(gem)
81
+ install_gem_to(LOCAL_GEM_ROOT, gem)
82
+ end
83
+
84
+ def uninstall_gem(gem)
85
+ `gem uninstall -i #{LOCAL_GEM_ROOT} #{gem}`
86
+ end
87
+
88
+ def prepend_path(path)
89
+ @environment_variables['PATH'] = path + ":" + @environment_variables['PATH']
90
+ end
91
+
92
+ private
93
+
94
+ def install_gem_to(root, gem)
95
+ `gem install -i #{root} --no-ri --no-rdoc #{gem}`
96
+ end
97
+
98
+ def environment_settings
99
+ @environment_variables.map { |key, value| "#{key}=#{value}" }.join(' ')
100
+ end
101
+
102
+ def gem_bin_path
103
+ File.join(LOCAL_GEM_ROOT, "bin")
104
+ end
105
+
106
+ attr_reader :logger
107
+ end
@@ -0,0 +1,102 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "hydraulic_brake"
8
+ s.version = "0.0.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Stephen Crosby"]
12
+ s.date = "2013-07-14"
13
+ s.description = "Sends notifications to an Airbrake server"
14
+ s.email = "stevecrozz@gmail.com"
15
+ s.extra_rdoc_files = [
16
+ "README.md"
17
+ ]
18
+ s.files = [
19
+ ".rbenv-version",
20
+ ".rvmrc",
21
+ ".yardopts",
22
+ "Gemfile",
23
+ "INSTALL",
24
+ "MIT-LICENSE",
25
+ "README.md",
26
+ "Rakefile",
27
+ "VERSION",
28
+ "features/rake.feature",
29
+ "features/step_definitions/rake_steps.rb",
30
+ "features/support/matchers.rb",
31
+ "features/support/rake/Rakefile",
32
+ "features/support/terminal.rb",
33
+ "hydraulic_brake.gemspec",
34
+ "lib/hydraulic_brake.rb",
35
+ "lib/hydraulic_brake/backtrace.rb",
36
+ "lib/hydraulic_brake/configuration.rb",
37
+ "lib/hydraulic_brake/notice.rb",
38
+ "lib/hydraulic_brake/sender.rb",
39
+ "lib/hydraulic_brake/version.rb",
40
+ "lib/hydraulic_brake_tasks.rb",
41
+ "resources/README.md",
42
+ "resources/ca-bundle.crt",
43
+ "script/integration_test.rb",
44
+ "test/airbrake_2_3.xsd",
45
+ "test/backtrace_test.rb",
46
+ "test/configuration_test.rb",
47
+ "test/helper.rb",
48
+ "test/logger_test.rb",
49
+ "test/notice_test.rb",
50
+ "test/notifier_test.rb",
51
+ "test/recursion_test.rb",
52
+ "test/sender_test.rb"
53
+ ]
54
+ s.homepage = "http://github.com/stevecrozz/hydraulic_brake"
55
+ s.licenses = ["MIT"]
56
+ s.require_paths = ["lib"]
57
+ s.rubygems_version = "1.8.23"
58
+ s.summary = "Simple Airbrake client"
59
+
60
+ if s.respond_to? :specification_version then
61
+ s.specification_version = 3
62
+
63
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
64
+ s.add_runtime_dependency(%q<builder>, [">= 0"])
65
+ s.add_development_dependency(%q<bourne>, [">= 1.0"])
66
+ s.add_development_dependency(%q<cucumber>, ["~> 0.10.6"])
67
+ s.add_development_dependency(%q<fakeweb>, ["~> 1.3.0"])
68
+ s.add_development_dependency(%q<jeweler>, [">= 0"])
69
+ s.add_development_dependency(%q<nokogiri>, ["~> 1.4.3.1"])
70
+ s.add_development_dependency(%q<rake>, [">= 0"])
71
+ s.add_development_dependency(%q<rspec>, ["~> 2.6.0"])
72
+ s.add_development_dependency(%q<shoulda>, ["~> 2.11.3"])
73
+ s.add_development_dependency(%q<simplecov>, [">= 0"])
74
+ s.add_development_dependency(%q<yard>, [">= 0"])
75
+ else
76
+ s.add_dependency(%q<builder>, [">= 0"])
77
+ s.add_dependency(%q<bourne>, [">= 1.0"])
78
+ s.add_dependency(%q<cucumber>, ["~> 0.10.6"])
79
+ s.add_dependency(%q<fakeweb>, ["~> 1.3.0"])
80
+ s.add_dependency(%q<jeweler>, [">= 0"])
81
+ s.add_dependency(%q<nokogiri>, ["~> 1.4.3.1"])
82
+ s.add_dependency(%q<rake>, [">= 0"])
83
+ s.add_dependency(%q<rspec>, ["~> 2.6.0"])
84
+ s.add_dependency(%q<shoulda>, ["~> 2.11.3"])
85
+ s.add_dependency(%q<simplecov>, [">= 0"])
86
+ s.add_dependency(%q<yard>, [">= 0"])
87
+ end
88
+ else
89
+ s.add_dependency(%q<builder>, [">= 0"])
90
+ s.add_dependency(%q<bourne>, [">= 1.0"])
91
+ s.add_dependency(%q<cucumber>, ["~> 0.10.6"])
92
+ s.add_dependency(%q<fakeweb>, ["~> 1.3.0"])
93
+ s.add_dependency(%q<jeweler>, [">= 0"])
94
+ s.add_dependency(%q<nokogiri>, ["~> 1.4.3.1"])
95
+ s.add_dependency(%q<rake>, [">= 0"])
96
+ s.add_dependency(%q<rspec>, ["~> 2.6.0"])
97
+ s.add_dependency(%q<shoulda>, ["~> 2.11.3"])
98
+ s.add_dependency(%q<simplecov>, [">= 0"])
99
+ s.add_dependency(%q<yard>, [">= 0"])
100
+ end
101
+ end
102
+
@@ -0,0 +1,108 @@
1
+ module HydraulicBrake
2
+ # Front end to parsing the backtrace for each notice
3
+ class Backtrace
4
+
5
+ # Handles backtrace parsing line by line
6
+ class Line
7
+
8
+ # regexp (optionnally allowing leading X: for windows support)
9
+ INPUT_FORMAT = %r{^((?:[a-zA-Z]:)?[^:]+):(\d+)(?::in `([^']+)')?$}.freeze
10
+
11
+ # The file portion of the line (such as app/models/user.rb)
12
+ attr_reader :file
13
+
14
+ # The line number portion of the line
15
+ attr_reader :number
16
+
17
+ # The method of the line (such as index)
18
+ attr_reader :method
19
+
20
+ # Parses a single line of a given backtrace
21
+ # @param [String] unparsed_line The raw line from +caller+ or some backtrace
22
+ # @return [Line] The parsed backtrace line
23
+ def self.parse(unparsed_line)
24
+ _, file, number, method = unparsed_line.match(INPUT_FORMAT).to_a
25
+ new(file, number, method)
26
+ end
27
+
28
+ def initialize(file, number, method)
29
+ self.file = file
30
+ self.number = number
31
+ self.method = method
32
+ end
33
+
34
+ # Reconstructs the line in a readable fashion
35
+ def to_s
36
+ "#{file}:#{number}:in `#{method}'"
37
+ end
38
+
39
+ def ==(other)
40
+ to_s == other.to_s
41
+ end
42
+
43
+ def inspect
44
+ "<Line:#{to_s}>"
45
+ end
46
+
47
+ private
48
+
49
+ attr_writer :file, :number, :method
50
+ end
51
+
52
+ # holder for an Array of Backtrace::Line instances
53
+ attr_reader :lines
54
+
55
+ def self.parse(ruby_backtrace, opts = {})
56
+ ruby_lines = split_multiline_backtrace(ruby_backtrace)
57
+
58
+ filters = opts[:filters] || []
59
+ filtered_lines = ruby_lines.to_a.map do |line|
60
+ filters.inject(line) do |line, proc|
61
+ proc.call(line)
62
+ end
63
+ end.compact
64
+
65
+ lines = filtered_lines.collect do |unparsed_line|
66
+ Line.parse(unparsed_line)
67
+ end
68
+
69
+ instance = new(lines)
70
+ end
71
+
72
+ def initialize(lines)
73
+ self.lines = lines
74
+ end
75
+
76
+ def inspect
77
+ "<Backtrace: " + lines.collect { |line| line.inspect }.join(", ") + ">"
78
+ end
79
+
80
+ def to_s
81
+ content = []
82
+ lines.each do |line|
83
+ content << line
84
+ end
85
+ content.join("\n")
86
+ end
87
+
88
+ def ==(other)
89
+ if other.respond_to?(:lines)
90
+ lines == other.lines
91
+ else
92
+ false
93
+ end
94
+ end
95
+
96
+ private
97
+
98
+ attr_writer :lines
99
+
100
+ def self.split_multiline_backtrace(backtrace)
101
+ if backtrace.to_a.size == 1
102
+ backtrace.to_a.first.split(/\n\s*/)
103
+ else
104
+ backtrace
105
+ end
106
+ end
107
+ end
108
+ end