trace_viewer 0.0.1
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/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
|
+
|