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 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
- module ORB
5
-
6
- def self.capture(bind)
7
- buf, line, last = [], "", ""
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("ORB >> ", true)
12
-
37
+ while line = Readline.readline(prompt, true)
13
38
  case line
14
- when "a"
15
- buf << last
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
- line, file = bind.eval("[__LINE__, __FILE__]")
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
- def self.write_buf_to_file(buf, line_num, file)
43
- lines = File.read(file).lines.to_a
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...line_num-1]
110
+ File.open(@file,"w") do |f|
111
+ f.puts lines[0...@line-1]
47
112
 
48
- orbline = lines[line_num-1]
113
+ orbline = lines[@line-1]
49
114
  indentation = orbline.index(/[^\s]/)
50
-
51
- buf.each do |bufline|
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[line_num..-1]
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
@@ -1,6 +1,15 @@
1
- require 'spec/example/pending'
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
- module ORB
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.capture(block.binding)
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
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |gem|
2
2
  gem.name = 'orb'
3
- gem.version = "0.0.4"
3
+ gem.version = "0.0.5"
4
4
 
5
5
  gem.author, gem.email = 'Burke Libbey', "burke@burkelibbey.org"
6
6
 
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 0
8
- - 4
9
- version: 0.0.4
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-01 00:00:00 -05:00
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:
@@ -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
-