[macruby-changes] [4726] DietRB/trunk

source_changes at macosforge.org source_changes at macosforge.org
Fri Oct 8 04:05:35 PDT 2010


Revision: 4726
          http://trac.macosforge.org/projects/ruby/changeset/4726
Author:   eloy.de.enige at gmail.com
Date:     2010-10-08 04:05:34 -0700 (Fri, 08 Oct 2010)
Log Message:
-----------
Make socket driver work. Each connection starts a session on a new thread. For now the binding is shared.

From: Eloy Duran <eloy.de.enige at gmail.com>

Modified Paths:
--------------
    DietRB/trunk/Rakefile
    DietRB/trunk/bin/dietrb
    DietRB/trunk/lib/irb/context.rb
    DietRB/trunk/lib/irb/driver/socket.rb
    DietRB/trunk/lib/irb/driver/tty.rb

Modified: DietRB/trunk/Rakefile
===================================================================
--- DietRB/trunk/Rakefile	2010-10-08 11:05:23 UTC (rev 4725)
+++ DietRB/trunk/Rakefile	2010-10-08 11:05:34 UTC (rev 4726)
@@ -12,7 +12,7 @@
 
 desc "Run dietrb"
 task :run do
-  sh "#{ruby_bin} -I lib ./bin/dietrb -r irb/ext/colorize -r pp"
+  sh "#{ruby_bin} -I lib ./bin/dietrb -d -r irb/ext/colorize -r pp"
 end
 
 namespace :macruby do

Modified: DietRB/trunk/bin/dietrb
===================================================================
--- DietRB/trunk/bin/dietrb	2010-10-08 11:05:23 UTC (rev 4725)
+++ DietRB/trunk/bin/dietrb	2010-10-08 11:05:34 UTC (rev 4726)
@@ -32,8 +32,8 @@
 IRB.formatter.filter_from_backtrace << /^#{__FILE__}/
 
 if ARGV.empty?
-  # irb(self, TOPLEVEL_BINDING.dup)
-  IRB::Driver::Socket.new.run
+  irb(self, TOPLEVEL_BINDING.dup)
+  puts "ENDED! and this message should show up on stdout"
 else
   path = ARGV.shift
   context = IRB::Context.new(self, TOPLEVEL_BINDING.dup)

Modified: DietRB/trunk/lib/irb/context.rb
===================================================================
--- DietRB/trunk/lib/irb/context.rb	2010-10-08 11:05:23 UTC (rev 4725)
+++ DietRB/trunk/lib/irb/context.rb	2010-10-08 11:05:34 UTC (rev 4726)
@@ -9,27 +9,26 @@
 module IRB
   class Context
     class << self
-      attr_accessor :current
+      # 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 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
     
     attr_reader :object, :binding, :line, :source, :processors
-    attr_accessor :io, :formatter
+    attr_accessor :io, :formatter, :driver
     
-    def initialize(driver, object, explicit_binding = nil)
-      @driver  = driver
+    def initialize(object, explicit_binding = nil)
       @object  = object
       @binding = explicit_binding || object.instance_eval { binding }
       @line    = 1

Modified: DietRB/trunk/lib/irb/driver/socket.rb
===================================================================
--- DietRB/trunk/lib/irb/driver/socket.rb	2010-10-08 11:05:23 UTC (rev 4725)
+++ DietRB/trunk/lib/irb/driver/socket.rb	2010-10-08 11:05:34 UTC (rev 4726)
@@ -2,34 +2,54 @@
 require 'irb/driver/tty'
 require 'socket'
 
-TOPLEVEL_OBJECT = self
-
 module IRB
   module Driver
+    class SocketTTY < TTY
+      # We don't want to put the real standard output object on the
+      # OutputRedirector target stack.
+      def assign_output_redirector!
+        before  = $stdout
+        $stdout = IRB::Driver::OutputRedirector.new
+        before
+      end
+    end
+    
     class Socket
-      # DEFAULTS = {
-      #   :tty_exit_on_eof => false,
-      #   :term => "\r\0"
-      # }
-      
-      def initialize(host = '127.0.0.1', port = 7829)
-        # @options = DEFAULTS.merge(options)
+      # 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
-          # TODO libedit doesn't use the right input and output!!
-          # IRB.driver = IRB::Driver::Readline.new(connection, connection)
-          IRB.driver = IRB::Driver::TTY.new(connection, connection)
-          context = IRB::Context.new(IRB.driver, TOPLEVEL_OBJECT, TOPLEVEL_BINDING.dup)
-          IRB.driver.run(context)
-          connection.close
+          Thread.new do
+            # assign driver with connection to current thread and start runloop
+            IRB.driver = SocketTTY.new(connection, connection)
+            irb(@object, @binding)
+            connection.close
+          end
         end
       end
     end
   end
+end
+
+def self.irb(object, binding = nil)
+  unless @server
+    @server = IRB::Driver::Socket.new(object, binding)
+    @server.run
+  else
+    super
+  end
 end
\ No newline at end of file

Modified: DietRB/trunk/lib/irb/driver/tty.rb
===================================================================
--- DietRB/trunk/lib/irb/driver/tty.rb	2010-10-08 11:05:23 UTC (rev 4725)
+++ DietRB/trunk/lib/irb/driver/tty.rb	2010-10-08 11:05:34 UTC (rev 4726)
@@ -1,67 +1,132 @@
 module IRB
-  class Context
-    class << self
-      # attr_accessor :current
-      def current
-        Thread.current[:context]
-      end
-      
-      def current=(context)
-        Thread.current[:context] = context
-      end
-      
-      # TODO move into driver
-      def make_current(context)
-        before, self.current = self.current, context
-        yield
-      ensure
-        self.current = before
-      end
-    end
-  end
+  # class Context
+  #   class << self
+  #     # attr_accessor :current
+  #     def current
+  #       Thread.current[:context]
+  #     end
+  #     
+  #     def current=(context)
+  #       Thread.current[:context] = context
+  #     end
+  #     
+  #     # TODO move into driver
+  #     # def make_current(context)
+  #     #   before, self.current = self.current, context
+  #     #   yield
+  #     # ensure
+  #     #   self.current = before
+  #     # end
+  #   end
+  # end
   
   class << self
-    # attr_accessor :driver
-    # def driver
-    #   @driver ||= Driver::TTY.new
-    # end
+    attr_accessor :driver_class
     
     def driver=(driver)
-      Thread.current[:driver] = driver
+      Thread.current[:irb_driver] = driver
     end
     
     def driver
-      Thread.current[:driver]
+      current_thread = Thread.current
+      current_thread[:irb_driver] ||= begin
+        driver = nil
+        if group = current_thread.group
+          group.list.each do |thread|
+            break if driver = thread[:irb_driver]
+          end
+        end
+        p driver
+        driver || driver_class.new
+      end
     end
   end
   
   module Driver
+    class OutputRedirector
+      # The output object for the current thread.
+      def self.target=(output)
+        Thread.current[:irb_stdout_target] = output
+      end
+      
+      # TODO cache, or not to cache?
+      def self.target
+        current_thread = Thread.current
+        if target = current_thread[:irb_stdout_target]
+        elsif group = current_thread.group
+          group.list.each do |thread|
+            break if target = thread[:irb_stdout_target]
+          end
+        end
+        target || $stderr
+      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
+    
     class TTY
+      attr_accessor :current_context
+      
       def initialize(input = $stdin, output = $stdout)
         @input  = input
         @output = output
-        @running = false
+        OutputRedirector.target = output
+        
+        @thread_group = ThreadGroup.new
+        @thread_group.add(Thread.current)
       end
       
-      def context
-        Context.current
-      end
-      
       def readline
-        @output.print(context.prompt)
+        @output.print(current_context.prompt)
         @input.gets
       end
       
+      # TODO make it take the current context instead of storing it
       def consume
         readline
       rescue Interrupt
-        context.clear_buffer
+        current_context.clear_buffer
         ""
       end
       
+      def puts(*args)
+        @output.puts(*args)
+      end
+      
+      def print(*args)
+        @output.print(*args)
+        @output.flush
+      end
+      
       def run(context)
-        with_io do
-          Context.make_current(context) do
+        ensure_output_redirector do
+          make_current(context) do
             while line = consume
               continue = context.process_line(line)
               break unless continue
@@ -70,34 +135,39 @@
         end
       end
       
-      def puts(*args)
-        @output.puts(*args)
+      def make_current(context)
+        context.driver = self
+        before, self.current_context = self.current_context, context
+        yield
+      ensure
+        self.current_context = before
       end
       
-      def print(*args)
-        @output.print(*args)
-        @output.flush
-      end
-      
-      def with_io
-        if @input_before.nil?
-          @input_before, @output_before = $stdin, $stdout
-          $stdin, $stdout = @input, @output
-        end
+      # Ensure that the standard output object is a OutputRedirector. If it's
+      # already a OutputRedirector, do nothing.
+      def ensure_output_redirector
+        before = assign_output_redirector! unless $stdout.is_a?(IRB::Driver::OutputRedirector)
         yield
       ensure
-        $stdin, $stdout = @input_before, @output_before
+        $stdout = before if before
       end
+      
+      def assign_output_redirector!
+        before  = IRB::Driver::OutputRedirector.target = $stdout
+        $stdout = IRB::Driver::OutputRedirector.new
+        before
+      end
     end
   end
 end
 
-IRB.driver = IRB::Driver::TTY.new
+IRB.driver_class = IRB::Driver::TTY
 
 module Kernel
   # Creates a new IRB::Context with the given +object+ and runs it.
   def irb(object, binding = nil)
-    IRB.driver.run(IRB::Context.new(IRB.driver, object, binding))
+    # IRB.driver.run(IRB::Context.new(IRB.driver, object, binding))
+    IRB.driver.run(IRB::Context.new(object, binding))
   end
   
   private :irb
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/macruby-changes/attachments/20101008/ae69e277/attachment-0001.html>


More information about the macruby-changes mailing list