You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by ac...@apache.org on 2017/12/13 20:37:03 UTC

[4/9] qpid-proton git commit: PROTON-1537: [ruby] Fixed encoding problems, simplified Codec::Data.

PROTON-1537: [ruby] Fixed encoding problems, simplified Codec::Data.

Code::Data decodes/encodes all specific AMQP types with methods named type/type=
General encoding of any ruby object with Data#object=, Data#<<
General decoding with Data#object, decodes as ruby object for the type.


Project: http://git-wip-us.apache.org/repos/asf/qpid-proton/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-proton/commit/c172383d
Tree: http://git-wip-us.apache.org/repos/asf/qpid-proton/tree/c172383d
Diff: http://git-wip-us.apache.org/repos/asf/qpid-proton/diff/c172383d

Branch: refs/heads/master
Commit: c172383d4439f0c47a094e403134a7d226b85fa7
Parents: 889c0c6
Author: Alan Conway <ac...@redhat.com>
Authored: Thu Dec 7 14:26:05 2017 -0500
Committer: Alan Conway <ac...@redhat.com>
Committed: Wed Dec 13 13:16:48 2017 -0500

----------------------------------------------------------------------
 proton-c/bindings/ruby/lib/codec/data.rb      | 465 ++++++---------------
 proton-c/bindings/ruby/lib/codec/mapping.rb   | 273 ++++++------
 proton-c/bindings/ruby/lib/core/container.rb  |   2 +-
 proton-c/bindings/ruby/lib/core/event.rb      |   1 -
 proton-c/bindings/ruby/lib/core/exceptions.rb |   3 +-
 proton-c/bindings/ruby/lib/core/message.rb    | 139 ++----
 proton-c/bindings/ruby/lib/qpid_proton.rb     |   1 +
 proton-c/bindings/ruby/lib/types/array.rb     | 188 +++------
 proton-c/bindings/ruby/lib/types/described.rb |  43 +-
 proton-c/bindings/ruby/lib/types/hash.rb      |  63 +--
 proton-c/bindings/ruby/lib/types/type.rb      |  68 +++
 proton-c/bindings/ruby/lib/util/wrapper.rb    |   1 +
 proton-c/bindings/ruby/spec/array_spec.rb     |  17 +-
 proton-c/bindings/ruby/spec/data_spec.rb      |  22 +-
 proton-c/bindings/ruby/spec/hash_spec.rb      |  12 -
 proton-c/bindings/ruby/tests/test_data.rb     |  66 +++
 proton-c/bindings/ruby/tests/test_interop.rb  |  76 ++--
 17 files changed, 541 insertions(+), 899 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/c172383d/proton-c/bindings/ruby/lib/codec/data.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/lib/codec/data.rb b/proton-c/bindings/ruby/lib/codec/data.rb
index 9814836..86af3a1 100644
--- a/proton-c/bindings/ruby/lib/codec/data.rb
+++ b/proton-c/bindings/ruby/lib/codec/data.rb
@@ -15,80 +15,40 @@
 # specific language governing permissions and limitations
 # under the License.
 
+module Qpid::Proton
+  # @private
+  module Codec
 
-module Qpid::Proton module Codec
-
-    # +DataError+ is raised when an error occurs while encoding
-    # or decoding data.
-    class DataError < Exception; end
-
-    # The +Data+ class provides an interface for decoding, extracting,
-    # creating, and encoding arbitrary AMQP data. A +Data+ object
-    # contains a tree of AMQP values. Leaf nodes in this tree correspond
-    # to scalars in the AMQP type system such as INT or STRING. Interior
-    # nodes in this tree correspond to compound values in the AMQP type
-    # system such as *LIST*,*MAP*, *ARRAY*, or *DESCRIBED*. The root node
-    # of the tree is the +Data+ object itself and can have an arbitrary
-    # number of children.
-    #
-    # A +Data+ object maintains the notion of the current sibling node
-    # and a current parent node. Siblings are ordered within their parent.
-    # Values are accessed and/or added by using the #next, #prev,
-    # #enter, and #exit methods to navigate to the desired location in
-    # the tree and using the supplied variety of mutator and accessor
-    # methods to access or add a value of the desired type.
-    #
-    # The mutator methods will always add a value _after_ the current node
-    # in the tree. If the current node has a next sibling the mutator method
-    # will overwrite the value on this node. If there is no current node
-    # or the current node has no next sibling then one will be added. The
-    # accessor methods always set the added/modified node to the current
-    # node. The accessor methods read the value of the current node and do
-    # not change which node is current.
-    #
-    # The following types of scalar values are supported:
-    #
-    # * NULL
-    # * BOOL
-    # * UBYTE
-    # * BYTE
-    # * USHORT
-    # * SHORT
-    # * UINT
-    # * INT
-    # * CHAR
-    # * ULONG
-    # * LONG
-    # * TIMESTAMP
-    # * FLOAT
-    # * DOUBLE
-    # * DECIMAL32
-    # * DECIMAL64
-    # * DECIMAL128
-    # * UUID
-    # * BINARY
-    # * STRING
-    # * SYMBOL
-    #
-    # The following types of compound values are supported:
-    #
-    # * DESCRIBED
-    # * ARRAY
-    # * LIST
-    # * MAP
-    #
+    DataError = ::TypeError
+
+    # @private wrapper for pn_data_t*
+    # Raises TypeError for invalid conversions
     class Data
 
       # @private
-      PROTON_METHOD_PREFIX = "pn_disposition"
+      PROTON_METHOD_PREFIX = "pn_data"
       # @private
       include Util::Wrapper
 
-      # Rewind and convert a pn_data_t* containing a single value to a ruby object.
-      def self.to_object(impl) Data.new(impl).rewind.object; end
+      # @private
+      # Convert a pn_data_t* containing a single value to a ruby object.
+      # @return [Object, nil] The ruby value extracted from +impl+ or nil if impl is empty
+      def self.to_object(impl)
+        if (Cproton.pn_data_size(impl) > 0)
+          d = Data.new(impl)
+          d.rewind
+          d.next_object
+        end
+      end
 
-      # Clear a pn_data_t* and convert a ruby object into it.
-      def self.from_object(impl, x) Data.new(impl).clear.object = x; end
+      # @private
+      # Clear a pn_data_t* and convert a ruby object into it. If x==nil leave it empty.
+      def self.from_object(impl, x)
+        d = Data.new(impl)
+        d.clear
+        d.object = x if x
+        nil
+      end
 
       # @overload initialize(capacity)
       #   @param capacity [Integer] capacity for the new data instance.
@@ -115,86 +75,20 @@ module Qpid::Proton module Codec
         }
       end
 
-      # Clears the object.
-      #
-      def clear
-        Cproton.pn_data_clear(@impl)
-        self
-      end
-
-      # Clears the current node and sets the parent to the root node.
-      #
-      # Clearing the current node sets it *before* the first node, calling
-      # #next will advance to the first node.
-      #
-      # @return self
-      def rewind
-        Cproton.pn_data_rewind(@impl)
-        self
-      end
+      proton_caller :clear, :rewind, :next, :prev, :enter, :exit, :type
 
-      # Advances the current node to its next sibling and returns its types.
-      #
-      # If there is no next sibling the current node remains unchanged
-      # and nil is returned.
-      #
-      def next
-        Cproton.pn_data_next(@impl)
+      def enter_exit()
+        enter
+        yield self
+      ensure
+        exit
       end
 
-      # Advances the current node to its previous sibling and returns its type.
-      #
-      # If there is no previous sibling then the current node remains unchanged
-      # and nil is return.
-      #
-      def prev
-        return Cproton.pn_data_prev(@impl) ? type : nil
-      end
-
-      # Sets the parent node to the current node and clears the current node.
-      #
-      # Clearing the current node sets it _before_ the first child.
-      #
-      def enter
-        Cproton.pn_data_enter(@impl)
-      end
-
-      # Sets the current node to the parent node and the parent node to its own
-      # parent.
-      #
-      def exit
-        Cproton.pn_data_exit(@impl)
-      end
-
-      # Returns the numeric type code of the current node.
-      #
-      # @return [Integer] The current node type.
-      # @return [nil] If there is no current node.
-      #
-      def type_code
-        dtype = Cproton.pn_data_type(@impl)
-        return (dtype == -1) ? nil : dtype
-      end
+      def code() Cproton.pn_data_type(@impl); end
 
-      # @return [Integer] The type object for the current node.
-      # @see #type_code
-      #
-      def type
-        Mapping.for_code(type_code)
-      end
+      def type() Mapping.for_code(Cproton.pn_data_type(@impl)); end
 
       # Returns a representation of the data encoded in AMQP format.
-      #
-      # @return [String] The context of the Data as an AMQP data string.
-      #
-      # @example
-      #
-      #   @impl.string = "This is a test."
-      #   @encoded = @impl.encode
-      #
-      #   # @encoded now contains the text "This is a test." encoded for
-      #   # AMQP transport.
-      #
       def encode
         buffer = "\0"*1024
         loop do
@@ -213,233 +107,101 @@ module Qpid::Proton module Codec
       # of bytes consumed.
       #
       # @param encoded [String] The encoded data.
-      #
-      # @example
-      #
-      #   # SCENARIO: A string of encoded data, @encoded, contains the text
-      #   #           of "This is a test." and is passed to an instance of Data
-      #   #           for decoding.
-      #
-      #   @impl.decode(@encoded)
-      #   @impl.string #=> "This is a test."
-      #
       def decode(encoded)
         check(Cproton.pn_data_decode(@impl, encoded, encoded.length))
       end
 
-      # Puts a list value.
-      #
-      # Elements may be filled by entering the list node and putting element
-      # values.
-      #
-      # @example
-      #
-      #   data = Qpid::Proton::Codec::Data.new
-      #   data.put_list
-      #   data.enter
-      #   data.int = 1
-      #   data.int = 2
-      #   data.int = 3
-      #   data.exit
-      #
-      def put_list
-        check(Cproton.pn_data_put_list(@impl))
-      end
+      proton_is :described, :array_described
+      proton_caller :put_described
+      proton_caller :put_list, :get_list, :put_map, :get_map
+      proton_get :array_type
+      proton_caller :put_array
+      def get_array() [Cproton.pn_data_get_array(@impl), array_described?, array_type]; end
 
-      # If the current node is a list, this returns the number of elements.
-      # Otherwise, it returns zero.
-      #
-      # List elements can be accessed by entering the list.
-      #
-      # @example
-      #
-      #   count = @impl.list
-      #   @impl.enter
-      #   (0...count).each
-      #     type = @impl.next
-      #     puts "Value: #{@impl.string}" if type == STRING
-      #     # ... process other node types
-      #   end
-      def list
-        Cproton.pn_data_get_list(@impl)
+      def expect(code)
+        unless code == self.code
+          raise TypeError, "expected #{Cproton.pn_type_name(code)}, got #{Cproton.pn_type_name(code)}"
+        end
       end
 
-      # Puts a map value.
-      #
-      # Elements may be filled by entering the map node and putting alternating
-      # key/value pairs.
-      #
-      # @example
-      #
-      #   data = Qpid::Proton::Codec::Data.new
-      #   data.put_map
-      #   data.enter
-      #   data.string = "key"
-      #   data.string = "value"
-      #   data.exit
-      #
-      def put_map
-        check(Cproton.pn_data_put_map(@impl))
+      def described
+        expect Cproton::PN_DESCRIBED
+        enter_exit { Types::Described.new(self.next_object, self.next_object) }
       end
 
-      # If the  current node is a map, this returns the number of child
-      # elements. Otherwise, it returns zero.
-      #
-      # Key/value pairs can be accessed by entering the map.
-      #
-      # @example
-      #
-      #   count = @impl.map
-      #   @impl.enter
-      #   (0...count).each do
-      #     type = @impl.next
-      #     puts "Key=#{@impl.string}" if type == STRING
-      #     # ... process other key types
-      #     type = @impl.next
-      #     puts "Value=#{@impl.string}" if type == STRING
-      #     # ... process other value types
-      #   end
-      #   @impl.exit
-      def map
-        Cproton.pn_data_get_map(@impl)
+      def described= d
+        put_described
+        enter_exit { self << d.descriptor << d.value }
       end
 
-      # @private
-      def get_map
-        ::Hash.proton_data_get(self)
+      def fill(a, count, what)
+        a << self.object while self.next
+        raise TypeError, "#{what} expected #{count} elements, got #{a.size}" unless a.size == count
+        a
       end
 
-      # Puts an array value.
-      #
-      # Elements may be filled by entering the array node and putting the
-      # element values. The values must all be of the specified array element
-      # type.
-      #
-      # If an array is *described* then the first child value of the array
-      # is the descriptor and may be of any type.
-      #
-      # @param described [Boolean] True if the array is described.
-      # @param element_type [Integer] The AMQP type for each element of the array.
-      #
-      # @example
-      #
-      #   # create an array of integer values
-      #   data = Qpid::Proton::Codec::Data.new
-      #   data.put_array(false, INT)
-      #   data.enter
-      #   data.int = 1
-      #   data.int = 2
-      #   data.int = 3
-      #   data.exit
-      #
-      #   # create a described  array of double values
-      #   data.put_array(true, DOUBLE)
-      #   data.enter
-      #   data.symbol = "array-descriptor"
-      #   data.double = 1.1
-      #   data.double = 1.2
-      #   data.double = 1.3
-      #   data.exit
-      #
-      def put_array(described, element_type)
-        check(Cproton.pn_data_put_array(@impl, described, element_type.code))
+      def list
+        return array if code == Cproton::PN_ARRAY
+        expect Cproton::PN_LIST
+        count = get_list
+        a = []
+        enter_exit { fill(a, count, __method__) }
       end
 
-      # If the current node is an array, returns a tuple of the element count, a
-      # boolean indicating whether the array is described, and the type of each
-      # element. Otherwise it returns +(0, false, nil).
-      #
-      # Array data can be accessed by entering the array.
-      #
-      # @example
-      #
-      #   # get the details of thecurrent array
-      #   count, described, array_type = @impl.array
-      #
-      #   # enter the node
-      #   data.enter
-      #
-      #   # get the next node
-      #   data.next
-      #   puts "Descriptor: #{data.symbol}" if described
-      #   (0...count).each do
-      #     @impl.next
-      #     puts "Element: #{@impl.string}"
-      #   end
-      def array
-        count = Cproton.pn_data_get_array(@impl)
-        described = Cproton.pn_data_is_array_described(@impl)
-        array_type = Cproton.pn_data_get_array_type(@impl)
-        return nil if array_type == -1
-        [count, described, Mapping.for_code(array_type) ]
+      def list=(a)
+        put_list
+        enter_exit { a.each { |x| self << x } }
       end
 
-      # @private
-      def get_array
-        ::Array.proton_get(self)
+      def array
+        return list if code == Cproton::PN_LIST
+        expect Cproton::PN_ARRAY
+        count, d, t = get_array
+        enter_exit do
+          desc = next_object if d
+          a = Types::UniformArray.new(t, nil, desc)
+          fill(a, count, "array")
+        end
       end
 
-      # Puts a described value.
-      #
-      # A described node has two children, the descriptor and the value.
-      # These are specified by entering the node and putting the
-      # desired values.
-      #
-      # @example
-      #
-      #   data = Qpid::Proton::Codec::Data.new
-      #   data.put_described
-      #   data.enter
-      #   data.symbol = "value-descriptor"
-      #   data.string = "the value"
-      #   data.exit
-      #
-      def put_described
-        check(Cproton.pn_data_put_described(@impl))
+      def array=(a)
+        t = a.type if a.respond_to? :type
+        d = a.descriptor if a.respond_to? :descriptor
+        if (h = a.instance_variable_get(:@proton_array_header))
+          t ||= h.type
+          d ||= h.descriptor
+        end
+        raise TypeError, "no type when converting #{a.class} to an array" unless t
+        put_array(!d.nil?, t.code)
+        m = Mapping[t]
+        enter_exit do
+          self << d unless d.nil?
+          a.each { |e| m.put(self, e); }
+        end
       end
 
-      # @private
-      def get_described
-        raise TypeError, "not a described type" unless self.described?
-        self.enter
-        self.next
-        type = self.type
-        descriptor = type.get(self)
-        self.next
-        type = self.type
-        value = type.get(self)
-        self.exit
-        Qpid::Proton::Types::Described.new(descriptor, value)
+      def map
+        expect Cproton::PN_MAP
+        count = self.get_map
+        raise TypeError, "invalid map, total of keys and values is odd" if count.odd?
+        enter_exit do
+          m = {}
+          m[object] = next_object while self.next
+          raise TypeError, "map expected #{count/2} entries, got #{m.size}" unless m.size == count/2
+          m
+        end
       end
 
-      # Checks if the current node is a described value.
-      #
-      # The described and value may be accessed by entering the described value.
-      #
-      # @example
-      #
-      #   if @impl.described?
-      #     @impl.enter
-      #     puts "The symbol is #{@impl.symbol}"
-      #     puts "The value is #{@impl.string}"
-      #   end
-      def described?
-        Cproton.pn_data_is_described(@impl)
+      def map= m
+        put_map
+        enter_exit { m.each_pair { |k,v| self << k << v } }
       end
 
-      # Puts a null value.
-      #
-      def null
-        check(Cproton.pn_data_put_null(@impl))
-      end
+      # Return nil if vallue is null, raise exception otherwise.
+      def null() raise TypeError, "expected null, got #{type || 'empty'}" unless null?; end
 
-      # Utility method for Qpid::Proton::Codec::Mapping
-      #
-      # @private
-      #
-      def null=(value)
-        null
-      end
+      # Set the current value to null
+      def null=(dummy=nil) check(Cproton.pn_data_put_null(@impl)); end
 
       # Puts an arbitrary object type.
       #
@@ -450,6 +212,16 @@ module Qpid::Proton module Codec
       #
       def object=(object)
         Mapping.for_class(object.class).put(self, object)
+        object
+      end
+
+      # Add an arbitrary data value using object=, return self
+      def <<(x) self.object=x; self; end
+
+      # Move forward to the next value and return it
+      def next_object
+        self.next or raise TypeError, "not enough data"
+        self.object
       end
 
       # Gets the current node, based on how it was encoded.
@@ -457,9 +229,7 @@ module Qpid::Proton module Codec
       # @return [Object] The current node.
       #
       def object
-        type = self.type
-        return nil if type.nil?
-        type.get(data)
+        self.type.get(self) if self.type
       end
 
       # Checks if the current node is null.
@@ -862,10 +632,10 @@ module Qpid::Proton module Codec
       # If the current node is a symbol, returns its value. Otherwise, it
       # returns an empty string ("").
       #
-      # @return [String] The symbolic string value.
+      # @return [Symbol] The symbol value.
       #
       def symbol
-        Cproton.pn_data_get_symbol(@impl)
+        Cproton.pn_data_get_symbol(@impl).to_sym
       end
 
       # Get the current value as a single object.
@@ -901,13 +671,12 @@ module Qpid::Proton module Codec
       # @private
       def check(err)
         if err < 0
-          raise DataError, "[#{err}]: #{Cproton.pn_data_error(@impl)}"
+          raise TypeError, "[#{err}]: #{Cproton.pn_data_error(@impl)}"
         else
           return err
         end
       end
 
     end
-
   end
 end

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/c172383d/proton-c/bindings/ruby/lib/codec/mapping.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/lib/codec/mapping.rb b/proton-c/bindings/ruby/lib/codec/mapping.rb
index 92d0858..1b8df6e 100644
--- a/proton-c/bindings/ruby/lib/codec/mapping.rb
+++ b/proton-c/bindings/ruby/lib/codec/mapping.rb
@@ -16,165 +16,170 @@
 # under the License.
 
 
-module Qpid::Proton::Codec
+module Qpid::Proton
+  module Codec
 
-  # Maps between Proton types and their Ruby native language counterparts.
-  #
-  # @private
-  class Mapping
+    # Maps between Proton types and their Ruby native language counterparts.
+    #
+    # @private
+    class Mapping
+
+      attr_reader :code
+      attr_reader :put_method
+      attr_reader :get_method
+
+      @@by_code = {}
+      @@by_class = {}
+
+      # Creates a new mapping.
+      #
+      # ==== Arguments
+      #
+      # * code    - the AMQP code for this type
+      # * name    - the AMQP name for this type
+      # * klasses - native Ruby classes that are mapped to this AMQP type
+      # * getter  - overrides the get method for the type
+      def initialize(code, name, klasses = nil, getter = nil)
+
+        @code = code
+        @name = name
+
+        @@by_code[code] = self
+
+        unless klasses.nil?
+          klasses.each do |klass|
+            raise "entry exists for #{klass}" if @@by_class.keys.include? klass
+            @@by_class[klass] = self unless klass.nil?
+          end
+        end
 
-    attr_reader :code
-    attr_reader :put_method
-    attr_reader :get_method
+        @put_method = (name + "=").intern
 
-    # Creates a new mapping.
-    #
-    # ==== Arguments
-    #
-    # * code    - the AMQP code for this type
-    # * name    - the AMQP name for this type
-    # * klasses - native Ruby classes that are mapped to this AMQP type
-    # * getter  - overrides the get method for the type
-    def initialize(code, name, klasses = nil, getter = nil)
-
-      @debug = (name == "bool")
-
-      @code = code
-      @name = name
-
-      @@by_preferred ||= {}
-      @@by_code ||= {}
-      @@by_code["#{code}"] = self
-      @@by_name ||= {}
-      @@by_name[name] = self
-      @@by_class ||= {}
-
-      unless klasses.nil?
-        klasses.each do |klass|
-          raise "entry exists for #{klass}" if @@by_class.keys.include? klass
-          @@by_class[klass] = self unless klass.nil?
+        if getter.nil?
+          @get_method = name.intern
+        else
+          @get_method = getter.intern
         end
       end
 
-      @put_method = (name + "=").intern
+      def to_s; @name; end
 
-      if getter.nil?
-        @get_method = name.intern
-      else
-        @get_method = getter.intern
+      def put(data, value)
+        data.__send__(@put_method, value)
       end
-    end
 
-    def to_s; @name; end
+      def get(data)
+        data.__send__(@get_method)
+      end
 
-    def put(data, value)
-      data.__send__(@put_method, value)
-    end
+      def self.for_class(klass)
+        c = klass
+        c = c.superclass while c && (x = @@by_class[c]).nil?
+        raise TypeError, "#{klass} cannot be converted to AMQP" unless x
+        x
+      end
 
-    def get(data)
-      data.__send__(@get_method)
-    end
+      def self.for_code(code)
+        @@by_code[code]
+      end
 
-    def self.for_class(klass)
-      c = klass
-      c = c.superclass while c && (x = @@by_class[c]).nil?
-      raise DataError, "no mapping for #{klass.inspect}" unless x
-      x
-    end
+      # Convert x to a Mapping
+      def self.[](x)
+        case x
+        when Mapping then x
+        when Integer then @@by_code[x]
+        when Types::Type then @@by_code[x.code]
+        end
+      end
 
-    def self.for_code(code)
-      @@by_code["#{code}"]
     end
 
-  end
-
-  NULL       = Mapping.new(Cproton::PN_NULL, "null", [NilClass], "nil?")
-  BOOL       = Mapping.new(Cproton::PN_BOOL, "bool", [TrueClass, FalseClass], "bool")
-  UBYTE      = Mapping.new(Cproton::PN_UBYTE, "ubyte")
-  BYTE       = Mapping.new(Cproton::PN_BYTE, "byte")
-  USHORT     = Mapping.new(Cproton::PN_USHORT, "ushort")
-  SHORT      = Mapping.new(Cproton::PN_SHORT, "short")
-  UINT       = Mapping.new(Cproton::PN_UINT, "uint")
-  INT        = Mapping.new(Cproton::PN_INT, "int")
-  CHAR       = Mapping.new(Cproton::PN_CHAR, "char")
-  ULONG      = Mapping.new(Cproton::PN_ULONG, "ulong")
-  LONG       = Mapping.new(Cproton::PN_LONG, "long", [Integer])
-  TIMESTAMP  = Mapping.new(Cproton::PN_TIMESTAMP, "timestamp", [Date, Time])
-  FLOAT      = Mapping.new(Cproton::PN_FLOAT, "float")
-  DOUBLE     = Mapping.new(Cproton::PN_DOUBLE, "double", [Float])
-  DECIMAL32  = Mapping.new(Cproton::PN_DECIMAL32, "decimal32")
-  DECIMAL64  = Mapping.new(Cproton::PN_DECIMAL64, "decimal64")
-  DECIMAL128 = Mapping.new(Cproton::PN_DECIMAL128, "decimal128")
-  UUID       = Mapping.new(Cproton::PN_UUID, "uuid")
-  BINARY     = Mapping.new(Cproton::PN_BINARY, "binary")
-  STRING     = Mapping.new(Cproton::PN_STRING, "string", [::String,
-                                                          Qpid::Proton::Types::UTFString,
-                                                          Qpid::Proton::Types::BinaryString])
-  SYMBOL     = Mapping.new(Cproton::PN_SYMBOL, "symbol", [::Symbol])
-  DESCRIBED  = Mapping.new(Cproton::PN_DESCRIBED, "described", [Qpid::Proton::Types::Described], "get_described")
-  ARRAY      = Mapping.new(Cproton::PN_ARRAY, "array", nil, "get_array")
-  LIST       = Mapping.new(Cproton::PN_LIST, "list", [::Array], "get_array")
-  MAP        = Mapping.new(Cproton::PN_MAP, "map", [::Hash], "get_map")
-
-
-  private
-
-  class << STRING
-    def put(data, value)
-      # if we have a symbol then convert it to a string
-      value = value.to_s if value.is_a?(Symbol)
-
-      isutf = false
-
-      if value.is_a?(Qpid::Proton::Types::UTFString)
-        isutf = true
-      else
-        # For Ruby 1.8 we will just treat all strings as binary.
-        # For Ruby 1.9+ we can check the encoding first to see what it is
-        if RUBY_VERSION >= "1.9"
-          # If the string is ASCII-8BIT then treat is as binary. Otherwise,
-          # try to convert it to UTF-8 and, if successful, send as that.
-          if value.encoding != Encoding::ASCII_8BIT &&
-             value.encode(Encoding::UTF_8).valid_encoding?
-            isutf = true
+    NULL       = Mapping.new(Cproton::PN_NULL, "null", [NilClass])
+    BOOL       = Mapping.new(Cproton::PN_BOOL, "bool", [TrueClass, FalseClass])
+    UBYTE      = Mapping.new(Cproton::PN_UBYTE, "ubyte")
+    BYTE       = Mapping.new(Cproton::PN_BYTE, "byte")
+    USHORT     = Mapping.new(Cproton::PN_USHORT, "ushort")
+    SHORT      = Mapping.new(Cproton::PN_SHORT, "short")
+    UINT       = Mapping.new(Cproton::PN_UINT, "uint")
+    INT        = Mapping.new(Cproton::PN_INT, "int")
+    CHAR       = Mapping.new(Cproton::PN_CHAR, "char")
+    ULONG      = Mapping.new(Cproton::PN_ULONG, "ulong")
+    LONG       = Mapping.new(Cproton::PN_LONG, "long", [Integer])
+    TIMESTAMP  = Mapping.new(Cproton::PN_TIMESTAMP, "timestamp", [Date, Time])
+    FLOAT      = Mapping.new(Cproton::PN_FLOAT, "float")
+    DOUBLE     = Mapping.new(Cproton::PN_DOUBLE, "double", [Float])
+    DECIMAL32  = Mapping.new(Cproton::PN_DECIMAL32, "decimal32")
+    DECIMAL64  = Mapping.new(Cproton::PN_DECIMAL64, "decimal64")
+    DECIMAL128 = Mapping.new(Cproton::PN_DECIMAL128, "decimal128")
+    UUID       = Mapping.new(Cproton::PN_UUID, "uuid")
+    BINARY     = Mapping.new(Cproton::PN_BINARY, "binary")
+    STRING     = Mapping.new(Cproton::PN_STRING, "string", [::String,
+                                                            Types::UTFString,
+                                                            Types::BinaryString])
+    SYMBOL     = Mapping.new(Cproton::PN_SYMBOL, "symbol", [::Symbol])
+    DESCRIBED  = Mapping.new(Cproton::PN_DESCRIBED, "described", [Types::Described])
+    ARRAY      = Mapping.new(Cproton::PN_ARRAY, "array", [Types::UniformArray])
+    LIST       = Mapping.new(Cproton::PN_LIST, "list", [::Array])
+    MAP        = Mapping.new(Cproton::PN_MAP, "map", [::Hash])
+
+    private
+
+    class << STRING
+      def put(data, value)
+        # if we have a symbol then convert it to a string
+        value = value.to_s if value.is_a?(Symbol)
+
+        isutf = false
+
+        if value.is_a?(Types::UTFString)
+          isutf = true
+        else
+          # For Ruby 1.8 we will just treat all strings as binary.
+          # For Ruby 1.9+ we can check the encoding first to see what it is
+          if RUBY_VERSION >= "1.9"
+            # If the string is ASCII-8BIT then treat is as binary. Otherwise,
+            # try to convert it to UTF-8 and, if successful, send as that.
+            if value.encoding != Encoding::ASCII_8BIT &&
+                value.encode(Encoding::UTF_8).valid_encoding?
+              isutf = true
+            end
           end
         end
-      end
 
-      data.string = value if isutf
-      data.binary = value if !isutf
+        data.string = value if isutf
+        data.binary = value if !isutf
+      end
     end
-  end
 
-  class << MAP
-    def put(data, map, options = {})
-      data.put_map
-      data.enter
-      map.each_pair do |key, value|
-        if options[:keys] == :SYMBOL
-          SYMBOL.put(data, key)
-        else
-          Mapping.for_class(key.class).put(data, key)
-        end
+    class << MAP
+      def put(data, map, options = {})
+        data.put_map
+        data.enter
+        map.each_pair do |key, value|
+          if options[:keys] == :SYMBOL
+            SYMBOL.put(data, key)
+          else
+            data.object = key
+          end
 
-        if value.nil?
-          data.null
-        else
-          Mapping.for_class(value.class).put(data, value)
+          if value.nil?
+            data.null
+          else
+            data.object = value
+          end
         end
+        data.exit
       end
-      data.exit
     end
-  end
 
-  class << DESCRIBED
-    def put(data, described)
-      data.put_described
-      data.enter
-      data.object = described.descriptor
-      data.object = described.value
-      data.exit
+    class << DESCRIBED
+      def put(data, described)
+        data.put_described
+        data.enter
+        data.object = described.descriptor
+        data.object = described.value
+        data.exit
+      end
     end
   end
-
 end

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/c172383d/proton-c/bindings/ruby/lib/core/container.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/lib/core/container.rb b/proton-c/bindings/ruby/lib/core/container.rb
index 91a3d2a..dddde03 100644
--- a/proton-c/bindings/ruby/lib/core/container.rb
+++ b/proton-c/bindings/ruby/lib/core/container.rb
@@ -105,7 +105,7 @@ module Qpid::Proton
     def initialize(handler = nil, id = nil)
       # Allow ID as sole argument
       (handler, id = nil, handler.to_str) if (id.nil? && handler.respond_to?(:to_str))
-      # Allow multiple handlers for backwards compatibility
+      # Allow multiple handlers ofor backwards compatibility
       a = Array(handler)
       @handler = a.size > 1 ? MessagingHandlers.new(a) : handler
       @id = ((id && id.to_s) || SecureRandom.uuid).freeze

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/c172383d/proton-c/bindings/ruby/lib/core/event.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/lib/core/event.rb b/proton-c/bindings/ruby/lib/core/event.rb
index a5aa08e..88fdbaf 100644
--- a/proton-c/bindings/ruby/lib/core/event.rb
+++ b/proton-c/bindings/ruby/lib/core/event.rb
@@ -84,7 +84,6 @@ module Qpid::Proton
       when Cproton::CID_pn_session then Session.wrap(Cproton.pn_cast_pn_session(x))
       when Cproton::CID_pn_link then Link.wrap(Cproton.pn_cast_pn_link(x))
       when Cproton::CID_pn_delivery then Delivery.wrap(Cproton.pn_cast_pn_delivery(x))
-      else raise TypeError, "bad class-id #{pn_class_id(Cproton.pn_event_class(impl))}"
       end
     end
 

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/c172383d/proton-c/bindings/ruby/lib/core/exceptions.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/lib/core/exceptions.rb b/proton-c/bindings/ruby/lib/core/exceptions.rb
index f88b3c8..34a3e60 100644
--- a/proton-c/bindings/ruby/lib/core/exceptions.rb
+++ b/proton-c/bindings/ruby/lib/core/exceptions.rb
@@ -18,8 +18,8 @@
 
 module Qpid::Proton
 
+  # @private
   module Error
-
     NONE = 0
     EOS = Cproton::PN_EOS
     ERROR = Cproton::PN_ERR
@@ -30,7 +30,6 @@ module Qpid::Proton
     TIMEOUT = Cproton::PN_TIMEOUT
     INTERRUPTED = Cproton::PN_INTR
     INPROGRESS = Cproton::PN_INPROGRESS
-
   end
 
   # Represents a generic error at the messaging level.

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/c172383d/proton-c/bindings/ruby/lib/core/message.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/lib/core/message.rb b/proton-c/bindings/ruby/lib/core/message.rb
index 8f4f67c..7041d04 100644
--- a/proton-c/bindings/ruby/lib/core/message.rb
+++ b/proton-c/bindings/ruby/lib/core/message.rb
@@ -48,26 +48,10 @@ module Qpid::Proton
     # @private
     def post_decode
       # decode elements from the message
-      @properties = {}
-      props = Codec::Data.new(Cproton::pn_message_properties(@impl))
-      if props.next
-        @properties = props.type.get(props)
-      end
-      @instructions = nil
-      insts = Codec::Data.new(Cproton::pn_message_instructions(@impl))
-      if insts.next
-        @instructions = insts.type.get(insts)
-      end
-      @annotations = nil
-      annts = Codec::Data.new(Cproton::pn_message_annotations(@impl))
-      if annts.next
-        @annotations = annts.type.get(annts)
-      end
-      @body = nil
-      body = Codec::Data.new(Cproton::pn_message_body(@impl))
-      if body.next
-        @body = body.type.get(body)
-      end
+      @properties = Codec::Data.to_object(Cproton::pn_message_properties(@impl)) || {}
+      @instructions = Codec:: Data.to_object(Cproton::pn_message_instructions(@impl)) || {}
+      @annotations = Codec::Data.to_object(Cproton::pn_message_annotations(@impl)) || {}
+      @body = Codec::Data.to_object(Cproton::pn_message_body(@impl))
     end
 
     # Encodes the message.
@@ -88,27 +72,15 @@ module Qpid::Proton
     # @private
     def pre_encode
       # encode elements from the message
-      props = Codec::Data.new(Cproton::pn_message_properties(@impl))
-      props.clear
-      Codec::Mapping.for_class(@properties.class).put(props, @properties) unless @properties.empty?
-      insts = Codec::Data.new(Cproton::pn_message_instructions(@impl))
-      insts.clear
-      if !@instructions.nil?
-        mapping = Codec::Mapping.for_class(@instructions.class)
-        mapping.put(insts, @instructions)
-      end
-      annts = Codec::Data.new(Cproton::pn_message_annotations(@impl))
-      annts.clear
-      if !@annotations.nil?
-        mapping = Codec::Mapping.for_class(@annotations.class)
-        mapping.put(annts, @annotations, :keys => :SYMBOL)
-      end
-      body = Codec::Data.new(Cproton::pn_message_body(@impl))
-      body.clear
-      if !@body.nil?
-        mapping = Codec::Mapping.for_class(@body.class)
-        mapping.put(body, @body)
+      Codec::Data.from_object(Cproton::pn_message_properties(@impl), !@properties.empty? && @properties)
+      Codec::Data.from_object(Cproton::pn_message_instructions(@impl), !@instructions.empty? && @instructions)
+      if @annotations           # Make sure keys are symbols
+        @annotations.keys.each do |k|
+          @annotations[k.to_sym] = @annotations.delete(k) unless k.is_a? Symbol
+        end
       end
+      Codec::Data.from_object(Cproton::pn_message_annotations(@impl), !@annotations.empty? && @annotations)
+      Codec::Data.from_object(Cproton::pn_message_body(@impl), @body)
     end
 
     # Creates a new +Message+ instance.
@@ -512,89 +484,32 @@ module Qpid::Proton
       Cproton.pn_message_get_reply_to_group_id(@impl)
     end
 
-    # Returns the list of property names for associated with this message.
-    #
-    # ==== Examples
-    #
-    #   msg.properties.each do |name|
-    #   end
-    #
-    def properties
-      @properties
-    end
+    # @return [Hash] Application properties for the message
+    attr_accessor :properties
 
-    # Use +properties+ as the message properties.
-    # @param properties [Hash] new properties
-    def properties=(properties)
-      @properties = properties
-    end
+    # Equivalent to +{#properties}[name] = value+
+    def []=(name, value) @properties[name] = value; end
 
-    # Assigns the value given to the named property.
-    #
-    # ==== Arguments
-    #
-    # * name - the property name
-    # * value - the property value
-    #
-    def []=(name, value)
-      @properties[name] = value
-    end
+    # Equivalent to +{#properties}[name]+
+    def [](name) @properties[name]; end
 
-    # Retrieves the value for the specified property name. If not found, then
-    # it returns nil.
-    #
-    def [](name)
-      @properties[name]
-    end
+    # Equivalent to +{#properties}.delete(name)+
+    def delete_property(name) @properties.delete(name); end
 
-    # Deletes the named property.
-    #
-    def delete_property(name)
-      @properties.delete(name)
-    end
-
-    # Returns the instructions for this message.
-    #
-    def instructions
-      @instructions
-    end
+    # @return [Hash] Delivery instructions for this message.
+    attr_accessor :instructions
 
-    # Assigns instructions to this message.
-    #
-    def instructions=(instr)
-      @instructions = instr
-    end
+    # @return [Hash] Delivery annotations for this message.
+    attr_accessor :annotations
 
-    # Returns the annotations for this message.
-    #
-    def annotations
-      @annotations
-    end
-
-    # Assigns annotations to this message.
-    #
-    def annotations=(annotations)
-      @annotations = annotations
-    end
-
-    # Returns the body property of the message.
-    #
-    def body
-      @body
-    end
-
-    # Assigns a new value to the body of the message.
-    #
-    def body=(body)
-      Qpid::Proton::Codec::Mapping.for_class(body.class) unless body.nil? # Fail now if not convertible
-      @body = body
-    end
+    # @return [Object] body of the message.
+    attr_accessor :body
 
     private
 
     def check(err) # :nodoc:
       if err < 0
-        raise DataError, "[#{err}]: #{Cproton.pn_message_error(@data)}"
+        raise TypeError, "[#{err}]: #{Cproton.pn_message_error(@data)}"
       else
         return err
       end

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/c172383d/proton-c/bindings/ruby/lib/qpid_proton.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/lib/qpid_proton.rb b/proton-c/bindings/ruby/lib/qpid_proton.rb
index a226838..3af6d39 100644
--- a/proton-c/bindings/ruby/lib/qpid_proton.rb
+++ b/proton-c/bindings/ruby/lib/qpid_proton.rb
@@ -51,6 +51,7 @@ require "util/wrapper"
 require "util/timeout"
 
 # Types
+require "types/type"
 require "types/strings"
 require "types/hash"
 require "types/array"

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/c172383d/proton-c/bindings/ruby/lib/types/array.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/lib/types/array.rb b/proton-c/bindings/ruby/lib/types/array.rb
index 88eb26b..3051f31 100644
--- a/proton-c/bindings/ruby/lib/types/array.rb
+++ b/proton-c/bindings/ruby/lib/types/array.rb
@@ -21,151 +21,83 @@
 # to a Qpid::Proton::Data instance.
 #++
 
-module Qpid::Proton::Types
+module Qpid::Proton
+  module Types
+
+    # @deprecated use {UniformArray}
+    class ArrayHeader
+      def initialize(type, descriptor = nil) @type, @descriptor = Codec::Mapping[type], descriptor; end
+      attr_reader :type, :descriptor
+      def described?() !@descriptor.nil?; end
+      def <=>(x) [@type, @descriptor] <=> [x.type, x.descriptor]; end
+      include Comparable
+    end
 
-  # Holds the information for an AMQP Array compound type.
-  #
-  # It holds the type for the array and the descriptor if the
-  # array is described.
-  #
-  # @private
-  #
-  class ArrayHeader
-    attr_reader :type
-    attr_reader :descriptor
+    # *Unsettled API* - An array that is converted to/from an AMQP array of uniform element type.
+    # A plain ruby +::Array+ is converted to/from an AMQP list, which can contain mixed type elements.
+    # If invalid elements are included, then {TypeError} will be raised when encoding to AMQP.
+    class UniformArray < ::Array
+
+      # Construct a uniform array, which will be converted to an AMQP array.
+      # A plain ruby +::Array+ is converted to/from an AMQP list, containing mixed type elements.
+      #
+      # @param type [Type] Elements must be convertible to this AMQP type.
+      # @param elements [Enumerator] Initial elements for the array
+      # @param descriptor [Object] Optional array descriptor
+      def initialize(type, elements=nil, descriptor=nil)
+        @type, @descriptor = type, descriptor
+        raise ArgumentError, "no type specified for array" if @type.nil?
+        super elements if elements
+        @proton_array_header = ArrayHeader.new(@type, @descriptor) # Deprecated
+      end
 
-    def initialize(type, descriptor = nil)
-      @type = type
-      @descriptor = descriptor
-    end
+      # @return [Type] Array elements must be convertible to this AMQP type
+      attr_reader :type
 
-    # Returns true if the array is described.
-    def described?
-      !@descriptor.nil?
-    end
+      # @return [Object] Optional descriptor.
+      def attr_reader() descriptor; end
 
-    def ==(that)
-      ((@type == that.type) && (@descriptor == that.descriptor))
+      def inspect() "#{self.class.name}<#{type}>#{super}"; end
     end
   end
-
 end
 
-# @private
-class Array # :nodoc:
+# {Array} is converted to/from an AMQP list, which is allowed to hold mixed-type elements.
+# Use  {UniformArray} to convert/from an AMQP array with uniform element type.
+class ::Array
+  # @deprecated use  {UniformArray}
+  def proton_array_header
+    Qpid.deprecated __method__, "UniformArray"
+    @proton_array_header
+  end
 
-  # Used to declare an array as an AMQP array.
-  #
-  # The value, if defined, is an instance of Qpid::Proton::Types::ArrayHeader
-  attr_accessor :proton_array_header
+  # @deprecated use  {UniformArray}
+  def proton_array_header=(h)
+    Qpid.deprecated __method__, "UniformArray"
+    @proton_array_header= h
+  end
 
-  # Returns true if the array is the a Proton described type.
-  def proton_described?
-    !@proton_array_header.nil? && @proton_array_header.described?
+  # @deprecated use  {UniformArray}
+  def proton_described?()
+    Qpid.deprecated __method__, "UniformArray"
+    @proton_array_header && @proton_array_header.described?
   end
 
-  # Puts the elements of the array into the specified Qpid::Proton::Data object.
+  # @deprecated
   def proton_put(data)
-    raise TypeError, "data object cannot be nil" if data.nil?
-
-    if @proton_array_header.nil?
-      proton_put_list(data)
+    Qpid.deprecated __method__, "Codec::Data#array=, Codec::Data#list="
+    raise TypeError, "nil data" unless data
+    if @proton_array_header && @proton_array_header.type
+      data.array = self
     else
-      proton_put_array(data)
+      data.list = self
     end
   end
 
-  private
-
-  def proton_put_list(data)
-    # create a list, then enter it and add each element
-    data.put_list
-    data.enter
-    each do |element|
-      # get the proton type for the element
-      mapping = Qpid::Proton::Codec::Mapping.for_class(element.class)
-      # add the element
-      mapping.put(data, element)
-    end
-    # exit the list
-    data.exit
+  # @deprecated
+  def self.proton_get(data)
+    Qpid.deprecated __method__, "Codec::Data#list"
+    data.list
   end
-
-  def proton_put_array(data)
-    data.put_array(@proton_array_header.described?, @proton_array_header.type)
-    data.enter
-    if @proton_array_header.described?
-      data.symbol = @proton_array_header.descriptor
-    end
-
-    each do |element|
-      @proton_array_header.type.put(data, element)
-    end
-
-    data.exit
-  end
-
-  class << self
-
-    # Gets the elements of an array or list out of the specified
-    # Qpid::Proton::Data object.
-    def proton_get(data)
-      raise TypeError, "can't convert nil into Qpid::Proton::Data" if data.nil?
-
-      type = data.type
-
-      if type == Qpid::Proton::Codec::LIST
-        result = proton_get_list(data)
-      elsif type == Qpid::Proton::Codec::ARRAY
-        result = proton_get_array(data)
-      else
-        raise TypeError, "element is not a list and not an array"
-      end
-    end
-
-    private
-
-    def proton_get_list(data)
-      size = data.list
-      raise TypeError, "not a list" unless data.enter
-      elements = []
-      (0...size).each do
-        data.next
-        type = data.type
-        raise TypeError, "missing next element in list" unless type
-        elements << type.get(data)
-      end
-      data.exit
-      return elements
-    end
-
-    def proton_get_array(data)
-      count, described, type = data.array
-
-      raise TypeError, "not an array" unless data.enter
-      elements = []
-
-      descriptor = nil
-
-      if described
-        data.next
-        descriptor = data.symbol
-      end
-
-      elements.proton_array_header = Qpid::Proton::Types::ArrayHeader.new(type, descriptor)
-      (0...count).each do |which|
-        if data.next
-          etype = data.type
-          raise TypeError, "missing next element in array" unless etype
-          raise TypeError, "invalid array element: #{etype}" unless etype == type
-          elements << type.get(data)
-        end
-      end
-      data.exit
-      return elements
-    end
-
-  end
-
 end
 

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/c172383d/proton-c/bindings/ruby/lib/types/described.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/lib/types/described.rb b/proton-c/bindings/ruby/lib/types/described.rb
index fb880ba..41d7ed2 100644
--- a/proton-c/bindings/ruby/lib/types/described.rb
+++ b/proton-c/bindings/ruby/lib/types/described.rb
@@ -17,46 +17,5 @@
 
 
 module Qpid::Proton::Types
-
-  # @private
-  class Described
-
-    attr_reader :descriptor
-    attr_reader :value
-
-    def initialize(descriptor, value)
-      @descriptor = descriptor
-      @value = value
-    end
-
-    # Puts the description into the Data object.
-    #
-    # ==== Arguments
-    #
-    # * data - the Qpid::Proton::Data instance
-    #
-    # ==== Examples
-    #
-    #   described = Qpid::Proton::Described.new("my-descriptor", "the value")
-    #   data = Qpid::Proton::Data.new
-    #   ...
-    #   described.put(data)
-    #
-    def put(data)
-      data.symbol = @descriptor
-      data.string = @value
-    end
-
-    def ==(that) # :nodoc:
-      (that.is_a?(Qpid::Proton::Types::Described) &&
-       (self.descriptor == that.descriptor) &&
-       (self.value == that.value))
-    end
-
-    def to_s # :nodoc:
-      "descriptor=#{descriptor} value=#{value}"
-    end
-
-  end
-
+  Described = Struct.new(:descriptor, :value)
 end

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/c172383d/proton-c/bindings/ruby/lib/types/hash.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/lib/types/hash.rb b/proton-c/bindings/ruby/lib/types/hash.rb
index 9415f86..736bb8f 100644
--- a/proton-c/bindings/ruby/lib/types/hash.rb
+++ b/proton-c/bindings/ruby/lib/types/hash.rb
@@ -23,64 +23,15 @@
 
 # @private
 class Hash # :nodoc:
-
-  # Places the contents of the hash into the specified data object.
-  #
-  # ==== Arguments
-  #
-  # * data - the Qpid::Proton::Data instance
-  #
-  # ==== Examples
-  #
-  #   data = Qpid::Proton::Data.new
-  #   values = {:foo => :bar}
-  #   values.proton_data_put(data)
-  #
+  # @deprecated
   def proton_data_put(data)
-    raise TypeError, "data object cannot be nil" if data.nil?
-
-    data.put_map
-    data.enter
-
-    each_pair do |key, value|
-      type = Qpid::Proton::Codec::Mapping.for_class(key.class)
-      type.put(data, key)
-      type = Qpid::Proton::Codec::Mapping.for_class(value.class)
-      type.put(data, value)
-    end
-
-    data.exit
+    Qpid.deprecated(__method__, "Codec::Data#map=")
+    data.map = self
   end
 
-  class << self
-
-    def proton_data_get(data)
-      raise TypeError, "data object cannot be nil" if data.nil?
-
-      type = data.type
-
-      raise TypeError, "element is not a map" unless type == Qpid::Proton::Codec::MAP
-
-      count = data.map
-      result = {}
-
-      data.enter
-
-      (0...(count/2)).each do
-        data.next
-        type = data.type
-        key = type.get(data)
-        data.next
-        type = data.type
-        value = type.get(data)
-        result[key] = value
-      end
-
-      data.exit
-
-      return result
-    end
-
+  # @deprecated
+  def self.proton_data_get(data)
+    Qpid.deprecated(__method__, "Codec::Data#map")
+    data.map
   end
-
 end

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/c172383d/proton-c/bindings/ruby/lib/types/type.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/lib/types/type.rb b/proton-c/bindings/ruby/lib/types/type.rb
new file mode 100644
index 0000000..8defa42
--- /dev/null
+++ b/proton-c/bindings/ruby/lib/types/type.rb
@@ -0,0 +1,68 @@
+#--
+# 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::Proton
+  module Types
+
+    # Represents an AMQP Type
+    class Type
+      private
+      @@builtin = {}
+      def initialize(code) @code = code; @@builtin[code] = self; end
+
+      public
+      def self.try_convert(code) code.is_a?(Type) ? code : @@builtin[code]; end
+      def self.[](code) try_convert(code) or raise IndexError, "unknown type code #{code}"; end
+
+      attr_reader :code
+      def name() Cproton.pn_type_name(@code); end
+      alias to_s name
+      def <=>(x) @code <=> x; end
+      def hash() @code.hash; end
+    end
+
+    # @!group
+    NULL       = Type.new(Cproton::PN_NULL)
+    BOOL       = Type.new(Cproton::PN_BOOL)
+    UBYTE      = Type.new(Cproton::PN_UBYTE)
+    BYTE       = Type.new(Cproton::PN_BYTE)
+    USHORT     = Type.new(Cproton::PN_USHORT)
+    SHORT      = Type.new(Cproton::PN_SHORT)
+    UINT       = Type.new(Cproton::PN_UINT)
+    INT        = Type.new(Cproton::PN_INT)
+    CHAR       = Type.new(Cproton::PN_CHAR)
+    ULONG      = Type.new(Cproton::PN_ULONG)
+    LONG       = Type.new(Cproton::PN_LONG)
+    TIMESTAMP  = Type.new(Cproton::PN_TIMESTAMP)
+    FLOAT      = Type.new(Cproton::PN_FLOAT)
+    DOUBLE     = Type.new(Cproton::PN_DOUBLE)
+    DECIMAL32  = Type.new(Cproton::PN_DECIMAL32)
+    DECIMAL64  = Type.new(Cproton::PN_DECIMAL64)
+    DECIMAL128 = Type.new(Cproton::PN_DECIMAL128)
+    UUID       = Type.new(Cproton::PN_UUID)
+    BINARY     = Type.new(Cproton::PN_BINARY)
+    STRING     = Type.new(Cproton::PN_STRING)
+    SYMBOL     = Type.new(Cproton::PN_SYMBOL)
+    DESCRIBED  = Type.new(Cproton::PN_DESCRIBED)
+    ARRAY      = Type.new(Cproton::PN_ARRAY)
+    LIST       = Type.new(Cproton::PN_LIST)
+    MAP        = Type.new(Cproton::PN_MAP)
+    #@!endgroup
+  end
+end

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/c172383d/proton-c/bindings/ruby/lib/util/wrapper.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/lib/util/wrapper.rb b/proton-c/bindings/ruby/lib/util/wrapper.rb
index 39cd7b4..347a9a5 100644
--- a/proton-c/bindings/ruby/lib/util/wrapper.rb
+++ b/proton-c/bindings/ruby/lib/util/wrapper.rb
@@ -113,6 +113,7 @@ module Qpid::Proton
       end
       RBCTX = self.hash.to_i
     end
+
     # @private
     module Wrapper
 

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/c172383d/proton-c/bindings/ruby/spec/array_spec.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/spec/array_spec.rb b/proton-c/bindings/ruby/spec/array_spec.rb
index 3bfe559..e760877 100644
--- a/proton-c/bindings/ruby/spec/array_spec.rb
+++ b/proton-c/bindings/ruby/spec/array_spec.rb
@@ -38,20 +38,10 @@ describe "The extended array type" do
     expect(value).respond_to? :proton_described?
   end
 
-  it "raises an error when putting into a nil Data object" do
-    expect { @list.proton_put(nil) }.must_raise(TypeError)
-  end
-
   it "raises an error when getting from a nil Data object" do
     expect {
       Array.proton_get(nil)
-    }.must_raise(TypeError)
-  end
-
-  it "raises an error when the data object is empty" do
-    expect {
-      Array.proton_get(@data)
-    }.must_raise(TypeError)
+    }.must_raise
   end
 
   it "raises an error when the current object is not a list" do
@@ -92,6 +82,7 @@ describe "The extended array type" do
   it "can be put into a Data object as an undescribed array" do
     @undescribed.proton_put(@data)
     result = Array.proton_get(@data)
+    expect(result).is_a? Qpid::Proton::Types::UniformArray
     expect(@undescribed).must_equal(result)
     expect(result.proton_array_header).wont_be_nil
     expect(result.proton_array_header).must_equal(@undescribed.proton_array_header)
@@ -101,8 +92,8 @@ describe "The extended array type" do
   it "can be put into a Data object as a described array" do
     @described.proton_put(@data)
     result = Array.proton_get(@data)
-    expect(@described) == result
-
+    expect(@described).must_equal(result)
+    expect(result).is_a? Qpid::Proton::Types::UniformArray
     expect(result.proton_array_header).wont_be_nil
     expect(result.proton_array_header).must_equal(@described.proton_array_header)
     expect(result.proton_array_header.described?).must_equal(true)

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/c172383d/proton-c/bindings/ruby/spec/data_spec.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/spec/data_spec.rb b/proton-c/bindings/ruby/spec/data_spec.rb
index f4041e4..d5c92fb 100644
--- a/proton-c/bindings/ruby/spec/data_spec.rb
+++ b/proton-c/bindings/ruby/spec/data_spec.rb
@@ -34,7 +34,7 @@ module Qpid
       end
 
       it "can hold a null" do
-        @data.null
+        @data.null = nil
         expect(@data.null?).must_equal(true)
       end
 
@@ -385,17 +385,17 @@ module Qpid
 
       it "can hold a null symbol" do
         @data.symbol = nil
-        expect(@data.symbol).must_equal("")
+        expect(@data.symbol).must_equal(:"")
       end
 
       it "can hold a symbol" do
-        value = random_string(128)
+        value = random_string(128).to_sym
         @data.symbol = value
         expect(@data.symbol).must_equal(value)
       end
 
       it "can hold a described value" do
-        name = random_string(16)
+        name = random_string(16).to_sym
         value = random_string(16)
         @data.put_described
         @data.enter
@@ -411,12 +411,16 @@ module Qpid
         expect(@data.string).must_equal(value)
       end
 
-      it "raises an error when setting the wrong type in an array"
+      it "raises an error when setting the wrong type in an array" do
+        expect {
+          @data << Qpid::Proton::Types::UniformArray.new(Qpid::Proton::Types::INT, [1, 2.0, :a, "b"])
+        }.must_raise(TypeError)
+      end
 
       it "can hold an array" do
         values = []
         (1..(rand(100) + 5)).each { values << rand(2**16) }
-        @data.put_array false, Qpid::Proton::Codec::INT
+        @data.put_array false, Qpid::Proton::Codec::INT.code
         @data.enter
         values.each { |value| @data.int = value }
         @data.exit
@@ -431,14 +435,14 @@ module Qpid
       it "can hold a described array" do
         values = []
         (1..(rand(100) + 5)).each { values << random_string(64) }
-        descriptor = random_string(32)
-        @data.put_array true, Qpid::Proton::Codec::STRING
+        descriptor = random_string(32).to_sym
+        @data.put_array true, Qpid::Proton::Codec::STRING.code
         @data.enter
         @data.symbol = descriptor
         values.each { |value| @data.string = value }
         @data.exit
 
-        expect(@data.array).must_equal([values.size, true, Qpid::Proton::Codec::STRING])
+        expect(@data.get_array).must_equal([values.size, true, Qpid::Proton::Codec::STRING.code])
         @data.enter
         @data.next
         expect(@data.symbol).must_equal(descriptor)

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/c172383d/proton-c/bindings/ruby/spec/hash_spec.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/spec/hash_spec.rb b/proton-c/bindings/ruby/spec/hash_spec.rb
index cd661c2..92acd0a 100644
--- a/proton-c/bindings/ruby/spec/hash_spec.rb
+++ b/proton-c/bindings/ruby/spec/hash_spec.rb
@@ -26,12 +26,6 @@ describe "The extended hash type" do
     @hash = random_hash(rand(128) + 64)
   end
 
-  it "raises an error when put into a nil Data instance" do
-    expect {
-      @hash.proton_data_put(nil)
-    }.must_raise(TypeError)
-  end
-
   it "can be put into an instance of Data" do
     @hash.proton_data_put(@data)
     result = Hash.proton_data_get(@data)
@@ -39,12 +33,6 @@ describe "The extended hash type" do
     expect(result.values).must_equal(@hash.values)
   end
 
-  it "raises an error when retrieved from a nil Data instance" do
-    expect {
-      Hash.proton_data_get(nil)
-    }.must_raise(TypeError)
-  end
-
   it "raises an error when trying to get what is not a Hash" do
     @data.string = random_string(128)
     @data.rewind

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/c172383d/proton-c/bindings/ruby/tests/test_data.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/tests/test_data.rb b/proton-c/bindings/ruby/tests/test_data.rb
new file mode 100644
index 0000000..b1ae2d9
--- /dev/null
+++ b/proton-c/bindings/ruby/tests/test_data.rb
@@ -0,0 +1,66 @@
+# 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 'minitest/autorun'
+require "securerandom"
+require 'qpid_proton'
+
+class TestData < Minitest::Test
+  include Qpid::Proton
+
+  def assert_from_to(*values)
+    d = Codec::Data.new
+    values.each do |x|
+      Codec::Data.from_object(d.impl, x)
+      assert_equal x, Codec::Data.to_object(d.impl)
+    end
+  end
+
+  def test_from_to
+    assert_from_to({ 1 => :one, 2=>:two })
+    assert_from_to([{:a => 1, "b" => 2}, 3, 4.4, :five])
+    assert_from_to(Types::UniformArray.new(Types::INT, [1, 2, 3, 4]))
+  end
+
+  def rnum(*arg) SecureRandom.random_number(*arg); end
+  def rstr(*arg) SecureRandom.base64(*arg); end
+
+  def test_nil()
+    assert_nil((Codec::Data.new << nil).object)
+  end
+
+  def assert_convert(*values)
+    values.each { |x| assert_equal x, ((Codec::Data.new << x).object) }
+  end
+
+  def test_bool()
+    assert_convert(true, false)
+  end
+
+  def test_float()
+    assert_convert(0.0, 1.0, -1.0, 1.23e123, rnum(), rnum(), rnum(), rnum())
+  end
+
+  def test_string()
+    assert_convert("", "foo", rstr(100000), rstr(rnum(1000)), rstr(rnum(1000)))
+  end
+
+  def test_symbol()
+    assert_convert(:"", :foo, rstr(256).to_sym)
+  end
+end

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/c172383d/proton-c/bindings/ruby/tests/test_interop.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/tests/test_interop.rb b/proton-c/bindings/ruby/tests/test_interop.rb
index e08ada1..0a13a3d 100755
--- a/proton-c/bindings/ruby/tests/test_interop.rb
+++ b/proton-c/bindings/ruby/tests/test_interop.rb
@@ -14,8 +14,8 @@ if ((RUBY_VERSION.split(".").map {|x| x.to_i}  <=> [1, 9]) < 0)
 end
 
 class InteropTest < MiniTest::Test
-  Data = Qpid::Proton::Codec::Data
-  Message = Qpid::Proton::Message
+  include  Qpid::Proton
+  Data = Codec::Data
 
   def setup
     @data = Data.new
@@ -53,87 +53,81 @@ class InteropTest < MiniTest::Test
   def assert_next(type, value)
     assert @data.next
     assert_equal(type, @data.type)
-    assert_equal(value, type.get(@data))
+    assert_equal(value, @data.object)
   end
 
-  def assert_array_next(expected, header)
-    assert_next(Qpid::Proton::Codec::ARRAY, expected)
-    result = @data.type.get(@data)
-    assert_equal(result.proton_array_header, header)
+  def assert_array_next(expected)
+    result = @data.next_object
+    assert_equal(expected, result)
   end
 
   def test_message
     decode_message_file("message")
-    assert_next(Qpid::Proton::Codec::STRING, "hello")
+    assert_next(Codec::STRING, "hello")
     assert !@data.next
   end
 
   def test_primitives
     decode_data_file("primitives")
-    assert_next(Qpid::Proton::Codec::BOOL, true)
-    assert_next(Qpid::Proton::Codec::BOOL, false)
-    assert_next(Qpid::Proton::Codec::UBYTE, 42)
-    assert_next(Qpid::Proton::Codec::USHORT, 42)
-    assert_next(Qpid::Proton::Codec::SHORT, -42)
-    assert_next(Qpid::Proton::Codec::UINT, 12345)
-    assert_next(Qpid::Proton::Codec::INT, -12345)
-    assert_next(Qpid::Proton::Codec::ULONG, 12345)
-    assert_next(Qpid::Proton::Codec::LONG, -12345)
-    assert_next(Qpid::Proton::Codec::FLOAT, 0.125)
-    assert_next(Qpid::Proton::Codec::DOUBLE, 0.125)
+    assert_next(Codec::BOOL, true)
+    assert_next(Codec::BOOL, false)
+    assert_next(Codec::UBYTE, 42)
+    assert_next(Codec::USHORT, 42)
+    assert_next(Codec::SHORT, -42)
+    assert_next(Codec::UINT, 12345)
+    assert_next(Codec::INT, -12345)
+    assert_next(Codec::ULONG, 12345)
+    assert_next(Codec::LONG, -12345)
+    assert_next(Codec::FLOAT, 0.125)
+    assert_next(Codec::DOUBLE, 0.125)
     assert !@data.next
   end
 
   def test_strings
     decode_data_file("strings")
-    assert_next(Qpid::Proton::Codec::BINARY, "abc\0defg")
-    assert_next(Qpid::Proton::Codec::STRING, "abcdefg")
-    assert_next(Qpid::Proton::Codec::SYMBOL, "abcdefg")
-    assert_next(Qpid::Proton::Codec::BINARY, "")
-    assert_next(Qpid::Proton::Codec::STRING, "")
-    assert_next(Qpid::Proton::Codec::SYMBOL, "")
+    assert_next(Codec::BINARY, "abc\0defg")
+    assert_next(Codec::STRING, "abcdefg")
+    assert_next(Codec::SYMBOL, :abcdefg)
+    assert_next(Codec::BINARY, "")
+    assert_next(Codec::STRING, "")
+    assert_next(Codec::SYMBOL, :"")
     assert !@data.next
   end
 
   def test_described
     decode_data_file("described")
-    assert_next(Qpid::Proton::Codec::DESCRIBED, Qpid::Proton::Types::Described.new("foo-descriptor", "foo-value"))
+    assert_next(Codec::DESCRIBED, Types::Described.new(:"foo-descriptor", "foo-value"))
     assert(@data.described?)
-    assert_next(Qpid::Proton::Codec::DESCRIBED, Qpid::Proton::Types::Described.new(12, 13))
+    assert_next(Codec::DESCRIBED, Types::Described.new(12, 13))
     assert(@data.described?)
     assert !@data.next
   end
 
   def test_described_array
     decode_data_file("described_array")
-    assert_array_next((0...10).to_a,
-                       Qpid::Proton::Types::ArrayHeader.new(Qpid::Proton::Codec::INT,
-                                                     "int-array"))
+    assert_array_next(Types::UniformArray.new(Types::INT, (0...10).to_a, :"int-array"))
   end
 
   def test_arrays
     decode_data_file("arrays")
-    assert_array_next((0...100).to_a,
-                      Qpid::Proton::Types::ArrayHeader.new(Qpid::Proton::Codec::INT))
-    assert_array_next(["a", "b", "c"],
-                      Qpid::Proton::Types::ArrayHeader.new(Qpid::Proton::Codec::STRING))
-    assert_array_next([],
-                      Qpid::Proton::Types::ArrayHeader.new(Qpid::Proton::Codec::INT))
+    assert_array_next(Types::UniformArray.new(Codec::INT, (0...100).to_a))
+    assert_array_next(Types::UniformArray.new(Codec::STRING, ["a", "b", "c"]))
+    assert_array_next(Types::UniformArray.new(Codec::INT))
     assert !@data.next
   end
 
   def test_lists
     decode_data_file("lists")
-    assert_next(Qpid::Proton::Codec::LIST, [32, "foo", true])
-    assert_next(Qpid::Proton::Codec::LIST, [])
+    assert_next(Codec::LIST, [32, "foo", true])
+    assert_next(Codec::LIST, [])
     assert !@data.next
   end
 
   def test_maps
     decode_data_file("maps")
-    assert_next(Qpid::Proton::Codec::MAP, {"one" => 1, "two" => 2, "three" => 3 })
-    assert_next(Qpid::Proton::Codec::MAP, {1 => "one", 2 => "two", 3 => "three"})
-    assert_next(Qpid::Proton::Codec::MAP, {})
+    assert_next(Codec::MAP, {"one" => 1, "two" => 2, "three" => 3 })
+    assert_next(Codec::MAP, {1 => "one", 2 => "two", 3 => "three"})
+    assert_next(Codec::MAP, {})
     assert !@data.next
   end
 end


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org