dev-utils 1.0
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/HISTORY.txt +36 -0
- data/MIT-LICENSE.txt +20 -0
- data/README.txt +21 -0
- data/Rakefile +135 -0
- data/VERSION +1 -0
- data/etc/doc/DebuggingAids.textile +450 -0
- data/etc/doc/UnitTestOrganisation.textile +253 -0
- data/etc/doc/generate.rb +187 -0
- data/etc/doc/index.textile +110 -0
- data/etc/doc/links.dat +9 -0
- data/etc/doc/textile.css +150 -0
- data/examples/breakpoint-example.rb +30 -0
- data/examples/debug.log +0 -0
- data/examples/log-trace-example.rb +35 -0
- data/lib/dev-utils/debug.rb +32 -0
- data/lib/dev-utils/debug/diff.rb +187 -0
- data/lib/dev-utils/debug/irb.rb +176 -0
- data/lib/dev-utils/debug/log.rb +107 -0
- data/lib/dev-utils/test.rb +267 -0
- data/test/TEST.rb +6 -0
- data/test/tc_debug.rb +153 -0
- metadata +76 -0
data/HISTORY.txt
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
(All changes by Gavin Sinclair <gsinclair@soyabean.com.au> unless otherwise
|
2
|
+
noted or input credited.)
|
3
|
+
|
4
|
+
=== 2004-10-08
|
5
|
+
|
6
|
+
Version 1.0 released.
|
7
|
+
|
8
|
+
=== 2004-10-06-8
|
9
|
+
|
10
|
+
Internal version 0.0.2, with the following changes:
|
11
|
+
|
12
|
+
* Commented out the code in <tt>test.rb</tt> and <tt>debug/diff.rb</tt>.
|
13
|
+
These files are not ready for release, but I don't want to delay the release
|
14
|
+
of the basic functionality (breakpoints, logging, and tracing).
|
15
|
+
|
16
|
+
* <tt>dev-utils/debug-inline.rb</tt> and <tt>dev-utils/test-inline.rb</tt>
|
17
|
+
removed; no longer needed. When you require 'dev-utils/debug', the
|
18
|
+
DevUtils::Debug module is included in the toplevel for convenience. I doubt
|
19
|
+
this will cause problematic clashes, but at least the option is there to
|
20
|
+
later allow a non-intrusive library, like <tt>dev-utils/debug/module</tt>.
|
21
|
+
|
22
|
+
* Removed Synopsis.textile and put simple code snippets into the main page.
|
23
|
+
|
24
|
+
* Added MIT license.
|
25
|
+
|
26
|
+
* Textile documentation polished up. Examples added, planned stuff so marked,
|
27
|
+
comments removed. RDoc documentation completed.
|
28
|
+
|
29
|
+
=== 2004-09-24
|
30
|
+
|
31
|
+
* Cementing version 0.0.1, as I'm going to introduce Florian Gross's
|
32
|
+
breakpoint.rb and Binding.of_caller (which will help trace() as well). Also
|
33
|
+
removing some documentation to clear the decks for a 0.0.2 actual release.
|
34
|
+
|
35
|
+
|
36
|
+
vim: ft=txt ai comments=fb\:* tw=80 fo=tcq
|
data/MIT-LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2004, Gavin Sinclair.
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
11
|
+
copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
19
|
+
SOFTWARE.
|
20
|
+
|
data/README.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
= The Ruby dev-utils Project
|
2
|
+
|
3
|
+
<tt>dev-utils</tt> is a collection of methods to aid Ruby development. It is
|
4
|
+
well described at its homepage: http://dev-utils.rubyforge.org.
|
5
|
+
|
6
|
+
Version 1.0 was released on Friday Oct 08, 2004. The only class of interest,
|
7
|
+
API-wise, is DevUtils::Debug.
|
8
|
+
|
9
|
+
== Installation
|
10
|
+
|
11
|
+
At the 1.0 release, only installation by RubyGems is supported. This will be
|
12
|
+
revisited shortly for 1.0.1, with RPA and tarball installation being offered.
|
13
|
+
|
14
|
+
== Documentation and Examples
|
15
|
+
|
16
|
+
The API is documented via RDoc. Users should visit the homepage for general
|
17
|
+
documentation. Examples are located in the +examples+ directory of the
|
18
|
+
distribution.
|
19
|
+
|
20
|
+
A local copy of the documentation can be created with the command <tt>rake
|
21
|
+
www</tt>. You need Rake, Extensions, and RedCloth.
|
data/Rakefile
ADDED
@@ -0,0 +1,135 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/rdoctask'
|
3
|
+
require 'rake/gempackagetask'
|
4
|
+
require 'rubygems'
|
5
|
+
Gem.manage_gems
|
6
|
+
|
7
|
+
#
|
8
|
+
# =========================== U N I T T E S T S ===========================
|
9
|
+
#
|
10
|
+
|
11
|
+
desc 'Run unit tests'
|
12
|
+
task :test do
|
13
|
+
# Might wait until I've got some...
|
14
|
+
end
|
15
|
+
|
16
|
+
#
|
17
|
+
# =========================== P A C K A G I N G =============================
|
18
|
+
#
|
19
|
+
|
20
|
+
PKG_VERSION = File.read('VERSION').strip
|
21
|
+
|
22
|
+
spec = Gem::Specification.new do |s|
|
23
|
+
s.name = 'dev-utils'
|
24
|
+
s.version = PKG_VERSION
|
25
|
+
s.summary = 'Debugging utilities: breakpoints, debugging, and tracing.'
|
26
|
+
s.description = s.summary
|
27
|
+
s.add_dependency('extensions', '>= 0.5')
|
28
|
+
s.required_ruby_version = '>= 1.8.1'
|
29
|
+
s.files = FileList['[A-Z]*', '{etc,examples,lib,test}/**/*'].to_a
|
30
|
+
s.require_path = 'lib'
|
31
|
+
s.autorequire = nil
|
32
|
+
s.has_rdoc = true
|
33
|
+
s.extra_rdoc_files = FileList['*.txt'].to_a
|
34
|
+
s.rdoc_options << '--main' << 'README.txt' << '--title' << 'dev-utils API Documentation'
|
35
|
+
s.test_files = []
|
36
|
+
s.author = 'Gavin Sinclair'
|
37
|
+
s.email = 'gsinclair@soyabean.com.au'
|
38
|
+
s.homepage = 'http://dev-utils.rubyforge.org'
|
39
|
+
s.rubyforge_project = 'dev-utils'
|
40
|
+
end
|
41
|
+
|
42
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
43
|
+
pkg.package_dir = 'build/pkg'
|
44
|
+
pkg.need_zip = false
|
45
|
+
pkg.need_tar = false
|
46
|
+
end
|
47
|
+
|
48
|
+
desc "Install the gem"
|
49
|
+
task :install => :repackage do
|
50
|
+
sh "gem install --local --no-rdoc build/pkg/dev-utils-#{PKG_VERSION}"
|
51
|
+
end
|
52
|
+
|
53
|
+
#
|
54
|
+
# ============================== W E B S I T E ===============================
|
55
|
+
#
|
56
|
+
|
57
|
+
directory 'build/www'
|
58
|
+
directory 'build/www/api'
|
59
|
+
|
60
|
+
desc "Build the RDoc documentation"
|
61
|
+
Rake::RDocTask.new(:rdoc) do |rt|
|
62
|
+
rt.rdoc_dir = 'build/www/api'
|
63
|
+
rt.rdoc_files.include('*.txt', 'lib/**/*.rb')
|
64
|
+
rt.main = 'README.txt'
|
65
|
+
rt.title = 'dev-utils API Documentation'
|
66
|
+
rt.options << '--inline-source'
|
67
|
+
end
|
68
|
+
|
69
|
+
#
|
70
|
+
# Building the web pages (source: etc/doc/*.textile; target: build/www/*.html)
|
71
|
+
#
|
72
|
+
# I should be able to name the targets, generated from the sources, and have a rule that
|
73
|
+
# generates HTML from Textile. This rule should take account of timestamps, and keep in mind
|
74
|
+
# that a change in the CSS or generate.rb or links.dat requires regeneration throughout.
|
75
|
+
#
|
76
|
+
|
77
|
+
desc "Build the Textile documentation"
|
78
|
+
task :textile => ['build/www']
|
79
|
+
|
80
|
+
dependencies = FileList['etc/doc/{*.css,*.rb,*.dat}'].to_a
|
81
|
+
Dir['etc/doc/*.textile'].each do |source|
|
82
|
+
target = source.sub('etc/doc', 'build/www').sub('.textile', '.html')
|
83
|
+
# Set the file task, so Rake knows about it.
|
84
|
+
file target => [source, *dependencies] do
|
85
|
+
require 'etc/doc/generate'
|
86
|
+
generate_document source, 'build/www'
|
87
|
+
end
|
88
|
+
# Collect the targets, so the 'textile' task can aggregate them all.
|
89
|
+
task :textile => target
|
90
|
+
end
|
91
|
+
|
92
|
+
desc "Build the web page"
|
93
|
+
task :www => [:rerdoc, :textile] do
|
94
|
+
end
|
95
|
+
|
96
|
+
desc 'Build a text version of the home page'
|
97
|
+
task :textpage => :textile do
|
98
|
+
text = `lynx -dump build/www/index.html`
|
99
|
+
# There is some filtering we need to do.
|
100
|
+
# ***** This is very delicate and needs to be kept up to date! *****
|
101
|
+
if true
|
102
|
+
# Remove top of page until the text "About dev-utils"
|
103
|
+
text.sub!(/\A.*?^\s*About dev-utils/m, "\nAbout dev-utils")
|
104
|
+
# Remove unwanted references.
|
105
|
+
unwanted_refs = '5678'
|
106
|
+
text.gsub!(/\[[#{unwanted_refs}]\]/, '')
|
107
|
+
unwanted_refs.split(//).each do |label|
|
108
|
+
text.sub!(/^\s*#{label}\. \S+\s*$/, '')
|
109
|
+
end
|
110
|
+
# Turn file:// URLs into http:// URLs.
|
111
|
+
text.gsub!(%r{file://\S+/www/}, 'http://dev-utils.rubyforge.org/')
|
112
|
+
end
|
113
|
+
puts text
|
114
|
+
end
|
115
|
+
|
116
|
+
#
|
117
|
+
# =========================== P U B L I S H I N G ===========================
|
118
|
+
#
|
119
|
+
|
120
|
+
desc 'Publish the web stuff to RubyForge'
|
121
|
+
task 'publish-textile' => :textile do
|
122
|
+
Dir.chdir 'build/www' do
|
123
|
+
system "scp #{Dir['*.html'].join(' ')} gsinclair@rubyforge.org:/var/www/gforge-projects/dev-utils"
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
desc 'Publish the API documentation to RubyForge'
|
128
|
+
task 'publish-api' => :rerdoc do
|
129
|
+
Dir.chdir 'build/www' do
|
130
|
+
system "scp -r api gsinclair@rubyforge.org:/var/www/gforge-projects/dev-utils"
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
# vim: ft=ruby
|
135
|
+
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.0
|
@@ -0,0 +1,450 @@
|
|
1
|
+
|
2
|
+
!header dev-utils/debug main
|
3
|
+
|
4
|
+
h1. Debugging Aids
|
5
|
+
|
6
|
+
!toc
|
7
|
+
|
8
|
+
|
9
|
+
h2. Introduction
|
10
|
+
|
11
|
+
Ruby offers several aids to debugging. @dev-utils/debug@ offers more.
|
12
|
+
|
13
|
+
With @dev-utils/debug@ you can:
|
14
|
+
|
15
|
+
* Escape to an IRB session from a running program.
|
16
|
+
|
17
|
+
<pre style="margin-left:5em">
|
18
|
+
breakpoint
|
19
|
+
breakpoint 'Person#name' # Identify it when it happens.
|
20
|
+
breakpoint { @name } # Default return value.
|
21
|
+
</pre>
|
22
|
+
|
23
|
+
* Access a no-config logfile for debugging.
|
24
|
+
|
25
|
+
<pre style="margin-left:5em">
|
26
|
+
debug 'Database connection established' # Look in ./debug.log
|
27
|
+
</pre>
|
28
|
+
|
29
|
+
* Trace expressions in that logfile.
|
30
|
+
|
31
|
+
<pre style="margin-left:5em">
|
32
|
+
trace 'x + y'
|
33
|
+
trace 'Process.pid'
|
34
|
+
trace 'names', :pp # Pretty-print.
|
35
|
+
trace 'page_structure', :yaml # YAML representation.
|
36
|
+
</pre>
|
37
|
+
|
38
|
+
* %(planned)Find the difference between complex objects (planned).%
|
39
|
+
|
40
|
+
These are discussed in detail below.
|
41
|
+
|
42
|
+
|
43
|
+
h2. Escape to an IRB Session
|
44
|
+
|
45
|
+
Ruby's debugger allows you to step through code and find the value of
|
46
|
+
local variables, but setting breakpoints can be difficult, code
|
47
|
+
stepping can be flaky, and running a complex program through the
|
48
|
+
debugger is slow.
|
49
|
+
|
50
|
+
A partial solution to these problems is to escape from a running program to an
|
51
|
+
open IRB session containing the current environment. This simulates the
|
52
|
+
setting of breakpoints, even allowing for conditions. Those "breakpoints"
|
53
|
+
remain in place between program runs as well, until you remove the relevant
|
54
|
+
line of code. The program runs normally, not through the debugger, so there
|
55
|
+
are no speed issues.
|
56
|
+
|
57
|
+
During the IRB session, you can interact with the program: get and set
|
58
|
+
local (or other) variables, call methods, and so on. When you finish
|
59
|
+
with IRB, end the session with @CTRL-D@ and the program will continue
|
60
|
+
running.
|
61
|
+
|
62
|
+
p. @dev-utils/debug@ defines two convenient *aliases* for you: *iv*
|
63
|
+
for @instance_variables@ and *lv* for @local_variables@. Note that IRB will
|
64
|
+
load your @$HOME/.irbrc@ file, even though it's being run from within your
|
65
|
+
program. This means you can define other aliases and helpful methods there.
|
66
|
+
|
67
|
+
h3. Breakpoint Example
|
68
|
+
|
69
|
+
The following code comes from @examples/breakpoint-example.rb@ in the distribution:
|
70
|
+
|
71
|
+
<pre>
|
72
|
+
require 'dev-utils/debug'
|
73
|
+
|
74
|
+
class Person
|
75
|
+
def initialize(name, age)
|
76
|
+
@name, @age = name, age
|
77
|
+
breakpoint 'Person#initialize'
|
78
|
+
end
|
79
|
+
|
80
|
+
attr_reader :age
|
81
|
+
def name
|
82
|
+
breakpoint('Person#name') { @name }
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
person = Person.new('John Smith', 23)
|
87
|
+
puts "Name: #{person.name}"
|
88
|
+
</pre>
|
89
|
+
|
90
|
+
In English: we have a @Person@ class containing a name and age. When a Person
|
91
|
+
object is initialized, we have a breakpoint. When a Person's name is queried
|
92
|
+
(via @Person#name@) we have another breakpoint. This one uses the default
|
93
|
+
return value feature, which means we can force a different return value when
|
94
|
+
that method is called. After the class is defined, a Person object is
|
95
|
+
created, and its name is printed, meaning both breakpoints will be triggered.
|
96
|
+
|
97
|
+
To demonstrate the behaviour of this short program, we will run it three times
|
98
|
+
and muck about with IRB. In the console output below, @^D@ represents the
|
99
|
+
keystroke _CTRL-D_, which terminates the breakpoint session. Be careful not
|
100
|
+
to type @exit@, as that will terminate the whole program.
|
101
|
+
|
102
|
+
In the first run, we will simply exit IRB each time, allowing the program to
|
103
|
+
proceed as intended.
|
104
|
+
|
105
|
+
<pre>
|
106
|
+
Executing breakpoint "Person#initialize" at breakpoint-example.rb:19 in 'initialize'
|
107
|
+
>> ^D
|
108
|
+
Executing breakpoint "Person#name" at breakpoint-example.rb:24 in 'name'
|
109
|
+
>> ^D
|
110
|
+
Name: John Smith
|
111
|
+
</pre>
|
112
|
+
|
113
|
+
In the second run, we inspect the @age@ local variable while the object is
|
114
|
+
being initialized, and set the @@name@ instance variable to something
|
115
|
+
different. We skip the next breakpoint. When the name is printed out, we can
|
116
|
+
see that changing the instance variable has been effective. (By the way, we
|
117
|
+
don't demonstrate it here, but changing _local_ variables is effective as
|
118
|
+
well.)
|
119
|
+
|
120
|
+
<pre>
|
121
|
+
Executing breakpoint "Person#initialize" at breakpoint-example.rb:19 in 'initialize'
|
122
|
+
>> lv
|
123
|
+
=> ["name", "age", "_"]
|
124
|
+
>> age
|
125
|
+
=> 23
|
126
|
+
>> name
|
127
|
+
=> "John Smith"
|
128
|
+
>> @name = "Mary Jones"
|
129
|
+
=> "Mary Jones"
|
130
|
+
>> ^D
|
131
|
+
Executing breakpoint "Person#name" at breakpoint-example.rb:24 in 'name'
|
132
|
+
>> ^D
|
133
|
+
Name: Mary Jones
|
134
|
+
</pre>
|
135
|
+
|
136
|
+
In the thirs and final run, we skip the first breakpoint. When the second one
|
137
|
+
is triggered, during @Person#name@, we call the @age@ method, inspect the
|
138
|
+
@@name@ instance variable, and then force the method to return a different
|
139
|
+
value. Again, we see the effectiveness of this when the name is printed after
|
140
|
+
we exit the breakpoint.
|
141
|
+
|
142
|
+
<pre>
|
143
|
+
Executing breakpoint "Person#initialize" at breakpoint-example.rb:19 in 'initialize'
|
144
|
+
>> ^D
|
145
|
+
Executing breakpoint "Person#name" at breakpoint-example.rb:24 in 'name'
|
146
|
+
>> age # This is calling a method...
|
147
|
+
=> 23
|
148
|
+
>> self.age() # ...this method, in fact.
|
149
|
+
=> 23
|
150
|
+
>> @name
|
151
|
+
=> "John Smith"
|
152
|
+
>> throw :debug_return, 'Maybellene Maconachie'
|
153
|
+
Name: Maybellene Maconachie
|
154
|
+
</pre>
|
155
|
+
|
156
|
+
Note that we haven't changed the value of @@name@ in the above example, only
|
157
|
+
forced a bogus return value from @Person#name@. If the program were to print
|
158
|
+
the person's name again, it would print "John Smith".
|
159
|
+
|
160
|
+
h3. Breakpoint Caveats
|
161
|
+
|
162
|
+
There are two things to note with this breakpoint functionality:
|
163
|
+
* It sets the interrupt handler so that IRB can deal with it. If there's
|
164
|
+
some way to reset this to whatever was in place beforehand, I'd like to know.
|
165
|
+
In practice, this shouldn't be a problem, because you only use breakpoints
|
166
|
+
while debugging, and you're not likely to care about interrupts at that time.
|
167
|
+
* The IRB prompt is set to @--simple-prompt@, no matter what's in your config
|
168
|
+
file. This is a matter of convenience, as you don't really need a fancy
|
169
|
+
prompt in this situation. However, I might try to make this more flexible in
|
170
|
+
a future version.
|
171
|
+
|
172
|
+
|
173
|
+
h2. Zero-conf Logfile for Debugging and Tracing
|
174
|
+
|
175
|
+
Logging is a very useful general tool, and there are two excellent
|
176
|
+
logging packages for Ruby: @logger@ (in the standard library) and the
|
177
|
+
more powerful @log4r@. But if all you want to do is record a few
|
178
|
+
pieces of debugging information, then configuring a logger can seem
|
179
|
+
like too much hassle. Throwing output to @STDERR@ is often a
|
180
|
+
reasonable solution, but that can interfere with the output of your
|
181
|
+
program. It also interferes with a unit test display, if that's
|
182
|
+
running in the console.
|
183
|
+
|
184
|
+
So @dev-utils/debug@ offers a no-frills log file for capturing any
|
185
|
+
debugging information you want to generate. You use it like this:
|
186
|
+
|
187
|
+
<pre>
|
188
|
+
debug 'Database connection established'
|
189
|
+
</pre>
|
190
|
+
|
191
|
+
The log file will be @debug.log@ in the current directory when you run the
|
192
|
+
program.
|
193
|
+
|
194
|
+
You can see an example of logging and tracing below.
|
195
|
+
|
196
|
+
|
197
|
+
h3. Tracing Expressions
|
198
|
+
|
199
|
+
This is an extra convenience to go with the zero-conf debug logger
|
200
|
+
described above. "Tracing" means recording the value of an exression,
|
201
|
+
typically a single variable. But that usually means a statement like
|
202
|
+
this: @puts "x = #{x}"@. The simple alternative offered here is this:
|
203
|
+
|
204
|
+
<pre>
|
205
|
+
trace 'x'
|
206
|
+
</pre>
|
207
|
+
|
208
|
+
Of course, the @'x'@ can be any exression, like
|
209
|
+
|
210
|
+
<pre>
|
211
|
+
trace 'x + y - avg(a,b,c)'
|
212
|
+
trace 'Process.pid'
|
213
|
+
</pre>
|
214
|
+
|
215
|
+
If these two trace statements are executed, then something like the
|
216
|
+
following lines will appear in your debugging log file:
|
217
|
+
|
218
|
+
<pre>
|
219
|
+
D, [#244] DEBUG : x = 5
|
220
|
+
D, [#244] DEBUG : x + y - avg(a,b,c) = 42
|
221
|
+
</pre>
|
222
|
+
|
223
|
+
You can see an example of logging and tracing below.
|
224
|
+
|
225
|
+
h3. Tailoring the Output Format
|
226
|
+
|
227
|
+
By default, the values emitted by @trace@ are formatted with @inspect@. This
|
228
|
+
works well for numbers, strings, and short arrays. However, for hashes, long
|
229
|
+
arrays, arrays of hashes, custom objects with many attributes, etc., other
|
230
|
+
formats are likely to be more suitable. The following examples show how you
|
231
|
+
can select a different format conveniently, using @ENV@, which will typically
|
232
|
+
contain a large number of elements. See the "API
|
233
|
+
Documentation":api/classes/DevUtils/Debug.html#M000005 for more detail.
|
234
|
+
|
235
|
+
<pre>
|
236
|
+
trace ENV, :p # inspect (default)
|
237
|
+
trace ENV, :s # to_s
|
238
|
+
trace ENV, :pp # pretty print
|
239
|
+
trace ENV, :yaml # YAML
|
240
|
+
</pre>
|
241
|
+
|
242
|
+
h3. Using Rake to View the Log
|
243
|
+
|
244
|
+
If you use "Rake":http://rake.rubyforge.org to run your program or unit tests,
|
245
|
+
then the working directory when the program starts will be the directory
|
246
|
+
containing the @Rakefile@. That means that the log file will be @debug.log@
|
247
|
+
in that directory.
|
248
|
+
|
249
|
+
The great thing about Rake is that you can run it from anywhere; well,
|
250
|
+
any _subdirectory_ in your project. Rake will keep looking in parent
|
251
|
+
directories until it finds a Rakefile. It will then @chdir@
|
252
|
+
accordingly. So it doesn't matter from where you _run_ Rake; the log
|
253
|
+
file will live in a predictable location.
|
254
|
+
|
255
|
+
This means you can easily create a Rake task to view the log file:
|
256
|
+
|
257
|
+
<pre>
|
258
|
+
task :log do
|
259
|
+
puts File.read('debug.log')
|
260
|
+
end
|
261
|
+
</pre>
|
262
|
+
|
263
|
+
If you have a @test@ task to run your unit tests, and they write to
|
264
|
+
the debug logfile, you can now do this:
|
265
|
+
|
266
|
+
<pre>
|
267
|
+
$ rake test
|
268
|
+
...
|
269
|
+
$ rake log
|
270
|
+
...
|
271
|
+
</pre>
|
272
|
+
|
273
|
+
Or:
|
274
|
+
|
275
|
+
<pre>
|
276
|
+
$ rake test && rake log
|
277
|
+
</pre>
|
278
|
+
|
279
|
+
<div class="planned">
|
280
|
+
|
281
|
+
h3. Planned Features
|
282
|
+
|
283
|
+
Configure the logfile with a statement like one of the following:
|
284
|
+
|
285
|
+
<pre>
|
286
|
+
Debug.logger = nil
|
287
|
+
Debug.logger = STDERR
|
288
|
+
Debug.logger = Logger.new(...)
|
289
|
+
Debug.logfile = '/var/log/projX/debug.log'
|
290
|
+
</pre>
|
291
|
+
|
292
|
+
The logger used is always a @Logger@ object (see @logger@ in the
|
293
|
+
standard library). So you can control it to the extent that it
|
294
|
+
allows. The default logger used has a bare minimum of extraneous
|
295
|
+
output, and if you use @Debug.logfile=@, then that maxim is
|
296
|
+
maintained.
|
297
|
+
|
298
|
+
There are also some improvements that can be made to the output:
|
299
|
+
* Show milliseconds instead of process ID at the start of each line.
|
300
|
+
* Less crap at the start of each line.
|
301
|
+
* Nice formatting of long lines.
|
302
|
+
|
303
|
+
Finally, making available an advanced @.irbrc@ file with @ri@ integration and
|
304
|
+
more would be a good addition to the project.
|
305
|
+
|
306
|
+
</div>
|
307
|
+
|
308
|
+
h3. Different Kinds of "Debugging" Information
|
309
|
+
|
310
|
+
Just a note of clarification. In many systems, especially long-running ones,
|
311
|
+
it is a good practice to keep logfiles, so that you can see what's going on,
|
312
|
+
monitor activity, measure performance, and so on. If you include some
|
313
|
+
debugging information, you can get some information on the internal state of
|
314
|
+
the system just before a crash. This kind of "debugging" information is part
|
315
|
+
of an overall logging strategy.
|
316
|
+
|
317
|
+
The "debugging" information that @dev-utils/debug@ is concerned with,
|
318
|
+
however, is _not_ part of any strategy. It is simply for when you
|
319
|
+
have a bug you need to track down, and want some temporary output
|
320
|
+
(especially tracing; see next section) to aid that cause. That is why
|
321
|
+
there's a simple default filename that keeps getting overwritten; we
|
322
|
+
simply don't need numbered backups or daily rolling logfiles for this
|
323
|
+
throwaway information.
|
324
|
+
|
325
|
+
h3. Logging and Tracing Example
|
326
|
+
|
327
|
+
The following code comes from @examples/log-trace-example.rb@ in the distribution:
|
328
|
+
|
329
|
+
<pre>
|
330
|
+
require 'dev-utils/debug'
|
331
|
+
require 'extensions/enumerable' # dev-utils depends on extensions anyway.
|
332
|
+
|
333
|
+
debug "Running sanity check of dev-utils/debug logging and tracing."
|
334
|
+
|
335
|
+
x, y = 5, 10
|
336
|
+
trace 'x + y'
|
337
|
+
trace 'Process.pid'
|
338
|
+
|
339
|
+
debug "Now we test the various output formatters."
|
340
|
+
|
341
|
+
words = %w(wren fibonnaci smelt bovine smeglicious craptacular
|
342
|
+
inoccidental myrmidon boondoggle)
|
343
|
+
word_lengths = words.build_hash { |word| [word, word.length] }
|
344
|
+
|
345
|
+
[:p, :s, :pp, :y].each_with_index do |symbol, idx|
|
346
|
+
debug ''
|
347
|
+
debug "#{idx+1}. #{symbol.inspect} format"
|
348
|
+
trace 'words', symbol
|
349
|
+
debug ''
|
350
|
+
trace 'word_lengths', symbol
|
351
|
+
end
|
352
|
+
</pre>
|
353
|
+
|
354
|
+
When run, it produces the following output (snipped):
|
355
|
+
|
356
|
+
<pre>
|
357
|
+
D, [#2168] DEBUG : Running sanity check of dev-utils/debug logging and tracing.
|
358
|
+
D, [#2168] DEBUG : x + y = 15
|
359
|
+
D, [#2168] DEBUG : Process.pid = 2168
|
360
|
+
D, [#2168] DEBUG : Now we test the various output formatters.
|
361
|
+
D, [#2168] DEBUG :
|
362
|
+
D, [#2168] DEBUG : 1. :p format
|
363
|
+
D, [#2168] DEBUG : words = ["wren", "fibonnaci", "smelt", "bovine", "smeglicious
|
364
|
+
", "craptacular", "inoccidental", "myrmidon", "boondoggle"]
|
365
|
+
D, [#2168] DEBUG :
|
366
|
+
D, [#2168] DEBUG : word_lengths = {"craptacular"=>11, "smelt"=>5, "boondoggle"=>
|
367
|
+
10, "myrmidon"=>8, "bovine"=>6, "fibonnaci"=>9, "smeglicious"=>11, "wren"=>4, "i
|
368
|
+
noccidental"=>12}
|
369
|
+
...
|
370
|
+
...
|
371
|
+
D, [#2168] DEBUG : 4. :y format
|
372
|
+
D, [#2168] DEBUG : words = ---
|
373
|
+
D, [#2168] DEBUG : - wren
|
374
|
+
D, [#2168] DEBUG : - fibonnaci
|
375
|
+
D, [#2168] DEBUG : - smelt
|
376
|
+
D, [#2168] DEBUG : - bovine
|
377
|
+
D, [#2168] DEBUG : - smeglicious
|
378
|
+
D, [#2168] DEBUG : - craptacular
|
379
|
+
D, [#2168] DEBUG : - inoccidental
|
380
|
+
D, [#2168] DEBUG : - myrmidon
|
381
|
+
D, [#2168] DEBUG : - boondoggle
|
382
|
+
D, [#2168] DEBUG :
|
383
|
+
D, [#2168] DEBUG : word_lengths = ---
|
384
|
+
D, [#2168] DEBUG : craptacular: 11
|
385
|
+
D, [#2168] DEBUG : smelt: 5
|
386
|
+
D, [#2168] DEBUG : boondoggle: 10
|
387
|
+
D, [#2168] DEBUG : myrmidon: 8
|
388
|
+
D, [#2168] DEBUG : bovine: 6
|
389
|
+
D, [#2168] DEBUG : fibonnaci: 9
|
390
|
+
D, [#2168] DEBUG : smeglicious: 11
|
391
|
+
D, [#2168] DEBUG : wren: 4
|
392
|
+
D, [#2168] DEBUG : inoccidental: 12
|
393
|
+
</pre>
|
394
|
+
|
395
|
+
h3. Vim Mappings
|
396
|
+
|
397
|
+
The following Vim shortcuts are useful for creating debug and trace lines.
|
398
|
+
Put them, or a preferred variant, in your
|
399
|
+
@$HOME/.vim/ftplugin/ruby_yourname.vim@ file (change @.vim@ to @vimfiles@ on
|
400
|
+
Windows).
|
401
|
+
|
402
|
+
p. @,D@ and @,T@ in _insert_ mode add a debug or trace statement after the cursor.
|
403
|
+
You'll want to use them on a blank line, usually after hitting ENTER, or 'o'
|
404
|
+
or 'O' in normal mode. In _normal_ mode, those key sequences create a line
|
405
|
+
_above_ the current one, and put their statement there. In all cases, the
|
406
|
+
cursor is left between the quotes.
|
407
|
+
|
408
|
+
<pre>
|
409
|
+
imap ,D debug ""<Esc>i
|
410
|
+
nmap ,D O,D
|
411
|
+
imap ,T trace ''<Esc>i
|
412
|
+
nmap ,T O,T
|
413
|
+
</pre>
|
414
|
+
|
415
|
+
If you translate these into your editor of choice, let me know and I'll
|
416
|
+
include them here.
|
417
|
+
|
418
|
+
|
419
|
+
<div class="planned">
|
420
|
+
|
421
|
+
|
422
|
+
|
423
|
+
h2. Finding Differences Between Complex Objects (Planned)
|
424
|
+
|
425
|
+
It is common when unit testing to compare objects, especially with a
|
426
|
+
method call like @assert_equal(expected, actual)@. But if you are
|
427
|
+
comparing two _complex_ objects (i.e. with many and possibly nested
|
428
|
+
composed objects), and find that they are _not_ equal, but when you
|
429
|
+
pretty-print them you find that they _look_ equal, then finding the
|
430
|
+
difference can be a pain.
|
431
|
+
|
432
|
+
Enter @Debug.diff@. You give it two objects, and it recurses their
|
433
|
+
composed objects, evaluating them until it finds a difference, and it
|
434
|
+
returns it. @diff@ is really intended for use in IRB. You can ask
|
435
|
+
for the <i>n</i>th difference, all the differences, or the number of
|
436
|
+
differences. For example:
|
437
|
+
|
438
|
+
<pre>
|
439
|
+
... irb session ...
|
440
|
+
</pre>
|
441
|
+
|
442
|
+
</div>
|
443
|
+
|
444
|
+
|
445
|
+
|
446
|
+
h2. Conclusion
|
447
|
+
|
448
|
+
p. @dev-utils/debug@ offers several very useful debugging aids that
|
449
|
+
are designed to work well with unit testing and with Rake. There are
|
450
|
+
plans for more, and suggestions are most welcome.
|