Author: mcpierce
Date: Fri May 31 18:13:12 2013
New Revision: 1488342

URL: http://svn.apache.org/r1488342
Log:
PROTON-322: Extended the Ruby Array class to work with the Data class.

Added attributes for setting the Proton type and description for
encoding an entire array as a described Array. If the type isn't set
then the array is added as an AMQP List instead.

Added:
    qpid/proton/trunk/proton-c/bindings/ruby/lib/qpid_proton/array.rb
    qpid/proton/trunk/proton-c/bindings/ruby/lib/qpid_proton/described.rb
    qpid/proton/trunk/proton-c/bindings/ruby/lib/qpid_proton/mapping.rb
    qpid/proton/trunk/proton-c/bindings/ruby/spec/qpid/proton/array_spec.rb
Modified:
    qpid/proton/trunk/proton-c/bindings/ruby/ChangeLog
    qpid/proton/trunk/proton-c/bindings/ruby/lib/qpid_proton.rb
    qpid/proton/trunk/proton-c/bindings/ruby/lib/qpid_proton/data.rb
    qpid/proton/trunk/proton-c/bindings/ruby/spec/spec_helper.rb

Modified: qpid/proton/trunk/proton-c/bindings/ruby/ChangeLog
URL: 
http://svn.apache.org/viewvc/qpid/proton/trunk/proton-c/bindings/ruby/ChangeLog?rev=1488342&r1=1488341&r2=1488342&view=diff
==============================================================================
--- qpid/proton/trunk/proton-c/bindings/ruby/ChangeLog (original)
+++ qpid/proton/trunk/proton-c/bindings/ruby/ChangeLog Fri May 31 18:13:12 2013
@@ -1,3 +1,6 @@
+version 0.5:
+       * Duck typed the Array class to work with Qpid::Proton::Data.
+
 version 0.4:
        * No language-specific features developed in this release.
 

Modified: qpid/proton/trunk/proton-c/bindings/ruby/lib/qpid_proton.rb
URL: 
http://svn.apache.org/viewvc/qpid/proton/trunk/proton-c/bindings/ruby/lib/qpid_proton.rb?rev=1488342&r1=1488341&r2=1488342&view=diff
==============================================================================
--- qpid/proton/trunk/proton-c/bindings/ruby/lib/qpid_proton.rb (original)
+++ qpid/proton/trunk/proton-c/bindings/ruby/lib/qpid_proton.rb Fri May 31 
18:13:12 2013
@@ -19,6 +19,9 @@
 
 require "cproton"
 
+require "qpid_proton/described"
+require "qpid_proton/mapping"
+require "qpid_proton/array"
 require "qpid_proton/exceptions"
 require "qpid_proton/exception_handling"
 require "qpid_proton/message_format"

Added: qpid/proton/trunk/proton-c/bindings/ruby/lib/qpid_proton/array.rb
URL: 
http://svn.apache.org/viewvc/qpid/proton/trunk/proton-c/bindings/ruby/lib/qpid_proton/array.rb?rev=1488342&view=auto
==============================================================================
--- qpid/proton/trunk/proton-c/bindings/ruby/lib/qpid_proton/array.rb (added)
+++ qpid/proton/trunk/proton-c/bindings/ruby/lib/qpid_proton/array.rb Fri May 
31 18:13:12 2013
@@ -0,0 +1,170 @@
+#
+# 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.
+#
+
+#--
+# Patch the Array class to provide methods for adding its contents
+# to a Qpid::Proton::Data instance.
+#++
+
+module Qpid
+
+  module Proton
+
+    # Holds the information for an AMQP Array compound type.
+    #
+    # It holds the type for the array and the descriptor if the
+    # array is described.
+    #
+    class ArrayHeader
+      attr_reader :type
+      attr_reader :descriptor
+
+      def initialize(type, descriptor = nil)
+        @type = type
+        @descriptor = descriptor
+      end
+
+      # Returns true if the array is described.
+      def described?
+        [email protected]?
+      end
+
+      def ==(that)
+        ((@type == that.type) && (@descriptor == that.descriptor))
+      end
+    end
+
+  end
+
+end
+
+class Array
+
+  # Used to declare an array as an AMQP array.
+  #
+  # The value, if defined, is an instance of Qpid::Proton::ArrayHeader
+  attr_accessor :proton_array_header
+
+  # Returns true if the array is the a Proton described type.
+  def proton_described?
+    !@proton_array_header.nil? && @proton_array_header.described?
+  end
+
+  # Puts the elements of the array into the specified Qpid::Proton::Data 
object.
+  def proton_put(data)
+    raise TypeError, "data object cannot be nil" if data.nil?
+
+    if @proton_array_header.nil?
+      proton_put_list(data)
+    else
+      proton_put_array(data)
+    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::Mapping.for_class(element.class)
+      # add the element
+      mapping.put(data, element)
+    end
+    # exit the list
+    data.exit
+  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::LIST
+        result = proton_get_list(data)
+      elsif type == Qpid::Proton::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
+        type = data.next
+        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::ArrayHeader.new(type, 
descriptor)
+      (0...count).each do |which|
+        etype = data.next
+        raise TypeError, "missing next element in array" unless etype
+        raise TypeError, "invalid array element: #{etype}" unless etype == type
+        elements << type.get(data)
+      end
+      data.exit
+      return elements
+    end
+
+  end
+
+end
+

Modified: qpid/proton/trunk/proton-c/bindings/ruby/lib/qpid_proton/data.rb
URL: 
http://svn.apache.org/viewvc/qpid/proton/trunk/proton-c/bindings/ruby/lib/qpid_proton/data.rb?rev=1488342&r1=1488341&r2=1488342&view=diff
==============================================================================
--- qpid/proton/trunk/proton-c/bindings/ruby/lib/qpid_proton/data.rb (original)
+++ qpid/proton/trunk/proton-c/bindings/ruby/lib/qpid_proton/data.rb Fri May 31 
18:13:12 2013
@@ -161,7 +161,7 @@ module Qpid
 
       # Return the Type object for the current node
       def type
-        Type.by_code(type_code)
+        Mapping.for_code(type_code)
       end
 
       # Returns a representation of the data encoded in AMQP format.
@@ -331,7 +331,7 @@ module Qpid
         described = Cproton.pn_data_is_array_described(@data)
         array_type = Cproton.pn_data_get_array_type(@data)
         return nil if array_type == -1
-        [count, described, Type.by_code(array_type) ]
+        [count, described, Mapping.for_code(array_type) ]
       end
 
       # Puts a described value.
@@ -614,7 +614,7 @@ module Qpid
       # * value - the decimal128 value
       def decimal128=(value)
         raise TypeError, "invalid decimal128 value: #{value}" if value.nil?
-        value = value.to_s(16).rjust(32, "0")
+        value = value.to_s.rjust(32, "0")
         bytes = []
         value.scan(/(..)/) {|v| bytes << v[0].to_i(16)}
         check(Cproton.pn_data_put_decimal128(@data, bytes))
@@ -726,132 +726,6 @@ module Qpid
         Cproton.pn_data_get_symbol(@data)
       end
 
-      # Convenience class for described types.
-      #
-      # Holds the descriptor and value, implements methods to get and put a
-      # described type in a Data object.
-      Described = Struct.new(:descriptor, :value)
-      class Described
-        def self.get(data)
-          def fail; raise 'Not a described type'; end
-          (data.described? and data.enter) or fail
-          begin
-            data.next or fail
-            descriptor = data.get
-            data.next or fail
-            value = data.get
-            return Described.new(descriptor, value)
-          ensure
-            data.exit
-          end
-        end
-
-        def put(data)
-          described
-          enter
-          data.put(@descriptor)
-          data.put(@value)
-        end
-      end
-
-      # Convenience class for arrays
-      #
-      # Convenience class for arrays.
-      #
-      # An Array with methods to get and put the array in a Data object
-      # as an AMQP array.
-      class Array < ::Array
-        def initialize(descriptor, type, elements=[])
-          @descriptor, @type = descriptor, type
-          super(elements.collect { |x| x })
-        end
-
-        attr_reader :descriptor, :type
-
-        def ==(o) super; end
-        def eql?(o)
-          o.class == self.class && @descriptor == o.descriptor && @type == 
o.type &&
-            super
-        end
-
-
-        def self.get(data)
-          count, described, type = data.array
-          data.enter or raise 'Not an array'
-          begin
-            descriptor = nil
-            if described then
-              data.next; descriptor = data.get
-            end
-            elements = []
-            while data.next do; elements << data.get; end
-            elements.size == count or
-              raise "Array wrong length, expected #{count} but got 
#{elements.size}"
-            return Array.new(descriptor, type, elements)
-          ensure
-            data.exit
-          end
-        end
-
-        def put(data)
-          data.put_array(@descriptor, @type)
-          data.enter
-          begin
-            data.put(@descriptor) if @descriptor
-            elements.each { |e| data.put(e); }
-          ensure
-            data.exit
-          end
-        end
-      end
-
-      # Convenience class for arrays.
-      #
-      # An array with methods to get and put the in a Data object
-      # as an AMQP list.
-      class List < ::Array
-
-        PROTON_TYPE_FOR_CLASS = {
-          ::NilClass   => Cproton::PN_NULL,
-          ::TrueClass  => Cproton::PN_BOOL,
-          ::FalseClass => Cproton::PN_BOOL,
-          ::Fixnum     => Cproton::PN_INT,
-          ::Bignum     => Cproton::PN_LONG,
-          ::String     => Cproton::PN_STRING,
-          ::Float      => Cproton::PN_FLOAT
-        }
-
-        def initialize(elements = []) super; end
-
-        def self.get(data)
-          def fail; raise 'Not a list'; end
-          size = data.list
-          data.enter or fail
-          begin
-            elements = []
-            while data.next do elements << data.get; end
-            elements.size == size or
-              raise "List wrong length, expected #{size} but got 
#{elements.size}"
-            return List.new(elements)
-          ensure
-            data.exit
-          end
-        end
-
-        def put(data)
-          data.put_list
-          data.enter
-          self.each do |e|
-            etype = PROTON_TYPE_FOR_CLASS[e.class]
-            puts "I think #{e} is of type #{etype}"
-            etype = Type.by_code(etype)
-            puts "The type helper is #{etype}"
-            etype.put(data, e)
-          end
-          data.exit
-        end
-      end
-
       # Convenience class for maps
       #
       # A Hash with methods to get and put the map in a Data object as an AMQP
@@ -898,49 +772,6 @@ module Qpid
         def self.put(data, value); data.null; end
       end
 
-      # Information about AMQP types including how to get/put an object of that
-      # type in a Data object.
-      #
-      # A convenience class is provided for each of the compound types
-      # to allow get/put of those types as a single object.
-      class Type
-        attr_reader :code, :name
-
-        def initialize(code,name,klass=nil)
-          @code, @name, @klass = code,name,klass;
-          @get,@put = name.intern,(name+"=").intern if !klass
-          @@by_code ||= {}
-          @@by_code[code] = self
-          @@by_name ||= {}
-          @@by_name[name] = self
-        end
-
-        def get(data)
-          if @klass then @klass.send(:get, data)
-          else data.send(@get); end
-        end
-
-        def put(data, value)
-          if @klass then
-            @klass.send(:put, data, value)
-          else
-            data.send(@put, value)
-          end
-        end
-
-        def to_s() return name; end
-        def self.by_name(name) @@by_name[name]; end
-        def self.by_code(code) @@by_code[code]; end
-
-
-        def self.get(data)
-          if @klass then @klass.send(:get, data)
-          else data.send(@get); end
-        end
-
-        def to_s; name; end
-      end
-
       # Get the current value as a single object.
       def get
         type.get(self);
@@ -951,33 +782,6 @@ module Qpid
         type_.put(self, value);
       end
 
-      # Constants for all the supported types
-      NULL       = Type.new(Cproton::PN_NULL,       "null",      Null)
-      BOOL       = Type.new(Cproton::PN_BOOL,       "bool")
-      UBYTE      = Type.new(Cproton::PN_UBYTE,      "ubyte")
-      BYTE       = Type.new(Cproton::PN_BYTE,       "byte")
-      USHORT     = Type.new(Cproton::PN_USHORT,     "ushort")
-      SHORT      = Type.new(Cproton::PN_SHORT,      "short")
-      UINT       = Type.new(Cproton::PN_UINT,       "uint")
-      INT        = Type.new(Cproton::PN_INT,        "int")
-      CHAR       = Type.new(Cproton::PN_CHAR,       "char")
-      ULONG      = Type.new(Cproton::PN_ULONG,      "ulong")
-      LONG       = Type.new(Cproton::PN_LONG,       "long")
-      TIMESTAMP  = Type.new(Cproton::PN_TIMESTAMP,  "timestamp")
-      FLOAT      = Type.new(Cproton::PN_FLOAT,      "float")
-      DOUBLE     = Type.new(Cproton::PN_DOUBLE,     "double")
-      DECIMAL32  = Type.new(Cproton::PN_DECIMAL32,  "decimal32")
-      DECIMAL64  = Type.new(Cproton::PN_DECIMAL64,  "decimal64")
-      DECIMAL128 = Type.new(Cproton::PN_DECIMAL128, "decimal128")
-      UUID       = Type.new(Cproton::PN_UUID,       "uuid")
-      BINARY     = Type.new(Cproton::PN_BINARY,     "binary")
-      STRING     = Type.new(Cproton::PN_STRING,     "string")
-      SYMBOL     = Type.new(Cproton::PN_SYMBOL,     "symbol")
-      DESCRIBED  = Type.new(Cproton::PN_DESCRIBED,  "described", Described)
-      ARRAY      = Type.new(Cproton::PN_ARRAY,      "array",     Array)
-      LIST       = Type.new(Cproton::PN_LIST,       "list",      List)
-      MAP        = Type.new(Cproton::PN_MAP,        "map",       Map)
-
       private
 
       def valid_uuid?(value)

Added: qpid/proton/trunk/proton-c/bindings/ruby/lib/qpid_proton/described.rb
URL: 
http://svn.apache.org/viewvc/qpid/proton/trunk/proton-c/bindings/ruby/lib/qpid_proton/described.rb?rev=1488342&view=auto
==============================================================================
--- qpid/proton/trunk/proton-c/bindings/ruby/lib/qpid_proton/described.rb 
(added)
+++ qpid/proton/trunk/proton-c/bindings/ruby/lib/qpid_proton/described.rb Fri 
May 31 18:13:12 2013
@@ -0,0 +1,72 @@
+#
+# 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 # :nodoc:
+
+  module Proton # :nodoc:
+
+    class Described
+
+      attr_reader :descriptor
+      attr_reader :value
+
+      def initialize(descriptor, value)
+        @descriptor = descriptor
+        @value = value
+      end
+
+      # Retrieves the descriptor and value from the supplied Data object.
+      #
+      # ==== Arguments
+      #
+      # * data - the Qpid::Proton::Data instance
+      #
+      def self.get(data)
+        type = data.next
+        raise TypeError, "not a described type" unless type == Mapping.SYMBOL
+        descriptor = data.symbol
+        type = data.next
+        raise TypeError, "not a described type" unless type == Mapping.STRING
+        value = data.string
+        Described.new(descriptor, 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
+
+    end
+
+  end
+
+end

Added: qpid/proton/trunk/proton-c/bindings/ruby/lib/qpid_proton/mapping.rb
URL: 
http://svn.apache.org/viewvc/qpid/proton/trunk/proton-c/bindings/ruby/lib/qpid_proton/mapping.rb?rev=1488342&view=auto
==============================================================================
--- qpid/proton/trunk/proton-c/bindings/ruby/lib/qpid_proton/mapping.rb (added)
+++ qpid/proton/trunk/proton-c/bindings/ruby/lib/qpid_proton/mapping.rb Fri May 
31 18:13:12 2013
@@ -0,0 +1,109 @@
+#
+# 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 # :nodoc:
+
+  module Proton # :nodoc:
+
+    # Maps between Proton types and their Ruby native language counterparts.
+    #
+    class Mapping
+
+      attr_reader :code
+      attr_reader :put_method
+      attr_reader :get_method
+
+      # Creates a new mapping.
+      #
+      # ==== Arguments
+      #
+      # * code    - the AMQP code for this type
+      # * name    - the AMQP name for this type
+      # * klasses - the Ruby classes for this type
+      def initialize(code, name, klasses = nil)
+        @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?
+          end
+        end
+
+        @put_method = (name + "=").intern
+        @get_method = name.intern
+      end
+
+      def to_s; @name; end
+
+      def put(data, value)
+        data.send(@put_method, value)
+      end
+
+      def get(data)
+        data.send(@get_method)
+      end
+
+      def self.for_class(klass) # :nodoc:
+        @@by_class[klass]
+      end
+
+      def self.for_code(code)
+        @@by_code["#{code}"]
+      end
+
+    end
+
+    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", [Fixnum, Bignum])
+    TIMESTAMP  = Mapping.new(Cproton::PN_TIMESTAMP, "timestamp", [Date])
+    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])
+    SYMBOL     = Mapping.new(Cproton::PN_SYMBOL, "symbol")
+    DESCRIBED  = Mapping.new(Cproton::PN_DESCRIBED, "described", 
[Qpid::Proton::Described])
+    ARRAY      = Mapping.new(Cproton::PN_ARRAY, "array")
+    LIST       = Mapping.new(Cproton::PN_LIST, "list", [::Array])
+    MAP        = Mapping.new(Cproton::PN_MAP, "map", [::Hash])
+
+  end
+
+end

Added: qpid/proton/trunk/proton-c/bindings/ruby/spec/qpid/proton/array_spec.rb
URL: 
http://svn.apache.org/viewvc/qpid/proton/trunk/proton-c/bindings/ruby/spec/qpid/proton/array_spec.rb?rev=1488342&view=auto
==============================================================================
--- qpid/proton/trunk/proton-c/bindings/ruby/spec/qpid/proton/array_spec.rb 
(added)
+++ qpid/proton/trunk/proton-c/bindings/ruby/spec/qpid/proton/array_spec.rb Fri 
May 31 18:13:12 2013
@@ -0,0 +1,114 @@
+#
+# 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 "spec_helper"
+
+describe "The extended array type" do
+
+  before :each do
+    @data        = Qpid::Proton::Data.new
+    @list        = random_list(rand(100))
+    @undescribed = random_array(rand(100))
+    @description = random_string(128)
+    @described   = random_array(rand(100), true, @description)
+  end
+
+  it "can be created like a normal array" do
+    value = []
+
+    value.should respond_to :proton_put
+    value.should respond_to :proton_array_header
+    value.class.should respond_to :proton_get
+    value.should respond_to :proton_described?
+  end
+
+  it "raises an error when putting into a nil Data object" do
+    expect {
+      @list.proton_put(nil)
+    }.to raise_error
+  end
+
+  it "raises an error when getting from a nil Data object" do
+    expect {
+      Array.proton_get(nil)
+    }.to raise_error(TypeError)
+  end
+
+  it "raises an error when the data object is empty" do
+    expect {
+      Array.proton_get(@data)
+    }.to raise_error(TypeError)
+  end
+
+  it "raises an error when the current object is not a list" do
+    @data.string = random_string(128)
+    @data.rewind
+
+    expect {
+      Array.proton_get(@data)
+    }.to raise_error(TypeError)
+  end
+
+  it "does not have an array header when it's a simple list" do
+    @list.proton_described?.should be_false
+  end
+
+  it "can be put into a Data object as a list" do
+    @list.proton_put(@data)
+    result = Array.proton_get(@data)
+    result.should =~ @list
+    result.proton_array_header.should be_nil
+  end
+
+  it "has an array header when it's an AMQP array" do
+    @undescribed.proton_array_header.should_not be_nil
+    @described.proton_array_header.should_not be_nil
+  end
+
+  it "raises an error when the elements of an Array are dissimilar and is put 
into a Data object" do
+    value = []
+    value.proton_array_header = 
Qpid::Proton::ArrayHeader.new(Qpid::Proton::INT)
+    value << random_string(16)
+
+    expect {
+      value.proton_put(@data)
+    }.to raise_error(TypeError)
+  end
+
+  it "can be put into a Data object as an undescribed array" do
+    @undescribed.proton_put(@data)
+    result = Array.proton_get(@data)
+    be_close_array(@undescribed, result)
+
+    result.proton_array_header.should_not be_nil
+    result.proton_array_header.should == @undescribed.proton_array_header
+    result.proton_array_header.described?.should be_false
+  end
+
+  it "can be put into a Data object as a described array" do
+    @described.proton_put(@data)
+    result = Array.proton_get(@data)
+    be_close_array(@described, result)
+
+    result.proton_array_header.should_not be_nil
+    result.proton_array_header.should == @described.proton_array_header
+    result.proton_array_header.described?.should be_true
+  end
+
+end

Modified: qpid/proton/trunk/proton-c/bindings/ruby/spec/spec_helper.rb
URL: 
http://svn.apache.org/viewvc/qpid/proton/trunk/proton-c/bindings/ruby/spec/spec_helper.rb?rev=1488342&r1=1488341&r2=1488342&view=diff
==============================================================================
--- qpid/proton/trunk/proton-c/bindings/ruby/spec/spec_helper.rb (original)
+++ qpid/proton/trunk/proton-c/bindings/ruby/spec/spec_helper.rb Fri May 31 
18:13:12 2013
@@ -30,6 +30,7 @@ rescue
   puts "simplecov not available"
 end
 
+require "securerandom"
 require "qpid_proton"
 
 # Generates a random string of the specified length
@@ -37,4 +38,74 @@ def random_string(length = 8)
   (0...length).map{65.+(rand(25)).chr}.join
 end
 
-require "securerandom"
+# Generates a random list of the specified length.
+def random_list(length)
+  result = []
+  (0...length).each do |element|
+    type = rand(8192) % 4
+    low = rand(512)
+    high = rand(8192)
+
+    case
+    when element == 0 then result << rand(128)
+    when element == 1 then result << random_string(rand(128))
+    when element == 2 then result << rand * (low - high).abs + low
+    when element == 3 then result << SecureRandom.uuid
+    end
+  end
+
+  return result
+end
+
+# Generates a random array of a random type.
+# Returns both the array and the type.
+def random_array(length, described = false, description = nil)
+  result = []
+  type = rand(128) % 4
+  low = rand(512)
+  high = rand(8192)
+
+  (0...length).each do |element|
+    case
+      when type == 0 then result << rand(1024)
+      when type == 1 then result << random_string(rand(128))
+      when type == 2 then result << rand * (low - high).abs + low
+      when type == 3 then result << SecureRandom.uuid
+    end
+  end
+
+  # create the array header
+  case
+    when type == 0 then type = Qpid::Proton::INT
+    when type == 1 then type = Qpid::Proton::STRING
+    when type == 2 then type = Qpid::Proton::FLOAT
+    when type == 3 then type = Qpid::Proton::UUID
+  end
+
+  result.proton_array_header = Qpid::Proton::ArrayHeader.new(type, description)
+
+  return result
+end
+
+# taken from 
http://stackoverflow.com/questions/6855944/rounding-problem-with-rspec-tests-when-comparing-float-arrays
+RSpec::Matchers.define :be_close_array do |expected, truth|
+  match do |actual|
+    same = 0
+    for i in 0..actual.length-1
+      same +=1 if actual[i].round(truth) == expected[i].round(truth)
+    end
+    same == actual.length
+  end
+
+  failure_message_for_should do |actual|
+    "expected that #{actual} would be close to #{expected}"
+  end
+
+  failure_message_for_should_not do |actual|
+    "expected that #{actual} would not be close to #{expected}"
+  end
+
+  description do
+    "be a close to #{expected}"
+  end
+end



---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to