dietrb 0.4.7 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,26 @@
1
+ require 'readline'
2
+ require 'irb/driver/tty'
3
+ require 'irb/ext/history'
4
+ require 'irb/ext/completion'
5
+
6
+ module IRB
7
+ module Driver
8
+ class Readline < TTY
9
+
10
+ def initialize(input = $stdin, output = $stdout)
11
+ super
12
+ ::Readline.input = @input
13
+ ::Readline.output = @output
14
+ ::Readline.completion_proc = IRB::Completion.new
15
+ end
16
+
17
+ def readline
18
+ source = ::Readline.readline(context.prompt, true)
19
+ IRB::History.input(source)
20
+ source
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+ IRB::Driver.current = IRB::Driver::Readline.new
@@ -0,0 +1,44 @@
1
+ require 'irb/driver/tty'
2
+ require 'socket'
3
+
4
+ module IRB
5
+ module Driver
6
+ class Socket
7
+ # Initializes with the object and binding that each new connection will
8
+ # get as Context. The binding is shared, so local variables will stay
9
+ # around. The benefit of this is that a socket based irb session is most
10
+ # probably used to debug a running application in development. In this
11
+ # scenario it could be beneficial to keep local vars in between sessions.
12
+ #
13
+ # TODO see if that actually works out ok.
14
+ def initialize(object, binding, host = '127.0.0.1', port = 7829)
15
+ @object, @binding = object, binding
16
+ @host, @port = host, port
17
+ @server = TCPServer.new(host, port)
18
+ end
19
+
20
+ # TODO libedit doesn't use the right input and output, so we can't use Readline for now!!
21
+ def run
22
+ $stderr.puts "[!] Running IRB server on #{@host}:#{@port}"
23
+ loop do
24
+ connection = @server.accept
25
+ Thread.new do
26
+ # assign driver with connection to current thread and start runloop
27
+ IRB::Driver.current = TTY.new(connection, connection)
28
+ irb(@object, @binding)
29
+ connection.close
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ def self.irb(object, binding = nil)
38
+ unless @server
39
+ @server = IRB::Driver::Socket.new(object, binding)
40
+ @server.run
41
+ else
42
+ super
43
+ end
44
+ end
@@ -0,0 +1,58 @@
1
+ require 'irb/driver'
2
+
3
+ module IRB
4
+ module Driver
5
+ class TTY
6
+ attr_reader :input, :output, :context_stack
7
+
8
+ def initialize(input = $stdin, output = $stdout)
9
+ @input = input
10
+ @output = output
11
+ @context_stack = []
12
+ end
13
+
14
+ def context
15
+ @context_stack.last
16
+ end
17
+
18
+ def readline
19
+ @output.print(context.prompt)
20
+ @input.gets
21
+ end
22
+
23
+ # TODO make it take the current context instead of storing it
24
+ def consume
25
+ readline
26
+ rescue Interrupt
27
+ context.clear_buffer
28
+ ""
29
+ end
30
+
31
+ # Feeds input into a given context.
32
+ #
33
+ # Ensures that the standard output object is a OutputRedirector, or a
34
+ # subclass thereof.
35
+ def run(context)
36
+ @context_stack << context
37
+ before, $stdout = $stdout, OutputRedirector.new unless $stdout.is_a?(OutputRedirector)
38
+ while line = consume
39
+ break unless context.process_line(line)
40
+ end
41
+ ensure
42
+ @context_stack.pop
43
+ $stdout = before if before
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ IRB::Driver.current = IRB::Driver::TTY.new
50
+
51
+ module Kernel
52
+ # Creates a new IRB::Context with the given +object+ and runs it.
53
+ def irb(object, binding = nil)
54
+ IRB::Driver.current.run(IRB::Context.new(object, binding))
55
+ end
56
+
57
+ private :irb
58
+ end
@@ -36,22 +36,23 @@ module IRB
36
36
  yield
37
37
  }
38
38
 
39
+ attr_reader :source
40
+
41
+ def context
42
+ IRB::Driver.current.context
43
+ end
44
+
39
45
  # Returns an array of possible completion results, with the current
40
46
  # IRB::Context.
41
47
  #
42
48
  # This is meant to be used with Readline which takes a completion proc.
43
- def self.call(source)
44
- new(IRB::Context.current, source).results
45
- end
46
-
47
- attr_reader :context, :source
48
-
49
- def initialize(context, source)
50
- @context, @source = context, source
49
+ def call(source)
50
+ @source = source
51
+ results
51
52
  end
52
53
 
53
54
  def evaluate(s)
54
- @context.__evaluate__(s)
55
+ context.__evaluate__(s)
55
56
  end
56
57
 
57
58
  def local_variables
@@ -59,7 +60,7 @@ module IRB
59
60
  end
60
61
 
61
62
  def instance_methods
62
- @context.object.methods.map(&:to_s)
63
+ context.object.methods.map(&:to_s)
63
64
  end
64
65
 
65
66
  def instance_methods_of(klass)
@@ -190,5 +191,5 @@ if defined?(Readline)
190
191
  # * Hash: = and >
191
192
  Readline.basic_word_break_characters= " \t\n`<;|&("
192
193
  end
193
- Readline.completion_proc = IRB::Completion
194
- end
194
+ # Readline.completion_proc = IRB::Completion
195
+ end
@@ -7,67 +7,62 @@
7
7
  # Portions Copyright (C) 2006-2010 Ben Bleything <ben@bleything.net> (Kernel#history & Kernel#history!)
8
8
 
9
9
  module IRB
10
- class History
10
+ module History
11
11
  class << self
12
12
  attr_accessor :file, :max_entries_in_overview
13
13
 
14
- def current
15
- IRB::Context.current.processors.find do |processor|
16
- processor.is_a?(IRB::History)
17
- end
14
+ def setup
15
+ to_a.each do |source|
16
+ Readline::HISTORY.push(source)
17
+ end if Readline::HISTORY.to_a.empty?
18
18
  end
19
- end
20
-
21
- def initialize(context)
22
- @context = context
23
19
 
24
- to_a.each do |source|
25
- Readline::HISTORY.push(source)
26
- end if Readline::HISTORY.to_a.empty?
27
- end
28
-
29
- def input(source)
30
- File.open(self.class.file, "a") { |f| f.puts(source) }
31
- source
32
- end
33
-
34
- def to_a
35
- file = self.class.file
36
- File.exist?(file) ? File.read(file).split("\n") : []
37
- end
38
-
39
- def clear!
40
- File.open(self.class.file, "w") { |f| f << "" }
41
- Readline::HISTORY.clear
42
- end
43
-
44
- def history(number_of_entries = max_entries_in_overview)
45
- history_size = Readline::HISTORY.size
46
- start_index = 0
20
+ def context
21
+ IRB::Driver.current.context
22
+ end
47
23
 
48
- # always remove one extra, because that's the `history' command itself
49
- if history_size <= number_of_entries
50
- end_index = history_size - 2
51
- else
52
- end_index = history_size - 2
53
- start_index = history_size - number_of_entries - 1
24
+ def input(source)
25
+ File.open(file, "a") { |f| f.puts(source) }
26
+ source
54
27
  end
55
28
 
56
- start_index.upto(end_index) do |i|
57
- puts "#{i}: #{Readline::HISTORY[i]}"
29
+ def to_a
30
+ File.exist?(file) ? File.read(file).split("\n") : []
31
+ end
32
+
33
+ def clear!
34
+ File.open(file, "w") { |f| f << "" }
35
+ Readline::HISTORY.clear
36
+ end
37
+
38
+ def history(number_of_entries = max_entries_in_overview)
39
+ history_size = Readline::HISTORY.size
40
+ start_index = 0
41
+
42
+ # always remove one extra, because that's the `history' command itself
43
+ if history_size <= number_of_entries
44
+ end_index = history_size - 2
45
+ else
46
+ end_index = history_size - 2
47
+ start_index = history_size - number_of_entries - 1
48
+ end
49
+
50
+ start_index.upto(end_index) do |i|
51
+ puts "#{i}: #{Readline::HISTORY[i]}"
52
+ end
58
53
  end
59
- end
60
-
61
- def history!(entry_or_range)
62
- # we don't want to execute history! again
63
- @context.clear_buffer
64
54
 
65
- if entry_or_range.is_a?(Range)
66
- entry_or_range.to_a.each do |i|
67
- @context.input_line(Readline::HISTORY[i])
55
+ def history!(entry_or_range)
56
+ # we don't want to execute history! again
57
+ context.clear_buffer
58
+
59
+ if entry_or_range.is_a?(Range)
60
+ entry_or_range.to_a.each do |i|
61
+ context.input_line(Readline::HISTORY[i])
62
+ end
63
+ else
64
+ context.input_line(Readline::HISTORY[entry_or_range])
68
65
  end
69
- else
70
- @context.input_line(Readline::HISTORY[entry_or_range])
71
66
  end
72
67
  end
73
68
  end
@@ -75,23 +70,23 @@ end
75
70
 
76
71
  module Kernel
77
72
  def history(number_of_entries = IRB::History.max_entries_in_overview)
78
- IRB::History.current.history(number_of_entries)
79
- nil
73
+ IRB::History.history(number_of_entries)
74
+ IRB::Context::IGNORE_RESULT
80
75
  end
81
76
  alias_method :h, :history
82
77
 
83
78
  def history!(entry_or_range)
84
- IRB::History.current.history!(entry_or_range)
85
- nil
79
+ IRB::History.history!(entry_or_range)
80
+ IRB::Context::IGNORE_RESULT
86
81
  end
87
82
  alias_method :h!, :history!
88
83
 
89
84
  def clear_history!
90
- IRB::History.current.clear!
91
- nil
85
+ IRB::History.clear!
86
+ true
92
87
  end
93
88
  end
94
89
 
95
- IRB::Context.processors << IRB::History
96
90
  IRB::History.file = File.expand_path("~/.irb_history")
97
- IRB::History.max_entries_in_overview = 50
91
+ IRB::History.max_entries_in_overview = 50
92
+ IRB::History.setup
@@ -38,7 +38,9 @@ module IRB
38
38
 
39
39
  def inspect_object(object)
40
40
  if @inspect
41
- object.respond_to?(:pretty_inspect) ? object.pretty_inspect : object.inspect
41
+ result = object.respond_to?(:pretty_inspect) ? object.pretty_inspect : object.inspect
42
+ result.strip!
43
+ result
42
44
  else
43
45
  address = object.__id__ * 2
44
46
  address += 0x100000000 if address < 0
@@ -8,8 +8,8 @@ module IRB
8
8
  module VERSION #:nodoc:
9
9
  NAME = 'DietRB'
10
10
  MAJOR = 0
11
- MINOR = 4
12
- TINY = 7
11
+ MINOR = 5
12
+ TINY = 0
13
13
 
14
14
  STRING = [MAJOR, MINOR, TINY].join('.')
15
15
  DESCRIPTION = "#{NAME} (#{STRING})"
@@ -1,22 +1,6 @@
1
1
  require File.expand_path('../spec_helper', __FILE__)
2
2
  require 'tempfile'
3
3
 
4
- def stub_Readline
5
- class << Readline
6
- attr_reader :received
7
-
8
- def stub_input(*input)
9
- @input = input
10
- end
11
-
12
- def readline(prompt, history)
13
- @received = [prompt, history]
14
- @input.shift
15
- end
16
- end
17
- end
18
- stub_Readline
19
-
20
4
  class TestProcessor
21
5
  def input(s)
22
6
  s * 2
@@ -28,6 +12,7 @@ main = self
28
12
  describe "IRB::Context" do
29
13
  before do
30
14
  @context = IRB::Context.new(main)
15
+ @context.extend(OutputStubMixin)
31
16
  end
32
17
 
33
18
  it "initializes with an object and stores a copy of its binding" do
@@ -41,46 +26,24 @@ describe "IRB::Context" do
41
26
  it "initializes with an object and an explicit binding" do
42
27
  context = IRB::Context.new(Object.new, TOPLEVEL_BINDING)
43
28
  eval("class InTopLevel; end", context.binding)
44
- lambda { ::InTopLevel }.should.not.raise NameError
29
+ lambda { ::InTopLevel }.should_not raise_error(NameError)
45
30
  end
46
31
 
47
32
  it "initializes with an 'empty' state" do
48
33
  @context.line.should == 1
49
- @context.source.should.be.instance_of IRB::Source
34
+ @context.source.class.should == IRB::Source
50
35
  @context.source.to_s.should == ""
51
36
  end
52
37
 
53
- it "initializes with an instance of each processor" do
54
- before = IRB::Context.processors.dup
55
- begin
56
- IRB::Context.processors << TestProcessor
57
- @context = IRB::Context.new(main)
58
- @context.processors.last.should.be.instance_of TestProcessor
59
- ensure
60
- IRB::Context.processors.replace(before)
61
- end
62
- end
63
-
64
38
  it "does not use the same binding copy of the top level object" do
65
- lambda { eval("x", @context.binding) }.should.raise NameError
66
- end
67
-
68
- it "makes itself the current running context during the runloop and resigns once it's done" do
69
- IRB::Context.current.should == nil
70
-
71
- Readline.stub_input("current_during_run = IRB::Context.current")
72
- @context.run
73
- eval('current_during_run', @context.binding).should == @context
74
-
75
- IRB::Context.current.should == nil
39
+ lambda { eval("x", @context.binding) }.should raise_error(NameError)
76
40
  end
77
41
  end
78
42
 
79
43
  describe "IRB::Context, when evaluating source" do
80
44
  before do
81
45
  @context = IRB::Context.new(main)
82
- def @context.printed; @printed ||= '' end
83
- def @context.puts(string); printed << "#{string}\n" end
46
+ @context.extend(OutputStubMixin)
84
47
  IRB.formatter = IRB::Formatter.new
85
48
  end
86
49
 
@@ -109,59 +72,43 @@ describe "IRB::Context, when evaluating source" do
109
72
  lambda {
110
73
  @context.evaluate("DoesNotExist")
111
74
  @context.evaluate("raise Exception")
112
- }.should.not.raise
75
+ }.should_not.raise_error
113
76
  end
114
77
 
115
- it "assigns the last raised exception to the global variable `$EXCEPTION' / `$e'" do
78
+ it "assigns the last raised exception to the variables `exception' / `e'" do
116
79
  @context.evaluate("DoesNotExist")
117
- $EXCEPTION.should.be.instance_of NameError
118
- $EXCEPTION.message.should.include 'DoesNotExist'
119
- $e.should == $EXCEPTION
80
+ @context.__evaluate__("exception").class.should == NameError
81
+ @context.__evaluate__("exception").message.should include('DoesNotExist')
82
+ @context.__evaluate__("e").should == @context.__evaluate__("exception")
120
83
  end
121
84
 
122
85
  it "prints the exception that occurs" do
123
86
  @context.evaluate("DoesNotExist")
124
- @context.printed.should.match /^NameError:.+DoesNotExist/
87
+ @context.printed.should =~ /^NameError:.+DoesNotExist/
125
88
  end
126
89
 
127
90
  it "uses the line number of the *first* line in the buffer, for the line parameter of eval" do
128
91
  @context.process_line("DoesNotExist")
129
- @context.printed.should.match /\(irb\):1:in/
92
+ @context.printed.should =~ /\(irb\):1:in/
130
93
  @context.process_line("class A")
131
94
  @context.process_line("DoesNotExist")
132
95
  @context.process_line("end")
133
- @context.printed.should.match /\(irb\):3:in.+\(irb\):2:in/m
96
+ @context.printed.should =~ /\(irb\):3:in.+\(irb\):2:in/m
134
97
  end
135
98
 
136
- it "inputs a line to be processed, skipping readline" do
137
- expected = "#{@context.formatter.prompt(@context)}2 * 21\n=> 42\n"
138
- @context.input_line("2 * 21")
139
- @context.printed.should == expected
99
+ it "ignores the result if it's IRB::Context::IGNORE_RESULT" do
100
+ @context.evaluate(":bananas")
101
+ @context.evaluate("IRB::Context::IGNORE_RESULT").should == nil
102
+ @context.printed.should == "=> :bananas\n"
103
+ @context.evaluate("_").should == :bananas
140
104
  end
141
105
  end
142
106
 
143
107
  describe "IRB::Context, when receiving input" do
144
108
  before do
145
109
  @context = IRB::Context.new(main)
146
- end
147
-
148
- it "prints the prompt, reads a line, saves it to the history and returns it" do
149
- Readline.stub_input("def foo")
150
- @context.readline.should == "def foo"
151
- Readline.received.should == ["irb(main):001:0> ", true]
152
- end
153
-
154
- it "passes the input to all processors, which may return a new value" do
155
- @context.processors << TestProcessor.new
156
- Readline.stub_input("foo")
157
- @context.readline.should == "foofoo"
158
- end
159
-
160
- it "processes the output" do
161
- Readline.stub_input("def foo")
162
- def @context.process_line(line); @received = line; false; end
163
- @context.run
164
- @context.instance_variable_get(:@received).should == "def foo"
110
+ @context.extend(InputStubMixin)
111
+ @context.extend(OutputStubMixin)
165
112
  end
166
113
 
167
114
  it "adds the received code to the source buffer" do
@@ -170,22 +117,10 @@ describe "IRB::Context, when receiving input" do
170
117
  @context.source.to_s.should == "def foo\np :ok"
171
118
  end
172
119
 
173
- it "clears the source buffer when an Interrupt signal is received" do
174
- begin
175
- @context.process_line("def foo")
176
-
177
- def Readline.readline(*args)
178
- unless @raised
179
- @raised = true
180
- raise Interrupt
181
- end
182
- end
183
-
184
- lambda { @context.run }.should.not.raise Interrupt
185
- @context.source.to_s.should == ""
186
- ensure
187
- stub_Readline
188
- end
120
+ it "clears the source buffer" do
121
+ @context.process_line("def foo")
122
+ @context.clear_buffer
123
+ @context.source.to_s.should == ""
189
124
  end
190
125
 
191
126
  it "increases the current line number" do
@@ -208,14 +143,11 @@ describe "IRB::Context, when receiving input" do
208
143
  end
209
144
 
210
145
  it "prints that a syntax error occurred on the last line and reset the buffer to the previous line" do
211
- def @context.puts(str); @printed = str; end
212
-
213
146
  @context.process_line("def foo")
214
147
  @context.process_line(" };")
215
148
 
216
149
  @context.source.to_s.should == "def foo"
217
- printed = @context.instance_variable_get(:@printed)
218
- printed.should == "SyntaxError: compile error\n(irb):2: syntax error, unexpected '}'"
150
+ @context.printed.should == "SyntaxError: compile error\n(irb):2: syntax error, unexpected '}'\n"
219
151
  end
220
152
 
221
153
  it "returns whether or not the runloop should continue, but only if the level is 0" do
@@ -226,19 +158,9 @@ describe "IRB::Context, when receiving input" do
226
158
  @context.process_line("quit").should == false
227
159
  end
228
160
 
229
- it "exits the runloop if the user wishes so" do
230
- Readline.stub_input("quit", "def foo")
231
- def @context.process_line(line); @received = line; super; end
232
- @context.run
233
- @context.instance_variable_get(:@received).should.not == "def foo"
234
- end
235
- end
236
-
237
- describe "Kernel::irb" do
238
- it "creates a new context for the given object and runs it" do
239
- Readline.stub_input("::IRBRan = self")
240
- o = Object.new
241
- irb(o)
242
- IRBRan.should == o
161
+ it "inputs a line to be processed" do
162
+ expected = "#{@context.formatter.prompt(@context)}2 * 21\n=> 42\n"
163
+ @context.input_line("2 * 21")
164
+ @context.printed.should == expected
243
165
  end
244
166
  end