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,9 @@
1
+ #
2
+ # The links that should appear on each page, in order. Used by generate.rb.
3
+ #
4
+
5
+ Main /index.html
6
+ Debugging Aids /DebuggingAids.html
7
+ Unit Test Organisation /UnitTestOrganisation.html
8
+ API Documentation /api/index.html
9
+
@@ -0,0 +1,150 @@
1
+
2
+ body {
3
+ font-family: georgia, verdana, serif;
4
+ margin: 20px 50px;
5
+ width: 45em;
6
+ }
7
+
8
+ .small-title {
9
+ font-family: courier new;
10
+ font-size: 10pt;
11
+ margin: 0px;
12
+ color: green;
13
+ }
14
+
15
+ .mylink {
16
+ font-family: georgia, verdana, serif;
17
+ font-size: 10pt;
18
+ }
19
+
20
+ h1 {
21
+ border-bottom: 1px solid;
22
+ padding-bottom: 3px;
23
+ margin-top: 2px;
24
+ color: green;
25
+ font-size: 30pt;
26
+ }
27
+
28
+ h2 {
29
+ font-size: 20pt;
30
+ color: darkblue;
31
+ }
32
+
33
+ h3 {
34
+ font-size: 16pt;
35
+ color: darkblue;
36
+ }
37
+
38
+ p,li {
39
+ margin: 10px 15px 5px 15px;
40
+ font-size: 12pt;
41
+ line-height: 1.5em;
42
+ text-align: justify;
43
+ }
44
+
45
+ p {
46
+ margin-top: 18px;
47
+ }
48
+
49
+ code {
50
+ padding-left: 0.15em;
51
+ padding-right: 0.1em;
52
+ background-color: #f9f9f9;
53
+ }
54
+
55
+ pre {
56
+ margin-left: 3em;
57
+ background-color: #f6f6f6;
58
+ padding-top: 1ex;
59
+ padding-bottom: 1ex;
60
+ }
61
+
62
+ .comment {
63
+ margin-left: 4em;
64
+ margin-right: 2em;
65
+ font-size: 11pt;
66
+ font-style: italic;
67
+ color: darkgreen;
68
+ }
69
+
70
+ /* Planned functionality. */
71
+
72
+ .planned {
73
+ border: 1px dotted lightgray;
74
+ background-color: rgb(224,255,221);
75
+ padding: 5px;
76
+ }
77
+
78
+ div.planned {
79
+ margin-top: 20pt;
80
+ }
81
+
82
+ .planned code, .planned pre {
83
+ background-color: rgb(219,255,215);
84
+ }
85
+
86
+ /* Table of contents and links. */
87
+
88
+ .toc_table {
89
+ font: 10pt, verdana,sans-serif;
90
+ }
91
+
92
+ .toc_table .header {
93
+ font-weight: bold;
94
+ color: #666;
95
+ }
96
+
97
+ .toc_table a {
98
+ text-decoration: none;
99
+ }
100
+
101
+ .toc_table td {
102
+ vertical-align: top;
103
+ padding-left: 1em;
104
+ padding-right: 1em;
105
+ }
106
+
107
+ .toc_table ul {
108
+ margin-left: 1em;
109
+ padding-left: 1em;
110
+ }
111
+
112
+ .toc_table ul li ul {
113
+ margin-left: 1ex;
114
+ padding-left: 1ex;
115
+ }
116
+
117
+ .toc_table ul li {
118
+ padding-left: 0;
119
+ margin-left: 0;
120
+ font-family: Arial, sans-serif;
121
+ font-size: 10pt;
122
+ text-align: left;
123
+ line-height: 9pt;
124
+ list-style-type: square;
125
+ color: #CCC;
126
+ }
127
+
128
+ .toc_table ul li ul li {
129
+ list-style-type: none;
130
+ font-style: italic;
131
+ }
132
+
133
+ #contents li a {
134
+ color: #55A;
135
+ }
136
+
137
+ #contents li a:hover {
138
+ color: #66F;
139
+ }
140
+
141
+ #links li a {
142
+ color: #5A5;
143
+ }
144
+
145
+ #links li a:hover {
146
+ color: #6D6;
147
+ }
148
+
149
+ /* vim: sw=2 ts=2 et sts=2
150
+ */
@@ -0,0 +1,30 @@
1
+ #
2
+ # To run this example, simply do
3
+ #
4
+ # ruby breakpoint-exqample.rb
5
+ #
6
+ # It will escape to IRB at various points. You can query the local variables, etc., and even
7
+ # try a "throw :debug_return, 5" to force a return from the method.
8
+ #
9
+
10
+ begin
11
+ require 'rubygems'
12
+ rescue LoadError
13
+ end
14
+ require 'dev-utils/debug'
15
+
16
+ class Person
17
+ def initialize(name, age)
18
+ @name, @age = name, age
19
+ breakpoint 'Person#initialize'
20
+ end
21
+
22
+ attr_reader :age
23
+ def name
24
+ breakpoint('Person#name') { @name }
25
+ end
26
+ end
27
+
28
+ person = Person.new('John Smith', 23)
29
+ puts "Name: #{person.name}"
30
+
File without changes
@@ -0,0 +1,35 @@
1
+ #
2
+ # To run this example, just do
3
+ #
4
+ # ruby log-trace-example.rb
5
+ #
6
+ # You should see a file "debug.log" in your current directory.
7
+ #
8
+
9
+ begin
10
+ require 'rubygems'
11
+ rescue LoadError
12
+ end
13
+ require 'dev-utils/debug'
14
+ require 'extensions/enumerable' # dev-utils depends on extensions anyway.
15
+
16
+ debug "Running sanity check of dev-utils/debug logging and tracing."
17
+
18
+ x, y = 5, 10
19
+ trace 'x + y'
20
+ trace 'Process.pid'
21
+
22
+ debug "Now we test the various output formatters."
23
+
24
+ words = %w(wren fibonnaci smelt bovine smeglicious craptacular
25
+ inoccidental myrmidon boondoggle)
26
+ word_lengths = words.build_hash { |word| [word, word.length] }
27
+
28
+ [:p, :s, :pp, :y].each_with_index do |symbol, idx|
29
+ debug ''
30
+ debug "#{idx+1}. #{symbol.inspect} format"
31
+ trace 'words', symbol
32
+ debug ''
33
+ trace 'word_lengths', symbol
34
+ end
35
+
@@ -0,0 +1,32 @@
1
+ #
2
+ # = dev-utils/debug.rb
3
+ #
4
+ # See DevUtils::Debug for documentation.
5
+ #
6
+
7
+ #
8
+ # Base module for <tt>dev-utils</tt>.
9
+ #
10
+ module DevUtils
11
+ #
12
+ # <tt>DevUtils::Debug</tt> contains methods to aid debugging Ruby programs, although when
13
+ # using these methods, you don't care about the module; it is included into the top-level
14
+ # when you <code>require 'dev-utils/debug'</code>.
15
+ #
16
+ # The methods are:
17
+ # * #breakpoint, for escaping to IRB from a running program, with local environment intact;
18
+ # * #debug, for logging debugging messages to a zero-conf logfile; and
19
+ # * #trace, for tracing expressions to that same file.
20
+ #
21
+ # Planned features include a method for determining the difference between two complex
22
+ # objects.
23
+ #
24
+ module Debug
25
+ end
26
+ end
27
+
28
+ require 'dev-utils/debug/irb'
29
+ require 'dev-utils/debug/log'
30
+ #require 'dev-utils/debug/diff'
31
+
32
+ include DevUtils::Debug
@@ -0,0 +1,187 @@
1
+ # = dev-utils/debug/diff.rb
2
+ #
3
+ # _Planned_ functionality to support <b>comparing complex objects</b> and <b>discovering object
4
+ # topologies</b>. Nothing here for now.
5
+ #
6
+
7
+ =begin
8
+
9
+ # Implement Debug.diff here. Do not load this file directly.
10
+
11
+ module DevUtils::Debug
12
+ #
13
+ # Helps identify the difference between two objects when it's not immediately obvious to
14
+ # the eye.
15
+ #
16
+ # +o1+ and +o2+ are simply objects to be compared. If one or the other is a built-in type,
17
+ # like String, then it's a simple yes/no answer and this method doesn't buy you much. If
18
+ # they are both complex objects (aggregating other data, like a Struct or data object),
19
+ # then the comparison takes place with each of their composed objects, recursively.
20
+ #
21
+ # The typical usage: <tt>diff(x, y)</tt> returns an array representing the first difference
22
+ # found, like <tt>['age: 31', 'age: 84']</tt>. This is designed to be output with +puts+
23
+ # in +irb+, so they appear one string per line.
24
+ #
25
+ # The +flags+ modify the result. Only one flag is currently observed. If it's a number,
26
+ # say 3, then the third difference, in alphabetical order, is returned. If it's
27
+ # <tt>:all</tt>, then all differences are returned in an array of arrays. If it's
28
+ # <tt>:n</tt>, then the number of differences is returned.
29
+ #
30
+ # == Examples
31
+ #
32
+ # ...
33
+ #
34
+ def diff(o1, o2, *flags)
35
+ case (flag = flags.first || 1)
36
+ when :n
37
+ op = :count
38
+ n = -1
39
+ when :all
40
+ op = :find_all
41
+ n = -1
42
+ when Numeric
43
+ op = :find_one
44
+ n = flag.to_i
45
+ n = 1 if n < 1
46
+ else
47
+ raise ArgumentError, flag.inspect
48
+ end
49
+ diff = catch(:found) do
50
+ diffs = _diff(o1, o2, '', n)
51
+ case op
52
+ when :find_one
53
+ return nil # If we get this far, no difference was found.
54
+ when :find_all
55
+ return diffs
56
+ when :count
57
+ return diffs.size
58
+ end
59
+ end
60
+ return diff # This was thrown from _diff; it's a single result.
61
+ end
62
+
63
+ private
64
+
65
+ #
66
+ # +o1+ and +o2+ are the objects being compared. +base+ is the base name for the fields so
67
+ # far (so we can report the full path of the field name, like 'person.name.first'). +n+ is
68
+ # the number of differences we should skip before immediately returning the one we're
69
+ # after.
70
+ #
71
+ # Each time we find a difference, we add it to +diffs+ and decrement n. If n is zero, we
72
+ # have found the difference we were looking for so we throw :found and the difference (a
73
+ # 2-tuple).
74
+ #
75
+ # We return an array of all the differences we have found.
76
+ #
77
+ def _diff(o1, o2, base, n)
78
+ if _immediate?(o1) or _immediate?(o2)
79
+ return "Immediate value(s)"
80
+ #return [o1,o2].map { |o| "#{field_path}: #{o.inspect}" }
81
+ else
82
+ # Both objects are complex, and we're happy.
83
+ h1 = _hash_representation(o1)
84
+ h2 = _hash_representation(o2)
85
+ fields = (h1.keys + h2.keys).uniq.sort
86
+ diffs = []
87
+ fields.each do |f|
88
+ v1 = h1[f]
89
+ v2 = h2[f]
90
+ goirb binding if $X
91
+ if v1 == v2
92
+ next
93
+ else
94
+ # We've found two unequal values. If they're immediate values,
95
+ # we count it as a difference. If they're complex, then we
96
+ # recurse into them.
97
+ field_path = [base, f].reject { |s| s.empty? }.join('.')
98
+ if _immediate?(v1) or _immediate?(v2)
99
+ diff = [v1,v2].map { |v| "#{field_path}: #{v.inspect}" }
100
+ throw :found, diff if (n -= 1).zero?
101
+ diffs << diff
102
+ else
103
+ diffs.concat _diff(v1, v2, field_path, n)
104
+ end
105
+ end
106
+ end
107
+ return diffs
108
+ end
109
+ end
110
+
111
+ #
112
+ # Returns true iff the given object is an "immediate value". That means a fundamenal Ruby
113
+ # data type. It's an arbitrary definition, and the choices are hardcoded. Hopefully I can
114
+ # think of a better heuristic.
115
+ #
116
+ def _immediate?(object)
117
+ [Numeric, String, Range, Array, Hash, NilClass, TrueClass,
118
+ FalseClass].any? { |klass| klass === object }
119
+ end
120
+
121
+ #
122
+ # Returns a hash representation of an object, mapping field names to values.
123
+ #
124
+ def _hash_representation(object)
125
+ _methods = object.public_methods(false)
126
+ if Struct === object
127
+ # In a Struct, if X is an attribute, then there are methods X and X=.
128
+ fields = _methods.grep(/=/).map { |meth| meth.to_s.tr('=', '') }
129
+ fields = fields.select { |meth| _methods.include? meth }
130
+ else
131
+ # In a general complex oject, if X in an attribute, then there is a method X and an
132
+ # instance variable @X.
133
+ _variables = object.instance_variables.map { |v| v.tr('@', '') }
134
+ fields = _variables.select { |v| _methods.include? v }
135
+ end
136
+ # Now we've got the fields, which in all cases are public methods. So we call them to
137
+ # get the values and construct our hash.
138
+ result = {}
139
+ fields.each do |field|
140
+ result[field] = object.send(field)
141
+ end
142
+ result
143
+ end
144
+
145
+ #
146
+ # Implementation notes for diff.
147
+ #
148
+ # Approach:
149
+ # * we don't dig into arrays or hashes, etc.; those are "immediate" values along
150
+ # with Numeric, String, Range, etc.
151
+ # * if object under inspection is immediate, we compare with 'equal?' and that's it
152
+ # * OK, so we've got two complex objects
153
+ # * get hash representations, one level deep: field => value
154
+ # * there is special logic for Structs
155
+ # * if field sets are different, do we care?
156
+ # * we can just report that field F is X in o1 but is nil in o2
157
+ # * examine fields in alphabetical order
158
+ # * compare values (with #equal?)
159
+ # * if equal, go to next field
160
+ # * if not equal
161
+ # * if value(s) are immediate, compare and report (or skip, or count,
162
+ # according to flags)
163
+ # * otherwise, dig into those values (recurse)
164
+ #
165
+
166
+ # Alternative notes for diff.
167
+ #
168
+ # Approach:
169
+ # * have a separate method 'structure(obj)', which returns the structure of the
170
+ # given object; e.g. structure(person) -> ['name', 'age', 'address.number',
171
+ # 'address.road', 'address.city']
172
+ # * that 'structure' method would be useful for some other things
173
+ # * you can yield each bit as it's done
174
+ # * use 'structure' and 'eval' to get the value of things
175
+ # * be smart about nil values; e.g. person.address could be nil, which
176
+ # would represent a different, but compatible, structure
177
+ #
178
+ # This seems simpler than mixing it all up as in the implementation above.
179
+
180
+ end # module Kernel
181
+
182
+ class Object
183
+ def topology
184
+ end
185
+ end
186
+
187
+ =end