THRIFT-1125 Multiplexing support for the Ruby Library
Client: Ruby
Patch: André Aizim Kelmanson <akelmanson@gmail.com>

This closes #406
diff --git a/lib/rb/lib/thrift.rb b/lib/rb/lib/thrift.rb
index fb9e04a..fadebca 100644
--- a/lib/rb/lib/thrift.rb
+++ b/lib/rb/lib/thrift.rb
@@ -27,6 +27,7 @@
 require 'thrift/exceptions'
 require 'thrift/types'
 require 'thrift/processor'
+require 'thrift/multiplexed_processor'
 require 'thrift/client'
 require 'thrift/struct'
 require 'thrift/union'
@@ -42,6 +43,7 @@
 require 'thrift/protocol/binary_protocol_accelerated'
 require 'thrift/protocol/compact_protocol'
 require 'thrift/protocol/json_protocol'
+require 'thrift/protocol/multiplexed_protocol'
 
 # transport
 require 'thrift/transport/base_transport'
diff --git a/lib/rb/lib/thrift/multiplexed_processor.rb b/lib/rb/lib/thrift/multiplexed_processor.rb
new file mode 100644
index 0000000..c734c04
--- /dev/null
+++ b/lib/rb/lib/thrift/multiplexed_processor.rb
@@ -0,0 +1,76 @@
+# 
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+# 
+#   http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+require 'thrift/protocol/protocol_decorator'
+require 'thrift/protocol/base_protocol'
+
+module Thrift
+  class MultiplexedProcessor
+    def initialize
+      @actual_processors = {}
+    end
+ 
+    def register_processor(service_name, processor)
+      @actual_processors[service_name] = processor
+    end
+ 
+    def process(iprot, oprot)
+      name, type, seqid = iprot.read_message_begin
+      check_type(type)
+      check_separator(name)
+      service_name, method = name.split(':')
+      processor(service_name).process(StoredMessageProtocol.new(iprot, [method, type, seqid]), oprot)
+    end
+
+    protected
+
+    def processor(service_name)
+      if @actual_processors.has_key?(service_name)
+        @actual_processors[service_name]
+      else
+        raise Thrift::Exception.new("Service name not found: #{service_name}. Did you forget to call #{self.class.name}#register_processor?")
+      end
+    end
+
+    def check_type(type)
+      unless [MessageTypes::CALL, MessageTypes::ONEWAY].include?(type)
+        raise Thrift::Exception.new('This should not have happened!?')
+      end
+    end
+
+    def check_separator(name)
+      if name.count(':') < 1
+        raise Thrift::Exception.new("Service name not found in message name: #{name}. Did you forget to use a Thrift::Protocol::MultiplexedProtocol in your client?")
+      end
+    end
+  end
+
+  class StoredMessageProtocol < BaseProtocol
+
+    include ProtocolDecorator
+    
+    def initialize(protocol, message_begin)
+      super(protocol)
+      @message_begin = message_begin
+    end
+
+    def read_message_begin
+      @message_begin
+    end
+  end
+end
diff --git a/lib/rb/lib/thrift/protocol/multiplexed_protocol.rb b/lib/rb/lib/thrift/protocol/multiplexed_protocol.rb
new file mode 100644
index 0000000..13c9d93
--- /dev/null
+++ b/lib/rb/lib/thrift/protocol/multiplexed_protocol.rb
@@ -0,0 +1,40 @@
+# 
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+# 
+#   http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+require 'thrift/protocol/protocol_decorator'
+
+module Thrift
+  class MultiplexedProtocol < BaseProtocol
+
+    include ProtocolDecorator
+
+    def initialize(protocol, service_name)
+      super(protocol)
+      @service_name = service_name
+    end
+
+    def write_message_begin(name, type, seqid)
+      case type
+      when MessageTypes::CALL, MessageTypes::ONEWAY
+        @protocol.write_message_begin("#{@service_name}:#{name}", type, seqid)
+      else
+        @protocol.write_message_begin(name, type, seqid)
+      end 
+    end
+  end
+end
\ No newline at end of file
diff --git a/lib/rb/lib/thrift/protocol/protocol_decorator.rb b/lib/rb/lib/thrift/protocol/protocol_decorator.rb
new file mode 100644
index 0000000..b1e3c15
--- /dev/null
+++ b/lib/rb/lib/thrift/protocol/protocol_decorator.rb
@@ -0,0 +1,194 @@
+# 
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+# 
+#   http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+module Thrift
+  module ProtocolDecorator
+
+    def initialize(protocol)
+      @protocol = protocol
+    end
+
+    def trans
+      @protocol.trans
+    end
+
+    def write_message_begin(name, type, seqid)
+      @protocol.write_message_begin
+    end
+
+    def write_message_end
+      @protocol.write_message_end
+    end
+
+    def write_struct_begin(name)
+      @protocol.write_struct_begin(name)
+    end
+
+    def write_struct_end
+      @protocol.write_struct_end
+    end
+
+    def write_field_begin(name, type, id)
+      @protocol.write_field_begin(name, type, id)
+    end
+
+    def write_field_end
+      @protocol.write_field_end
+    end
+
+    def write_field_stop
+      @protocol.write_field_stop
+    end
+
+    def write_map_begin(ktype, vtype, size)
+      @protocol.write_map_begin(ktype, vtype, size)
+    end
+
+    def write_map_end
+      @protocol.write_map_end
+    end
+
+    def write_list_begin(etype, size)
+      @protocol.write_list_begin(etype, size)
+    end
+
+    def write_list_end
+      @protocol.write_list_end
+    end
+
+    def write_set_begin(etype, size)
+      @protocol.write_set_begin(etype, size)
+    end
+
+    def write_set_end
+      @protocol.write_set_end
+    end
+
+    def write_bool(bool)
+      @protocol.write_bool(bool)
+    end
+
+    def write_byte(byte)
+      @protocol.write_byte(byte)
+    end
+
+    def write_i16(i16)
+      @protocol.write_i16(i16)
+    end
+
+    def write_i32(i32)
+      @protocol.write_i32(i32)
+    end
+
+    def write_i64(i64)
+      @protocol.write_i64(i64)
+    end
+
+    def write_double(dub)
+      @protocol.write_double(dub)
+    end
+
+    def write_string(str)
+      @protocol.write_string(str)
+    end
+
+    def write_binary(buf)
+      @protocol.write_binary(buf)
+    end
+
+    def read_message_begin
+      @protocol.read_message_begin
+    end
+
+    def read_message_end
+      @protocol.read_message_end
+    end
+
+    def read_struct_begin
+      @protocol.read_struct_begin
+    end
+
+    def read_struct_end
+      @protocol.read_struct_end
+    end
+
+    def read_field_begin
+      @protocol.read_field_begin
+    end
+
+    def read_field_end
+      @protocol.read_field_end
+    end
+
+    def read_map_begin
+      @protocol.read_map_begin
+    end
+
+    def read_map_end
+      @protocol.read_map_end
+    end
+
+    def read_list_begin
+      @protocol.read_list_begin
+    end
+
+    def read_list_end
+      @protocol.read_list_end
+    end
+
+    def read_set_begin
+      @protocol.read_set_begin
+    end
+
+    def read_set_end
+      @protocol.read_set_end
+    end
+
+    def read_bool
+      @protocol.read_bool
+    end
+
+    def read_byte
+      @protocol.read_byte
+    end
+
+    def read_i16
+      @protocol.read_i16
+    end
+
+    def read_i32
+      @protocol.read_i32
+    end
+
+    def read_i64
+      @protocol.read_i64
+    end
+
+    def read_double
+      @protocol.read_double
+    end
+
+    def read_string
+      @protocol.read_string
+    end
+
+    def read_binary
+      @protocol.read_binary
+    end
+  end
+end
\ No newline at end of file