pretty_debug 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.
- 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: []
|