dev-utils 1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -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
+
@@ -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.
@@ -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.