kefka 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +20 -0
- data/README +50 -0
- data/README.rdoc +19 -0
- data/Rakefile +43 -0
- data/TODO +49 -0
- data/VERSION +1 -0
- data/bin/kefka +18 -0
- data/examples/sample1.rb +28 -0
- data/examples/trace_specific_lines.rb +18 -0
- data/kefka.gemspec +59 -0
- data/lib/kefka.rb +122 -0
- data/test/helper.rb +18 -0
- data/test/test_kefka.rb +7 -0
- metadata +91 -0
data/.document
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2012 Reginald Tan
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
== Kefka
|
2
|
+
|
3
|
+
A tool for understanding unfamiliar codebases and 3rd party libraries. It shows you callgraphs for different execution paths of a program.
|
4
|
+
|
5
|
+
== Installation
|
6
|
+
|
7
|
+
`gem install kefka`
|
8
|
+
|
9
|
+
== Usage
|
10
|
+
|
11
|
+
From the command line ,
|
12
|
+
|
13
|
+
`kefka filename.rb`
|
14
|
+
|
15
|
+
Examples are provided under the examples directory of kefka gem
|
16
|
+
|
17
|
+
`kefka /path_to_examples_dir_of_kefka/sample1.rb`
|
18
|
+
|
19
|
+
== Guinea Pigs
|
20
|
+
|
21
|
+
1. Rack ( lots of procs/lambdas usage, understand middleware)
|
22
|
+
2. Rails/Thin/WEbrick ( understand full request response cycle )
|
23
|
+
3. Pry
|
24
|
+
4. EventMachine ( reactor scheduler, threadpool, defer )
|
25
|
+
5. Celluloid
|
26
|
+
|
27
|
+
== Related Papers
|
28
|
+
|
29
|
+
http://relo.csail.mit.edu/documentation/relo-vlhcc06.pdf
|
30
|
+
http://dmrussell.net/CHI2010/docs/p2503.pdf
|
31
|
+
|
32
|
+
== Useful Links
|
33
|
+
|
34
|
+
https://github.com/ruby/ruby/blob/trunk/test/ruby/test_settracefunc.rb
|
35
|
+
|
36
|
+
== Contributing to kefka
|
37
|
+
|
38
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
|
39
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
|
40
|
+
* Fork the project.
|
41
|
+
* Start a feature/bugfix branch.
|
42
|
+
* Commit and push until you are happy with your contribution.
|
43
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
44
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
45
|
+
|
46
|
+
== Copyright
|
47
|
+
|
48
|
+
Copyright (c) 2012 Reginald Tan. See LICENSE.txt for
|
49
|
+
further details.
|
50
|
+
|
data/README.rdoc
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
= kefka
|
2
|
+
|
3
|
+
Description goes here.
|
4
|
+
|
5
|
+
== Contributing to kefka
|
6
|
+
|
7
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
|
8
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
|
9
|
+
* Fork the project.
|
10
|
+
* Start a feature/bugfix branch.
|
11
|
+
* Commit and push until you are happy with your contribution.
|
12
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
13
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
14
|
+
|
15
|
+
== Copyright
|
16
|
+
|
17
|
+
Copyright (c) 2012 Reginald Tan. See LICENSE.txt for
|
18
|
+
further details.
|
19
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,43 @@
|
|
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 is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
17
|
+
gem.name = "kefka"
|
18
|
+
gem.homepage = "http://github.com/redgetan/kefka"
|
19
|
+
gem.license = "MIT"
|
20
|
+
gem.summary = %Q{A tool for reading an unfamiliar codebase }
|
21
|
+
gem.description = %Q{ It traces the execution path of a program and displays the source code of each method call in the callgraph }
|
22
|
+
gem.email = "redge.tan@gmail.com"
|
23
|
+
gem.authors = ["Reginald Tan"]
|
24
|
+
# dependencies defined in Gemfile
|
25
|
+
end
|
26
|
+
Jeweler::RubygemsDotOrgTasks.new
|
27
|
+
|
28
|
+
require 'rake/testtask'
|
29
|
+
Rake::TestTask.new(:test) do |test|
|
30
|
+
test.libs << 'lib' << 'test'
|
31
|
+
test.pattern = 'test/**/test_*.rb'
|
32
|
+
test.verbose = true
|
33
|
+
end
|
34
|
+
|
35
|
+
require 'rdoc/task'
|
36
|
+
Rake::RDocTask.new do |rdoc|
|
37
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
38
|
+
|
39
|
+
rdoc.rdoc_dir = 'rdoc'
|
40
|
+
rdoc.title = "kefka #{version}"
|
41
|
+
rdoc.rdoc_files.include('README*')
|
42
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
43
|
+
end
|
data/TODO
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
== TODO
|
2
|
+
|
3
|
+
1st Iteration
|
4
|
+
|
5
|
+
method graph
|
6
|
+
has name of method
|
7
|
+
should display all method calls that resulted from invoking the given method
|
8
|
+
should display dynamic methods (metaprogrammed via instance_eval, class_eval)
|
9
|
+
|
10
|
+
code box (node)
|
11
|
+
needs
|
12
|
+
method_call via (id, klass, file, lineno)
|
13
|
+
method_source via (file, lineno) - use 'method_source' gem
|
14
|
+
|
15
|
+
display
|
16
|
+
text
|
17
|
+
|
18
|
+
has source_code of a method definition
|
19
|
+
has line numbers
|
20
|
+
has filename of source_code
|
21
|
+
has values of local variables
|
22
|
+
each function should link to another code box
|
23
|
+
self must have parent codebox unless its main
|
24
|
+
|
25
|
+
trace
|
26
|
+
can step forward and backward line execution
|
27
|
+
can show value of a variable if mouse hovers over it
|
28
|
+
can show all variables being affected during line execution
|
29
|
+
must highlight variables being changed during line execution
|
30
|
+
|
31
|
+
filter
|
32
|
+
can filter out code initialization line execution
|
33
|
+
Class#inherited, Module#method_added
|
34
|
+
can filter out display of certain variables
|
35
|
+
only belonging to current file where line execution is happening
|
36
|
+
only ones where its changing
|
37
|
+
|
38
|
+
visualization
|
39
|
+
Dendrogram - http://mbostock.github.com/d3/ex/cluster.html
|
40
|
+
Dynamic Node-Link tree - http://bl.ocks.org/999346
|
41
|
+
Interactive Tree - http://bl.ocks.org/1061834
|
42
|
+
|
43
|
+
edge cases
|
44
|
+
what if method has multiple definitions (overriden )
|
45
|
+
for outputing local variables specified by a line, what if it's in a loop (i.e executed 10000 times)
|
46
|
+
maybe - do not output everything (just first 10 and last 10 to see the pattern)
|
47
|
+
- if the user really wants to see everything, he has to specify granurality cmd line option
|
48
|
+
what if there's a binding.pry in the code
|
49
|
+
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
data/bin/kefka
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift("#{File.expand_path(File.dirname(__FILE__))}/../lib")
|
4
|
+
|
5
|
+
require 'kefka'
|
6
|
+
require 'pp'
|
7
|
+
|
8
|
+
if ARGV.count < 1
|
9
|
+
puts "Usage: kefka [path_to_file]"
|
10
|
+
exit
|
11
|
+
end
|
12
|
+
|
13
|
+
path = ARGV[0]
|
14
|
+
file = File.open(path)
|
15
|
+
|
16
|
+
Kefka.trace(file, :callgraph_handler)
|
17
|
+
Kefka.display
|
18
|
+
|
data/examples/sample1.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
class Displayer
|
2
|
+
def initialize(out)
|
3
|
+
@out = out
|
4
|
+
end
|
5
|
+
|
6
|
+
def puts(val)
|
7
|
+
@out.puts val
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def hello
|
12
|
+
num = init
|
13
|
+
x = 4
|
14
|
+
x = x * 2
|
15
|
+
display = Displayer.new($stdout)
|
16
|
+
display.puts "Hello #{num}"
|
17
|
+
end
|
18
|
+
|
19
|
+
def init
|
20
|
+
a = 1 + rand(9)
|
21
|
+
(0..3).each do |x|
|
22
|
+
a = a + x
|
23
|
+
end
|
24
|
+
a
|
25
|
+
end
|
26
|
+
|
27
|
+
hello
|
28
|
+
|
@@ -0,0 +1,18 @@
|
|
1
|
+
set_trace_func proc { |event, file, line, id, binding, classname|
|
2
|
+
printf "%8s %s:%-2d %10s %8s\n", event, file, line, id, classname
|
3
|
+
set_trace_func nil if line == 10
|
4
|
+
}
|
5
|
+
|
6
|
+
def fuck
|
7
|
+
x = 1
|
8
|
+
x = x + 3
|
9
|
+
|
10
|
+
(1..10).each do |x|
|
11
|
+
x
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
fuck
|
17
|
+
|
18
|
+
|
data/kefka.gemspec
ADDED
@@ -0,0 +1,59 @@
|
|
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 = "kefka"
|
8
|
+
s.version = "0.0.1"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Reginald Tan"]
|
12
|
+
s.date = "2012-07-14"
|
13
|
+
s.description = " It traces the execution path of a program and displays the source code of each method call in the callgraph "
|
14
|
+
s.email = "redge.tan@gmail.com"
|
15
|
+
s.executables = ["kefka"]
|
16
|
+
s.extra_rdoc_files = [
|
17
|
+
"LICENSE.txt",
|
18
|
+
"README",
|
19
|
+
"README.rdoc",
|
20
|
+
"TODO"
|
21
|
+
]
|
22
|
+
s.files = [
|
23
|
+
".document",
|
24
|
+
"Gemfile",
|
25
|
+
"LICENSE.txt",
|
26
|
+
"README",
|
27
|
+
"README.rdoc",
|
28
|
+
"Rakefile",
|
29
|
+
"VERSION",
|
30
|
+
"bin/kefka",
|
31
|
+
"examples/sample1.rb",
|
32
|
+
"examples/trace_specific_lines.rb",
|
33
|
+
"kefka.gemspec",
|
34
|
+
"lib/kefka.rb",
|
35
|
+
"test/helper.rb",
|
36
|
+
"test/test_kefka.rb"
|
37
|
+
]
|
38
|
+
s.homepage = "http://github.com/redgetan/kefka"
|
39
|
+
s.licenses = ["MIT"]
|
40
|
+
s.require_paths = ["lib"]
|
41
|
+
s.rubygems_version = "1.8.10"
|
42
|
+
s.summary = "A tool for reading an unfamiliar codebase"
|
43
|
+
|
44
|
+
if s.respond_to? :specification_version then
|
45
|
+
s.specification_version = 3
|
46
|
+
|
47
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
48
|
+
s.add_development_dependency(%q<rspec>, [">= 0"])
|
49
|
+
s.add_development_dependency(%q<jeweler>, [">= 0"])
|
50
|
+
else
|
51
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
52
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
53
|
+
end
|
54
|
+
else
|
55
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
56
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
data/lib/kefka.rb
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
module Kefka
|
2
|
+
|
3
|
+
@@values = {}
|
4
|
+
@@method_source = {}
|
5
|
+
|
6
|
+
class << self
|
7
|
+
|
8
|
+
def get_values_of_locals_from_binding(binding)
|
9
|
+
locals = binding.eval("local_variables")
|
10
|
+
locals.inject({}) do |result,l|
|
11
|
+
val = binding.eval(l.to_s)
|
12
|
+
val = begin
|
13
|
+
# deep copy
|
14
|
+
Marshal.load(Marshal.dump(val)) if val
|
15
|
+
rescue TypeError
|
16
|
+
"_unknown_"
|
17
|
+
end
|
18
|
+
|
19
|
+
result.merge!({ l => val })
|
20
|
+
result
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Things to IGNORE
|
25
|
+
# 1. loading of rubygems/libraries
|
26
|
+
def callgraph_handler(event, file, line, id, binding, classname)
|
27
|
+
# do not trace current file (TODO: and anything in this library)
|
28
|
+
return if file == __FILE__
|
29
|
+
case event
|
30
|
+
when "call"
|
31
|
+
# mark the start of method call
|
32
|
+
|
33
|
+
# key must be uniquely identifiable -
|
34
|
+
# Class methodname is not enough
|
35
|
+
# perhaps include:
|
36
|
+
# 1. line
|
37
|
+
# 2. file
|
38
|
+
key = "#{classname}_#{id}"
|
39
|
+
@@method_source[key] = [file, caller[1], line]
|
40
|
+
when "line"
|
41
|
+
when "return"
|
42
|
+
key = "#{classname}_#{id}"
|
43
|
+
@@method_source[key] << line if @@method_source[key]
|
44
|
+
else
|
45
|
+
# do nothing
|
46
|
+
end
|
47
|
+
rescue Exception => e
|
48
|
+
puts "#{e.message} from -- #{e.backtrace.join("\n")}"
|
49
|
+
end
|
50
|
+
|
51
|
+
def locals_values_handler(event, file, line, id, binding, classname)
|
52
|
+
|
53
|
+
return if file == __FILE__
|
54
|
+
|
55
|
+
case event
|
56
|
+
when "call"
|
57
|
+
#puts "Entering method #{classname} - #{id}"
|
58
|
+
when "line"
|
59
|
+
# variables that should not be tracked
|
60
|
+
# 1. anything in current lib
|
61
|
+
# 2. all local variables that are in TOP LEVEL BINDING before tracing
|
62
|
+
# - but these variables may be overwritten by the traced program,
|
63
|
+
# excluding them would mean not displaying certain relevant
|
64
|
+
# vars in that program
|
65
|
+
key = "#{file}_#{line}".to_sym
|
66
|
+
@@values[key] = get_values_of_locals_from_binding(binding)
|
67
|
+
else
|
68
|
+
# do nothing
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def start(handler)
|
73
|
+
set_trace_func method(handler).to_proc
|
74
|
+
end
|
75
|
+
|
76
|
+
def stop
|
77
|
+
set_trace_func nil
|
78
|
+
end
|
79
|
+
|
80
|
+
def trace(file, handler = :callgraph_handler)
|
81
|
+
puts "\nTracing Execution using #{handler}...\n\n"
|
82
|
+
start(handler)
|
83
|
+
file.rewind if file.eof?
|
84
|
+
code = file.read
|
85
|
+
eval(code, TOPLEVEL_BINDING, file.path, 1)
|
86
|
+
stop
|
87
|
+
end
|
88
|
+
|
89
|
+
def method_graph
|
90
|
+
@@method_source
|
91
|
+
end
|
92
|
+
|
93
|
+
def display
|
94
|
+
puts "\n==== Generating Method Callgraph...\n\n"
|
95
|
+
@@method_source.each do |meth,props|
|
96
|
+
puts
|
97
|
+
puts meth
|
98
|
+
puts
|
99
|
+
|
100
|
+
file, parent_caller, start_line, finish_line = props
|
101
|
+
|
102
|
+
File.open(file) { |f|
|
103
|
+
(start_line - 1).times { f.readline }
|
104
|
+
|
105
|
+
code = ""
|
106
|
+
(finish_line - start_line + 1).times {
|
107
|
+
code << f.readline
|
108
|
+
}
|
109
|
+
puts code
|
110
|
+
}
|
111
|
+
|
112
|
+
puts
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def values
|
117
|
+
@@values
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
begin
|
4
|
+
Bundler.setup(:default, :development)
|
5
|
+
rescue Bundler::BundlerError => e
|
6
|
+
$stderr.puts e.message
|
7
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
8
|
+
exit e.status_code
|
9
|
+
end
|
10
|
+
require 'test/unit'
|
11
|
+
require 'shoulda'
|
12
|
+
|
13
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
14
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
15
|
+
require 'kefka'
|
16
|
+
|
17
|
+
class Test::Unit::TestCase
|
18
|
+
end
|
data/test/test_kefka.rb
ADDED
metadata
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: kefka
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Reginald Tan
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-07-14 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
requirement: &70266566895280 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70266566895280
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: jeweler
|
27
|
+
requirement: &70266566888960 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70266566888960
|
36
|
+
description: ! ' It traces the execution path of a program and displays the source
|
37
|
+
code of each method call in the callgraph '
|
38
|
+
email: redge.tan@gmail.com
|
39
|
+
executables:
|
40
|
+
- kefka
|
41
|
+
extensions: []
|
42
|
+
extra_rdoc_files:
|
43
|
+
- LICENSE.txt
|
44
|
+
- README
|
45
|
+
- README.rdoc
|
46
|
+
- TODO
|
47
|
+
files:
|
48
|
+
- .document
|
49
|
+
- Gemfile
|
50
|
+
- LICENSE.txt
|
51
|
+
- README
|
52
|
+
- README.rdoc
|
53
|
+
- Rakefile
|
54
|
+
- VERSION
|
55
|
+
- bin/kefka
|
56
|
+
- examples/sample1.rb
|
57
|
+
- examples/trace_specific_lines.rb
|
58
|
+
- kefka.gemspec
|
59
|
+
- lib/kefka.rb
|
60
|
+
- test/helper.rb
|
61
|
+
- test/test_kefka.rb
|
62
|
+
- TODO
|
63
|
+
homepage: http://github.com/redgetan/kefka
|
64
|
+
licenses:
|
65
|
+
- MIT
|
66
|
+
post_install_message:
|
67
|
+
rdoc_options: []
|
68
|
+
require_paths:
|
69
|
+
- lib
|
70
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
71
|
+
none: false
|
72
|
+
requirements:
|
73
|
+
- - ! '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
segments:
|
77
|
+
- 0
|
78
|
+
hash: 972812171248094550
|
79
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
80
|
+
none: false
|
81
|
+
requirements:
|
82
|
+
- - ! '>='
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: '0'
|
85
|
+
requirements: []
|
86
|
+
rubyforge_project:
|
87
|
+
rubygems_version: 1.8.10
|
88
|
+
signing_key:
|
89
|
+
specification_version: 3
|
90
|
+
summary: A tool for reading an unfamiliar codebase
|
91
|
+
test_files: []
|