You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by rh...@apache.org on 2008/11/13 03:45:19 UTC

svn commit: r713616 [1/3] - in /incubator/qpid/trunk/qpid/ruby: ./ examples/ lib/ lib/qpid/ qpid/ tests/ tests_0-8/

Author: rhs
Date: Wed Nov 12 18:45:18 2008
New Revision: 713616

URL: http://svn.apache.org/viewvc?rev=713616&view=rev
Log:
merged 0-10 ruby client from QPID-1443 into existing ruby client

Added:
    incubator/qpid/trunk/qpid/ruby/Rakefile
    incubator/qpid/trunk/qpid/ruby/examples/
    incubator/qpid/trunk/qpid/ruby/examples/hello-world.rb
    incubator/qpid/trunk/qpid/ruby/examples/qmf-libvirt.rb
    incubator/qpid/trunk/qpid/ruby/lib/
    incubator/qpid/trunk/qpid/ruby/lib/qpid/
    incubator/qpid/trunk/qpid/ruby/lib/qpid.rb
    incubator/qpid/trunk/qpid/ruby/lib/qpid/assembler.rb
    incubator/qpid/trunk/qpid/ruby/lib/qpid/client.rb
    incubator/qpid/trunk/qpid/ruby/lib/qpid/codec.rb
    incubator/qpid/trunk/qpid/ruby/lib/qpid/codec08.rb
    incubator/qpid/trunk/qpid/ruby/lib/qpid/connection.rb
    incubator/qpid/trunk/qpid/ruby/lib/qpid/connection08.rb
    incubator/qpid/trunk/qpid/ruby/lib/qpid/datatypes.rb
    incubator/qpid/trunk/qpid/ruby/lib/qpid/delegates.rb
    incubator/qpid/trunk/qpid/ruby/lib/qpid/fields.rb
    incubator/qpid/trunk/qpid/ruby/lib/qpid/framer.rb
    incubator/qpid/trunk/qpid/ruby/lib/qpid/invoker.rb
    incubator/qpid/trunk/qpid/ruby/lib/qpid/packer.rb
    incubator/qpid/trunk/qpid/ruby/lib/qpid/peer.rb
    incubator/qpid/trunk/qpid/ruby/lib/qpid/qmf.rb
    incubator/qpid/trunk/qpid/ruby/lib/qpid/queue.rb
    incubator/qpid/trunk/qpid/ruby/lib/qpid/session.rb
    incubator/qpid/trunk/qpid/ruby/lib/qpid/spec.rb
    incubator/qpid/trunk/qpid/ruby/lib/qpid/spec010.rb
    incubator/qpid/trunk/qpid/ruby/lib/qpid/spec08.rb
    incubator/qpid/trunk/qpid/ruby/lib/qpid/test.rb
    incubator/qpid/trunk/qpid/ruby/lib/qpid/traverse.rb
    incubator/qpid/trunk/qpid/ruby/lib/qpid/util.rb
    incubator/qpid/trunk/qpid/ruby/tests/
    incubator/qpid/trunk/qpid/ruby/tests/assembler.rb
    incubator/qpid/trunk/qpid/ruby/tests/codec010.rb
    incubator/qpid/trunk/qpid/ruby/tests/connection.rb
    incubator/qpid/trunk/qpid/ruby/tests/datatypes.rb
    incubator/qpid/trunk/qpid/ruby/tests/framer.rb
    incubator/qpid/trunk/qpid/ruby/tests/qmf.rb
    incubator/qpid/trunk/qpid/ruby/tests/queue.rb
    incubator/qpid/trunk/qpid/ruby/tests/spec010.rb
    incubator/qpid/trunk/qpid/ruby/tests/util.rb
    incubator/qpid/trunk/qpid/ruby/tests_0-8/
      - copied from r712864, incubator/qpid/trunk/qpid/ruby/tests/
    incubator/qpid/trunk/qpid/ruby/tests_0-8/basic.rb
      - copied, changed from r713172, incubator/qpid/trunk/qpid/ruby/tests/basic.rb
    incubator/qpid/trunk/qpid/ruby/tests_0-8/channel.rb
      - copied, changed from r713172, incubator/qpid/trunk/qpid/ruby/tests/channel.rb
Removed:
    incubator/qpid/trunk/qpid/ruby/qpid/
    incubator/qpid/trunk/qpid/ruby/qpid.rb
    incubator/qpid/trunk/qpid/ruby/run-tests

Added: incubator/qpid/trunk/qpid/ruby/Rakefile
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/ruby/Rakefile?rev=713616&view=auto
==============================================================================
--- incubator/qpid/trunk/qpid/ruby/Rakefile (added)
+++ incubator/qpid/trunk/qpid/ruby/Rakefile Wed Nov 12 18:45:18 2008
@@ -0,0 +1,75 @@
+# Rakefile for ruby-rpm -*- ruby -*-
+require 'rake/clean'
+require 'rake/testtask'
+require 'rake/gempackagetask'
+require 'pathname'
+
+PKG_NAME='ruby-qpid'
+PKG_VERSION='0.10.2'
+GEM_NAME='qpid'
+
+AMQP_SPEC_SRC=Pathname.new("../specs").realpath
+AMQP_SPEC_PATH=["/usr/share/amqp", AMQP_SPEC_SRC].join(File::PATH_SEPARATOR)
+AMQP_SPEC_FILES = FileList["amqp.0-10-qpid-errata.xml"]
+
+ENV["AMQP_SPEC_PATH"] = AMQP_SPEC_PATH unless ENV["AMQP_SPEC_PATH"]
+
+#
+# Additional files for clean/clobber
+#
+
+CLEAN.include [ "**/*~", "lib/*/spec_cache" ]
+
+Rake::TestTask.new(:test) do |t|
+  t.test_files = FileList['tests/*.rb'].exclude("tests/util.rb")
+  t.libs = [ 'lib' ]
+end
+
+Rake::TestTask.new(:"test_0-8") do |t|
+  t.test_files = FileList["tests_0-8/*.rb"]
+  t.libs = [ 'lib' ]
+end
+
+desc "Create cached versions of the AMQP specs"
+task :spec_cache do |t|
+  AMQP_SPEC_FILES.each do |f|
+    pid = fork do
+      $: << "lib"
+      require 'qpid'
+      Qpid::Spec010::load(f)
+      puts "Cached #{f}"
+    end
+    Process.wait(pid)
+  end
+end
+
+#
+# Packaging
+#
+
+PKG_FILES = FileList[
+  "DISCLAIMER", "LICENSE.txt", "NOTICE.txt",
+  "Rakefile", "RELEASE_NOTES",
+  "lib/**/*.rb", "lib/*/spec_cache/*.rb*", "tests/**/*", "examples/**"
+]
+
+DIST_FILES = FileList[
+  "pkg/*.tgz", "pkg/*.gem"
+]
+
+SPEC = Gem::Specification.new do |s|
+  s.name = GEM_NAME
+  s.version = PKG_VERSION
+  s.email = "qpid-dev@incubator.apache.org"
+  s.homepage = "http://cwiki.apache.org/qpid/"
+  s.summary = "Ruby client for Qpid"
+  s.files = PKG_FILES
+  s.required_ruby_version = '>= 1.8.1'
+  s.description = "Ruby client for Qpid"
+end
+
+Rake::GemPackageTask.new(SPEC) do |pkg|
+  task pkg.package_dir => [ :spec_cache ]
+  pkg.need_tar = true
+  pkg.need_zip = true
+end

Added: incubator/qpid/trunk/qpid/ruby/examples/hello-world.rb
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/ruby/examples/hello-world.rb?rev=713616&view=auto
==============================================================================
--- incubator/qpid/trunk/qpid/ruby/examples/hello-world.rb (added)
+++ incubator/qpid/trunk/qpid/ruby/examples/hello-world.rb Wed Nov 12 18:45:18 2008
@@ -0,0 +1,53 @@
+#!/usr/bin/ruby
+#
+# 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 "rubygems"
+require "qpid"
+require "socket"
+
+conn = Qpid::Connection.new(TCPSocket.new("localhost", 5672))
+conn.start(10)
+
+ssn = conn.session("test")
+
+# create a queue
+ssn.queue_declare("test-queue")
+
+# publish a message
+dp = ssn.delivery_properties(:routing_key => "test-queue")
+mp = ssn.message_properties(:content_type => "text/plain")
+msg = Qpid::Message.new(dp, mp, "Hello World!")
+ssn.message_transfer(:message => msg)
+
+# subscribe to a queue
+ssn.message_subscribe(:destination => "messages", :queue => "test-queue",
+                      :accept_mode => ssn.message_accept_mode.none)
+incoming = ssn.incoming("messages")
+
+# start incoming message flow
+incoming.start()
+
+# grab a message from the queue
+p incoming.get(10)
+
+# cancel the subscription and close the session and connection
+ssn.message_cancel(:destination => "messages")
+ssn.close()
+conn.close()

Added: incubator/qpid/trunk/qpid/ruby/examples/qmf-libvirt.rb
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/ruby/examples/qmf-libvirt.rb?rev=713616&view=auto
==============================================================================
--- incubator/qpid/trunk/qpid/ruby/examples/qmf-libvirt.rb (added)
+++ incubator/qpid/trunk/qpid/ruby/examples/qmf-libvirt.rb Wed Nov 12 18:45:18 2008
@@ -0,0 +1,81 @@
+#!/usr/bin/ruby
+#
+# 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 "rubygems"
+require "qpid"
+
+s = Qpid::Qmf::Session.new()
+b = s.add_broker("amqp://localhost:5672")
+
+while true:
+    nodes = s.objects(:class => "node")
+  nodes.each do |node|
+    puts "node: #{node.hostname}"
+    for (key, val) in node.properties
+      puts "  property: #{key}, #{val}"
+    end
+
+    # Find any domains that on the current node.
+    domains = s.objects(:class => "domain", 'node' => node.object_id)
+    domains.each do |domain|
+      r = domain.getXMLDesc()
+      puts "status: #{r.status}"
+      if r.status == 0
+        puts "xml description: #{r.description}"
+        puts "length: #{r.description.length}"
+      end
+
+      puts "  domain: #{domain.name}, state: #{domain.state}, id: #{domain.id}"
+      for (key, val) in domain.properties
+        puts "    property: #{key}, #{val}"
+      end
+    end
+
+    pools = s.objects(:class => "pool", 'node' => node.object_id)
+    pools.each do |pool|
+      puts "  pool: #{pool.name}"
+      for (key, val) in pool.properties
+        puts "    property: #{key}, #{val}"
+      end
+
+      r = pool.getXMLDesc()
+      puts "status: #{r.status}"
+      puts "text: #{r.text}"
+      if r.status == 0
+        puts "xml description: #{r.description}"
+        puts "length: #{r.description.length}"
+      end
+
+      # Find volumes that are part of the pool.
+      volumes = s.objects(:class => "volume", 'pool' => pool.object_id)
+      volumes.each do |volume|
+        puts "    volume: #{volume.name}"
+        for (key, val) in volume.properties
+          puts "      property: #{key}, #{val}"
+        end
+      end
+    end
+
+  end
+
+  puts '----------------------------'
+  sleep(5)
+
+end

Added: incubator/qpid/trunk/qpid/ruby/lib/qpid.rb
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/ruby/lib/qpid.rb?rev=713616&view=auto
==============================================================================
--- incubator/qpid/trunk/qpid/ruby/lib/qpid.rb (added)
+++ incubator/qpid/trunk/qpid/ruby/lib/qpid.rb Wed Nov 12 18:45:18 2008
@@ -0,0 +1,41 @@
+#
+# 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 Qpid
+  def self.logger
+    @logger ||= {}
+    @logger
+  end
+end
+
+require "qpid/util"
+require "qpid/queue"
+require "qpid/packer"
+require "qpid/framer"
+require "qpid/codec"
+require 'qpid/datatypes'
+require 'qpid/spec010'
+require 'qpid/delegates'
+require 'qpid/invoker'
+require "qpid/assembler"
+require 'qpid/session'
+require "qpid/connection"
+require "qpid/spec"
+require 'qpid/queue'
+require 'qpid/qmf'

Added: incubator/qpid/trunk/qpid/ruby/lib/qpid/assembler.rb
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/ruby/lib/qpid/assembler.rb?rev=713616&view=auto
==============================================================================
--- incubator/qpid/trunk/qpid/ruby/lib/qpid/assembler.rb (added)
+++ incubator/qpid/trunk/qpid/ruby/lib/qpid/assembler.rb Wed Nov 12 18:45:18 2008
@@ -0,0 +1,148 @@
+#
+# 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 Qpid
+
+  class << self
+    attr_accessor :asm_logger
+  end
+
+  class Segment
+
+    attr_reader :type, :payload, :track, :channel
+    attr_accessor :id, :offset
+
+    def initialize(first, last, type, track, channel, payload)
+      @id = nil
+      @offset = nil
+      @first = first
+      @last = last
+      @type = type
+      @track = track
+      @channel = channel
+      @payload = payload
+    end
+
+    def first_segment? ; @first ; end
+
+    def last_segment? ; @last ; end
+
+    def decode(spec)
+      segs = spec[:segment_type]
+      choice = segs.enum.choices[type]
+      return method("decode_#{choice.name}").call(spec)
+    end
+
+    def decode_control(spec)
+      sc = StringCodec.new(spec, payload)
+      return sc.read_control()
+    end
+
+    def decode_command(spec)
+      sc = StringCodec.new(spec, payload)
+      hdr, cmd = sc.read_command()
+      cmd.id = id
+      return hdr, cmd
+    end
+
+    def decode_header(spec)
+      sc = StringCodec.new(spec, payload)
+      values = []
+      until sc.encoded.empty?
+        values << sc.read_struct32()
+      end
+      return values
+    end
+
+    def decode_body(spec)
+      payload
+    end
+
+    def append(frame)
+      @payload += frame.payload
+    end
+
+    def to_s
+      f = first_segment? ? 'F' : '.'
+      l = last_segment? ? 'L' : '.'
+      return "%s%s %s %s %s %s" % [f, l, @type,
+                                   @track, @channel, @payload.inspect]
+    end
+
+  end
+
+  class Assembler < Framer
+
+    def logger; Qpid::asm_logger; end
+
+    def initialize(sock, max_payload = Frame::MAX_PAYLOAD)
+      super(sock)
+      @max_payload = max_payload
+      @fragments = {}
+    end
+
+    def read_segment
+      loop do
+        frame = read_frame
+        key = [frame.channel, frame.track]
+        seg = @fragments[key]
+        unless seg
+          seg = Segment.new(frame.first_segment?,
+                            frame.last_segment?,
+                            frame.type, frame.track,
+                            frame.channel, "")
+          @fragments[key] = seg
+        end
+
+        seg.append(frame)
+
+        if frame.last_frame?
+          @fragments.delete(key)
+          logger.debug("RECV #{seg}") if logger
+          return seg
+        end
+      end
+    end
+
+    def write_segment(segment)
+      remaining = segment.payload
+
+      first = true
+      while first or remaining
+        payload = remaining[0, @max_payload]
+        remaining = remaining[@max_payload, remaining.size]
+
+        flags = 0
+
+        flags |= FIRST_FRM if first
+        flags |= LAST_FRM  unless remaining
+        flags |= FIRST_SEG if segment.first_segment?
+        flags |= LAST_SEG  if segment.last_segment?
+
+        frame = Frame.new(flags, segment.type, segment.track,
+                          segment.channel, payload)
+        write_frame(frame)
+
+        first = false
+      end
+
+      logger.debug("SENT #{segment}") if logger
+    end
+  end
+end

Added: incubator/qpid/trunk/qpid/ruby/lib/qpid/client.rb
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/ruby/lib/qpid/client.rb?rev=713616&view=auto
==============================================================================
--- incubator/qpid/trunk/qpid/ruby/lib/qpid/client.rb (added)
+++ incubator/qpid/trunk/qpid/ruby/lib/qpid/client.rb Wed Nov 12 18:45:18 2008
@@ -0,0 +1,136 @@
+#
+# 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 "thread"
+require "qpid/peer"
+require "qpid/queue"
+
+module Qpid08
+
+  class Client
+    def initialize(host, port, spec, vhost = "/")
+      @host = host
+      @port = port
+      @spec = spec
+      @vhost = vhost
+
+      @mechanism = nil
+      @response = nil
+      @locale = nil
+
+      @queues = {}
+      @mutex = Mutex.new()
+
+      @closed = false
+      @code = nil
+      @started = ConditionVariable.new()
+
+      @conn = Connection.new(@host, @port, @spec)
+      @peer = Peer.new(@conn, ClientDelegate.new(self))
+    end
+
+    attr_reader :mechanism, :response, :locale
+
+    def closed?; @closed end
+    def closed=(value); @closed = value end
+    def code; @code end
+
+    def wait()
+      @mutex.synchronize do
+        @started.wait(@mutex)
+      end
+      raise EOFError.new() if closed?
+    end
+
+    def signal_start()
+      @started.broadcast()
+    end
+
+    def queue(key)
+      @mutex.synchronize do
+        q = @queues[key]
+        if q.nil?
+          q = Queue.new()
+          @queues[key] = q
+        end
+        return q
+      end
+    end
+
+    def start(response, mechanism="AMQPLAIN", locale="en_US")
+      @response = response
+      @mechanism = mechanism
+      @locale = locale
+
+      @conn.connect()
+      @conn.init()
+      @peer.start()
+      wait()
+      channel(0).connection_open(@vhost)
+    end
+
+    def channel(id)
+      return @peer.channel(id)
+    end
+
+    def close(msg = nil)
+      @closed = true
+      @code = msg
+      @peer.close()
+    end
+  end
+
+  class ClientDelegate
+
+    include Delegate
+
+    def initialize(client)
+      @client = client
+    end
+
+    def connection_start(ch, msg)
+      ch.connection_start_ok(:mechanism => @client.mechanism,
+                             :response => @client.response,
+                             :locale => @client.locale)
+    end
+
+    def connection_tune(ch, msg)
+      ch.connection_tune_ok(*msg.fields)
+      @client.signal_start()
+    end
+
+    def connection_close(ch, msg)
+      puts "CONNECTION CLOSED: #{msg.args.join(", ")}"
+      @client.close(msg)
+    end
+
+    def channel_close(ch, msg)
+      puts "CHANNEL[#{ch.id}] CLOSED: #{msg.args.join(", ")}"
+      ch.channel_close_ok()
+      ch.close()
+    end
+
+    def basic_deliver(ch, msg)
+      queue = @client.queue(msg.consumer_tag)
+      queue << msg
+    end
+
+  end
+
+end

Added: incubator/qpid/trunk/qpid/ruby/lib/qpid/codec.rb
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/ruby/lib/qpid/codec.rb?rev=713616&view=auto
==============================================================================
--- incubator/qpid/trunk/qpid/ruby/lib/qpid/codec.rb (added)
+++ incubator/qpid/trunk/qpid/ruby/lib/qpid/codec.rb Wed Nov 12 18:45:18 2008
@@ -0,0 +1,455 @@
+#
+# 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 'qpid/packer.rb'
+require 'iconv'
+
+module Qpid
+
+  class Codec
+
+    include Qpid::Packer
+
+    def initialize(spec = "")
+      @spec = spec
+    end
+
+    def write_void(v)
+      unless v.nil?
+        raise Exception.new("void not nil: #{v}")
+      end
+    end
+
+    def read_void
+      return nil
+    end
+
+    def write_bit(b)
+      unless b
+        raise Exception.new("bit is nil: #{b}")
+      end
+    end
+
+    def read_bit
+      return true
+    end
+
+    def read_uint8
+      return unpack("C", 1)
+    end
+
+    def write_uint8(n)
+      return pack("C", n)
+    end
+
+    def read_int8
+      return unpack("c", 1)
+    end
+
+    def write_int8(n)
+      pack("c", n)
+    end
+
+    def read_char
+      return unpack("c", 1)
+    end
+
+    def write_char(c)
+      pack("c")
+    end
+
+    def read_boolean
+      return read_uint8 != 0
+    end
+
+    def write_boolean(b)
+      n = 0
+      n = 1 if b != 0
+      write_uint8(n)
+    end
+
+    def read_uint16
+      return unpack("n", 2)
+    end
+
+    def write_uint16(n)
+      pack("n", n)
+    end
+
+    def read_int16
+      # XXX: holy moly.. pack/unpack doesn't have signed network byte order.  Crazy hackery.
+      val = unpack("n", 2)
+      val -= 2 ** 16  if val >= 2 ** 15
+      return val
+    end
+
+    def write_int16(n)
+      # XXX: Magically this one works even though it's not signed.
+      pack("n", n)
+    end
+
+    def read_uint32
+      return unpack("N", 4)
+    end
+
+    def write_uint32(n)
+      pack("N", n)
+    end
+
+    def read_int32
+      # Again no pack/unpack for signed int
+      return unpack("N", 4)
+    end
+
+    def write_int32(n)
+      # FIXME
+      pack("N", n)
+    end
+
+    def read_float
+      return unpack("g", 4)
+    end
+
+    def write_float(n)
+      pack("g", n)
+    end
+
+    def read_sequence_no
+      return read_uint32.to_serial
+    end
+
+    def write_sequence_no(n)
+      write_uint32(n.value)
+    end
+
+    def encode_64bit(num, signed = false)
+      b = []
+
+      if num < 0 && signed
+        num += 2 ** 64
+      end
+
+      (0..7).each do |c|
+        d = 7 - c
+        b[c] = (num & (0xff << d * 8)) >> d * 8
+      end
+      pack('C8', *b)
+    end
+
+
+    def decode_64bit(signed = false)
+      # Silly ruby pack/unpack does not implement 64 bit network byte order
+      # encode/decode.
+      a = unpack('C8', 8)
+      num = 0
+      (0..7).each do |c|
+        d = 7 - c
+        num |= a[c] << 8 * d
+      end
+
+      if signed && num >= 2 ** 63
+        num -= 2 ** 64
+      end
+      return num
+    end
+
+    def read_uint64
+      return decode_64bit
+    end
+
+    def write_uint64(n)
+      encode_64bit(n)
+    end
+
+    def read_int64
+      return decode_64bit(signed = true)
+    end
+
+    def write_int64(n)
+      encode_64bit(n, signed = true)
+    end
+
+    def read_datetime
+      return read_uint64
+    end
+
+    def write_datetime(n)
+      write_uint64(n)
+    end
+
+    def read_double
+      return unpack("G", 8)
+    end
+
+    def write_double(n)
+      pack("G", n)
+    end
+
+    def read_vbin8
+      # XXX
+      return read(read_uint8)
+    end
+
+    def write_vbin8(b)
+      # XXX
+      write_uint8(b.length)
+      write(b)
+    end
+
+    def read_str8
+      # FIXME: Check iconv.. I think this will throw if there are odd characters.
+      return Iconv.conv("ASCII", "UTF-8", read_vbin8)
+    end
+
+    def write_str8(s)
+      write_vbin8(Iconv.conv("UTF-8", "ASCII", s))
+    end
+
+    def read_str16
+      return Iconv.conv("ASCII", "UTF-8", read_vbin16)
+    end
+
+    def write_str16(s)
+      write_vbin16(Iconv.conv("UTF-8", "ASCII", s))
+    end
+
+    def read_vbin16
+      # XXX: Using read method?
+      return read(read_uint16)
+    end
+
+    def write_vbin16(b)
+      write_uint16(b.length)
+      write(b)
+    end
+
+    def read_sequence_set
+      # FIXME: Need datatypes
+      result = RangedSet.new
+      size = read_uint16
+      nranges = size / 8
+      nranges.times do |i|
+        lower = read_sequence_no
+        upper = read_sequence_no
+        result.add(lower, upper)
+      end
+      return result
+    end
+
+    def write_sequence_set(ss)
+      size = 8 * ss.ranges.length
+      write_uint16(size)
+      ss.ranges.each do |range|
+        write_sequence_no(range.lower)
+        write_sequence_no(range.upper)
+      end
+    end
+
+    def read_vbin32
+      return read(read_uint32)
+    end
+
+    def write_vbin32(b)
+      write_uint32(b.length)
+      write(b)
+    end
+
+    def write_map(m)
+      sc = StringCodec.new(@spec)
+      unless m.nil?
+        sc.write_uint32(m.size)
+        m.each do |k, v|
+          unless type = @spec.encoding(v.class)
+            raise Exception.new("no encoding for: #{v.class}")
+          end
+          sc.write_str8(k)
+          sc.write_uint8(type.code)
+          type.encode(sc, v)
+        end
+      end
+      write_vbin32(sc.encoded)
+    end
+
+    def read_map
+      sc = StringCodec.new(@spec, read_vbin32)
+      return nil unless sc.encoded
+      count = sc.read_uint32
+      result = nil
+      if count
+        result = {}
+        until sc.encoded.empty?
+          k = sc.read_str8
+          code = sc.read_uint8
+          type = @spec.types[code]
+          v = type.decode(sc)
+          result[k] = v
+        end
+      end
+      return result
+    end
+
+    def write_array(a)
+      sc = StringCodec.new(@spec)
+      unless a.nil?
+        if a.length > 0
+          type = @spec.encoding(a[0].class)
+        else
+          type = @spec.encoding(nil.class)
+        end
+        sc.write_uint8(type.code)
+        sc.write_uint32(a.size)
+        a.each { |o| type.encode(sc, o) }
+      end
+      write_vbin32(sc.encoded)
+    end
+
+    def read_array
+      sc = StringCodec.new(@spec, read_vbin32)
+      return nil if not sc.encoded
+      type = @spec.types[sc.read_uint8]
+      count = sc.read_uint32
+      result = nil
+      if count
+        result = []
+        count.times { |i| result << (type.decode(sc)) }
+      end
+      return result
+    end
+
+    def write_list(l)
+      sc = StringCodec.new(@spec)
+      unless l.nil?
+        sc.write_uint32(l.length)
+        l.each do |o|
+          type = @spec.encoding(o.class)
+          sc.write_uint8(type.code)
+          type.encode(sc, o)
+        end
+      end
+      write_vbin32(sc.encoded)
+    end
+
+    def read_list
+      sc = StringCodec.new(@spec, read_vbin32)
+      return nil if not sc.encoded
+      count = sc.read_uint32
+      result = nil
+      if count
+        result = []
+        count.times do |i|
+          type = @spec.types[sc.read_uint8]
+          result << type.decode(sc)
+        end
+      end
+      return result
+    end
+
+    def read_struct32
+      size = read_uint32
+      code = read_uint16
+      type = @spec.structs[code]
+      # XXX: BLEH!
+      fields = type.decode_fields(self)
+      return Qpid::struct(type, fields)
+    end
+
+    def write_struct32(value)
+      type = value.type
+      sc = StringCodec.new(@spec)
+      sc.write_uint16(type.code)
+      type.encode_fields(sc, value)
+      write_vbin32(sc.encoded)
+    end
+
+    def read_control
+      cntrl = @spec.controls[read_uint16]
+      return Qpid::struct(cntrl, cntrl.decode_fields(self))
+    end
+
+    def write_control(ctrl)
+      type = ctrl.type
+      write_uint16(type.code)
+      type.encode_fields(self, ctrl)
+    end
+
+    def read_command
+      type = @spec.commands[read_uint16]
+      hdr = @spec[:header].decode(self)
+      cmd = Qpid::struct(type, type.decode_fields(self))
+      return hdr, cmd
+    end
+
+    def write_command(hdr, cmd)
+      type = cmd.type
+      write_uint16(type.code)
+      hdr.type.encode(self, hdr)
+      type.encode_fields(self, cmd)
+    end
+
+    def read_size(width)
+      if width > 0
+        return send(:"read_uint#{width * 8}")
+      end
+    end
+
+    def write_size(width, n)
+      if width > 0
+        send(:"write_uint#{width * 8}", n)
+      end
+    end
+
+    def read_uuid
+      return unpack("A16", 16)
+    end
+
+    def write_uuid(s)
+      pack("A16", s)
+    end
+
+    def read_bin128
+      return unpack("A16", 16)
+    end
+
+    def write_bin128(b)
+      pack("A16", b)
+    end
+
+  end
+
+  class StringCodec < Codec
+
+    def initialize(spec, encoded = "")
+      @spec = spec
+      @encoded = encoded
+    end
+
+    attr_reader :encoded
+
+    def write(s)
+      @encoded += s
+    end
+
+    def read(n)
+      return "" if n.nil?
+      result = @encoded[0...n]
+      @encoded = @encoded[n...@encoded.size] || ""
+      return result
+    end
+  end
+end

Added: incubator/qpid/trunk/qpid/ruby/lib/qpid/codec08.rb
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/ruby/lib/qpid/codec08.rb?rev=713616&view=auto
==============================================================================
--- incubator/qpid/trunk/qpid/ruby/lib/qpid/codec08.rb (added)
+++ incubator/qpid/trunk/qpid/ruby/lib/qpid/codec08.rb Wed Nov 12 18:45:18 2008
@@ -0,0 +1,265 @@
+#
+# 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 Qpid08
+  # is there a better way to do this?
+  class StringWriter
+
+    def initialize(str = "")
+      @str = str
+    end
+
+    def write(value)
+      @str << value
+    end
+
+    def to_s()
+      return @str
+    end
+
+  end
+
+  class EOF < Exception; end
+
+  class Encoder
+
+    def initialize(out)
+      @out = out
+      @bits = []
+    end
+
+    attr_reader(:out)
+
+    def encode(type, value)
+      send(type, value)
+    end
+
+    def bit(b)
+      @bits << b
+    end
+
+    def octet(o)
+      pack("C", o)
+    end
+
+    def short(s)
+      pack("n", s)
+    end
+
+    def long(l)
+      pack("N", l)
+    end
+
+    def longlong(l)
+      lower = l & 0xffffffff
+      upper = (l & ~0xffffffff) >> 32
+      long(upper)
+      long(lower)
+    end
+
+    def timestamp(l)
+      longlong(l)
+    end
+
+    def shortstr(s)
+      # shortstr is actually octetstr
+      octet(s.length)
+      write(s)
+    end
+
+    def longstr(s)
+      case s
+      when Hash
+        table(s)
+      else
+        long(s.length)
+        write(s)
+      end
+    end
+
+    def table(t)
+      t = {} if t.nil?
+      enc = Encoder.new(StringWriter.new())
+      t.each {|key, value|
+        enc.shortstr(key)
+        # I offer this chicken to the gods of polymorphism. May they
+        # choke on it.
+        case value
+        when String
+          type = :longstr
+          desc = "S"
+        when Numeric
+          type = :long
+          desc = "I"
+        else
+          raise Exception.new("unknown table value: #{value.class}")
+        end
+        enc.write(desc)
+        enc.encode(type, value)
+      }
+      longstr(enc.out.to_s())
+    end
+
+    def write(str)
+      flushbits()
+      @out.write(str)
+      #      puts "OUT #{str.inspect()}"
+    end
+
+    def pack(fmt, *args)
+      write(args.pack(fmt))
+    end
+
+    def flush()
+      flushbits()
+    end
+
+    private
+
+    def flushbits()
+      if @bits.empty? then return end
+
+      bytes = []
+      index = 0
+      @bits.each {|b|
+        bytes << 0 if index == 0
+        if b then bytes[-1] |= 1 << index end
+        index = (index + 1) % 8
+      }
+      @bits.clear()
+      bytes.each {|b|
+        octet(b)
+      }
+    end
+
+  end
+
+  class StringReader
+
+    def initialize(str)
+      @str = str
+      @index = 0
+    end
+
+    def read(n)
+      result = @str[@index, n]
+      @index += result.length
+      return result
+    end
+
+  end
+
+  class Decoder
+
+    def initialize(_in)
+      @in = _in
+      @bits = []
+    end
+
+    def decode(type)
+      return send(type)
+    end
+
+    def bit()
+      if @bits.empty?
+        byte = octet()
+        7.downto(0) {|i|
+          @bits << (byte[i] == 1)
+        }
+      end
+      return @bits.pop()
+    end
+
+    def octet()
+      return unpack("C", 1)
+    end
+
+    def short()
+      return unpack("n", 2)
+    end
+
+    def long()
+      return unpack("N", 4)
+    end
+
+    def longlong()
+      upper = long()
+      lower = long()
+      return upper << 32 | lower
+    end
+
+    def timestamp()
+      return longlong()
+    end
+
+    def shortstr()
+      # shortstr is actually octetstr
+      return read(octet())
+    end
+
+    def longstr()
+      return read(long())
+    end
+
+    def table()
+      dec = Decoder.new(StringReader.new(longstr()))
+      result = {}
+      while true
+        begin
+          key = dec.shortstr()
+        rescue EOF
+          break
+        end
+        desc = dec.read(1)
+        case desc
+        when "S"
+          value = dec.longstr()
+        when "I"
+          value = dec.long()
+        else
+          raise Exception.new("unrecognized descriminator: #{desc.inspect()}")
+        end
+        result[key] = value
+      end
+      return result
+    end
+
+    def read(n)
+      return "" if n == 0
+      result = @in.read(n)
+      if result.nil? or result.empty?
+        raise EOF.new()
+      else
+        #        puts " IN #{result.inspect()}"
+        return result
+      end
+    end
+
+    def unpack(fmt, size)
+      result = read(size).unpack(fmt)
+      if result.length == 1
+        return result[0]
+      else
+        return result
+      end
+    end
+
+  end
+
+end

Added: incubator/qpid/trunk/qpid/ruby/lib/qpid/connection.rb
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/ruby/lib/qpid/connection.rb?rev=713616&view=auto
==============================================================================
--- incubator/qpid/trunk/qpid/ruby/lib/qpid/connection.rb (added)
+++ incubator/qpid/trunk/qpid/ruby/lib/qpid/connection.rb Wed Nov 12 18:45:18 2008
@@ -0,0 +1,221 @@
+#
+# 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 'monitor'
+
+module Qpid
+
+  class ChannelBusy< Exception ; end
+
+  class ChannelsBusy < Exception ; end
+
+  class SessionBusy < Exception ; end
+
+  class ConnectionFailed < Exception ; end
+
+  class Timeout < Exception ; end
+
+  class Connection < Assembler
+
+    include MonitorMixin
+
+    attr_reader :spec, :attached, :sessions, :thread
+    attr_accessor :opened, :failed, :close_code
+
+    def initialize(sock, args={})
+      super(sock)
+
+      delegate = args[:delegate] || Qpid::Delegate::Client.method(:new)
+      spec = args[:spec] || nil
+
+      @spec = Qpid::Spec010::load(spec)
+      @track = @spec["track"]
+
+      @attached = {}
+      @sessions = {}
+
+      @condition = new_cond
+      @opened = false
+      @failed = false
+      @close_code = [nil, "connection aborted"]
+
+      @thread = nil
+
+      @channel_max = 65535
+
+      @delegate = delegate.call(self, args)
+    end
+
+    def attach(name, ch, delegate, force=false)
+      synchronize do
+        ssn = @attached[ch.id]
+        if ssn
+          raise ChannelBusy.new(ch, ssn) unless ssn.name == name
+        else
+          ssn = @sessions[name]
+          if ssn.nil?
+            ssn = Session.new(name, @spec, :delegate => delegate)
+            @sessions[name] = ssn
+          elsif ssn.channel
+            if force
+              @attached.delete(ssn.channel.id)
+              ssn.channel = nil
+            else
+              raise SessionBusy.new(ssn)
+            end
+          end
+          @attached[ch.id] = ssn
+          ssn.channel = ch
+        end
+        ch.session = ssn
+        return ssn
+      end
+    end
+
+    def detach(name, ch)
+      synchronize do
+        @attached.delete(ch.id)
+        ssn = @sessions.delete(name)
+        if ssn
+          ssn.channel = nil
+          ssn.closed
+          return ssn
+        end
+      end
+    end
+
+    def session(name, kwargs = {})
+      timeout = kwargs[:timeout]
+      delegate = kwargs[:delegate] || Qpid::Session::Client.method(:new)
+
+      # FIXME: Python has cryptic comment about 'ch 0 ?'
+      channel = (0..@channel_max).detect { |i| ! @attached.key?(i) }
+      raise ChannelsBusy unless channel
+
+      synchronize do
+        ch = Channel.new(self, channel)
+        ssn = attach(name, ch, delegate)
+        ssn.channel.session_attach(name)
+        if ssn.wait_for(timeout) { ssn.channel }
+          return ssn
+        else
+          detach(name, ch)
+          raise Timeout
+        end
+      end
+    end
+
+    def detach_all
+      synchronize do
+        attached.values.each do |ssn|
+          ssn.exceptions << @close_code unless @close_code[0] == 200
+          detach(ssn.name, ssn.channel)
+        end
+      end
+    end
+
+    def start(timeout=nil)
+      @delegate.start
+      @thread = Thread.new { run }
+      @thread[:name] = 'conn'
+      synchronize do
+        unless @condition.wait_for(timeout) { @opened || @failed }
+          raise Timeout
+        end
+      end
+      if @failed
+        raise ConnectionFailed.new(@close_code)
+      end
+    end
+
+    def run
+      # XXX: we don't really have a good way to exit this loop without
+      # getting the other end to kill the socket
+      loop do
+        begin
+          seg = read_segment
+        rescue Qpid::Closed => e
+          detach_all
+          break
+        end
+        @delegate.received(seg)
+      end
+    end
+
+    def close(timeout=nil)
+      return unless @opened
+      Channel.new(self, 0).connection_close(200)
+      synchronize do
+        unless @condition.wait_for(timeout) { ! @opened }
+          raise Timeout
+        end
+      end
+      @thread.join(timeout)
+      @thread = nil
+    end
+
+    def signal
+      synchronize { @condition.signal }
+    end
+
+    def to_s
+      # FIXME: We'd like to report something like HOST:PORT
+      return @sock.to_s
+    end
+
+    class Channel < Invoker
+
+      attr_reader :id, :connection
+      attr_accessor :session
+
+      def initialize(connection, id)
+        @connection = connection
+        @id = id
+        @session = nil
+      end
+
+      def resolve_method(name)
+        inst = @connection.spec[name]
+        if inst.is_a?(Qpid::Spec010::Control)
+          return invocation(:method, inst)
+        else
+          return invocation(:error, nil)
+        end
+      end
+
+      def invoke(type, args)
+        ctl = type.create(*args)
+        sc = StringCodec.new(@connection.spec)
+        sc.write_control(ctl)
+        @connection.write_segment(Segment.new(true, true, type.segment_type,
+                                              type.track, self.id, sc.encoded))
+
+        log = Qpid::logger["qpid.io.ctl"]
+        log.debug("SENT %s", ctl) if log
+      end
+
+      def to_s
+        return "#{@connection}[#{@id}]"
+      end
+
+    end
+
+  end
+
+end

Added: incubator/qpid/trunk/qpid/ruby/lib/qpid/connection08.rb
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/ruby/lib/qpid/connection08.rb?rev=713616&view=auto
==============================================================================
--- incubator/qpid/trunk/qpid/ruby/lib/qpid/connection08.rb (added)
+++ incubator/qpid/trunk/qpid/ruby/lib/qpid/connection08.rb Wed Nov 12 18:45:18 2008
@@ -0,0 +1,252 @@
+#
+# 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 "socket"
+require "qpid/codec08"
+
+module Qpid08
+
+  class Connection
+
+    def initialize(host, port, spec)
+      @host = host
+      @port = port
+      @spec = spec
+    end
+
+    attr_reader(:host, :port, :spec)
+
+    def connect()
+      @sock = TCPSocket.open(@host, @port)
+      @out = Encoder.new(@sock)
+      @in = Decoder.new(@sock)
+    end
+
+    def init()
+      @out.write("AMQP")
+      [1, 1, @spec.major, @spec.minor].each {|o|
+        @out.octet(o)
+      }
+    end
+
+    def write(frame)
+      #      puts "OUT #{frame.inspect()}"
+      @out.octet(@spec.constants[frame.payload.type].id)
+      @out.short(frame.channel)
+      frame.payload.encode(@out)
+      @out.octet(frame_end)
+    end
+
+    def read()
+      type = @spec.constants[@in.octet()].name
+      channel = @in.short()
+      payload = Payload.decode(type, @spec, @in)
+      oct = @in.octet()
+      if oct != frame_end
+        raise Exception.new("framing error: expected #{frame_end}, got #{oct}")
+      end
+      frame = Frame.new(channel, payload)
+      #      puts " IN #{frame.inspect}"
+      return frame
+    end
+
+    private
+
+    def frame_end
+      @spec.constants[:"frame_end"].id
+    end
+
+  end
+
+  class Frame
+
+    def initialize(channel, payload)
+      @channel = channel
+      @payload = payload
+    end
+
+    attr_reader(:channel, :payload)
+
+  end
+
+  class Payload
+
+    TYPES = {}
+
+    def Payload.singleton_method_added(name)
+      if name == :type
+        TYPES[type] = self
+      end
+    end
+
+    def Payload.decode(type, spec, dec)
+      klass = TYPES[type]
+      klass.decode(spec, dec)
+    end
+
+  end
+
+  class Method < Payload
+
+    def initialize(method, args)
+      if args.size != method.fields.size
+        raise ArgumentError.new("argument mismatch #{method} #{args}")
+      end
+      @method = method
+      @args = args
+    end
+
+    attr_reader(:method, :args)
+
+    def Method.type; :frame_method end
+
+    def type; Method.type end
+
+    def encode(encoder)
+      buf = StringWriter.new()
+      enc = Encoder.new(buf)
+      enc.short(@method.parent.id)
+      enc.short(@method.id)
+      @method.fields.zip(self.args).each {|f, a|
+        if a.nil?; a = f.default end
+        enc.encode(f.type, a)
+      }
+      enc.flush()
+      encoder.longstr(buf.to_s)
+    end
+
+    def Method.decode(spec, decoder)
+      buf = decoder.longstr()
+      dec = Decoder.new(StringReader.new(buf))
+      klass = spec.classes[dec.short()]
+      meth = klass.methods[dec.short()]
+      args = meth.fields.map {|f| dec.decode(f.type)}
+      return Method.new(meth, args)
+    end
+
+    def inspect(); "#{method.qname}(#{args.join(", ")})" end
+
+  end
+
+  class Header < Payload
+
+    def Header.type; :frame_header end
+
+    def initialize(klass, weight, size, properties)
+      @klass = klass
+      @weight = weight
+      @size = size
+      @properties = properties
+    end
+
+    attr_reader :weight, :size, :properties
+
+    def type; Header.type end
+
+    def encode(encoder)
+      buf = StringWriter.new()
+      enc = Encoder.new(buf)
+      enc.short(@klass.id)
+      enc.short(@weight)
+      enc.longlong(@size)
+
+      # property flags
+      nprops = @klass.fields.size
+      flags = 0
+      0.upto(nprops - 1) do |i|
+        f = @klass.fields[i]
+        flags <<= 1
+        flags |= 1 unless @properties[f.name].nil?
+        # the last bit indicates more flags
+        if i > 0 and (i % 15) == 0
+          flags <<= 1
+          if nprops > (i + 1)
+            flags |= 1
+            enc.short(flags)
+            flags = 0
+          end
+        end
+      end
+      flags <<= ((16 - (nprops % 15)) % 16)
+      enc.short(flags)
+
+      # properties
+      @klass.fields.each do |f|
+        v = @properties[f.name]
+        enc.encode(f.type, v) unless v.nil?
+      end
+      enc.flush()
+      encoder.longstr(buf.to_s)
+    end
+
+    def Header.decode(spec, decoder)
+      dec = Decoder.new(StringReader.new(decoder.longstr()))
+      klass = spec.classes[dec.short()]
+      weight = dec.short()
+      size = dec.longlong()
+
+      # property flags
+      bits = []
+      while true
+        flags = dec.short()
+        15.downto(1) do |i|
+          if flags >> i & 0x1 != 0
+            bits << true
+          else
+            bits << false
+          end
+        end
+        break if flags & 0x1 == 0
+      end
+
+      # properties
+      properties = {}
+      bits.zip(klass.fields).each do |b, f|
+        properties[f.name] = dec.decode(f.type) if b
+      end
+      return Header.new(klass, weight, size, properties)
+    end
+
+    def inspect(); "#{@klass.name}(#{@properties.inspect()})" end
+
+  end
+
+  class Body < Payload
+
+    def Body.type; :frame_body end
+
+    def type; Body.type end
+
+    def initialize(content)
+      @content = content
+    end
+
+    attr_reader :content
+
+    def encode(enc)
+      enc.longstr(@content)
+    end
+
+    def Body.decode(spec, dec)
+      return Body.new(dec.longstr())
+    end
+
+  end
+
+end

Added: incubator/qpid/trunk/qpid/ruby/lib/qpid/datatypes.rb
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/ruby/lib/qpid/datatypes.rb?rev=713616&view=auto
==============================================================================
--- incubator/qpid/trunk/qpid/ruby/lib/qpid/datatypes.rb (added)
+++ incubator/qpid/trunk/qpid/ruby/lib/qpid/datatypes.rb Wed Nov 12 18:45:18 2008
@@ -0,0 +1,353 @@
+#
+# 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 Qpid
+
+  def self.struct(type, *args)
+    # FIXME: This is fragile; the last arg could be a hash,
+    # without being hte keywords
+    kwargs = {}
+    kwargs = args.pop if args.any? && args[-1].is_a?(Hash)
+
+    if args.size > type.fields.size
+      raise TypeError,
+      "%s() takes at most %d arguments (%d given)" %
+        [type.name, type.fields.size, args.size]
+    end
+
+    attrs = type.fields.inject({}) do |attrs, field|
+      if args.any?
+        attrs[field.name] = args.shift
+        if kwargs.key?(field.name)
+          raise TypeError,
+          "%s() got multiple values for keyword argument '%s'" %
+            [type.name, field.name]
+        end
+      elsif kwargs.key?(field.name)
+        attrs[field.name] = kwargs.delete(field.name)
+      else
+        attrs[field.name] = field.default
+      end
+      attrs
+    end
+
+    unless kwargs.empty?
+      unexpected = kwargs.keys[0]
+      raise TypeError,
+      "%s() got an unexpected keyword argument '%s'" %
+        [type.name, unexpected]
+    end
+
+    attrs[:type] = type
+    attrs[:id] = nil
+
+    name = "Qpid_" + type.name.to_s.capitalize
+    unless ::Struct.const_defined?(name)
+      vars = type.fields.collect { |f| f.name } << :type << :id
+      ::Struct.new(name, *vars)
+    end
+    st = ::Struct.const_get(name)
+
+    result = st.new
+    attrs.each { |k, v| result[k] = v }
+    return result
+  end
+
+  class Message
+
+    attr_accessor :headers, :body, :id
+
+    def initialize(*args)
+      @body = nil
+      @headers = nil
+
+      @body = args.pop unless args.empty?
+      @headers = args unless args.empty?
+
+      @id = nil
+    end
+
+    def has(name)
+      return ! get(name).nil?
+    end
+
+    def get(name)
+      if @headers
+        name = name.to_sym
+        @headers.find { |h| h.type.name == name }
+      end
+    end
+
+    def set(header)
+      @headers ||= []
+      if h = @headers.find { |h| h.type == header.type }
+        ind = @headers.index(h)
+        @headers[ind] = header
+      else
+        @headers << header
+      end
+    end
+
+    def clear(name)
+      if @headers
+        name = name.to_sym
+        @headers.delete_if { |h| h.type.name == name }
+      end
+    end
+
+    # FIXME: Not sure what to do here
+    # Ruby doesn't have a notion of a evaluable string representation
+    # def __repr__(self):
+    #     args = []
+    #     if self.headers:
+    #       args.extend(map(repr, self.headers))
+    #     if self.body:
+    #       args.append(repr(self.body))
+    #     if self.id is not None:
+    #       args.append("id=%s" % self.id)
+    #     return "Message(%s)" % ", ".join(args)
+    #   end
+  end
+
+  class ::Object
+
+    def to_serial
+      Qpid::Serial.new(self)
+    end
+  end
+
+  class Serial
+
+    include Comparable
+
+    attr_accessor :value
+
+    def initialize(value)
+      @value = value & 0xFFFFFFFF
+    end
+
+    def hash
+      @value.hash
+    end
+
+    def to_serial
+      self
+    end
+
+    def eql?(other)
+      other = other.to_serial
+      value.eql?(other.value)
+    end
+
+    def <=>(other)
+      return 1 if other.nil?
+
+      other = other.to_serial
+
+      delta = (value - other.value) & 0xFFFFFFFF
+      neg = delta & 0x80000000
+      mag = delta & 0x7FFFFFFF
+
+      return (neg>0) ? -mag : mag
+    end
+
+    def +(other)
+      result = other.to_serial
+      result.value += value
+      return result
+    end
+
+    def -(other)
+      result = other.to_serial
+      result.value = value - result.value
+      return result
+    end
+
+    def succ
+      Serial.new(value + 1)
+    end
+
+    # FIXME: Not sure what to do here
+    # Ruby doesn't have a notion of a evaluable string representation
+    # def __repr__(self):
+    #         return "serial(%s)" % self.value
+    # end
+
+    def to_s
+      value.to_s
+    end
+
+  end
+
+  # The Python class datatypes.Range is emulated by the standard
+  # Range class with a few additions
+  class ::Range
+
+    alias :lower :begin
+    alias :upper :end
+
+    def touches(r)
+      # XXX: are we doing more checks than we need?
+      return (r.include?(lower - 1) ||
+              r.include?(upper + 1) ||
+              include?(r.lower - 1) ||
+              include?(r.upper + 1) ||
+              r.include?(lower)     ||
+              r.include?(upper)     ||
+              include?(r.lower)     ||
+              include?(r.upper))
+    end
+
+    def span(r)
+      Range.new([lower, r.lower].min, [upper, r.upper].max)
+    end
+
+    def intersect(r)
+      l = [lower, r.lower].max
+      u = [upper, r.upper].min
+      return l > u ? nil : Range.new(l, u)
+    end
+
+  end
+
+  class RangedSet
+
+    include Enumerable
+
+    attr_accessor :ranges
+
+    def initialize(*args)
+      @ranges = []
+      args.each { |n| add(n) }
+    end
+
+    def each(&block)
+      ranges.each { |r| yield(r) }
+    end
+
+    def include?(n)
+      if (n.is_a?(Range))
+        super(n)
+      else
+        ranges.find { |r| r.include?(n) }
+      end
+    end
+
+    def add_range(range)
+      ranges.delete_if do |r|
+        if range.touches(r)
+          range = range.span(r)
+          true
+        else
+          false
+        end
+      end
+      ranges << range
+    end
+
+    def add(lower, upper = nil)
+      upper = lower if upper.nil?
+      add_range(Range.new(lower, upper))
+    end
+
+    def to_s
+      repr = ranges.sort { |a,b| b.lower <=> a.lower }.
+        map { |r| r.to_s }.join(",")
+      "<RangedSet: {#{repr}}"
+    end
+  end
+
+  class Future
+    def initialize(initial=nil, exception=Exception)
+      @value = initial
+      @error = nil
+      @set = Util::Event.new
+      @exception = exception
+    end
+
+    def error(error)
+      @error = error
+      @set.set
+    end
+
+    def set(value)
+      @value = value
+      @set.set
+    end
+
+    def get(timeout=nil)
+      @set.wait(timeout)
+      unless @error.nil?
+        raise @exception.new(@error)
+      end
+      @value
+    end
+  end
+
+  class UUID
+    include Comparable
+
+    attr_accessor :bytes
+
+    def initialize(bytes)
+      @bytes = bytes
+    end
+
+    def <=>(other)
+      if other.respond_to?(:bytes)
+        return bytes <=> other.bytes
+      else
+        raise NotImplementedError
+      end
+    end
+
+    def to_s
+      UUID::format(bytes)
+    end
+
+    # FIXME: Not sure what to do here
+    # Ruby doesn't have a notion of a evaluable string representation
+    #   def __repr__(self):
+    #     return "UUID(%r)" % str(self)
+    #   end
+
+    def self.random_uuid
+      bytes = (1..16).collect { |i| rand(256) }
+
+      # From RFC4122, the version bits are set to 0100
+      bytes[7] &= 0x0F
+      bytes[7] |= 0x40
+
+      # From RFC4122, the top two bits of byte 8 get set to 01
+      bytes[8] &= 0x3F
+      bytes[8] |= 0x80
+      return bytes.pack("C16")
+    end
+
+    def self.uuid4
+      UUID.new(random_uuid)
+    end
+
+    def self.format(s)
+      # Python format !LHHHHL
+      # big-endian, ulong, ushort x 4, ulong
+      "%08x-%04x-%04x-%04x-%04x%08x" % bytes.unpack("NnnnnN")
+    end
+  end
+end

Added: incubator/qpid/trunk/qpid/ruby/lib/qpid/delegates.rb
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/ruby/lib/qpid/delegates.rb?rev=713616&view=auto
==============================================================================
--- incubator/qpid/trunk/qpid/ruby/lib/qpid/delegates.rb (added)
+++ incubator/qpid/trunk/qpid/ruby/lib/qpid/delegates.rb Wed Nov 12 18:45:18 2008
@@ -0,0 +1,204 @@
+#
+# 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 'rbconfig'
+
+module Qpid
+
+  class Delegate
+
+    def initialize(connection, args={})
+      @connection = connection
+      @spec = connection.spec
+      @delegate = args[:delegate] || Qpid::Delegate::Client.method(:new)
+      @control = @spec[:track].enum[:control].value
+    end
+
+    def log ; Qpid::logger["qpid.io.ctl"]; end
+
+    def received(seg)
+      ssn = @connection.attached[seg.channel]
+      unless ssn
+        ch = Qpid::Connection::Channel.new(@connection, seg.channel)
+      else
+        ch = ssn.channel
+      end
+
+      if seg.track == @control
+        ctl = seg.decode(@spec)
+        log.debug("RECV %s", ctl) if log
+        attr = ctl.type.name
+        method(attr).call(ch, ctl)
+      elsif ssn.nil?
+        ch.session_detached
+      else
+        ssn.received(seg)
+      end
+    end
+
+    def connection_close(ch, close)
+      @connection.close_code = [close.reply_code, close.reply_text]
+      ch.connection_close_ok
+      @connection.sock.close_write()
+      unless @connection.opened
+        @connection.failed = true
+        @connection.signal
+      end
+    end
+
+    def connection_close_ok(ch, close_ok)
+      @connection.opened = false
+      @connection.signal
+    end
+
+    def session_attach(ch, a)
+      begin
+        @connection.attach(a.name, ch, @delegate, a.force)
+        ch.session_attached(a.name)
+      rescue Qpid::ChannelBusy
+        ch.session_detached(a.name)
+      rescue Qpid::SessionBusy
+        ch.session_detached(a.name)
+      end
+    end
+
+    def session_attached(ch, a)
+      ch.session.signal
+    end
+
+    def session_detach(ch, d)
+      #send back the confirmation of detachment before removing the
+      #channel from the attached set; this avoids needing to hold the
+      #connection lock during the sending of this control and ensures
+      #that if the channel is immediately reused for a new session the
+      #attach request will follow the detached notification.
+      ch.session_detached(d.name)
+      ssn = @connection.detach(d.name, ch)
+    end
+
+    def session_detached(ch, d)
+      @connection.detach(d.name, ch)
+    end
+
+    def session_request_timeout(ch, rt)
+      ch.session_timeout(rt.timeout)
+    end
+
+    def session_command_point(ch, cp)
+      ssn = ch.session
+      ssn.receiver.next_id = cp.command_id
+      ssn.receiver.next_offset = cp.command_offset
+    end
+
+    def session_completed(ch, cmp)
+      ch.session.sender.has_completed(cmp.commands)
+      if cmp.timely_reply
+        ch.session_known_completed(cmp.commands)
+      end
+      ch.session.signal
+    end
+
+    def session_known_completed(ch, kn_cmp)
+      ch.session.receiver.known_completed(kn_cmp.commands)
+    end
+
+    def session_flush(ch, f)
+      rcv = ch.session.receiver
+      if f.expected
+        if rcv.next_id
+          exp = Qpid::RangedSet.new(rcv.next_id)
+        else
+          exp = nil
+        end
+        ch.session_expected(exp)
+      end
+      if f.confirmed
+        ch.session_confirmed(rcv.completed)
+      end
+      if f.completed
+        ch.session_completed(rcv.completed)
+      end
+    end
+
+    class Server < Delegate
+
+      def start
+        @connection.read_header()
+        @connection.write_header(@spec.major, @spec.minor)
+        ch = Qpid::Connection::Channel.new(@connection, 0)
+        ch.connection_start(:mechanisms => ["ANONYMOUS"])
+        ch
+      end
+
+      def connection_start_ok(ch, start_ok)
+        ch.connection_tune(:channel_max => 65535)
+      end
+
+      def connection_tune_ok(ch, tune_ok)
+        nil
+      end
+
+      def connection_open(ch, open)
+        @connection.opened = true
+        ch.connection_open_ok()
+        @connection.signal
+      end
+    end
+
+    class Client < Delegate
+
+      # FIXME: Python uses os.name for platform - we don't have an exact
+      # analog in Ruby
+      PROPERTIES = {"product"  => "qpid python client",
+        "version"  => "development",
+        "platform" => Config::CONFIG["build_os"]}
+
+
+      def initialize(connection, args)
+        super(connection)
+
+        @username = args[:username] || "guest"
+        @password = args[:password] || "guest"
+        @mechanism= args[:mechanism] || "PLAIN"
+      end
+
+      def start
+        @connection.write_header(@spec.major, @spec.minor)
+        @connection.read_header
+      end
+
+      def connection_start(ch, start)
+        r = "\0%s\0%s" % [@username, @password]
+        ch.connection_start_ok(:client_properties => PROPERTIES,
+                               :mechanism => @mechanism,
+                               :response => r)
+      end
+
+      def connection_tune(ch, tune)
+        ch.connection_tune_ok()
+        ch.connection_open()
+      end
+
+      def connection_open_ok(ch, open_ok)
+        @connection.opened = true
+        @connection.signal
+      end
+    end
+  end
+end

Added: incubator/qpid/trunk/qpid/ruby/lib/qpid/fields.rb
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/ruby/lib/qpid/fields.rb?rev=713616&view=auto
==============================================================================
--- incubator/qpid/trunk/qpid/ruby/lib/qpid/fields.rb (added)
+++ incubator/qpid/trunk/qpid/ruby/lib/qpid/fields.rb Wed Nov 12 18:45:18 2008
@@ -0,0 +1,49 @@
+#
+# 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.
+#
+
+class Class
+  def fields(*fields)
+    module_eval {
+      def initialize(*args, &block)
+        args = init_fields(*args)
+
+        if respond_to? :init
+          init(*args) {|*a| yield(*a)}
+        elsif args.any?
+          raise ArgumentError, "extra arguments: #{args.inspect}"
+        end
+      end
+    }
+
+    vars = fields.map {|f| :"@#{f.to_s().chomp("?")}"}
+
+    define_method(:init_fields) {|*args|
+      vars.each {|v|
+        instance_variable_set(v, args.shift())
+      }
+      args
+    }
+
+    vars.each_index {|i|
+      define_method(fields[i]) {
+        instance_variable_get(vars[i])
+      }
+    }
+  end
+end

Added: incubator/qpid/trunk/qpid/ruby/lib/qpid/framer.rb
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/ruby/lib/qpid/framer.rb?rev=713616&view=auto
==============================================================================
--- incubator/qpid/trunk/qpid/ruby/lib/qpid/framer.rb (added)
+++ incubator/qpid/trunk/qpid/ruby/lib/qpid/framer.rb Wed Nov 12 18:45:18 2008
@@ -0,0 +1,195 @@
+#
+# 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 'monitor'
+require 'logger'
+
+module Qpid
+
+  FIRST_SEG = 0x08
+  LAST_SEG = 0x04
+  FIRST_FRM = 0x02
+  LAST_FRM = 0x01
+
+  class << self
+    attr_accessor :raw_logger, :frm_logger
+  end
+
+  def self.packed_size(format)
+    # FIXME: This is a total copout to simulate Python's
+    # struct.calcsize
+    ([0]*256).pack(format).size
+  end
+
+  class Frame
+    attr_reader :payload, :track, :flags, :type, :channel
+
+    # HEADER = "!2BHxBH4x"
+    # Python  Meaning         Ruby
+    # !       big endian      (implied by format char)
+    # 2B      2 uchar         C2
+    # H       unsigned short  n
+    # x       pad byte        x
+    # B       uchar           C
+    # H       unsigned short  n
+    # 4x      pad byte        x4
+    HEADER = "C2nxCnx4"
+    HEADER_SIZE = Qpid::packed_size(HEADER)
+    MAX_PAYLOAD = 65535 - HEADER_SIZE
+
+    def initialize(flags, type, track, channel, payload)
+      if payload.size > MAX_PAYLOAD
+        raise ArgumentError, "max payload size exceeded: #{payload.size}"
+      end
+
+      @flags = flags
+      @type = type
+      @track = track
+      @channel = channel
+      @payload = payload
+    end
+
+    def first_segment? ; FIRST_SEG & @flags > 0 ; end
+
+    def last_segment? ; LAST_SEG & @flags > 0 ; end
+
+    def first_frame? ; FIRST_FRM & @flags > 0 ; end
+
+    def last_frame? ; LAST_FRM & @flags > 0 ; end
+
+    def to_s
+      fs = first_segment? ? 'S' : '.'
+      ls = last_segment? ? 's' : '.'
+      ff = first_frame? ? 'F' : '.'
+      lf = last_frame? ? 'f' : '.'
+
+      return "%s%s%s%s %s %s %s %s" % [fs, ls, ff, lf,
+                                       @type,
+                                       @track,
+                                       @channel,
+                                       @payload.inspect]
+    end
+  end
+
+  class FramingError < Exception ; end
+
+  class Closed < Exception ; end
+
+  class Framer
+    include Packer
+
+    # Python: "!4s4B"
+    HEADER = "a4C4"
+    HEADER_SIZE = 8
+
+    def raw
+      Qpid::raw_logger
+    end
+
+    def frm
+      Qpid::frm_logger
+    end
+
+    def initialize(sock)
+      @sock = sock
+      @sock.extend(MonitorMixin)
+      @buf = ""
+    end
+
+    attr_reader :sock
+
+    def aborted? ; false ; end
+
+    def write(buf)
+      @buf += buf
+    end
+
+    def flush
+      @sock.synchronize do
+        _write(@buf)
+        @buf = ""
+        frm.debug("FLUSHED") if frm
+      end
+    end
+
+    def _write(buf)
+      while buf && buf.size > 0
+        # FIXME: Catch errors
+        n = @sock.write(buf)
+        raw.debug("SENT #{buf[0, n].inspect}") if raw
+        buf[0,n] = ""
+        @sock.flush
+      end
+    end
+
+    def read(n)
+      data = ""
+      while data.size < n
+        begin
+          s = @sock.read(n - data.size)
+        rescue IOError => e
+          raise e if data != ""
+          @sock.close unless @sock.closed?
+          raise Closed
+        end
+        # FIXME: Catch errors
+        if s.nil? or s.size == 0
+          @sock.close unless @sock.closed?
+          raise Closed
+        end
+        data += s
+        raw.debug("RECV #{n}/#{data.size} #{s.inspect}") if raw
+      end
+      return data
+    end
+
+    def read_header
+      unpack(Framer::HEADER, Framer::HEADER_SIZE)
+    end
+
+    def write_header(major, minor)
+      @sock.synchronize do
+        pack(Framer::HEADER, "AMQP", 1, 1, major, minor)
+        flush()
+      end
+    end
+
+    def write_frame(frame)
+      @sock.synchronize do
+        size = frame.payload.size + Frame::HEADER_SIZE
+        track = frame.track & 0x0F
+        pack(Frame::HEADER, frame.flags, frame.type, size, track, frame.channel)
+        write(frame.payload)
+        if frame.last_segment? and frame.last_frame?
+          flush()
+          frm.debug("SENT #{frame}") if frm
+        end
+      end
+    end
+
+    def read_frame
+      flags, type, size, track, channel = unpack(Frame::HEADER, Frame::HEADER_SIZE)
+      raise FramingError if (flags & 0xF0 > 0)
+      payload = read(size - Frame::HEADER_SIZE)
+      frame = Frame.new(flags, type, track, channel, payload)
+      frm.debug("RECV #{frame}") if frm
+      return frame
+    end
+  end
+end

Added: incubator/qpid/trunk/qpid/ruby/lib/qpid/invoker.rb
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/ruby/lib/qpid/invoker.rb?rev=713616&view=auto
==============================================================================
--- incubator/qpid/trunk/qpid/ruby/lib/qpid/invoker.rb (added)
+++ incubator/qpid/trunk/qpid/ruby/lib/qpid/invoker.rb Wed Nov 12 18:45:18 2008
@@ -0,0 +1,65 @@
+#
+# 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.
+#
+
+class Qpid::Invoker
+
+  # Requires that client defines a invoke method and overrides
+  # resolve_method
+
+  # FIXME: Is it really worth defining methods in method_missing ? We
+  # could just dispatch there directly
+
+  def invc_method(name, resolved)
+    define_singleton_method(name) { |*args| invoke(resolved, args) }
+    # FIXME: the Python code also attaches docs from resolved.pydoc
+  end
+
+  def invc_value(name, resolved)
+    define_singleton_method(name) { | | resolved }
+  end
+
+  def invc_error(name, resolved)
+    msg = "%s instance has no attribute '%s'" % [self.class.name, name]
+    if resolved
+      msg += "\n%s" % resolved
+    end
+    raise NameError, msg
+  end
+
+  def resolve_method(name)
+    invocation(:error, nil)
+  end
+
+  def method_missing(name, *args)
+    disp, resolved = resolve_method(name)
+    disp.call(name, resolved)
+    send(name, *args)
+  end
+
+  def invocation(kind, name = nil)
+    [ method("invc_#{kind}"), name ]
+  end
+
+  private
+  def define_singleton_method(name, &body)
+    singleton_class = class << self; self; end
+    singleton_class.send(:define_method, name, &body)
+  end
+
+end

Added: incubator/qpid/trunk/qpid/ruby/lib/qpid/packer.rb
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/ruby/lib/qpid/packer.rb?rev=713616&view=auto
==============================================================================
--- incubator/qpid/trunk/qpid/ruby/lib/qpid/packer.rb (added)
+++ incubator/qpid/trunk/qpid/ruby/lib/qpid/packer.rb Wed Nov 12 18:45:18 2008
@@ -0,0 +1,33 @@
+#
+# 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 Qpid
+  module Packer
+    def unpack(fmt, len)
+      raw = read(len)
+      values = raw.unpack(fmt)
+      values = values[0] if values.size == 1
+      return values
+    end
+
+    def pack(fmt, *args)
+      write(args.pack(fmt))
+    end
+  end
+end

Added: incubator/qpid/trunk/qpid/ruby/lib/qpid/peer.rb
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/ruby/lib/qpid/peer.rb?rev=713616&view=auto
==============================================================================
--- incubator/qpid/trunk/qpid/ruby/lib/qpid/peer.rb (added)
+++ incubator/qpid/trunk/qpid/ruby/lib/qpid/peer.rb Wed Nov 12 18:45:18 2008
@@ -0,0 +1,289 @@
+#
+# 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 "thread"
+require "qpid/queue"
+require "qpid/connection08"
+require "qpid/fields"
+
+module Qpid08
+
+  Queue = Qpid::Queue
+
+  class Peer
+
+    def initialize(conn, delegate)
+      @conn = conn
+      @delegate = delegate
+      @outgoing = Queue.new()
+      @work = Queue.new()
+      @channels = {}
+      @mutex = Mutex.new()
+    end
+
+    def channel(id)
+      @mutex.synchronize do
+        ch = @channels[id]
+        if ch.nil?
+          ch = Channel.new(id, self, @outgoing, @conn.spec)
+          @channels[id] = ch
+        end
+        return ch
+      end
+    end
+
+    def channel_delete(id)
+      @channels.delete(id)
+    end
+
+    def start()
+      spawn(:writer)
+      spawn(:reader)
+      spawn(:worker)
+    end
+
+    def close()
+      @mutex.synchronize do
+        @channels.each_value do |ch|
+          ch.close()
+        end
+        @outgoing.close()
+        @work.close()
+      end
+    end
+
+    private
+
+    def spawn(method, *args)
+      Thread.new do
+        begin
+          send(method, *args)
+          # is this the standard way to catch any exception?
+        rescue Closed => e
+          puts "#{method} #{e}"
+        rescue Object => e
+          print e
+          e.backtrace.each do |line|
+            print "\n  ", line
+          end
+          print "\n"
+        end
+      end
+    end
+
+    def reader()
+      while true
+        frame = @conn.read()
+        ch = channel(frame.channel)
+        ch.dispatch(frame, @work)
+      end
+    end
+
+    def writer()
+      while true
+        @conn.write(@outgoing.get())
+      end
+    end
+
+    def worker()
+      while true
+        dispatch(@work.get())
+      end
+    end
+
+    def dispatch(queue)
+      frame = queue.get()
+      ch = channel(frame.channel)
+      payload = frame.payload
+      if payload.method.content?
+        content = Qpid08::read_content(queue)
+      else
+        content = nil
+      end
+
+      message = Message.new(payload.method, payload.args, content)
+      @delegate.dispatch(ch, message)
+    end
+
+  end
+
+  class Channel
+    def initialize(id, peer, outgoing, spec)
+      @id = id
+      @peer = peer
+      @outgoing = outgoing
+      @spec = spec
+      @incoming = Queue.new()
+      @responses = Queue.new()
+      @queue = nil
+      @closed = false
+    end
+
+    attr_reader :id
+
+    def closed?; @closed end
+
+    def close()
+      return if closed?
+      @peer.channel_delete(@id)
+      @closed = true
+      @incoming.close()
+      @responses.close()
+    end
+
+    def dispatch(frame, work)
+      payload = frame.payload
+      case payload
+      when Method
+        if payload.method.response?
+          @queue = @responses
+        else
+          @queue = @incoming
+          work << @incoming
+        end
+      end
+      @queue << frame
+    end
+
+    def method_missing(name, *args)
+      method = @spec.find_method(name)
+      if method.nil?
+        raise NoMethodError.new("undefined method '#{name}' for #{self}:#{self.class}")
+      end
+
+      if args.size == 1 and args[0].instance_of? Hash
+        kwargs = args[0]
+        invoke_args = method.fields.map do |f|
+          kwargs[f.name]
+        end
+        content = kwargs[:content]
+      else
+        invoke_args = []
+        method.fields.each do |f|
+          if args.any?
+            invoke_args << args.shift()
+          else
+            invoke_args << f.default
+          end
+        end
+        if method.content? and args.any?
+          content = args.shift()
+        else
+          content = nil
+        end
+        if args.any? then raise ArgumentError.new("#{args.size} extr arguments") end
+      end
+      return invoke(method, invoke_args, content)
+    end
+
+    def invoke(method, args, content = nil)
+      raise Closed() if closed?
+      frame = Frame.new(@id, Method.new(method, args))
+      @outgoing << frame
+
+      if method.content?
+        content = Content.new() if content.nil?
+        write_content(method.parent, content, @outgoing)
+      end
+
+      nowait = false
+      f = method.fields[:"nowait"]
+      nowait = args[method.fields.index(f)] unless f.nil?
+
+      unless nowait or method.responses.empty?
+        resp = @responses.get().payload
+        if resp.method.content?
+          content = read_content(@responses)
+        else
+          content = nil
+        end
+        if method.responses.include? resp.method
+          return Message.new(resp.method, resp.args, content)
+        else
+          # XXX: ValueError doesn't actually exist
+          raise ValueError.new(resp)
+        end
+      end
+    end
+
+    def write_content(klass, content, queue)
+      size = content.size
+      header = Frame.new(@id, Header.new(klass, content.weight, size, content.headers))
+      queue << header
+      content.children.each {|child| write_content(klass, child, queue)}
+      queue << Frame.new(@id, Body.new(content.body)) if size > 0
+    end
+
+  end
+
+  def Qpid08.read_content(queue)
+    frame = queue.get()
+    header = frame.payload
+    children = []
+    1.upto(header.weight) { children << read_content(queue) }
+    size = header.size
+    read = 0
+    buf = ""
+    while read < size
+      body = queue.get()
+      content = body.payload.content
+      buf << content
+      read += content.size
+    end
+    buf.freeze()
+    return Content.new(header.properties.clone(), buf, children)
+  end
+
+  class Content
+    def initialize(headers = {}, body = "", children = [])
+      @headers = headers
+      @body = body
+      @children = children
+    end
+
+    attr_reader :headers, :body, :children
+
+    def size; body.size end
+    def weight; children.size end
+
+    def [](key); @headers[key] end
+    def []=(key, value); @headers[key] = value end
+  end
+
+  class Message
+    fields(:method, :args, :content)
+
+    alias fields args
+
+    def method_missing(name)
+      return args[@method.fields[name].id]
+    end
+
+    def inspect()
+      "#{method.qname}(#{args.join(", ")})"
+    end
+  end
+
+  module Delegate
+    def dispatch(ch, msg)
+      send(msg.method.qname, ch, msg)
+    end
+  end
+
+end