trace_viewer 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Manifest +7 -0
- data/Rakefile +12 -0
- data/lib/trace_viewer/line_detail.rb +24 -0
- data/lib/trace_viewer.rb +97 -0
- data/lib/views/index.html.haml +90 -0
- data/spec/spec_helper.rb +88 -0
- data/spec/trace_viewer/trace_viewer_spec.rb +10 -0
- data/trace_viewer.gemspec +30 -0
- metadata +77 -0
data/Manifest
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'echoe'
|
4
|
+
|
5
|
+
Echoe.new('trace_viewer', '0.0.1') do |p|
|
6
|
+
p.description = "Launches your web browser to view the functions in a stack trace in chronological order"
|
7
|
+
p.url = "http://github.com/cj2/trace_viewer"
|
8
|
+
p.author = "Chris Young"
|
9
|
+
p.email = "beesucker @nospam@ gmail.com"
|
10
|
+
p.ignore_pattern = ["tmp/*", "script/*"]
|
11
|
+
p.development_dependencies = []
|
12
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module TraceViewer
|
2
|
+
class LineDetail
|
3
|
+
attr_accessor :filename, :line_number, :source, :function_node
|
4
|
+
|
5
|
+
def line_count
|
6
|
+
formatted_source.size
|
7
|
+
end
|
8
|
+
|
9
|
+
def formatted_source
|
10
|
+
@formatted_source ||= begin
|
11
|
+
if (source)
|
12
|
+
source = self.source.clone
|
13
|
+
source[0] =~ /^(\s+)/
|
14
|
+
leading_characters = $1 || ''
|
15
|
+
source.map {|line|
|
16
|
+
line ? line.sub(leading_characters, '') : nil
|
17
|
+
}
|
18
|
+
else
|
19
|
+
[]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/trace_viewer.rb
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'launchy'
|
2
|
+
require 'haml'
|
3
|
+
require 'redparse'
|
4
|
+
require 'trace_viewer/line_detail'
|
5
|
+
|
6
|
+
module TraceViewer
|
7
|
+
class << self
|
8
|
+
def run
|
9
|
+
trace = load_and_parse_trace
|
10
|
+
loaded_trace = map_trace_to_line_details(trace)
|
11
|
+
html = build_html(loaded_trace)
|
12
|
+
filename = store_html(html)
|
13
|
+
open_html(filename)
|
14
|
+
end
|
15
|
+
|
16
|
+
def open_html(filename)
|
17
|
+
Launchy::Browser.run(filename)
|
18
|
+
end
|
19
|
+
|
20
|
+
def store_html(html)
|
21
|
+
filename = File.join(Dir.pwd, "trace_viewer_#{Time.now.to_i}.html")
|
22
|
+
File.open(filename, "w") do |file|
|
23
|
+
file.puts html
|
24
|
+
end
|
25
|
+
filename
|
26
|
+
end
|
27
|
+
|
28
|
+
def build_html(trace)
|
29
|
+
template_name = File.join(File.dirname(__FILE__), "views", "index.html.haml")
|
30
|
+
template_contents = File.read(template_name)
|
31
|
+
|
32
|
+
engine = Haml::Engine.new(template_contents)
|
33
|
+
engine.render(Object.new, :trace => trace)
|
34
|
+
end
|
35
|
+
|
36
|
+
def load_and_parse_trace
|
37
|
+
trace_lines = filtered_trace
|
38
|
+
trace_lines.map do |line|
|
39
|
+
parse_trace_line(line)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def map_trace_to_line_details(parsed_trace)
|
44
|
+
parsed_trace.map do |line|
|
45
|
+
filename,line_number = line
|
46
|
+
|
47
|
+
function_node, source = load_function(filename, line_number)
|
48
|
+
|
49
|
+
detail = LineDetail.new
|
50
|
+
detail.filename = filename
|
51
|
+
detail.line_number = line_number
|
52
|
+
detail.source = source
|
53
|
+
detail.function_node = function_node
|
54
|
+
|
55
|
+
detail
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def filtered_trace
|
60
|
+
caller.select {|line|
|
61
|
+
# line !~ /\/trace_viewer\// &&
|
62
|
+
line !~ /\/rspec/
|
63
|
+
}
|
64
|
+
end
|
65
|
+
|
66
|
+
def parse_trace_line(line)
|
67
|
+
path,line_number,function = line.split(':')
|
68
|
+
line_number = line_number.to_i
|
69
|
+
if (function)
|
70
|
+
[path,line_number]
|
71
|
+
else
|
72
|
+
[]
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def load_function(filename,line_number)
|
77
|
+
lines = File.read(filename)
|
78
|
+
parser=RedParse.new(lines)
|
79
|
+
tree=parser.parse
|
80
|
+
walk_tree(tree) do |function_node|
|
81
|
+
if (function_node.kind_of?(RedParse::MethodNode) && (line_number.between?(function_node.startline, function_node.endline)))
|
82
|
+
source = lines.split("\n").slice(function_node.startline-1, function_node.endline-function_node.startline+1)
|
83
|
+
return [function_node, source]
|
84
|
+
end
|
85
|
+
end
|
86
|
+
nil
|
87
|
+
end
|
88
|
+
|
89
|
+
def walk_tree(node,depth=0,&block)
|
90
|
+
return if node.nil? || !node.kind_of?(RedParse::Node)
|
91
|
+
node.each do |child|
|
92
|
+
yield child if child.kind_of?(RedParse::MethodNode)
|
93
|
+
walk_tree(child,depth+1,&block) unless depth >= 10
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
%script(type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.3/jquery.min.js")
|
2
|
+
|
3
|
+
:css
|
4
|
+
body {
|
5
|
+
font-size: 14px;
|
6
|
+
}
|
7
|
+
a {
|
8
|
+
text-decoration: none;
|
9
|
+
color: #000;
|
10
|
+
}
|
11
|
+
.clear { clear: both; }
|
12
|
+
|
13
|
+
#controls ul {
|
14
|
+
margin: 20px 0 20px 10px;
|
15
|
+
font-size: 16px;
|
16
|
+
list-style-type: none;
|
17
|
+
padding: 0px;
|
18
|
+
}
|
19
|
+
#controls li {
|
20
|
+
float: left; margin: 0 30px 10px 0; padding: 0;
|
21
|
+
}
|
22
|
+
.box, #controls {
|
23
|
+
margin: auto;
|
24
|
+
width: 1000px;
|
25
|
+
}
|
26
|
+
.box {
|
27
|
+
border: 1px dashed #000;
|
28
|
+
margin-bottom: 15px;
|
29
|
+
}
|
30
|
+
.filename {
|
31
|
+
padding: 5px 10px;
|
32
|
+
background-color: #eef;
|
33
|
+
}
|
34
|
+
.line_numbers {
|
35
|
+
float: left;
|
36
|
+
background-color: #eee;
|
37
|
+
}
|
38
|
+
.code, .line_numbers {
|
39
|
+
padding: 0px 10px;
|
40
|
+
}
|
41
|
+
.code {
|
42
|
+
overflow: hidden;
|
43
|
+
}
|
44
|
+
|
45
|
+
#controls
|
46
|
+
%ul
|
47
|
+
%li
|
48
|
+
%a#collapse_all{:href => '#collapse'} Collapse All
|
49
|
+
%li
|
50
|
+
%a#expand_all{:href => '#expand'} Expand All
|
51
|
+
|
52
|
+
.clear
|
53
|
+
|
54
|
+
- trace.each do |line_detail|
|
55
|
+
.box
|
56
|
+
.filename
|
57
|
+
= line_detail.filename
|
58
|
+
line
|
59
|
+
= line_detail.line_number
|
60
|
+
|
61
|
+
.line_numbers
|
62
|
+
%pre
|
63
|
+
- line_detail.line_count.times do |i|
|
64
|
+
- perform_highlight = line_detail.line_number - line_detail.function_node.startline == i
|
65
|
+
- if (perform_highlight)
|
66
|
+
%strong= line_detail.function_node.startline+i
|
67
|
+
- else
|
68
|
+
= line_detail.function_node.startline+i
|
69
|
+
|
70
|
+
.code
|
71
|
+
%a{:href => "txmt://open/?url=file://#{line_detail.filename}&line=#{line_detail.line_number}"}
|
72
|
+
%pre
|
73
|
+
- line_detail.formatted_source.each_with_index do |line,i|
|
74
|
+
- perform_highlight = line_detail.line_number - line_detail.function_node.startline == i
|
75
|
+
- if (perform_highlight)
|
76
|
+
%strong= line
|
77
|
+
- else
|
78
|
+
= line
|
79
|
+
|
80
|
+
:javascript
|
81
|
+
$("#expand_all").click(function() {
|
82
|
+
$(".filename").siblings().show();
|
83
|
+
})
|
84
|
+
$("#collapse_all").click(function() {
|
85
|
+
$(".filename").siblings().hide();
|
86
|
+
})
|
87
|
+
$(".filename").click(function() {
|
88
|
+
$(this).siblings().toggle();
|
89
|
+
});
|
90
|
+
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
unless $spec_helper_loaded
|
2
|
+
$spec_helper_loaded = true
|
3
|
+
|
4
|
+
$LOAD_PATH.unshift(File.expand_path('../lib', File.dirname(__FILE__)))
|
5
|
+
SPEC_TMP_DIR = File.expand_path('tmp', File.dirname(__FILE__))
|
6
|
+
require 'spork'
|
7
|
+
require 'stringio'
|
8
|
+
require 'fileutils'
|
9
|
+
require 'rspec'
|
10
|
+
require 'trace_viewer'
|
11
|
+
|
12
|
+
RSpec.configure do |config|
|
13
|
+
config.before(:each) do
|
14
|
+
$test_stdout = StringIO.new
|
15
|
+
$test_stderr = StringIO.new
|
16
|
+
@current_dir = nil
|
17
|
+
end
|
18
|
+
|
19
|
+
config.after(:each) do
|
20
|
+
FileUtils.rm_rf(SPEC_TMP_DIR) if File.directory?(SPEC_TMP_DIR)
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
def create_file(filename, contents)
|
25
|
+
FileUtils.mkdir_p(SPEC_TMP_DIR) unless File.directory?(SPEC_TMP_DIR)
|
26
|
+
|
27
|
+
in_current_dir do
|
28
|
+
FileUtils.mkdir_p(File.dirname(filename))
|
29
|
+
File.open(filename, 'wb') { |f| f << contents }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def create_helper_file(test_framework = FakeFramework)
|
34
|
+
create_file(test_framework.helper_file, "# stub spec helper file")
|
35
|
+
end
|
36
|
+
|
37
|
+
def in_current_dir(&block)
|
38
|
+
Dir.chdir(current_dir, &block)
|
39
|
+
end
|
40
|
+
|
41
|
+
def current_dir
|
42
|
+
@current_dir ||= SPEC_TMP_DIR
|
43
|
+
end
|
44
|
+
|
45
|
+
def change_current_dir(sub_path)
|
46
|
+
@current_dir = File.expand_path(sub_path, SPEC_TMP_DIR)
|
47
|
+
end
|
48
|
+
|
49
|
+
def windows?
|
50
|
+
ENV['OS'] == 'Windows_NT'
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
module RSpec
|
56
|
+
module Matchers
|
57
|
+
class IncludeAStringLike
|
58
|
+
def initialize(substring_or_regex)
|
59
|
+
case substring_or_regex
|
60
|
+
when String
|
61
|
+
@regex = Regexp.new(Regexp.escape(substring_or_regex))
|
62
|
+
when Regexp
|
63
|
+
@regex = substring_or_regex
|
64
|
+
else
|
65
|
+
raise ArgumentError, "don't know what to do with the #{substring_or_regex.class} you provided"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def matches?(list_of_strings)
|
70
|
+
@list_of_strings = list_of_strings
|
71
|
+
@list_of_strings.any? { |s| s =~ @regex }
|
72
|
+
end
|
73
|
+
def failure_message
|
74
|
+
"#{@list_of_strings.inspect} expected to include a string like #{@regex.inspect}"
|
75
|
+
end
|
76
|
+
def negative_failure_message
|
77
|
+
"#{@list_of_strings.inspect} expected to not include a string like #{@regex.inspect}, but did"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def include_a_string_like(substring_or_regex)
|
82
|
+
IncludeAStringLike.new(substring_or_regex)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
Dir.glob(File.dirname(__FILE__) + "/support/*.rb").each { |f| require(f) }
|
88
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{trace_viewer}
|
5
|
+
s.version = "0.0.1"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Chris Young"]
|
9
|
+
s.date = %q{2010-10-18}
|
10
|
+
s.description = %q{Launches your web browser to view the functions in a stack trace in chronological order}
|
11
|
+
s.email = %q{beesucker @nospam@ gmail.com}
|
12
|
+
s.extra_rdoc_files = ["lib/trace_viewer.rb", "lib/trace_viewer/line_detail.rb", "lib/views/index.html.haml"]
|
13
|
+
s.files = ["Rakefile", "lib/trace_viewer.rb", "lib/trace_viewer/line_detail.rb", "lib/views/index.html.haml", "spec/spec_helper.rb", "spec/trace_viewer/trace_viewer_spec.rb", "Manifest", "trace_viewer.gemspec"]
|
14
|
+
s.homepage = %q{http://github.com/cj2/trace_viewer}
|
15
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Trace_viewer"]
|
16
|
+
s.require_paths = ["lib"]
|
17
|
+
s.rubyforge_project = %q{trace_viewer}
|
18
|
+
s.rubygems_version = %q{1.3.7}
|
19
|
+
s.summary = %q{Launches your web browser to view the functions in a stack trace in chronological order}
|
20
|
+
|
21
|
+
if s.respond_to? :specification_version then
|
22
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
23
|
+
s.specification_version = 3
|
24
|
+
|
25
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
26
|
+
else
|
27
|
+
end
|
28
|
+
else
|
29
|
+
end
|
30
|
+
end
|
metadata
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: trace_viewer
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
version: 0.0.1
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Chris Young
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-10-18 00:00:00 -06:00
|
18
|
+
default_executable:
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description: Launches your web browser to view the functions in a stack trace in chronological order
|
22
|
+
email: beesucker @nospam@ gmail.com
|
23
|
+
executables: []
|
24
|
+
|
25
|
+
extensions: []
|
26
|
+
|
27
|
+
extra_rdoc_files:
|
28
|
+
- lib/trace_viewer.rb
|
29
|
+
- lib/trace_viewer/line_detail.rb
|
30
|
+
- lib/views/index.html.haml
|
31
|
+
files:
|
32
|
+
- Rakefile
|
33
|
+
- lib/trace_viewer.rb
|
34
|
+
- lib/trace_viewer/line_detail.rb
|
35
|
+
- lib/views/index.html.haml
|
36
|
+
- spec/spec_helper.rb
|
37
|
+
- spec/trace_viewer/trace_viewer_spec.rb
|
38
|
+
- Manifest
|
39
|
+
- trace_viewer.gemspec
|
40
|
+
has_rdoc: true
|
41
|
+
homepage: http://github.com/cj2/trace_viewer
|
42
|
+
licenses: []
|
43
|
+
|
44
|
+
post_install_message:
|
45
|
+
rdoc_options:
|
46
|
+
- --line-numbers
|
47
|
+
- --inline-source
|
48
|
+
- --title
|
49
|
+
- Trace_viewer
|
50
|
+
require_paths:
|
51
|
+
- lib
|
52
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
segments:
|
58
|
+
- 0
|
59
|
+
version: "0"
|
60
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ">="
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
segments:
|
66
|
+
- 1
|
67
|
+
- 2
|
68
|
+
version: "1.2"
|
69
|
+
requirements: []
|
70
|
+
|
71
|
+
rubyforge_project: trace_viewer
|
72
|
+
rubygems_version: 1.3.7
|
73
|
+
signing_key:
|
74
|
+
specification_version: 3
|
75
|
+
summary: Launches your web browser to view the functions in a stack trace in chronological order
|
76
|
+
test_files: []
|
77
|
+
|