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.
@@ -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.