pretty_debug 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/pretty_debug.rb +226 -0
- metadata +43 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 63d046bb64c02a4934a7552366ac1bbda9b7eb48
|
4
|
+
data.tar.gz: 7234ce3741b46d5330503d39eaa1d420c48af558
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4fd77aec4709afb9dd87655766060311bcac256881b76bbcd4f352e1b0ac934d010d00d6d0629ccc42a4a84855316aba9266b92e6b305b4bdbebeee42a0eeb3f
|
7
|
+
data.tar.gz: 53fc2eda6b9662257774bd1af2b4958b6bcbabe9f530fbd4a905126370db7e5a96f4204a01826cc49f907961d2c8bbbec56346df7f996c5740b559b6a57e127a
|
data/lib/pretty_debug.rb
ADDED
@@ -0,0 +1,226 @@
|
|
1
|
+
#!ruby
|
2
|
+
require "string"
|
3
|
+
require "ruby-prof"
|
4
|
+
|
5
|
+
module Kernel
|
6
|
+
at_exit do
|
7
|
+
case $!
|
8
|
+
when SystemStackError
|
9
|
+
puts PrettyDebug.mesage
|
10
|
+
##TODO Parse the backtrace and attempt to extract a call cycle.
|
11
|
+
# PrettyDebug.clean($@)
|
12
|
+
when nil, SystemExit, Interrupt
|
13
|
+
else
|
14
|
+
puts PrettyDebug.message
|
15
|
+
puts PrettyDebug.clean($@).align(":").map(&:indent)
|
16
|
+
end
|
17
|
+
$stderr.reopen(IO::NULL)
|
18
|
+
$stdout.reopen(IO::NULL)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class ValidationError < Exception; end
|
23
|
+
|
24
|
+
class Object
|
25
|
+
def expect s; raise ArgumentError.new("Expecting `#{s.inspect}`: #{inspect}") end
|
26
|
+
def case? *kases; kases.any?{|kase| kase === self} end
|
27
|
+
end
|
28
|
+
|
29
|
+
class Array
|
30
|
+
def compatible? other
|
31
|
+
other.case?(Array) and
|
32
|
+
other.length == length
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class Hash
|
37
|
+
def compatible? other
|
38
|
+
other.case?(Hash) and
|
39
|
+
other.size == size and
|
40
|
+
other.keys.all?{|k| key?(k)}
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class PrettyDebug
|
45
|
+
attr_accessor :backtrace, :message
|
46
|
+
def self.new_internal
|
47
|
+
new.tap do |error|
|
48
|
+
called_location = clean(caller)[2]
|
49
|
+
error.backtrace = clean($@).take_while{|a| a != called_location}
|
50
|
+
error.message = $!.message
|
51
|
+
end
|
52
|
+
end
|
53
|
+
Stackline = /\A(?'f'.+?):(?'l'\d+)(?::in `(?'m'.*)'|(?'m'.*))?\z/m
|
54
|
+
NoncallLine = /\Ablock(?: \(\d+ levels\))? in |\A\</
|
55
|
+
def self.message; $!.message.dup.tap{|s| s[0] = s[0].upcase}.sub(/(?<=[^.])\z/, ".") end
|
56
|
+
def self.clean stack
|
57
|
+
raise "Cannot parse. No backtrace given" unless stack
|
58
|
+
return [] if stack.empty?
|
59
|
+
caller_file_i = $LOADED_FEATURES.index(caller.first.match(Stackline)[:f]).to_i
|
60
|
+
stack
|
61
|
+
.map{|l| m = l.match(Stackline); [m[:f].ellipsis(55), m[:l].to_i, m[:m]]}
|
62
|
+
.transpose.tap do |_, _, m|
|
63
|
+
m.rotate!(-1)
|
64
|
+
m[0] = ""
|
65
|
+
while i = m.index{|m| m == "method_missing"}; m[i] = m[i - 1] end
|
66
|
+
end.transpose
|
67
|
+
.reject{|_, _, m| m =~ NoncallLine}
|
68
|
+
# .reject{|f, _, _| $LOADED_FEATURES.include?(f)}
|
69
|
+
# .reject{|f, _, _| $LOADED_FEATURES.index(f).to_i > caller_file_i}
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
#############################################
|
74
|
+
# Terminal formatting
|
75
|
+
#############################################
|
76
|
+
|
77
|
+
class Object
|
78
|
+
def intercept &pr
|
79
|
+
tap{|x| puts "[Debug] #{caller[2][/.*?:\d+/]}:".color(:yellow); pr ? pr.call(x) : p(x)}
|
80
|
+
end
|
81
|
+
def forwardtrace sym
|
82
|
+
tap{puts "#{inspect}##{sym} defined at:", method(sym).source_location.join(":").indent}
|
83
|
+
end
|
84
|
+
def follow m; tap{puts "Next step: #{method(m).source_location
|
85
|
+
.chain{|a| a ? a.join(":") : "Unknown #{self}.#{m}"}
|
86
|
+
}"} end
|
87
|
+
def _?; tap{Test.testee.push(self)} end
|
88
|
+
end
|
89
|
+
|
90
|
+
=begin
|
91
|
+
class Module
|
92
|
+
def inspect
|
93
|
+
ans = ancestors.map{|m| "#{m.class.send(:to_s).downcase} #{m}"}.join(" < ")
|
94
|
+
var = (class_variables + instance_variables).join(", ")
|
95
|
+
var.empty? ? ans : "#{ans}#$/#{var}"
|
96
|
+
end
|
97
|
+
end
|
98
|
+
=end
|
99
|
+
|
100
|
+
class Proc
|
101
|
+
def source_location
|
102
|
+
to_s.match(/\A\#\<#{self.class}:.+?@(.+)\:(\d+)(?: \(lambda\))?\>\z/).values_at(1, 2)
|
103
|
+
end
|
104
|
+
def inspect
|
105
|
+
f, l = source_location
|
106
|
+
"Proc@#{File.basename(f)}:#{l}"
|
107
|
+
rescue
|
108
|
+
"Proc@source_unknown"
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
class Method
|
113
|
+
def inspect; to_s end
|
114
|
+
def to_s; "#{receiver}.#{name}" end
|
115
|
+
end
|
116
|
+
|
117
|
+
class UnboundMethod
|
118
|
+
def inspect; to_s end
|
119
|
+
def to_s; "#{owner}##{name}" end
|
120
|
+
end
|
121
|
+
|
122
|
+
class Array
|
123
|
+
def inspect
|
124
|
+
map(&:inspect)
|
125
|
+
.chain{|s| length < 2 ? "[#{s.join}]" : "[#$/#{s.join(",#$/").indent}#$/]"}
|
126
|
+
end
|
127
|
+
def align sep = " ", ellipsis_limit = nil
|
128
|
+
col_widths = transpose.map{|col| col.map{|cell| cell.to_s.length}.max}
|
129
|
+
map{|row| [row, col_widths].parallel{|col, l|
|
130
|
+
max = ellipsis_limit || l
|
131
|
+
l = l.at_most(max)
|
132
|
+
case col
|
133
|
+
when Numeric then col.to_s.ellipsis(max).rjust(l)
|
134
|
+
else col.to_s.ellipsis(max).ljust(l)
|
135
|
+
end
|
136
|
+
}.join(sep)}
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
class Hash
|
141
|
+
KeyLengthMax = 30
|
142
|
+
def inspect
|
143
|
+
keys = self.keys.map{|k| k.inspect.utf8}
|
144
|
+
# When `self == empty?`, `...max` becomes `nil`. `to_i` turns it to `0`.
|
145
|
+
w = keys.map(&:length).max.to_i.at_most(KeyLengthMax)
|
146
|
+
[keys, values].parallel{|k, v| "#{k.ljust(w)} => #{v.inspect}"}
|
147
|
+
.chain{|s| length < 2 ? "{#{s.join}}" : "{#$/#{s.join(",#$/").indent}#$/}"}
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
module Test
|
152
|
+
def self.testee; @@testee end
|
153
|
+
def self.raise? exception = Exception, &pr
|
154
|
+
pr.call; false
|
155
|
+
rescue Exception => e
|
156
|
+
e.kind_of?(exception)
|
157
|
+
end
|
158
|
+
def self.testee_clear; @@testee = [] end
|
159
|
+
def self.testee_refer; @@testee end
|
160
|
+
def self.test title = "Test", &pr
|
161
|
+
testee_clear
|
162
|
+
t = Time.now
|
163
|
+
if pr.call == true
|
164
|
+
puts "#{title}. Succeeded (#{"%.2e" % t.till_now} secs)".color(:green)
|
165
|
+
else
|
166
|
+
puts "#{title}. Failed".color(:red),
|
167
|
+
*(testee_refer.map{|o| o.inspect.indent.color(:red)} unless testee_refer.empty?)
|
168
|
+
end
|
169
|
+
rescue Exception
|
170
|
+
puts "#{title} ... Test Error".color(:red),
|
171
|
+
[$!.message, *PrettyDebug.clean($@).align(":")].map{|l| l.indent.color(:red)}
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
module Kernel
|
176
|
+
def test *args, ≺ Test.test(*args, &pr) end
|
177
|
+
def raise? *args, ≺ Test.raise?(*args, &pr) end
|
178
|
+
def timer
|
179
|
+
t = Time.now
|
180
|
+
yield
|
181
|
+
puts "(#{"%.2e" % t.till_now} secs)".color(:green)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
=begin
|
186
|
+
module Kernel
|
187
|
+
def benchmark i, &pr
|
188
|
+
Benchmark.prs = []
|
189
|
+
pr.call()
|
190
|
+
Benchmark.bm(i){|br| Benchmark.prs.each{|pr| br.report(""){pr.call()}}}
|
191
|
+
end
|
192
|
+
def alternative ≺ Benchmark.prs.push(pr) end
|
193
|
+
end
|
194
|
+
|
195
|
+
require "benchmark"
|
196
|
+
Benchmark.singleton_class.class_eval{attr_accessor :prs}
|
197
|
+
=end
|
198
|
+
|
199
|
+
|
200
|
+
=begin
|
201
|
+
require "ruby-prof"
|
202
|
+
$prof_begin = false
|
203
|
+
def profile_on; $prof_begin = true; end
|
204
|
+
def profile switch = nil
|
205
|
+
yield if switch && $prof_begin.!
|
206
|
+
RubyProf.resume{$prof_result = yield}
|
207
|
+
open("/tmp/profile", "w"){|io|
|
208
|
+
# RubyProf::CallStackPrinter. # for kcachegring
|
209
|
+
RubyProf::GraphHtmlPrinter.
|
210
|
+
# RubyProf::GraphPrinter.
|
211
|
+
# RubyProf::AbstractPrinter.
|
212
|
+
new(RubyProf.stop).print(io)}
|
213
|
+
# IO.popen("kcachegrind /tmp/profile > #{File::NULL} 2>&1")
|
214
|
+
# IO.popen("firefox >#{File::NULL} /tmp/profile ")
|
215
|
+
IO.popen("google-chrome >#{File::NULL} /tmp/profile ")
|
216
|
+
$prof_result
|
217
|
+
end
|
218
|
+
|
219
|
+
module Profiling
|
220
|
+
def self.start; RubyProf.start end
|
221
|
+
def self.stop
|
222
|
+
result = RubyProf.stop
|
223
|
+
RubyProf::FlatPrinter.new(result).print
|
224
|
+
end
|
225
|
+
end
|
226
|
+
=end
|
metadata
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pretty_debug
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- sawa
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-07-29 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: ''
|
14
|
+
email: []
|
15
|
+
executables: []
|
16
|
+
extensions: []
|
17
|
+
extra_rdoc_files: []
|
18
|
+
files:
|
19
|
+
- lib/pretty_debug.rb
|
20
|
+
homepage: http://sawa.github.io/pretty_debug
|
21
|
+
licenses: []
|
22
|
+
metadata: {}
|
23
|
+
post_install_message:
|
24
|
+
rdoc_options: []
|
25
|
+
require_paths:
|
26
|
+
- lib
|
27
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
28
|
+
requirements:
|
29
|
+
- - '>='
|
30
|
+
- !ruby/object:Gem::Version
|
31
|
+
version: '0'
|
32
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
33
|
+
requirements:
|
34
|
+
- - '>='
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: '0'
|
37
|
+
requirements: []
|
38
|
+
rubyforge_project:
|
39
|
+
rubygems_version: 2.0.6
|
40
|
+
signing_key:
|
41
|
+
specification_version: 4
|
42
|
+
summary: ''
|
43
|
+
test_files: []
|