orb 0.0.4 → 0.0.5
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.
- 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
|
-
|