letters 0.3.1 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +1 -7
- data/README.md +83 -198
- data/lib/letters/config.rb +14 -1
- data/lib/letters/core_ext.rb +59 -61
- data/lib/letters/helpers.rb +29 -6
- data/lib/letters/version.rb +1 -1
- data/spec/letters/config_spec.rb +21 -1
- data/spec/letters/core_ext_spec.rb +6 -1
- metadata +40 -21
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
**Letters** is a little alphabetical library that makes sophisticated debugging easy & fun.
|
2
2
|
|
3
|
-
*Quick note about Rails*: Until I build a Rails-specific gem, I'm changing Letters to patch `Object` by default. To only patch core classes, `require "letters/patch/core"`. For Rails support, `require "letters/patch/rails"`. Make sure to do this after `Bundler.require` in `application.rb`.
|
3
|
+
*Quick note about Rails*: Until I build a Rails-specific gem, I'm changing Letters to patch `Object` by default. To only patch core classes, `require "letters/patch/core"`. For Rails support, `require "letters/patch/rails"`. Make sure to do this after `Bundler.require` in `application.rb`.
|
4
4
|
|
5
5
|
For many of us, troubleshooting begins and ends with the `print` statement. Others recruit the debugger, too. (Maybe you use `print` statements to look at changes over time but the debugger to focus on a small bit of code.) These tools are good, but they are the lowest level of how we can debug in Ruby. Letters leverages `print`, the debugger, control transfer, computer beeps, and other side-effects for more well-rounded visibility into code and state.
|
6
6
|
|
@@ -21,7 +21,7 @@ There are almost 20 Letters methods so far. You can find them [in the documentat
|
|
21
21
|
Let's use with the `o` method as an example. It is one of the most familiar methods. Calling it prints the receiver to STDOUT and returns the receiver:
|
22
22
|
|
23
23
|
```ruby
|
24
|
-
{ foo: "bar" }.o
|
24
|
+
{ foo: "bar" }.o
|
25
25
|
# => { foo: "bar" }
|
26
26
|
# prints { foo: "bar" }
|
27
27
|
```
|
@@ -35,7 +35,7 @@ words.grep(/interesting/).
|
|
35
35
|
values_at(5, 10).
|
36
36
|
slice(0..2).
|
37
37
|
join(", ")
|
38
|
-
```
|
38
|
+
```
|
39
39
|
|
40
40
|
If I want to know the state of your code after lines 3 and 5, all I have to do is add `.o` to each one:
|
41
41
|
|
@@ -55,6 +55,14 @@ This is significantly easier than breaking apart the pipeline using variable ass
|
|
55
55
|
The `o` method takes options, too, so you can add a prefix message to the output or choose another output format -- like [YAML]() or [pretty print]().
|
56
56
|
|
57
57
|
### The methods ###
|
58
|
+
|
59
|
+
Here are the methods, listed with any options that can be passed in to modify their behavior. Some options are available to all methods and are not listed in the table below:
|
60
|
+
|
61
|
+
- `:message (string)`: Print out the specified message as the method is being called.
|
62
|
+
- `:line_no (boolean)`: Print out the line number where a method is called as it is being called
|
63
|
+
|
64
|
+
You can easily set these for an entire project using global configuration if you wish (see below).
|
65
|
+
|
58
66
|
<table>
|
59
67
|
<tr>
|
60
68
|
<th>Letter</th>
|
@@ -64,246 +72,115 @@ The `o` method takes options, too, so you can add a prefix message to the output
|
|
64
72
|
</tr>
|
65
73
|
|
66
74
|
<tr>
|
67
|
-
<td>
|
68
|
-
|
69
|
-
</td>
|
70
|
-
<td>
|
71
|
-
Assert
|
72
|
-
</td>
|
73
|
-
<td>
|
74
|
-
:message,
|
75
|
-
:error_class
|
76
|
-
</td>
|
77
|
-
<td>
|
78
|
-
asserts in the context of its receiver or Letters::AssertionError
|
79
|
-
</td>
|
75
|
+
<td><a href="http://lettersrb.com/api#a">a</a></td>
|
76
|
+
<td>Assert</td>
|
77
|
+
<td><code>:error_class</code></td>
|
78
|
+
<td>asserts in the context of its receiver or <code>Letters::AssertionError</code></td>
|
80
79
|
</tr>
|
81
80
|
|
82
81
|
<tr>
|
83
|
-
<td>
|
84
|
-
|
85
|
-
|
86
|
-
<td>
|
87
|
-
Beep
|
88
|
-
</td>
|
89
|
-
<td>
|
90
|
-
</td>
|
91
|
-
<td>
|
92
|
-
causes your terminal to beep
|
93
|
-
</td>
|
82
|
+
<td><a href="http://lettersrb.com/api#b">b</a></td>
|
83
|
+
<td>Beep</td>
|
84
|
+
<td></td>
|
85
|
+
<td>causes your terminal to beep</td>
|
94
86
|
</tr>
|
95
87
|
|
96
88
|
<tr>
|
97
|
-
<td>
|
98
|
-
|
99
|
-
|
100
|
-
<td>
|
101
|
-
Callstack
|
102
|
-
</td>
|
103
|
-
<td>
|
104
|
-
:message
|
105
|
-
</td>
|
106
|
-
<td>
|
107
|
-
prints the current callstack
|
108
|
-
</td>
|
89
|
+
<td><a href="http://lettersrb.com/api#c">c</a></td>
|
90
|
+
<td>Callstack</td>
|
91
|
+
<td></td>
|
92
|
+
<td>prints the current callstack</td>
|
109
93
|
</tr>
|
110
94
|
|
111
95
|
<tr>
|
112
|
-
<td>
|
113
|
-
|
114
|
-
|
115
|
-
<td>
|
116
|
-
Debugger
|
117
|
-
</td>
|
118
|
-
<td>
|
119
|
-
|
120
|
-
</td>
|
121
|
-
<td>
|
122
|
-
passes control to the debugger
|
123
|
-
</td>
|
96
|
+
<td><a href="http://lettersrb.com/api#d">d</a></td>
|
97
|
+
<td>Debugger</td>
|
98
|
+
<td></td>
|
99
|
+
<td>passes control to the debugger</td>
|
124
100
|
</tr>
|
125
101
|
|
126
102
|
<tr>
|
127
|
-
<td>
|
128
|
-
|
129
|
-
</td>
|
130
|
-
<td>
|
131
|
-
Diff
|
132
|
-
</td>
|
133
|
-
<td>
|
134
|
-
:message,
|
135
|
-
:format,
|
136
|
-
:stream
|
137
|
-
</td>
|
138
|
-
<td>
|
139
|
-
prints a diff between first and second receivers
|
140
|
-
</td>
|
103
|
+
<td><a href="http://lettersrb.com/api#d1/d2">d1/d2</a></td>
|
104
|
+
<td>Diff</td>
|
105
|
+
<td><code>:format</code>,<code>:stream</code></td>
|
106
|
+
<td>prints a diff between first and second receivers</td>
|
141
107
|
</tr>
|
142
108
|
|
143
109
|
<tr>
|
144
|
-
<td>
|
145
|
-
|
146
|
-
|
147
|
-
<td>
|
148
|
-
Empty
|
149
|
-
</td>
|
150
|
-
<td>
|
151
|
-
:message
|
152
|
-
</td>
|
153
|
-
<td>
|
154
|
-
raises a Letters::EmptyError if its receiver is empty
|
155
|
-
</td>
|
110
|
+
<td><a href="http://lettersrb.com/api#e">e</a></td>
|
111
|
+
<td>Empty</td>
|
112
|
+
<td></td>
|
113
|
+
<td>raises a <code>Letters::EmptyError</code> if its receiver is empty</td>
|
156
114
|
</tr>
|
157
115
|
|
158
116
|
<tr>
|
159
|
-
<td>
|
160
|
-
|
161
|
-
</td>
|
162
|
-
<td>
|
163
|
-
File
|
164
|
-
</td>
|
165
|
-
<td>
|
166
|
-
:format, :name
|
167
|
-
</td>
|
168
|
-
<td>
|
169
|
-
writes its receiver into a file in a given format
|
170
|
-
</td>
|
117
|
+
<td><a href="http://lettersrb.com/api#f">f</a></td>
|
118
|
+
<td>File</td>
|
119
|
+
<td><code>:format</code>, <code>:name</code></td>
|
120
|
+
<td>writes its receiver into a file in a given format</td>
|
171
121
|
</tr>
|
172
122
|
|
173
123
|
<tr>
|
174
|
-
<td>
|
175
|
-
|
176
|
-
</td>
|
177
|
-
<td>
|
178
|
-
Jump
|
179
|
-
</td>
|
180
|
-
<td>
|
181
|
-
(&block)
|
182
|
-
</td>
|
183
|
-
<td>
|
184
|
-
executes its block in the context of its receiver
|
185
|
-
</td>
|
124
|
+
<td><a href="http://lettersrb.com/api#j">j</a></td>
|
125
|
+
<td>Jump</td>
|
126
|
+
<td>(<code>&block</code>)</td>
|
127
|
+
<td>executes its block in the context of its receiver</td>
|
186
128
|
</tr>
|
187
129
|
|
188
130
|
<tr>
|
189
|
-
<td>
|
190
|
-
|
191
|
-
</td>
|
192
|
-
<td>
|
193
|
-
Kill
|
194
|
-
</td>
|
195
|
-
<td>
|
196
|
-
:on
|
197
|
-
</td>
|
198
|
-
<td>
|
199
|
-
raises Letters::KillError at a specified number of calls
|
200
|
-
</td>
|
131
|
+
<td><a href="http://lettersrb.com/api#k">k</a></td>
|
132
|
+
<td>Kill</td>
|
133
|
+
<td><code>:on</code></td>
|
134
|
+
<td>raises <code>Letters::KillError</code> at a specified number of calls</td>
|
201
135
|
</tr>
|
202
136
|
|
203
137
|
<tr>
|
204
|
-
<td>
|
205
|
-
|
206
|
-
</td>
|
207
|
-
<td>
|
208
|
-
Logger
|
209
|
-
</td>
|
210
|
-
<td>
|
211
|
-
:format, :level
|
212
|
-
</td>
|
213
|
-
<td>
|
214
|
-
logs its receivers on the available logger instance
|
215
|
-
</td>
|
138
|
+
<td><a href="http://lettersrb.com/api#l">l</a></td>
|
139
|
+
<td>Logger</td>
|
140
|
+
<td><code>:format</code>, <code>:level</code></td>
|
141
|
+
<td>logs its receivers on the available logger instance</td>
|
216
142
|
</tr>
|
217
143
|
|
218
144
|
<tr>
|
219
|
-
<td>
|
220
|
-
|
221
|
-
</td>
|
222
|
-
<td>
|
223
|
-
Mark as tainted
|
224
|
-
</td>
|
225
|
-
<td>
|
226
|
-
(true|false)
|
227
|
-
</td>
|
228
|
-
<td>
|
229
|
-
taints (or untaints) its receiver
|
230
|
-
</td>
|
145
|
+
<td><a href="http://lettersrb.com/api#m">m</a></td>
|
146
|
+
<td>Mark as tainted</td>
|
147
|
+
<td>(<code>true</code>|<code>false</code>)</td>
|
148
|
+
<td>taints (or untaints) its receiver</td>
|
231
149
|
</tr>
|
232
150
|
|
233
151
|
<tr>
|
234
|
-
<td>
|
235
|
-
|
236
|
-
|
237
|
-
<td>
|
238
|
-
Nil
|
239
|
-
</td>
|
240
|
-
<td>
|
241
|
-
|
242
|
-
</td>
|
243
|
-
<td>
|
244
|
-
raises a Letters::NilError if its receiver is nil
|
245
|
-
</td>
|
152
|
+
<td><a href="http://lettersrb.com/api#n">n</a></td>
|
153
|
+
<td>Nil</td>
|
154
|
+
<td></td>
|
155
|
+
<td>raises a <code>Letters::NilError</code> if its receiver is nil</td>
|
246
156
|
</tr>
|
247
157
|
|
248
158
|
<tr>
|
249
|
-
<td>
|
250
|
-
|
251
|
-
</td>
|
252
|
-
<td>
|
253
|
-
Output
|
254
|
-
</td>
|
255
|
-
<td>
|
256
|
-
:format,
|
257
|
-
:stream
|
258
|
-
</td>
|
259
|
-
<td>
|
260
|
-
prints its receiver to standard output
|
261
|
-
</td>
|
159
|
+
<td><a href="http://lettersrb.com/api#o">o</a></td>
|
160
|
+
<td>Output</td>
|
161
|
+
<td><code>:format</code>, <code>:stream</code></td>
|
162
|
+
<td>prints its receiver to standard output</td>
|
262
163
|
</tr>
|
263
164
|
|
264
165
|
<tr>
|
265
|
-
<td>
|
266
|
-
|
267
|
-
</td>
|
268
|
-
<td>
|
269
|
-
Ri
|
270
|
-
</td>
|
271
|
-
<td>
|
272
|
-
(method name as symbol)
|
273
|
-
</td>
|
274
|
-
<td>
|
275
|
-
prints RI documentation of its receiver class
|
276
|
-
</td>
|
166
|
+
<td><a href="http://lettersrb.com/api#r">r</a></td>
|
167
|
+
<td>Ri</td>
|
168
|
+
<td>(method name as symbol)</td>
|
169
|
+
<td>prints RI documentation of its receiver class</td>
|
277
170
|
</tr>
|
278
171
|
|
279
172
|
<tr>
|
280
|
-
<td>
|
281
|
-
|
282
|
-
</td>
|
283
|
-
<td>
|
284
|
-
Safety
|
285
|
-
</td>
|
286
|
-
<td>
|
287
|
-
(level number)
|
288
|
-
</td>
|
289
|
-
<td>
|
290
|
-
bumps the safety level (by one or as specified)
|
291
|
-
</td>
|
173
|
+
<td><a href="http://lettersrb.com/api#s">s</a></td>
|
174
|
+
<td>Safety</td>
|
175
|
+
<td>(level number)</td>
|
176
|
+
<td>bumps the safety level (by one or as specified)</td>
|
292
177
|
</tr>
|
293
178
|
|
294
179
|
<tr>
|
295
|
-
<td>
|
296
|
-
|
297
|
-
</td>
|
298
|
-
<td>
|
299
|
-
Timestamp
|
300
|
-
</td>
|
301
|
-
<td>
|
302
|
-
:time_format
|
303
|
-
</td>
|
304
|
-
<td>
|
305
|
-
prints out the current timestamp
|
306
|
-
</td>
|
180
|
+
<td><a href="http://lettersrb.com/api#t">t</a></td>
|
181
|
+
<td>Timestamp</td>
|
182
|
+
<td><code>:time_format</code></td>
|
183
|
+
<td>prints out the current timestamp</td>
|
307
184
|
</tr>
|
308
185
|
</table>
|
309
186
|
|
@@ -318,3 +195,11 @@ Letters.config do
|
|
318
195
|
f :format => "pp", :name => "my-special-file"
|
319
196
|
end
|
320
197
|
```
|
198
|
+
|
199
|
+
You can also change options globally, for methods where the global option is appropriate. For example, if you want every Letters method to print out its line number when called, you can do this for all methods at once:
|
200
|
+
|
201
|
+
```ruby
|
202
|
+
Letters.config do
|
203
|
+
all :line_no => true
|
204
|
+
end
|
205
|
+
```
|
data/lib/letters/config.rb
CHANGED
@@ -18,15 +18,24 @@ module Letters
|
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
|
+
def self.global_defaults=(opts)
|
22
|
+
@global_defaults = opts
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.global_defaults
|
26
|
+
@global_defaults || {}
|
27
|
+
end
|
28
|
+
|
21
29
|
def self.user_defaults
|
22
30
|
@user_defaults ||= Hash.new {|h, k| h[k] = Hash.new }
|
23
31
|
end
|
24
32
|
|
25
33
|
def self.defaults_with(letter, opts={})
|
26
|
-
defaults[letter]
|
34
|
+
[global_defaults, defaults[letter], user_defaults[letter], opts].reduce({}, &:merge)
|
27
35
|
end
|
28
36
|
|
29
37
|
def self.reset_config!
|
38
|
+
global_defaults.clear
|
30
39
|
user_defaults.clear
|
31
40
|
end
|
32
41
|
|
@@ -35,6 +44,10 @@ module Letters
|
|
35
44
|
end
|
36
45
|
|
37
46
|
module Config
|
47
|
+
define_singleton_method("all") do |opts={}|
|
48
|
+
Letters.global_defaults = opts
|
49
|
+
end
|
50
|
+
|
38
51
|
("a".."z").each do |letter|
|
39
52
|
define_singleton_method(letter) do |opts={}|
|
40
53
|
Letters.user_defaults[letter.to_sym] = opts
|
data/lib/letters/core_ext.rb
CHANGED
@@ -7,107 +7,109 @@ require "letters/empty_error"
|
|
7
7
|
require "letters/kill_error"
|
8
8
|
require "letters/nil_error"
|
9
9
|
|
10
|
+
require "colorize"
|
11
|
+
|
10
12
|
module Letters
|
11
13
|
module CoreExt
|
12
14
|
DELIM = "-" * 20
|
13
15
|
|
16
|
+
def ubertap(letter, opts={}, orig_caller=[], &block)
|
17
|
+
full_opts = Letters.defaults_with(letter, opts)
|
18
|
+
Helpers.message full_opts
|
19
|
+
Helpers.print_line(orig_caller[0]) if full_opts[:line_no]
|
20
|
+
|
21
|
+
tap do |o|
|
22
|
+
block.call(o, full_opts)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
14
26
|
# Assert
|
15
27
|
def a(opts={}, &block)
|
16
|
-
|
17
|
-
tap do |o|
|
18
|
-
Helpers.message opts
|
28
|
+
ubertap(:a, opts, caller) do |o, full_opts|
|
19
29
|
if block_given? && !o.instance_eval(&block)
|
20
|
-
raise
|
30
|
+
raise full_opts[:error_class]
|
21
31
|
end
|
22
32
|
end
|
23
33
|
end
|
24
34
|
|
25
35
|
# Beep
|
26
|
-
def b
|
27
|
-
|
36
|
+
def b(opts={})
|
37
|
+
ubertap(:b, opts, caller) do |_, _|
|
28
38
|
$stdout.print "\a"
|
29
39
|
end
|
30
40
|
end
|
31
41
|
|
32
42
|
# Callstack
|
33
43
|
def c(opts={})
|
34
|
-
|
35
|
-
Helpers.
|
36
|
-
Helpers.out caller(4), opts
|
44
|
+
ubertap(:b, opts, caller) do |_, full_opts|
|
45
|
+
Helpers.out caller(4), full_opts
|
37
46
|
end
|
38
47
|
end
|
39
48
|
|
40
49
|
# Debug
|
41
|
-
def d
|
42
|
-
|
43
|
-
Helpers.call_debugger
|
50
|
+
def d(opts={})
|
51
|
+
ubertap(:d, opts, caller) do |_, _|
|
52
|
+
Helpers.call_debugger
|
44
53
|
end
|
45
54
|
end
|
46
55
|
|
47
56
|
# Diff 1
|
48
|
-
def d1
|
49
|
-
|
57
|
+
def d1(opts={})
|
58
|
+
ubertap(:d1, opts, caller) do |o, _|
|
50
59
|
Letters.object_for_diff = o
|
51
60
|
end
|
52
61
|
end
|
53
62
|
|
54
63
|
# Diff 2
|
55
64
|
def d2(opts={})
|
56
|
-
|
57
|
-
tap do |o|
|
65
|
+
ubertap(:d2, opts, caller) do |o, full_opts|
|
58
66
|
diff = Helpers.diff(Letters.object_for_diff, o)
|
59
|
-
Helpers.
|
60
|
-
Helpers.out diff, opts
|
67
|
+
Helpers.out diff, full_opts
|
61
68
|
Letters.object_for_diff = nil
|
62
69
|
end
|
63
70
|
end
|
64
|
-
|
71
|
+
|
65
72
|
# Empty check
|
66
73
|
def e(opts={})
|
67
74
|
opts.merge! :error_class => EmptyError
|
68
|
-
|
69
|
-
|
70
|
-
o.a(opts) { !empty? }
|
75
|
+
ubertap(:e, opts, caller) do |o, full_opts|
|
76
|
+
o.a(full_opts) { !empty? }
|
71
77
|
end
|
72
78
|
end
|
73
79
|
|
74
80
|
# File
|
75
81
|
def f(opts={})
|
76
|
-
|
77
|
-
tap do |o|
|
82
|
+
ubertap(:f, opts, caller) do |o, full_opts|
|
78
83
|
suffixes = [""] + (1..50).to_a
|
79
|
-
deduper = suffixes.detect {|x| !File.directory? "#{
|
84
|
+
deduper = suffixes.detect {|x| !File.directory? "#{full_opts[:name]}#{x}" }
|
80
85
|
|
81
|
-
File.open("#{
|
86
|
+
File.open("#{full_opts[:name]}#{deduper}", "w+") do |file|
|
82
87
|
# Override :stream
|
83
|
-
|
84
|
-
Helpers.
|
85
|
-
Helpers.out o, opts
|
88
|
+
full_opts.merge! :stream => file
|
89
|
+
Helpers.out o, full_opts
|
86
90
|
end
|
87
91
|
end
|
88
92
|
end
|
89
93
|
|
90
94
|
# Jump
|
91
|
-
def j(&block)
|
92
|
-
|
95
|
+
def j(opts={}, &block)
|
96
|
+
ubertap(:j, opts, caller) do |o, full_opts|
|
93
97
|
o.instance_eval &block
|
94
98
|
end
|
95
99
|
end
|
96
100
|
|
97
101
|
# Kill
|
98
102
|
def k(opts={})
|
99
|
-
opts = Letters.defaults_with(:k, opts)
|
100
|
-
|
101
|
-
# Support :max option until I can deprecate it
|
102
|
-
opts[:on] ||= opts[:max]
|
103
|
-
|
104
103
|
opts.merge! :error_class => KillError
|
105
|
-
|
104
|
+
ubertap(:k, opts, caller) do |o, full_opts|
|
105
|
+
# Support :max option until I can deprecate it
|
106
|
+
full_opts[:on] ||= full_opts[:max]
|
107
|
+
|
106
108
|
Letters.kill_count ||= 0
|
107
109
|
|
108
|
-
if Letters.kill_count >=
|
110
|
+
if Letters.kill_count >= full_opts[:on]
|
109
111
|
Letters.kill_count = 0
|
110
|
-
o.a(
|
112
|
+
o.a(full_opts) { false }
|
111
113
|
end
|
112
114
|
|
113
115
|
Letters.kill_count += 1
|
@@ -116,11 +118,10 @@ module Letters
|
|
116
118
|
|
117
119
|
# Log
|
118
120
|
def l(opts={})
|
119
|
-
|
120
|
-
tap do |o|
|
121
|
+
ubertap(:l, opts, caller) do |o, full_opts|
|
121
122
|
begin
|
122
|
-
logger.send(
|
123
|
-
logger.send(
|
123
|
+
logger.send(full_opts[:level], full_opts[:message]) if full_opts[:message]
|
124
|
+
logger.send(full_opts[:level], Helpers.send(full_opts[:format], o))
|
124
125
|
rescue
|
125
126
|
$stdout.puts "[warning] No logger available"
|
126
127
|
end
|
@@ -128,8 +129,8 @@ module Letters
|
|
128
129
|
end
|
129
130
|
|
130
131
|
# Taint and untaint object
|
131
|
-
def m(taint=true)
|
132
|
-
|
132
|
+
def m(taint=true, opts={})
|
133
|
+
ubertap(:m, opts, caller) do |o, _|
|
133
134
|
if taint
|
134
135
|
o.taint
|
135
136
|
else
|
@@ -141,32 +142,31 @@ module Letters
|
|
141
142
|
# Nil check
|
142
143
|
def n(opts={})
|
143
144
|
opts.merge! :error_class => NilError
|
144
|
-
|
145
|
-
o.a(
|
145
|
+
ubertap(:n, opts, caller) do |o, full_opts|
|
146
|
+
o.a(full_opts) { !nil? }
|
146
147
|
end
|
147
148
|
end
|
148
149
|
|
149
150
|
# Print to STDOUT
|
150
151
|
def o(opts={}, &block)
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
Helpers.out obj, opts
|
152
|
+
ubertap(:o, opts, caller) do |o, full_opts|
|
153
|
+
Helpers.message full_opts
|
154
|
+
obj = block_given? ? o.instance_eval(&block) : o
|
155
|
+
Helpers.out obj, full_opts
|
156
156
|
end
|
157
157
|
end
|
158
158
|
|
159
159
|
# RI
|
160
|
-
def r(method=nil)
|
161
|
-
|
160
|
+
def r(method=nil, opts={})
|
161
|
+
ubertap(:r, opts, caller) do |o, _|
|
162
162
|
method_or_empty = method ? "##{method}" : method
|
163
|
-
system "ri #{o.class}#{method_or_empty}"
|
163
|
+
system "ri #{o.class}#{method_or_empty}"
|
164
164
|
end
|
165
165
|
end
|
166
166
|
|
167
167
|
# Change safety level
|
168
|
-
def s(level=nil)
|
169
|
-
|
168
|
+
def s(level=nil, opts={})
|
169
|
+
ubertap(:s, opts) do |_, _|
|
170
170
|
level ||= $SAFE + 1
|
171
171
|
Helpers.change_safety level
|
172
172
|
end
|
@@ -174,10 +174,8 @@ module Letters
|
|
174
174
|
|
175
175
|
# Timestamp
|
176
176
|
def t(opts={})
|
177
|
-
|
178
|
-
|
179
|
-
Helpers.message opts
|
180
|
-
Helpers.out Time.now.to_s(opts[:time_format].to_sym), opts
|
177
|
+
ubertap(:t, opts) do |_, full_opts|
|
178
|
+
Helpers.out Time.now.to_s(full_opts[:time_format].to_sym), full_opts
|
181
179
|
end
|
182
180
|
end
|
183
181
|
end
|
data/lib/letters/helpers.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require "colorize"
|
2
|
+
require "pathname"
|
2
3
|
|
3
4
|
module Letters
|
4
5
|
module Helpers
|
@@ -13,13 +14,13 @@ module Letters
|
|
13
14
|
when String
|
14
15
|
diff(obj1.split("\n"), obj2.split("\n"))
|
15
16
|
else
|
16
|
-
{
|
17
|
+
{
|
17
18
|
removed: Array(obj1 - obj2),
|
18
19
|
added: Array(obj2 - obj1)
|
19
20
|
}
|
20
21
|
end
|
21
22
|
rescue
|
22
|
-
raise "cannot diff the two marked objects"
|
23
|
+
raise "cannot diff the two marked objects"
|
23
24
|
end
|
24
25
|
|
25
26
|
def self.message(opts={})
|
@@ -60,6 +61,23 @@ module Letters
|
|
60
61
|
object.to_yaml
|
61
62
|
end
|
62
63
|
|
64
|
+
def self.print_line(caller_line)
|
65
|
+
file, line_no = caller_line.split.first.sub(/:in$/, "").split(":")
|
66
|
+
|
67
|
+
line = if File.exist?(file) && File.file?(file)
|
68
|
+
File.readlines(file)[Integer(line_no) - 1]
|
69
|
+
else
|
70
|
+
""
|
71
|
+
end
|
72
|
+
|
73
|
+
rel_file = Pathname.new(file).expand_path.relative_path_from(Pathname.getwd)
|
74
|
+
|
75
|
+
puts "Letters call at #{file}, line #{line_no}".underline
|
76
|
+
puts
|
77
|
+
puts " " + line.strip.chomp.sub(/^\W*/, "").green
|
78
|
+
puts
|
79
|
+
end
|
80
|
+
|
63
81
|
def self.pretty_callstack(callstack)
|
64
82
|
home = ENV["MY_RUBY_HOME"]
|
65
83
|
|
@@ -81,12 +99,12 @@ module Letters
|
|
81
99
|
|
82
100
|
parsed.unshift headers
|
83
101
|
|
84
|
-
longest_line =
|
102
|
+
longest_line =
|
85
103
|
parsed.map {|entry| entry[:line] }.
|
86
104
|
sort_by(&:length).
|
87
105
|
last
|
88
106
|
|
89
|
-
longest_method =
|
107
|
+
longest_method =
|
90
108
|
parsed.map {|entry| entry[:method_name] }.
|
91
109
|
sort_by(&:length).
|
92
110
|
last
|
@@ -98,8 +116,13 @@ module Letters
|
|
98
116
|
|
99
117
|
# This provides a mockable method for testing
|
100
118
|
def self.call_debugger
|
101
|
-
|
102
|
-
|
119
|
+
if (defined?(RUBY_ENGINE) && RUBY_ENGINE == 'rbx')
|
120
|
+
require 'rubinius/debugger'
|
121
|
+
Rubinius::Debugger.start
|
122
|
+
else
|
123
|
+
require 'debug'
|
124
|
+
end
|
125
|
+
|
103
126
|
nil
|
104
127
|
end
|
105
128
|
|
data/lib/letters/version.rb
CHANGED
data/spec/letters/config_spec.rb
CHANGED
@@ -12,7 +12,8 @@ module Letters
|
|
12
12
|
end
|
13
13
|
|
14
14
|
after do
|
15
|
-
|
15
|
+
Letters.reset_config!
|
16
|
+
FileUtils.rm_rf "log"
|
16
17
|
end
|
17
18
|
|
18
19
|
describe ".config" do
|
@@ -20,6 +21,25 @@ module Letters
|
|
20
21
|
hash.f
|
21
22
|
File.read("log").should == hash.pretty_inspect
|
22
23
|
end
|
24
|
+
|
25
|
+
it "allows global default argument configuration" do
|
26
|
+
Letters.config do
|
27
|
+
all :line_no => true
|
28
|
+
end
|
29
|
+
|
30
|
+
$stdout.should_receive(:puts).exactly(4).times
|
31
|
+
hash.b
|
32
|
+
end
|
33
|
+
|
34
|
+
it "allows specific defaults to override global defaults" do
|
35
|
+
Letters.config do
|
36
|
+
all :line_no => true
|
37
|
+
b :line_no => false
|
38
|
+
end
|
39
|
+
|
40
|
+
$stdout.should_receive(:puts).never
|
41
|
+
hash.b
|
42
|
+
end
|
23
43
|
end
|
24
44
|
|
25
45
|
describe ".reset_config!" do
|
@@ -35,6 +35,11 @@ module Letters
|
|
35
35
|
hash.o { nil }.should == hash
|
36
36
|
end
|
37
37
|
|
38
|
+
it "all letter methods have the option of outputting the line where they are called" do
|
39
|
+
pending "I need to figure out a good way to test this"
|
40
|
+
# hash.j(:line_no => true) { nil }.should == hash
|
41
|
+
end
|
42
|
+
|
38
43
|
describe "#a (assert)" do
|
39
44
|
it "jumps into the receiver's calling context" do
|
40
45
|
lambda do
|
@@ -44,7 +49,7 @@ module Letters
|
|
44
49
|
|
45
50
|
it "raises a Letters::AssertionError if the block returns false" do
|
46
51
|
lambda do
|
47
|
-
[1, 2, 3].a { count > 3 }
|
52
|
+
[1, 2, 3].a { count > 3 }
|
48
53
|
end.should raise_error(Letters::AssertionError)
|
49
54
|
end
|
50
55
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: letters
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2013-07-11 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: awesome_print
|
16
|
-
requirement:
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,15 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements:
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
25
30
|
- !ruby/object:Gem::Dependency
|
26
31
|
name: activesupport
|
27
|
-
requirement:
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
28
33
|
none: false
|
29
34
|
requirements:
|
30
35
|
- - ! '>='
|
@@ -32,10 +37,15 @@ dependencies:
|
|
32
37
|
version: '0'
|
33
38
|
type: :runtime
|
34
39
|
prerelease: false
|
35
|
-
version_requirements:
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
36
46
|
- !ruby/object:Gem::Dependency
|
37
47
|
name: xml-simple
|
38
|
-
requirement:
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
39
49
|
none: false
|
40
50
|
requirements:
|
41
51
|
- - ! '>='
|
@@ -43,10 +53,15 @@ dependencies:
|
|
43
53
|
version: '0'
|
44
54
|
type: :runtime
|
45
55
|
prerelease: false
|
46
|
-
version_requirements:
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
47
62
|
- !ruby/object:Gem::Dependency
|
48
63
|
name: colorize
|
49
|
-
requirement:
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
50
65
|
none: false
|
51
66
|
requirements:
|
52
67
|
- - ! '>='
|
@@ -54,21 +69,15 @@ dependencies:
|
|
54
69
|
version: '0'
|
55
70
|
type: :runtime
|
56
71
|
prerelease: false
|
57
|
-
version_requirements:
|
58
|
-
- !ruby/object:Gem::Dependency
|
59
|
-
name: debugger
|
60
|
-
requirement: &70186112048660 !ruby/object:Gem::Requirement
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
61
73
|
none: false
|
62
74
|
requirements:
|
63
75
|
- - ! '>='
|
64
76
|
- !ruby/object:Gem::Version
|
65
77
|
version: '0'
|
66
|
-
type: :runtime
|
67
|
-
prerelease: false
|
68
|
-
version_requirements: *70186112048660
|
69
78
|
- !ruby/object:Gem::Dependency
|
70
79
|
name: timecop
|
71
|
-
requirement:
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
72
81
|
none: false
|
73
82
|
requirements:
|
74
83
|
- - ! '>='
|
@@ -76,10 +85,15 @@ dependencies:
|
|
76
85
|
version: '0'
|
77
86
|
type: :development
|
78
87
|
prerelease: false
|
79
|
-
version_requirements:
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
80
94
|
- !ruby/object:Gem::Dependency
|
81
95
|
name: rspec
|
82
|
-
requirement:
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
83
97
|
none: false
|
84
98
|
requirements:
|
85
99
|
- - ! '>='
|
@@ -87,7 +101,12 @@ dependencies:
|
|
87
101
|
version: '0'
|
88
102
|
type: :development
|
89
103
|
prerelease: false
|
90
|
-
version_requirements:
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
91
110
|
description: Letters brings Ruby debugging into the 21st century. It leverages print,
|
92
111
|
the debugger, control transfer, even computer beeps to let you see into your code's
|
93
112
|
state.
|
@@ -143,7 +162,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
143
162
|
version: 1.3.6
|
144
163
|
requirements: []
|
145
164
|
rubyforge_project:
|
146
|
-
rubygems_version: 1.8.
|
165
|
+
rubygems_version: 1.8.23
|
147
166
|
signing_key:
|
148
167
|
specification_version: 3
|
149
168
|
summary: A tiny debugging library for Ruby
|