<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array"/>
  <modified type="array">
    <modified>
      <diff>@@ -13,66 +13,47 @@ module MiniBot
     end
 
     def read
-      if @messages.first &amp;&amp; message_complete?(@messages.first)
-        to_message(@messages.shift)
-      else
-        read_messages
+      if @buffer.empty?
+        @buffer &lt;&lt; @socket.recvfrom(512).first
+        read
+      elsif @buffer.start_with? &quot;\r\n&quot;
+        @buffer = @buffer[2 .. -1]
         read
-      end
-    end
-
-    def match_code(code, message)
-      if (match = reply?(message)) &amp;&amp; match[1] == code
-        match[1, 2]
       else
-        nil
+        message, buffer = *@buffer.split(/\r\n/, 2)
+
+        if !message.empty? &amp;&amp; buffer
+          @buffer = buffer
+          message
+        else
+          @buffer &lt;&lt; @socket.recvfrom(512).first
+          read
+        end
       end
     end
 
     def write(msg, *replies)
-      @socket.print &quot;#{msg}\r\n&quot;
+      print_to_socket msg
 
       unless replies.empty?
-        index = 0
-        matches, messages = catch :halted do
-          loop do
-            index, message = next_message(index)
-            replies.each do |r|
-              case r
-              when String
-                if match = /:\S+ (#{r}) \S+ :?(.+)/.match(message)
-                  throw :halted, [[ match ], [ message ]]
-                end
-              when Array
-                matched = []
-                messages = []
-                r.each do |ri|
-                  if match = /:\S+ (#{ri}) \S+ :?(.+)/.match(message)
-                    matched &lt;&lt; match
-                    messages &lt;&lt; message
-                    index, message = next_message(index)
-                  elsif !matched.empty?
-                    index -= matched.length
-                    message = matched.first
-                    break
-                  end
-                end
-
-                throw :halted, [matched, messages] unless matched.empty?
-              else
-                raise ArgumentError, &quot;Unknown reply argument type: #{r.inspect}&quot;, caller
-              end
-            end
+        buffer = []
+        matches = nil
+        until matches
+          replies.map { |r| Array === r ? r : [ r ] }.each do |r|
+            break if (matches = match(r))
           end
-        end
 
-        messages.each { |m| delete_message(m) }
-        matches.each { |m| yield m[1, 2] }
-      end
-    end
+          buffer &lt;&lt; read unless matches
+        end
 
-    def extract(regexp)
+        unread *buffer
 
+        if block_given?
+          matches.each { |m| yield *m }
+        else
+          matches
+        end
+      end
     end
 
     def disconnect
@@ -81,22 +62,31 @@ module MiniBot
 
     private
 
-    def delete_message(m)
-      @messages.delete(&quot;#{m}\r&quot;)
+    def match_code(code, message)
+      if (match = reply?(message)) &amp;&amp; match[1] == code
+        match[1, 2]
+      else
+        nil
+      end
     end
 
-    def message_complete?(message)
-      message[-1] == ?\r
-    end
+    def match(codes)
+      catch :halt do 
+        matches = []
 
-    def to_message(raw)
-      raw.chomp
-    end
+        codes.each do |code|
+          msg = read
 
-    def next_message(index)
-      read_messages if !@messages[index] || !message_complete?(@messages[index])
+          if match = match_code(code, msg)
+            matches &lt;&lt; match
+          else
+            unread *matches
+            throw :halt, nil
+          end
+        end
 
-      [index + 1, to_message(@messages[index])]
+        matches
+      end
     end
 
     def reply?(msg)
@@ -106,16 +96,15 @@ module MiniBot
     def initialize(server, port)
       @server = server
       @port = port
-      @messages = []
+      @buffer = &quot;&quot;
     end
 
-    def read_messages
-      buffer = @socket.recvfrom(512).first
-      messages = buffer.split /\n/
-
-      @messages.last &lt;&lt; messages.shift if @messages.last &amp;&amp; @messages.last[-1] != ?\r
+    def print_to_socket(msg)
+      @socket.print &quot;#{msg}\r\n&quot;
+    end
 
-      @messages += messages
+    def unread(*messages)
+      @buffer = messages.join(&quot;\r\n&quot;) &lt;&lt; @buffer
     end
   end
 end</diff>
      <filename>lib/minibot/server.rb</filename>
    </modified>
    <modified>
      <diff>@@ -15,109 +15,43 @@ describe MiniBot::Server do
 
   it &quot;should fetch messages&quot; do
     socket = mock &quot;socket&quot;, :null_object =&gt; true
-    buffer = (&quot;a&quot; * 254) + &quot;\r\n&quot; + (&quot;b&quot; * 254) + &quot;\r\n&quot;
-    socket.stub!(:recvfrom).and_return([buffer, nil])
+    buffer1 = (&quot;a&quot; * 154) + &quot;\r\n&quot; + (&quot;b&quot; * 354) + &quot;\r\n&quot;
+    socket.stub!(:recvfrom).and_return([buffer1, nil])
 
     s = server
 
     s.instance_variable_set &quot;@socket&quot;, socket
-    s.send :read_messages
-    s.instance_variable_get(&quot;@messages&quot;).should == buffer.split(/\n/)
+    s.send(:read).should == ('a' * 154)
+    s.send(:read).should == ('b' * 354)
   end
 
   it &quot;should write messages&quot; do
-    socket = mock &quot;socket&quot;
-    socket.should_receive(:print).with(&quot;how now brown cow\r\n&quot;)
-
     s = server
-    s.instance_variable_set &quot;@socket&quot;, socket
+    s.should_receive(:print_to_socket).with(&quot;how now brown cow&quot;)
     s.write &quot;how now brown cow&quot;
   end
 
+  # TODO: go back to mocking the socket. Tests are hard to understand this way.
   describe &quot;writing with replies&quot; do
-    describe &quot;advancing messages&quot; do
-      it &quot;should not destroy them&quot; do
-        s = server
-        s.instance_variable_set &quot;@messages&quot;, [&quot;aaa\r&quot;, &quot;bbb\r&quot;]
-
-        cursor, msg = s.send :next_message, 0
-        cursor.should == 1
-        msg.should == &quot;aaa&quot;
-
-        cursor, msg = s.send :next_message, cursor
-        cursor.should == 2
-        msg.should == &quot;bbb&quot;
-
-        s.instance_variable_get(&quot;@messages&quot;).should == [&quot;aaa\r&quot;, &quot;bbb\r&quot;]
-      end
-
-      it &quot;should fetch new ones when no more are left&quot; do
-        socket = mock &quot;socket&quot;, :null_object =&gt; true
-        socket.stub!(:recvfrom).and_return([&quot;aaa\r\nbbb\r\n&quot;, nil])
-
-        s = server
-        s.instance_variable_set &quot;@socket&quot;, socket
-        s.instance_variable_set &quot;@messages&quot;, []
-
-        cursor, msg = s.send :next_message, 0
-        msg.should == &quot;aaa&quot;
-      end
-
-      it &quot;should detect incomplete ones and fetch more before returning&quot; do
-        socket = mock &quot;socket&quot;, :null_object =&gt; true
-        socket.stub!(:recvfrom).and_return([&quot;aaa\r\nbbb\r\n&quot;, nil])
-
-        s = server
-        s.instance_variable_set &quot;@socket&quot;, socket
-        s.instance_variable_set &quot;@messages&quot;, [&quot;aa&quot;]
-
-        cursor, msg = s.send :next_message, 0
-        msg.should == &quot;aaaaa&quot;
-      end
-    end
-
     it &quot;should return the right reply&quot; do
-      socket = mock &quot;socket&quot;
-      socket.stub! :print
-
       s = server
-      s.instance_variable_set &quot;@socket&quot;, socket
-      s.instance_variable_set &quot;@messages&quot;, [&quot;aaa\r&quot;, &quot;:my.server.com 432 whatever :A message\r&quot;]
+      s.stub!(:print_to_socket)
+      s.should_receive(:read).and_return(&quot;aaa&quot;, &quot;aaa&quot;, &quot;:my.server.com 432 whatever :A message&quot;)
 
       tester = mock &quot;tester&quot;
       tester.should_receive(:call).with(&quot;432&quot;, &quot;A message&quot;)
       s.write(&quot;duh&quot;, &quot;432&quot;) { |code, reply| tester.call code, reply }
     end
 
-    it &quot;should return the second wanted reply when it's found first&quot; do
-      socket = mock &quot;socket&quot;
-      socket.stub! :print
-
-      s = server
-      s.instance_variable_set &quot;@socket&quot;, socket
-      s.instance_variable_set &quot;@messages&quot;, [
-        &quot;aaa\r&quot;, 
-        &quot;:my.server.com 433 whatever :A message2\r&quot;, 
-        &quot;:my.server.com 432 whatever :A message\r&quot;
-      ]
-
-      tester = mock &quot;tester&quot;
-      tester.should_receive(:call).with(&quot;433&quot;, &quot;A message2&quot;)
-      s.write(&quot;duh&quot;, &quot;433&quot;) { |code, reply| tester.call code, reply }
-    end
-
     it &quot;should return all replies when it's passed an array&quot; do
-      socket = mock &quot;socket&quot;, :null_object =&gt; true
-      socket.stub! :print
-      socket.stub!(:recvfrom).and_return(&quot;1\r\n&quot;)
-
       s = server
-      s.instance_variable_set &quot;@socket&quot;, socket
-      s.instance_variable_set &quot;@messages&quot;, [
-        &quot;aaa\r&quot;, 
-        &quot;:my.server.com 433 whatever :A message2\r&quot;, 
-        &quot;:my.server.com 432 whatever :A message\r&quot;
-      ]
+      s.stub!(:print_to_socket)
+      s.should_receive(:read).and_return(
+        &quot;aaa&quot;, 
+        &quot;aaa&quot;, 
+        &quot;:my.server.com 433 whatever :A message2&quot;, 
+        &quot;:my.server.com 432 whatever :A message&quot;
+      )
 
       tester = mock &quot;tester&quot;
       tester.should_receive(:call).with(&quot;433&quot;, &quot;A message2&quot;)
@@ -131,33 +65,30 @@ describe MiniBot::Server do
 
       s = server
       s.instance_variable_set &quot;@socket&quot;, socket
-      s.instance_variable_set &quot;@messages&quot;, [
-        &quot;aaa\r&quot;, 
-        &quot;:my.server.com 433 whatever :A message2\r&quot;, 
-        &quot;bbb\r&quot;,
-        &quot;:my.server.com 432 whatever :A message\r&quot;
-      ]
+      s.instance_variable_set &quot;@buffer&quot;, 
+        &quot;aaa\r\n&quot; &lt;&lt; 
+        &quot;:my.server.com 433 whatever :A message2\r\n&quot; &lt;&lt; 
+        &quot;bbb\r\n&quot; &lt;&lt;
+        &quot;:my.server.com 432 whatever :A message\r\n&quot;
 
       tester = mock &quot;tester&quot;
       tester.should_receive(:call).with(&quot;433&quot;, &quot;A message2&quot;)
       s.write(&quot;duh&quot;, [&quot;433&quot;, &quot;432&quot;], &quot;433&quot;) { |code, reply| tester.call code, reply }
     end
 
-    it &quot;should delete replies from message buffer&quot; do
-      socket = mock &quot;socket&quot;, :null_object =&gt; true
-      socket.stub! :print
-      socket.stub!(:recvfrom).and_return(&quot;1\r\n&quot;)
-
+    it &quot;should put back unused messages&quot; do
       s = server
-      s.instance_variable_set &quot;@socket&quot;, socket
-      s.instance_variable_set &quot;@messages&quot;, [
-        &quot;aaa\r&quot;, 
-        &quot;:my.server.com 433 whatever :A message2\r&quot;, 
-        &quot;:my.server.com 432 whatever :A message\r&quot;
-      ]
-
-      s.write(&quot;duh&quot;, [&quot;433&quot;, &quot;432&quot;]) { |code, reply| nil }
-      s.instance_variable_get(&quot;@messages&quot;).should == [&quot;aaa\r&quot;, &quot;1\r&quot;]
+      s.stub!(:print_to_socket)
+      s.should_receive(:read).and_return(
+        &quot;aaa&quot;,
+        &quot;aaa&quot;, 
+        &quot;:my.server.com 433 whatever :A message2&quot;, 
+        &quot;:my.server.com 432 whatever :A message&quot;
+      )
+      s.should_receive(:unread).with(no_args)
+      s.should_receive(:unread).with(&quot;aaa&quot;)
+
+      s.write(&quot;duh&quot;, [&quot;433&quot;, &quot;432&quot;])
     end
   end
 
@@ -171,7 +102,19 @@ describe MiniBot::Server do
       s.instance_variable_set &quot;@socket&quot;, socket
 
       s.read.should == &quot;a&quot; * 254
-      s.instance_variable_get(&quot;@messages&quot;).should == [&quot;b&quot; * 256]
+      s.instance_variable_get(&quot;@buffer&quot;).should == (&quot;b&quot; * 256)
+    end
+
+    it &quot;should skip over empty messages&quot; do
+      socket = mock &quot;socket&quot;, :null_object =&gt; true
+      buffer = &quot;\r\n&quot; &lt;&lt; (&quot;a&quot; * 254) &lt;&lt; &quot;\r\n&quot; &lt;&lt; (&quot;b&quot; * 256) &lt;&lt; &quot;\r\n&quot;
+      socket.should_receive(:recvfrom).and_return([buffer, nil])
+
+      s = server
+      s.instance_variable_set &quot;@socket&quot;, socket
+
+      s.read.should == &quot;a&quot; * 254
+      s.instance_variable_get(&quot;@buffer&quot;).should == (&quot;b&quot; * 256) &lt;&lt; &quot;\r\n&quot;
     end
 
     it &quot;should join incomplete messages&quot; do
@@ -181,17 +124,17 @@ describe MiniBot::Server do
 
       s = server
       s.instance_variable_set &quot;@socket&quot;, socket
-      s.instance_variable_set &quot;@messages&quot;, [buffer]
+      s.instance_variable_set &quot;@buffer&quot;, buffer
 
       s.read.should == &quot;b&quot; * 260
     end
 
-    it &quot;should return a single complete messages&quot; do
+    it &quot;should return a single complete message&quot; do
       s = server
-      s.instance_variable_set &quot;@messages&quot;, [(&quot;b&quot; * 256) + &quot;\r&quot;]
+      s.instance_variable_set &quot;@buffer&quot;, (&quot;b&quot; * 256) + &quot;\r\n&quot;
 
       s.read.should == &quot;b&quot; * 256
-      s.instance_variable_get(&quot;@messages&quot;).should == []
+      s.instance_variable_get(&quot;@buffer&quot;).should == &quot;&quot;
     end
   end
 </diff>
      <filename>spec/server_spec.rb</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>4d6833d47b8ae7933723c03a2dd018fca478967e</id>
    </parent>
  </parents>
  <author>
    <name>david</name>
    <email>dgleal@gmail.com</email>
  </author>
  <url>http://github.com/david/minibot/commit/2e3abc52b3efa99ee53b8c1bd925fafb8581752c</url>
  <id>2e3abc52b3efa99ee53b8c1bd925fafb8581752c</id>
  <committed-date>2008-07-10T04:23:25-07:00</committed-date>
  <authored-date>2008-07-10T04:23:25-07:00</authored-date>
  <message>Fixed entering an infinite loop when the buffer starts with \r\n</message>
  <tree>9cc26f52f1d0df6b95d263fecb7b167a510a9523</tree>
  <committer>
    <name>david</name>
    <email>dgleal@gmail.com</email>
  </committer>
</commit>
