Revision: 4376 http://trac.macosforge.org/projects/ruby/changeset/4376 Author: eloy.de.enige@gmail.com Date: 2010-07-24 08:11:24 -0700 (Sat, 24 Jul 2010) Log Message: ----------- Update DietRB to 0.5.1 (ee35b7726a48c036e78623f111353ef2b8294a38) Modified Paths: -------------- MacRuby/trunk/bin/irb MacRuby/trunk/lib/irb/completion.rb MacRuby/trunk/lib/irb/context.rb MacRuby/trunk/lib/irb/ext/completion.rb MacRuby/trunk/lib/irb/ext/history.rb MacRuby/trunk/lib/irb/version.rb MacRuby/trunk/lib/irb.rb MacRuby/trunk/spec/dietrb/context_spec.rb MacRuby/trunk/spec/dietrb/ext/completion_spec.rb MacRuby/trunk/spec/dietrb/ext/history_spec.rb MacRuby/trunk/spec/dietrb/formatter_spec.rb MacRuby/trunk/spec/dietrb/spec_helper.rb Added Paths: ----------- MacRuby/trunk/lib/irb/driver/ MacRuby/trunk/lib/irb/driver/readline.rb MacRuby/trunk/lib/irb/driver/socket.rb MacRuby/trunk/lib/irb/driver/tty.rb MacRuby/trunk/lib/irb/driver.rb MacRuby/trunk/spec/dietrb/driver/ MacRuby/trunk/spec/dietrb/driver/readline_spec.rb MacRuby/trunk/spec/dietrb/driver/tty_spec.rb MacRuby/trunk/spec/dietrb/driver_spec.rb Removed Paths: ------------- MacRuby/trunk/lib/irb/ext/macruby.rb Modified: MacRuby/trunk/bin/irb =================================================================== --- MacRuby/trunk/bin/irb 2010-07-24 03:06:27 UTC (rev 4375) +++ MacRuby/trunk/bin/irb 2010-07-24 15:11:24 UTC (rev 4376) @@ -2,41 +2,55 @@ require 'irb' -unless ARGV.empty? - require 'optparse' - - ignore_irbrc = false - - OptionParser.new do |opt| - bin = File.basename($0) - opt.banner = "Usage: #{bin} [options] [programfile] [arguments]" - opt.on("-f", "Ignore ~/.irbrc") { |ignore| ignore_irbrc = ignore } - opt.on("-r load-lib", "Loads the given library (same as `ruby -r')") { |lib| require lib } - opt.on("-d", "Set $DEBUG to true (same as `ruby -d')") { $DEBUG = true } - opt.on("-I path", "Add path to $LOAD_PATH") { |path| $LOAD_PATH.unshift(path) } - opt.on("--noinspect", "Don't use inspect for output") { IRB.formatter.inspect = false } - opt.on("--simple-prompt", "Simple prompt mode") { IRB.formatter.prompt = :simple } - opt.on("--noprompt", "No prompt mode") { IRB.formatter.prompt = nil } - opt.on("-v", "--version", "Print the version of #{bin}") do - puts IRB::VERSION::DESCRIPTION - exit +IRB_CONTEXT_TOPLEVEL_ARGS = [self, TOPLEVEL_BINDING.dup] + +module IRB + # Just a namespace so not to pollute the toplevel namespace with lvars. + module Bin + driver = nil + ignore_irbrc = false + + unless ARGV.empty? + require 'optparse' + + OptionParser.new do |opt| + bin = File.basename($0) + opt.banner = "Usage: #{bin} [options] [programfile] [arguments]" + opt.on("-f", "Ignore ~/.irbrc") { |i| ignore_irbrc = i } + opt.on("-r load-lib", "Loads the given library (same as `ruby -r')") { |l| require l } + opt.on("-d", "Set $DEBUG to true (same as `ruby -d')") { $DEBUG = true } + opt.on("-I path", "Add path to $LOAD_PATH") { |p| $LOAD_PATH.unshift(p) } + opt.on("--driver name", "As driver, use one of: tty, readline, or socket") { |d| driver = d } + opt.on("--noinspect", "Don't use inspect for output") { IRB.formatter.inspect = false } + opt.on("--simple-prompt", "Simple prompt mode") { IRB.formatter.prompt = :simple } + opt.on("--noprompt", "No prompt mode") { IRB.formatter.prompt = nil } + opt.on("-v", "--version", "Print the version of #{bin}") do + $stdout.puts IRB::VERSION::DESCRIPTION + exit + end + end.parse!(ARGV) end - end.parse!(ARGV) -end - -unless ignore_irbrc - irbrc = File.expand_path("~/.irbrc") - load(irbrc) if File.exist?(irbrc) -end - -IRB.formatter.filter_from_backtrace << /^#{__FILE__}/ - -if ARGV.empty? - irb(self, TOPLEVEL_BINDING.dup) -else - path = ARGV.shift - context = IRB::Context.new(self, TOPLEVEL_BINDING.dup) - File.open(path, 'r') do |file| - file.each_line { |line| context.input_line(line) } + + unless ignore_irbrc + irbrc = File.expand_path("~/.irbrc") + load(irbrc) if File.exist?(irbrc) + end + + IRB.formatter.filter_from_backtrace << /^#{__FILE__}/ + + if ARGV.empty? + driver ||= begin + require 'readline' + 'readline' + rescue LoadError + 'tty' + end + require "irb/driver/#{driver}" + irb(*IRB_CONTEXT_TOPLEVEL_ARGS) + else + path = ARGV.shift + context = IRB::Context.new(*IRB_CONTEXT_TOPLEVEL_ARGS) + File.open(path, 'r') { |f| f.each_line { |line| context.input_line(line) } } + end end end \ No newline at end of file Modified: MacRuby/trunk/lib/irb/completion.rb =================================================================== --- MacRuby/trunk/lib/irb/completion.rb 2010-07-24 03:06:27 UTC (rev 4375) +++ MacRuby/trunk/lib/irb/completion.rb 2010-07-24 15:11:24 UTC (rev 4376) @@ -1 +1 @@ -IRB.deprecated "The file `irb/completion' has moved to `irb/ext/ecompletion' and is loaded by default.", caller \ No newline at end of file +IRB.deprecated "The file `irb/completion' has moved to `irb/ext/completion' and is loaded by default.", caller \ No newline at end of file Modified: MacRuby/trunk/lib/irb/context.rb =================================================================== --- MacRuby/trunk/lib/irb/context.rb 2010-07-24 03:06:27 UTC (rev 4375) +++ MacRuby/trunk/lib/irb/context.rb 2010-07-24 15:11:24 UTC (rev 4376) @@ -5,28 +5,13 @@ # Copyright (C) 2009-2010, Eloy Duran <eloy.de.enige@gmail.com> require 'irb/formatter' -require 'readline' module IRB class Context - class << self - attr_accessor :current - - def make_current(context) - # Messing with a current context is gonna bite me in the ass when we - # get to multi-threading, but we'll it when we get there. - before, @current = @current, context - yield - ensure - @current = before - end - - def processors - @processors ||= [] - end - end + IGNORE_RESULT = :irb_ignore_result - attr_reader :object, :binding, :line, :source, :processors + attr_reader :object, :binding, :line, :source + attr_accessor :formatter def initialize(object, explicit_binding = nil) @object = object @@ -34,8 +19,8 @@ @line = 1 clear_buffer - @underscore_assigner = __evaluate__("_ = nil; proc { |val| _ = val }") - @processors = self.class.processors.map { |processor| processor.new(self) } + @last_result_assigner = __evaluate__("_ = nil; proc { |val| _ = val }") + @exception_assigner = __evaluate__("e = exception = nil; proc { |ex| e = exception = ex }") end def __evaluate__(source, file = __FILE__, line = __LINE__) @@ -44,33 +29,16 @@ def evaluate(source) result = __evaluate__(source.to_s, '(irb)', @line - @source.buffer.size + 1) - store_result(result) - puts formatter.result(result) - result + unless result == IGNORE_RESULT + store_result(result) + puts(formatter.result(result)) + result + end rescue Exception => e store_exception(e) - puts formatter.exception(e) + puts(formatter.exception(e)) end - # Reads input and passes it to all processors. - def readline - input = Readline.readline(formatter.prompt(self), true) - @processors.each { |processor| input = processor.input(input) } - input - rescue Interrupt - clear_buffer - "" - end - - def run - self.class.make_current(self) do - while line = readline - continue = process_line(line) - break unless continue - end - end - end - # Returns whether or not the user wants to continue the current runloop. # This can only be done at a code block indentation level of 0. # @@ -88,7 +56,7 @@ return false if @source.terminate? if @source.syntax_error? - puts formatter.syntax_error(@line, @source.syntax_error) + puts(formatter.syntax_error(@line, @source.syntax_error)) @source.pop elsif @source.code_block? evaluate(@source) @@ -99,13 +67,17 @@ true end + def prompt + formatter.prompt(self) + end + def input_line(line) - puts formatter.prompt(self) + line + puts(formatter.prompt(self) + line) process_line(line) end def formatter - IRB.formatter + @formatter ||= IRB.formatter end def clear_buffer @@ -113,20 +85,11 @@ end def store_result(result) - @underscore_assigner.call(result) + @last_result_assigner.call(result) end def store_exception(exception) - $e = $EXCEPTION = exception + @exception_assigner.call(exception) end end -end - -module Kernel - # Creates a new IRB::Context with the given +object+ and runs it. - def irb(object, binding = nil) - IRB::Context.new(object, binding).run - end - - private :irb -end +end \ No newline at end of file Added: MacRuby/trunk/lib/irb/driver/readline.rb =================================================================== --- MacRuby/trunk/lib/irb/driver/readline.rb (rev 0) +++ MacRuby/trunk/lib/irb/driver/readline.rb 2010-07-24 15:11:24 UTC (rev 4376) @@ -0,0 +1,26 @@ +require 'readline' +require 'irb/driver/tty' +require 'irb/ext/history' +require 'irb/ext/completion' + +module IRB + module Driver + class Readline < TTY + + def initialize(input = $stdin, output = $stdout) + super + ::Readline.input = @input + ::Readline.output = @output + ::Readline.completion_proc = IRB::Completion.new + end + + def readline + source = ::Readline.readline(context.prompt, true) + IRB::History.input(source) + source + end + end + end +end + +IRB::Driver.current = IRB::Driver::Readline.new Added: MacRuby/trunk/lib/irb/driver/socket.rb =================================================================== --- MacRuby/trunk/lib/irb/driver/socket.rb (rev 0) +++ MacRuby/trunk/lib/irb/driver/socket.rb 2010-07-24 15:11:24 UTC (rev 4376) @@ -0,0 +1,58 @@ +require 'irb/driver/tty' +require 'socket' + +module IRB + module Driver + class Socket + class << self + attr_reader :instance + + def run(object, binding) + @instance = new(object, binding) + @instance.run + end + end + + # Initializes with the object and binding that each new connection will + # get as Context. The binding is shared, so local variables will stay + # around. The benefit of this is that a socket based irb session is most + # probably used to debug a running application in development. In this + # scenario it could be beneficial to keep local vars in between sessions. + # + # TODO see if that actually works out ok. + def initialize(object, binding, host = '127.0.0.1', port = 7829) + @object, @binding = object, binding + @host, @port = host, port + @server = TCPServer.new(host, port) + end + + # TODO libedit doesn't use the right input and output, so we can't use Readline for now!! + def run + $stderr.puts "[!] Running IRB server on #{@host}:#{@port}" + loop do + connection = @server.accept + Thread.new do + # assign driver with connection to current thread and start runloop + IRB::Driver.current = TTY.new(connection, connection) + irb(@object, @binding) + connection.close + end + end + end + end + end +end + +module Kernel + alias_method :irb_before_socket, :irb + + def irb(object, binding = nil) + if IRB::Driver::Socket.instance.nil? + IRB::Driver::Socket.run(object, binding) + else + irb_before_socket(object, binding) + end + end + + private :irb, :irb_before_socket +end \ No newline at end of file Added: MacRuby/trunk/lib/irb/driver/tty.rb =================================================================== --- MacRuby/trunk/lib/irb/driver/tty.rb (rev 0) +++ MacRuby/trunk/lib/irb/driver/tty.rb 2010-07-24 15:11:24 UTC (rev 4376) @@ -0,0 +1,58 @@ +require 'irb/driver' + +module IRB + module Driver + class TTY + attr_reader :input, :output, :context_stack + + def initialize(input = $stdin, output = $stdout) + @input = input + @output = output + @context_stack = [] + end + + def context + @context_stack.last + end + + def readline + @output.print(context.prompt) + @input.gets + end + + # TODO make it take the current context instead of storing it + def consume + readline + rescue Interrupt + context.clear_buffer + "" + end + + # Feeds input into a given context. + # + # Ensures that the standard output object is a OutputRedirector, or a + # subclass thereof. + def run(context) + @context_stack << context + before, $stdout = $stdout, OutputRedirector.new unless $stdout.is_a?(OutputRedirector) + while line = consume + break unless context.process_line(line) + end + ensure + @context_stack.pop + $stdout = before if before + end + end + end +end + +IRB::Driver.current = IRB::Driver::TTY.new + +module Kernel + # Creates a new IRB::Context with the given +object+ and runs it. + def irb(object, binding = nil) + IRB::Driver.current.run(IRB::Context.new(object, binding)) + end + + private :irb +end Added: MacRuby/trunk/lib/irb/driver.rb =================================================================== --- MacRuby/trunk/lib/irb/driver.rb (rev 0) +++ MacRuby/trunk/lib/irb/driver.rb 2010-07-24 15:11:24 UTC (rev 4376) @@ -0,0 +1,61 @@ +module IRB + module Driver + class << self + def current=(driver) + ThreadGroup.new.add(Thread.current) + Thread.current[:irb_driver] = driver + end + + def current + current_thread = Thread.current + current_thread[:irb_driver] ||= begin + if group = current_thread.group + driver = nil + group.list.each do |thread| + break if driver = thread[:irb_driver] + end + driver + end + end + end + end + + class OutputRedirector + def self.target + if driver = IRB::Driver.current + driver.output + else + $stderr + end + end + + # A standard output object has only one mandatory method: write. + # It returns the number of characters written + def write(object) + string = object.respond_to?(:to_str) ? object : object.to_s + send_to_target :write, string + string.length + end + + # if puts is not there, Ruby will automatically use the write + # method when calling Kernel#puts, but defining it has 2 advantages: + # - if puts is not defined, you cannot of course use $stdout.puts directly + # - (objc) when Ruby emulates puts, it calls write twice + # (once for the string and once for the carriage return) + # but here we send the calls to another thread so it's nice + # to be able to save up one (slow) interthread call + def puts(*args) + send_to_target :puts, *args + nil + end + + # Override this if for your situation you need to dispatch from a thread + # in a special manner. + # + # TODO: for macruby send to main thread + def send_to_target(method, *args) + self.class.target.__send__(method, *args) + end + end + end +end \ No newline at end of file Modified: MacRuby/trunk/lib/irb/ext/completion.rb =================================================================== --- MacRuby/trunk/lib/irb/ext/completion.rb 2010-07-24 03:06:27 UTC (rev 4375) +++ MacRuby/trunk/lib/irb/ext/completion.rb 2010-07-24 15:11:24 UTC (rev 4376) @@ -36,22 +36,23 @@ yield } + attr_reader :source + + def context + IRB::Driver.current.context + end + # Returns an array of possible completion results, with the current # IRB::Context. # # This is meant to be used with Readline which takes a completion proc. - def self.call(source) - new(IRB::Context.current, source).results + def call(source) + @source = source + results end - attr_reader :context, :source - - def initialize(context, source) - @context, @source = context, source - end - def evaluate(s) - @context.__evaluate__(s) + context.__evaluate__(s) end def local_variables @@ -59,7 +60,7 @@ end def instance_methods - @context.object.methods.map(&:to_s) + context.object.methods.map(&:to_s) end def instance_methods_of(klass) @@ -190,5 +191,5 @@ # * Hash: = and > Readline.basic_word_break_characters= " \t\n`<;|&(" end - Readline.completion_proc = IRB::Completion -end \ No newline at end of file + # Readline.completion_proc = IRB::Completion +end Modified: MacRuby/trunk/lib/irb/ext/history.rb =================================================================== --- MacRuby/trunk/lib/irb/ext/history.rb 2010-07-24 03:06:27 UTC (rev 4375) +++ MacRuby/trunk/lib/irb/ext/history.rb 2010-07-24 15:11:24 UTC (rev 4376) @@ -7,91 +7,86 @@ # Portions Copyright (C) 2006-2010 Ben Bleything <ben@bleything.net> (Kernel#history & Kernel#history!) module IRB - class History + module History class << self attr_accessor :file, :max_entries_in_overview - def current - IRB::Context.current.processors.find do |processor| - processor.is_a?(IRB::History) - end + def setup + to_a.each do |source| + Readline::HISTORY.push(source) + end if Readline::HISTORY.to_a.empty? end - end - - def initialize(context) - @context = context - to_a.each do |source| - Readline::HISTORY.push(source) - end if Readline::HISTORY.to_a.empty? - end - - def input(source) - File.open(self.class.file, "a") { |f| f.puts(source) } - source - end - - def to_a - file = self.class.file - File.exist?(file) ? File.read(file).split("\n") : [] - end - - def clear! - File.open(self.class.file, "w") { |f| f << "" } - Readline::HISTORY.clear - end - - def history(number_of_entries = max_entries_in_overview) - history_size = Readline::HISTORY.size - start_index = 0 + def context + IRB::Driver.current.context + end - # always remove one extra, because that's the `history' command itself - if history_size <= number_of_entries - end_index = history_size - 2 - else - end_index = history_size - 2 - start_index = history_size - number_of_entries - 1 + def input(source) + File.open(file, "a") { |f| f.puts(source) } + source end - start_index.upto(end_index) do |i| - puts "#{i}: #{Readline::HISTORY[i]}" + def to_a + File.exist?(file) ? File.read(file).split("\n") : [] end - end - - def history!(entry_or_range) - # we don't want to execute history! again - @context.clear_buffer - if entry_or_range.is_a?(Range) - entry_or_range.to_a.each do |i| - @context.input_line(Readline::HISTORY[i]) + def clear! + File.open(file, "w") { |f| f << "" } + Readline::HISTORY.clear + end + + def history(number_of_entries = max_entries_in_overview) + history_size = Readline::HISTORY.size + start_index = 0 + + # always remove one extra, because that's the `history' command itself + if history_size <= number_of_entries + end_index = history_size - 2 + else + end_index = history_size - 2 + start_index = history_size - number_of_entries - 1 end - else - @context.input_line(Readline::HISTORY[entry_or_range]) + + start_index.upto(end_index) do |i| + puts "#{i}: #{Readline::HISTORY[i]}" + end end + + def history!(entry_or_range) + # we don't want to execute history! again + context.clear_buffer + + if entry_or_range.is_a?(Range) + entry_or_range.to_a.each do |i| + context.input_line(Readline::HISTORY[i]) + end + else + context.input_line(Readline::HISTORY[entry_or_range]) + end + end end end end module Kernel def history(number_of_entries = IRB::History.max_entries_in_overview) - IRB::History.current.history(number_of_entries) - nil + IRB::History.history(number_of_entries) + IRB::Context::IGNORE_RESULT end alias_method :h, :history def history!(entry_or_range) - IRB::History.current.history!(entry_or_range) - nil + IRB::History.history!(entry_or_range) + IRB::Context::IGNORE_RESULT end alias_method :h!, :history! def clear_history! - IRB::History.current.clear! - nil + IRB::History.clear! + true end end -IRB::Context.processors << IRB::History IRB::History.file = File.expand_path("~/.irb_history") -IRB::History.max_entries_in_overview = 50 \ No newline at end of file +IRB::History.max_entries_in_overview = 50 +IRB::History.setup \ No newline at end of file Deleted: MacRuby/trunk/lib/irb/ext/macruby.rb =================================================================== --- MacRuby/trunk/lib/irb/ext/macruby.rb 2010-07-24 03:06:27 UTC (rev 4375) +++ MacRuby/trunk/lib/irb/ext/macruby.rb 2010-07-24 15:11:24 UTC (rev 4376) @@ -1,25 +0,0 @@ -# MacRuby implementation of IRB. -# -# This file is covered by the Ruby license. See COPYING for more details. -# -# Copyright (C) 2009-2010, Eloy Duran <eloy.de.enige@gmail.com> - -framework 'AppKit' - -module IRB - class Context - alias_method :_run, :run - - def run - if NSApplication.sharedApplication.running? - _run - else - Thread.new do - _run - NSApplication.sharedApplication.terminate(self) - end - NSApplication.sharedApplication.run - end - end - end -end \ No newline at end of file Modified: MacRuby/trunk/lib/irb/version.rb =================================================================== --- MacRuby/trunk/lib/irb/version.rb 2010-07-24 03:06:27 UTC (rev 4375) +++ MacRuby/trunk/lib/irb/version.rb 2010-07-24 15:11:24 UTC (rev 4376) @@ -8,8 +8,8 @@ module VERSION #:nodoc: NAME = 'DietRB' MAJOR = 0 - MINOR = 4 - TINY = 7 + MINOR = 5 + TINY = 1 STRING = [MAJOR, MINOR, TINY].join('.') DESCRIPTION = "#{NAME} (#{STRING})" Modified: MacRuby/trunk/lib/irb.rb =================================================================== --- MacRuby/trunk/lib/irb.rb 2010-07-24 03:06:27 UTC (rev 4375) +++ MacRuby/trunk/lib/irb.rb 2010-07-24 15:11:24 UTC (rev 4376) @@ -10,13 +10,6 @@ require 'irb/deprecated' -require 'irb/ext/history' -require 'irb/ext/completion' - -# if !ENV['SPECCING'] && defined?(RUBY_ENGINE) && RUBY_ENGINE == "macruby" -# require 'irb/ext/macruby' -# end - module IRB class << self # This is just here for so the ruby 1.9 IRB will seemingly work, but actually Modified: MacRuby/trunk/spec/dietrb/context_spec.rb =================================================================== --- MacRuby/trunk/spec/dietrb/context_spec.rb 2010-07-24 03:06:27 UTC (rev 4375) +++ MacRuby/trunk/spec/dietrb/context_spec.rb 2010-07-24 15:11:24 UTC (rev 4376) @@ -1,22 +1,6 @@ require File.expand_path('../spec_helper', __FILE__) require 'tempfile' -def stub_Readline - class << Readline - attr_reader :received - - def stub_input(*input) - @input = input - end - - def readline(prompt, history) - @received = [prompt, history] - @input.shift - end - end -end -stub_Readline - class TestProcessor def input(s) s * 2 @@ -28,6 +12,7 @@ describe "IRB::Context" do before do @context = IRB::Context.new(main) + @context.extend(OutputStubMixin) end it "initializes with an object and stores a copy of its binding" do @@ -50,37 +35,15 @@ @context.source.to_s.should == "" end - it "initializes with an instance of each processor" do - before = IRB::Context.processors.dup - begin - IRB::Context.processors << TestProcessor - @context = IRB::Context.new(main) - @context.processors.last.class.should == TestProcessor - ensure - IRB::Context.processors.replace(before) - end - end - it "does not use the same binding copy of the top level object" do lambda { eval("x", @context.binding) }.should raise_error(NameError) end - - it "makes itself the current running context during the runloop and resigns once it's done" do - IRB::Context.current.should == nil - - Readline.stub_input("current_during_run = IRB::Context.current") - @context.run - eval('current_during_run', @context.binding).should == @context - - IRB::Context.current.should == nil - end end describe "IRB::Context, when evaluating source" do before do @context = IRB::Context.new(main) - def @context.printed; @printed ||= '' end - def @context.puts(string); printed << "#{string}\n" end + @context.extend(OutputStubMixin) IRB.formatter = IRB::Formatter.new end @@ -112,11 +75,11 @@ }.should_not.raise_error end - it "assigns the last raised exception to the global variable `$EXCEPTION' / `$e'" do + it "assigns the last raised exception to the variables `exception' / `e'" do @context.evaluate("DoesNotExist") - $EXCEPTION.class.should == NameError - $EXCEPTION.message.should include('DoesNotExist') - $e.should == $EXCEPTION + @context.__evaluate__("exception").class.should == NameError + @context.__evaluate__("exception").message.should include('DoesNotExist') + @context.__evaluate__("e").should == @context.__evaluate__("exception") end it "prints the exception that occurs" do @@ -133,59 +96,31 @@ @context.printed.should =~ /\(irb\):3:in.+\(irb\):2:in/m end - it "inputs a line to be processed, skipping readline" do - expected = "#{@context.formatter.prompt(@context)}2 * 21\n=> 42\n" - @context.input_line("2 * 21") - @context.printed.should == expected + it "ignores the result if it's IRB::Context::IGNORE_RESULT" do + @context.evaluate(":bananas") + @context.evaluate("IRB::Context::IGNORE_RESULT").should == nil + @context.printed.should == "=> :bananas\n" + @context.evaluate("_").should == :bananas end end describe "IRB::Context, when receiving input" do before do @context = IRB::Context.new(main) + @context.extend(InputStubMixin) + @context.extend(OutputStubMixin) end - it "prints the prompt, reads a line, saves it to the history and returns it" do - Readline.stub_input("def foo") - @context.readline.should == "def foo" - Readline.received.should == ["irb(main):001:0> ", true] - end - - it "passes the input to all processors, which may return a new value" do - @context.processors << TestProcessor.new - Readline.stub_input("foo") - @context.readline.should == "foofoo" - end - - it "processes the output" do - Readline.stub_input("def foo") - def @context.process_line(line); @received = line; false; end - @context.run - @context.instance_variable_get(:@received).should == "def foo" - end - it "adds the received code to the source buffer" do @context.process_line("def foo") @context.process_line("p :ok") @context.source.to_s.should == "def foo\np :ok" end - it "clears the source buffer when an Interrupt signal is received" do - begin - @context.process_line("def foo") - - def Readline.readline(*args) - unless @raised - @raised = true - raise Interrupt - end - end - - lambda { @context.run }.should_not raise_error(Interrupt) - @context.source.to_s.should == "" - ensure - stub_Readline - end + it "clears the source buffer" do + @context.process_line("def foo") + @context.clear_buffer + @context.source.to_s.should == "" end it "increases the current line number" do @@ -208,14 +143,11 @@ end it "prints that a syntax error occurred on the last line and reset the buffer to the previous line" do - def @context.puts(str); @printed = str; end - @context.process_line("def foo") @context.process_line(" };") @context.source.to_s.should == "def foo" - printed = @context.instance_variable_get(:@printed) - printed.should == "SyntaxError: compile error\n(irb):2: syntax error, unexpected '}'" + @context.printed.should == "SyntaxError: compile error\n(irb):2: syntax error, unexpected '}'\n" end it "returns whether or not the runloop should continue, but only if the level is 0" do @@ -226,19 +158,9 @@ @context.process_line("quit").should == false end - it "exits the runloop if the user wishes so" do - Readline.stub_input("quit", "def foo") - def @context.process_line(line); @received = line; super; end - @context.run - @context.instance_variable_get(:@received).should_not == "def foo" + it "inputs a line to be processed" do + expected = "#{@context.formatter.prompt(@context)}2 * 21\n=> 42\n" + @context.input_line("2 * 21") + @context.printed.should == expected end end - -describe "Kernel::irb" do - it "creates a new context for the given object and runs it" do - Readline.stub_input("::IRBRan = self") - o = Object.new - irb(o) - IRBRan.should == o - end -end Added: MacRuby/trunk/spec/dietrb/driver/readline_spec.rb =================================================================== --- MacRuby/trunk/spec/dietrb/driver/readline_spec.rb (rev 0) +++ MacRuby/trunk/spec/dietrb/driver/readline_spec.rb 2010-07-24 15:11:24 UTC (rev 4376) @@ -0,0 +1,70 @@ +require File.expand_path("../../spec_helper", __FILE__) +require "irb/driver/readline" + +module Readline + extend InputStubMixin + extend OutputStubMixin + + def self.input=(input) + @given_input = input + end + + def self.given_input + @given_input + end + + def self.output=(output) + @given_output = output + end + + def self.given_output + @given_output + end + + def self.use_history=(use_history) + @use_history = use_history + end + + def self.use_history + @use_history + end + + def self.readline(prompt, use_history) + @use_history = use_history + print prompt + @input.shift + end +end + +describe "IRB::Driver::Readline" do + before do + @driver = IRB::Driver::Readline.new(InputStub.new, OutputStub.new) + @context = IRB::Context.new(Object.new) + @driver.context_stack << @context + end + + it "is a subclass of IRB::Driver::TTY" do + IRB::Driver::Readline.superclass.should == IRB::Driver::TTY + end + + it "assigns the given input and output to the Readline module" do + Readline.given_input.should == @driver.input + Readline.given_output.should == @driver.output + end + + it "assigns a completion object" do + Readline.completion_proc.class.should == IRB::Completion + end + + it "reads a line through the Readline module" do + Readline.stub_input "nom nom nom" + @driver.readline.should == "nom nom nom" + end + + it "tells the Readline module to use the history" do + Readline.use_history = false + Readline.stub_input "nom nom nom" + @driver.readline + Readline.use_history.should == true + end +end Added: MacRuby/trunk/spec/dietrb/driver/tty_spec.rb =================================================================== --- MacRuby/trunk/spec/dietrb/driver/tty_spec.rb (rev 0) +++ MacRuby/trunk/spec/dietrb/driver/tty_spec.rb 2010-07-24 15:11:24 UTC (rev 4376) @@ -0,0 +1,70 @@ +require File.expand_path('../../spec_helper', __FILE__) +require 'irb/driver/tty' + +describe "IRB::Driver::TTY" do + before do + @driver = IRB::Driver::TTY.new(InputStub.new, OutputStub.new) + @context = IRB::Context.new(Object.new) + @driver.context_stack << @context + end + + it "prints the prompt and reads a line of input" do + @driver.input.stub_input "calzone" + @driver.readline.should == "calzone" + @driver.output.printed.should == @context.prompt + end + + it "consumes input" do + @driver.input.stub_input "calzone" + @driver.consume.should == "calzone" + end + + it "clears the context buffer if an Interrupt signal is received while consuming input" do + @context.process_line("class A") + def @driver.readline; raise Interrupt; end + @driver.consume.should == "" + @context.source.to_s.should == "" + end +end + +describe "IRB::Driver::TTY, when starting the runloop" do + before do + @driver = IRB::Driver::TTY.new(InputStub.new, OutputStub.new) + IRB::Driver.current = @driver + @context = IRB::Context.new(Object.new) + end + + it "makes the given context the current one, for this driver, for the duration of the runloop" do + $from_context = nil + @driver.input.stub_input "$from_context = IRB::Driver.current.context" + @driver.run(@context) + $from_context.should == @context + IRB::Driver.current.context.should == nil + end + + it "feeds input into a given context" do + $from_context = false + @driver.input.stub_input "$from_context = true", "exit" + @driver.run(@context) + $from_context.should == true + end + + it "makes sure there's a global output redirector while running a context" do + before = $stdout + $from_context = nil + @driver.input.stub_input "$from_context = $stdout", "exit" + @driver.run(@context) + $from_context.class == IRB::Driver::OutputRedirector + $stdout.should == before + end +end + +# describe "Kernel::irb" do +# it "creates a new context for the given object and runs it" do +# IRB.io = CaptureIO.new +# IRB.io.stub_input("::IRBRan = self") +# o = Object.new +# irb(o) +# IRBRan.should == o +# end +# end \ No newline at end of file Added: MacRuby/trunk/spec/dietrb/driver_spec.rb =================================================================== --- MacRuby/trunk/spec/dietrb/driver_spec.rb (rev 0) +++ MacRuby/trunk/spec/dietrb/driver_spec.rb 2010-07-24 15:11:24 UTC (rev 4376) @@ -0,0 +1,65 @@ +require File.expand_path('../spec_helper', __FILE__) +require 'irb/driver' + +describe "IRB::Driver" do + before :all do + @driver = StubDriver.new + IRB::Driver.current = @driver + end + + it "assigns the driver for the current thread" do + Thread.current[:irb_driver].should == @driver + end + + it "returns the same driver for child threads" do + Thread.new do + IRB::Driver.current = other = StubDriver.new + Thread.new { IRB::Driver.current.should == other }.join + end.join + Thread.new { IRB::Driver.current.should == @driver }.join + end +end + +describe "IRB::Driver::OutputRedirector" do + before :each do + @driver = StubDriver.new + @driver.output = OutputStub.new + IRB::Driver.current = @driver + + @redirector = IRB::Driver::OutputRedirector.new + end + + it "returns $stderr as the target if no current driver could be found" do + IRB::Driver.current = nil + IRB::Driver::OutputRedirector.target.should == $stderr + end + + it "returns the current driver's output as the target" do + IRB::Driver::OutputRedirector.target.should == @driver.output + end + + it "forwards method calls to the current target" do + @redirector.send_to_target(:eql?, @driver.output).should == true + end + + it "writes to the current target's output" do + @redirector.write("strawberry coupe") + @driver.output.printed.should == "strawberry coupe" + end + + it "returns the amount of bytes written" do + @redirector.write("banana coupe").should == 12 + end + + it "coerces an object to a string before writing" do + o = Object.new + def o.to_s; "cherry coupe"; end + @redirector.write(o) + @driver.output.printed.should == "cherry coupe" + end + + it "forwards puts to the current target's output" do + @redirector.puts("double", "coupe") + @driver.output.printed.should == "double\ncoupe\n" + end +end \ No newline at end of file Modified: MacRuby/trunk/spec/dietrb/ext/completion_spec.rb =================================================================== --- MacRuby/trunk/spec/dietrb/ext/completion_spec.rb 2010-07-24 03:06:27 UTC (rev 4375) +++ MacRuby/trunk/spec/dietrb/ext/completion_spec.rb 2010-07-24 15:11:24 UTC (rev 4376) @@ -1,9 +1,11 @@ require File.expand_path('../../spec_helper', __FILE__) +require 'irb/driver' require 'irb/ext/completion' module CompletionHelper def complete(str) - IRB::Completion.new(@context, str).results + # IRB::Completion.new(@context, str).results + @completion.call(str) end def imethods(klass, receiver = nil) @@ -35,12 +37,14 @@ describe "IRB::Completion" do extend CompletionHelper - before do + before :all do + @completion = IRB::Completion.new @context = IRB::Context.new(Playground.new) + IRB::Driver.current = StubDriver.new(@context) end it "quacks like a Proc" do - IRB::Completion.call('//.').should == imethods(Regexp, '//') + @completion.call('//.').should == imethods(Regexp, '//') end describe "when doing a method call on an explicit receiver," do Modified: MacRuby/trunk/spec/dietrb/ext/history_spec.rb =================================================================== --- MacRuby/trunk/spec/dietrb/ext/history_spec.rb 2010-07-24 03:06:27 UTC (rev 4375) +++ MacRuby/trunk/spec/dietrb/ext/history_spec.rb 2010-07-24 15:11:24 UTC (rev 4376) @@ -1,6 +1,10 @@ require File.expand_path('../../spec_helper', __FILE__) +require 'readline' require "tempfile" +require 'irb/ext/history' +require 'irb/driver' + describe "IRB::History, by default," do it "stores the history in ~/.irb_history" do IRB::History.file.should == File.expand_path("~/.irb_history") @@ -11,7 +15,7 @@ before do @file = Tempfile.new("irb_history.txt") IRB::History.file = @file.path - @history = IRB::History.new(nil) + @history = IRB::History end after do @@ -54,8 +58,8 @@ @history.input "puts :ok" @history.input "foo(x)" - IRB::History.new(nil) - IRB::History.new(nil) + IRB::History.setup + IRB::History.setup Readline::HISTORY.to_a.should == ["puts :ok", "foo(x)"] end @@ -70,25 +74,16 @@ end end -class IRB::History - def printed - @printed ||= "" +module IRB::History + class << self + def clear! + @cleared = true + end + + def cleared? + @cleared + end end - - def print(s) - printed << s - end - - def puts(s) - printed << "#{s}\n" - end - - def clear! - @cleared = true - end - def cleared? - @cleared - end end describe "IRB::History, concerning the user api, by default," do @@ -115,24 +110,22 @@ IRB::History.max_entries_in_overview = 5 - @context = IRB::Context.new(Object.new) - IRB::Context.current = @context + IRB::History.extend(OutputStubMixin) + IRB::History.clear_printed! - @history = @context.processors.find { |p| p.is_a?(IRB::History) } + @context = IRB::Context.new(Object.new) + @context.extend(OutputStubMixin) + IRB::Driver.current = StubDriver.new(@context) end - after do - IRB::Context.current = nil - end - it "returns nil so that IRB doesn't cache some arbitrary line number" do - history.should == nil + history.should == IRB::Context::IGNORE_RESULT end - it "prints a formatted list with, by default IRB::History.max_entries_in_overview, number of history entries" do + it "prints a formatted list with IRB::History.max_entries_in_overview number of history entries" do history - @history.printed.should == %{ + IRB::History.printed.should == %{ 2: class AAA 3: def bar 4: :ok @@ -144,7 +137,7 @@ it "prints a formatted list of N most recent history entries" do history(7) - @history.printed.should == %{ + IRB::History.printed.should == %{ 0: puts :ok 1: x = foo(x) 2: class AAA @@ -158,7 +151,7 @@ it "prints a formatted list of all history entries if the request number of entries is more than there is" do history(777) - @history.printed.should == %{ + IRB::History.printed.should == %{ 0: puts :ok 1: x = foo(x) 2: class AAA @@ -182,6 +175,6 @@ it "clears the history and history file" do clear_history! - @history.cleared?.should == true + IRB::History.cleared?.should == true end end \ No newline at end of file Modified: MacRuby/trunk/spec/dietrb/formatter_spec.rb =================================================================== --- MacRuby/trunk/spec/dietrb/formatter_spec.rb 2010-07-24 03:06:27 UTC (rev 4375) +++ MacRuby/trunk/spec/dietrb/formatter_spec.rb 2010-07-24 15:11:24 UTC (rev 4376) @@ -40,13 +40,15 @@ it "does not filter the backtrace if $DEBUG is true" do begin - before, $DEBUG = $DEBUG, true + stderr, $stderr = $stderr, OutputStub.new + debug, $DEBUG = $DEBUG, true begin; @context.__evaluate__('DoesNotExist'); rescue NameError => e; exception = e; end @formatter.exception(exception).should == "NameError: uninitialized constant IRB::Context::DoesNotExist\n\t#{exception.backtrace.join("\n\t")}" ensure - $DEBUG = before + $stderr = stderr + $DEBUG = debug end end @@ -67,7 +69,7 @@ def object.inspect; @inspected = true; "Never called!"; end def object.__id__; 2158110700; end - @formatter.result(object).should == "=> #<Object:0x101444fd8>" + @formatter.result(object).should == "=> #<#{object.class.name}:0x101444fd8>" object.instance_variable_get(:@inspected).should_not == true end Modified: MacRuby/trunk/spec/dietrb/spec_helper.rb =================================================================== --- MacRuby/trunk/spec/dietrb/spec_helper.rb 2010-07-24 03:06:27 UTC (rev 4375) +++ MacRuby/trunk/spec/dietrb/spec_helper.rb 2010-07-24 15:11:24 UTC (rev 4376) @@ -14,4 +14,62 @@ end $:.unshift File.join(ROOT, 'lib') -require 'irb' \ No newline at end of file +require 'irb' + +module InputStubMixin + def stub_input(*input) + @input = input + end + + def readline(prompt) + # print prompt + @input.shift + end + + def gets + @input.shift + end +end + +class InputStub + include InputStubMixin +end + +module OutputStubMixin + def printed + @printed ||= '' + end + + def write(string) + printed << string + end + + def print(string) + printed << string + end + + def puts(*args) + print "#{args.join("\n")}\n" + end + + def clear_printed! + @printed = '' + end +end + +class OutputStub + include OutputStubMixin +end + +class StubDriver + attr_reader :context + attr_writer :output + + def initialize(context = nil) + @context = context + end + + def output + @output || $stdout + end +end