dietrb 0.4.7 → 0.5.0

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.
@@ -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