orb 0.0.4 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +5 -0
- data/lib/orb.rb +98 -22
- data/lib/orb/adapters/rspec.rb +18 -3
- data/orb.gemspec +1 -1
- metadata +3 -4
- data/lib/orb/binding.rb +0 -32
data/README.md
CHANGED
@@ -12,6 +12,11 @@ the file and replacing the `ORB{}` call.
|
|
12
12
|
|
13
13
|
[Watch the video](#) (Coming Soon).
|
14
14
|
|
15
|
+
# I just want to use it with Rspec. How?
|
16
|
+
|
17
|
+
Add `ORB{}` to a pending example, then run your spec suite with
|
18
|
+
`-r orb`, for example, `spec -rorb spec/**/*_spec.rb`, or add it to `spec_opts`.
|
19
|
+
|
15
20
|
# Can I use this with \#{framework}?
|
16
21
|
|
17
22
|
Probably. Have a look at `lib/orb/adapters/rspec.rb`. It's a pretty simple
|
data/lib/orb.rb
CHANGED
@@ -1,30 +1,83 @@
|
|
1
|
+
# ORB is a tool to write tests interactively.
|
2
|
+
|
3
|
+
# Most ruby programmers, even the ones with extensive test suites, frequently
|
4
|
+
# find themselves in IRB, trying snippets of code here and there.
|
5
|
+
# It's not uncommon to run some code in IRB, then copy parts of it almost
|
6
|
+
# verbatim into a test file for use as a test. ORB makes this a much more smooth
|
7
|
+
# process.
|
8
|
+
|
9
|
+
# The basic workflow with ORB is:
|
10
|
+
|
11
|
+
# 1. Add `ORB{}` to each new test, at the point where you'd like to insert code.
|
12
|
+
# 2. Run your test suite with `orb` loaded. See `adapters/rspec.rb`.
|
13
|
+
# 3. Use the REPL provided for each call to ORB to create a new test.
|
14
|
+
|
15
|
+
# When a call to `ORB` is encountered, you'll be dropped to to a REPL with full
|
16
|
+
# access to the test's context (much like a debugger). You can run code, and
|
17
|
+
# interact with the context however you like. When you're ready to write your
|
18
|
+
# test, you simply run the commands, then add them to a buffer. Once your
|
19
|
+
# buffer contains the code you'd like to save as a test, you can write
|
20
|
+
# it back to the file in place of the call to `ORB` with just two keystrokes.
|
21
|
+
|
22
|
+
# The REPL uses readline for line editing.
|
1
23
|
require 'readline'
|
24
|
+
|
25
|
+
# Adapters are near-trivial to write, and provide test-framework integration.
|
2
26
|
require 'orb/adapters/rspec'
|
3
27
|
|
4
|
-
|
5
|
-
|
6
|
-
def
|
7
|
-
|
28
|
+
class ORB
|
29
|
+
@@index=0
|
30
|
+
def initialize(bind)
|
31
|
+
@@index += 1
|
32
|
+
@buf, @hist = [], []
|
33
|
+
@line, @file = bind.eval("[__LINE__, __FILE__]")
|
8
34
|
|
9
|
-
puts ORB::Adapter.header(bind)
|
35
|
+
puts "\n\n[[ #{ORB::Adapter.header(bind)} ]]"
|
10
36
|
|
11
|
-
while line = Readline.readline(
|
12
|
-
|
37
|
+
while line = Readline.readline(prompt, true)
|
13
38
|
case line
|
14
|
-
|
15
|
-
|
39
|
+
|
40
|
+
# The `a` command appends one or more recent lines to the test buffer.
|
41
|
+
when /^a\s?(\d*)$/
|
42
|
+
append_to_buffer($1.to_i)
|
16
43
|
next
|
44
|
+
|
45
|
+
# The `h` command shows this session's history. Each line starts with
|
46
|
+
# a coloured space. A red space indicates that this line is not in
|
47
|
+
# the buffer, and a green space indicates that this line is in the
|
48
|
+
# buffer, and will be written back to the file when the test is saved.
|
49
|
+
when "h"
|
50
|
+
red = "\x1B[41m \x1B[m"
|
51
|
+
green = "\x1B[42m \x1B[m"
|
52
|
+
@hist.each do |line, in_buf|
|
53
|
+
print in_buf ? green : red
|
54
|
+
print ' '
|
55
|
+
puts line
|
56
|
+
end
|
57
|
+
next
|
58
|
+
|
59
|
+
# The `p` command prints the current contents of the buffer. If the test
|
60
|
+
# is written back to the file right now, this is what will be inserted.
|
17
61
|
when "p"
|
18
|
-
puts buf
|
62
|
+
puts @buf
|
19
63
|
next
|
64
|
+
|
65
|
+
# The `q` command exits this session without writing a test. Next time
|
66
|
+
# the suite is run, you'll get a REPL here again.
|
20
67
|
when "q"
|
21
|
-
__orb_original_pending(message)
|
22
68
|
break
|
69
|
+
|
70
|
+
# The `s` command writes the contents of the buffer out to the file.
|
23
71
|
when "s"
|
24
|
-
|
25
|
-
ORB.write_buf_to_file(buf, line, file)
|
72
|
+
write_buf_to_file
|
26
73
|
break
|
74
|
+
|
75
|
+
# If this line wasn't a command, evaluate it in the context of the code
|
76
|
+
# that called `ORB{}`. We're stealing that context from the block.
|
77
|
+
# There are other, trickier tricks to get it without requiring a block,
|
78
|
+
# but they haven't been reliable since ruby 1.8.5.
|
27
79
|
else
|
80
|
+
@hist << [line,false]
|
28
81
|
begin
|
29
82
|
this = bind.eval("self")
|
30
83
|
ret = this.send(:eval, line, this.send(:binding))
|
@@ -35,26 +88,49 @@ module ORB
|
|
35
88
|
end
|
36
89
|
end
|
37
90
|
|
38
|
-
last = line
|
39
91
|
end
|
40
92
|
end
|
41
93
|
|
42
|
-
|
43
|
-
|
94
|
+
private
|
95
|
+
|
96
|
+
# Append some number of recent lines to the test buffer. This is triggered
|
97
|
+
# by the command `a` or `a <n>`, eg. `a 3`. If called with no number, just
|
98
|
+
# appends the last command run, otherwise, appends n lines.
|
99
|
+
def append_to_buffer(n=1)
|
100
|
+
@hist.last[1] = true
|
101
|
+
@buf << @hist.last[0]
|
102
|
+
end
|
103
|
+
|
104
|
+
# When this command is run, ORB will save the contents of the test buffer
|
105
|
+
# back into the file where `ORB{}` was called from. It does this by reading
|
106
|
+
# in the entire file, then rewriting it with ORB{} replaced by the buffer.
|
107
|
+
def write_buf_to_file
|
108
|
+
lines = File.read(@file).lines.to_a
|
44
109
|
|
45
|
-
File.open(file,"w") do |f|
|
46
|
-
f.puts lines[0
|
110
|
+
File.open(@file,"w") do |f|
|
111
|
+
f.puts lines[0...@line-1]
|
47
112
|
|
48
|
-
orbline = lines[
|
113
|
+
orbline = lines[@line-1]
|
49
114
|
indentation = orbline.index(/[^\s]/)
|
50
|
-
|
51
|
-
|
115
|
+
|
116
|
+
# Here we're indenting the inserted code to the same level as `ORB{}`
|
117
|
+
# was indented.
|
118
|
+
@buf.each do |bufline|
|
52
119
|
f.print " "*indentation
|
53
120
|
f.puts bufline
|
54
121
|
end
|
55
122
|
|
56
|
-
f.puts lines[
|
123
|
+
f.puts lines[@line..-1]
|
57
124
|
end
|
58
125
|
end
|
59
126
|
|
127
|
+
# ORB's prompt looks like `1:0 >>`. The first number is the index of this
|
128
|
+
# test, starting at one, and incrementing with each test during an
|
129
|
+
# ORB-enhanced run of your test suite. The second number is the current size
|
130
|
+
# of the test buffer; that is, the number of lines that will be written to
|
131
|
+
# the file if the test is saved right now.
|
132
|
+
def prompt
|
133
|
+
"#{@@index}:#{@buf.size} >> "
|
134
|
+
end
|
135
|
+
|
60
136
|
end
|
data/lib/orb/adapters/rspec.rb
CHANGED
@@ -1,6 +1,15 @@
|
|
1
|
-
|
1
|
+
### ORB RSpec Adapter
|
2
|
+
# This integrates ORB with RSpec. To run your test suite with ORB enabled,
|
3
|
+
# run `spec` with `-rorb`, or add `-rorb` to `spec_opts`, or simply
|
4
|
+
# `require 'orb'` in your `spec_helper.rb`.
|
2
5
|
|
3
|
-
|
6
|
+
# ORB's requirements of an adapter are minimal:
|
7
|
+
|
8
|
+
# - (optional) Some convenient way to start an ORB session.
|
9
|
+
# - (optional) A header describing this specific instance.
|
10
|
+
# Each RSpec example has a description that's printed when it fails.
|
11
|
+
# We re-create that here to provide context when an ORB session is started.
|
12
|
+
class ORB
|
4
13
|
module Adapters
|
5
14
|
module RSpec
|
6
15
|
def self.header(bind)
|
@@ -12,14 +21,20 @@ module ORB
|
|
12
21
|
end
|
13
22
|
end
|
14
23
|
|
24
|
+
# Initially, I wanted to override `pending` as the method to start ORB,
|
25
|
+
# but as it turns out, the Object hackery I need to do to make ORB work
|
26
|
+
# requires a block. Requiring that pending be called with a block seems like
|
27
|
+
# an odd design choice, so instead I've made the method ORB,
|
28
|
+
# called like `ORB{}`.
|
15
29
|
module Spec
|
16
30
|
module Example
|
17
31
|
module Pending
|
18
32
|
def ORB(&block)
|
19
|
-
ORB.
|
33
|
+
ORB.new(block.binding)
|
20
34
|
end
|
21
35
|
end
|
22
36
|
end
|
23
37
|
end
|
24
38
|
|
39
|
+
# Finally, now that the RSpec Adapter is defined, set ORB's default Adapter.
|
25
40
|
ORB::Adapter = ORB::Adapters::RSpec
|
data/orb.gemspec
CHANGED
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
version: 0.0.
|
8
|
+
- 5
|
9
|
+
version: 0.0.5
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Burke Libbey
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-05-
|
17
|
+
date: 2010-05-02 00:00:00 -05:00
|
18
18
|
default_executable:
|
19
19
|
dependencies: []
|
20
20
|
|
@@ -31,7 +31,6 @@ files:
|
|
31
31
|
- README.md
|
32
32
|
- orb.gemspec
|
33
33
|
- lib/orb/adapters/rspec.rb
|
34
|
-
- lib/orb/binding.rb
|
35
34
|
- lib/orb.rb
|
36
35
|
has_rdoc: true
|
37
36
|
homepage:
|
data/lib/orb/binding.rb
DELETED
@@ -1,32 +0,0 @@
|
|
1
|
-
def Binding.of_caller(&block)
|
2
|
-
old_critical, Thread.critical = Thread.critical, true
|
3
|
-
count = 0
|
4
|
-
cc = nil
|
5
|
-
result, error = callcc{|c|cc=c;nil}
|
6
|
-
error.call if error
|
7
|
-
|
8
|
-
tracer = lambda do |*args|
|
9
|
-
type, _, _, _, context = args
|
10
|
-
if ["return", "c-return"].include?(type)
|
11
|
-
count += 1
|
12
|
-
# First this method and then calling one will return -- the trace event of the second event gets the context
|
13
|
-
# of the method which called the method that called method.
|
14
|
-
if count == 2
|
15
|
-
set_trace_func(nil)
|
16
|
-
cc.call(eval("binding", context), nil)
|
17
|
-
end
|
18
|
-
elsif type != "line"
|
19
|
-
set_trace_func(nil)
|
20
|
-
cc.call(nil, lambda { raise(Exception, "Binding.of_caller used in non-method context or trailing statements of method using it aren't in the block." ) })
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
if result
|
25
|
-
Thread.critical = old_critical
|
26
|
-
yield result
|
27
|
-
else
|
28
|
-
set_trace_func(tracer)
|
29
|
-
return nil
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|