dev-utils 1.0

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