rstruct 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.document +5 -0
- data/.rspec +2 -0
- data/Gemfile +13 -0
- data/LICENSE.txt +675 -0
- data/README.rdoc +137 -0
- data/Rakefile +50 -0
- data/VERSION +1 -0
- data/lib/rstruct.rb +70 -0
- data/lib/rstruct/base_types.rb +5 -0
- data/lib/rstruct/base_types/container_type.rb +102 -0
- data/lib/rstruct/base_types/packed_type.rb +78 -0
- data/lib/rstruct/base_types/type.rb +55 -0
- data/lib/rstruct/field.rb +22 -0
- data/lib/rstruct/registry.rb +66 -0
- data/lib/rstruct/struct_builder.rb +30 -0
- data/lib/rstruct/structure.rb +59 -0
- data/lib/rstruct/types.rb +44 -0
- data/samples/fatparse.rb +78 -0
- data/spec/registry_behaviors.rb +64 -0
- data/spec/registry_spec.rb +61 -0
- data/spec/rstruct_spec.rb +88 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/structure_spec.rb +297 -0
- data/spec/type_behaviors.rb +158 -0
- metadata +144 -0
| @@ -0,0 +1,55 @@ | |
| 1 | 
            +
            module Rstruct
         | 
| 2 | 
            +
              class Type
         | 
| 3 | 
            +
                attr_reader :name, :params
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                def initialize(name, params={}, &block)
         | 
| 6 | 
            +
                  @params = params.dup
         | 
| 7 | 
            +
                  @name = name.to_sym
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                  reg = @params.delete(:register)
         | 
| 10 | 
            +
                  aliases = @params.delete(:alias)
         | 
| 11 | 
            +
                  regnames = ((aliases)? ([aliases] << @name).flatten : [@name]).uniq.compact
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                  reg=nil if reg==true
         | 
| 14 | 
            +
                  register(regnames, reg) unless reg == false
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                  instance_eval &block if block
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                def register(names=nil, reg=nil)
         | 
| 20 | 
            +
                  names ||= to_sym
         | 
| 21 | 
            +
                  reg ||= Registry::DEFAULT_REGISTRY
         | 
| 22 | 
            +
                  reg.register(self, *names)
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                def groupable?
         | 
| 26 | 
            +
                  @groupable or false
         | 
| 27 | 
            +
                end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                def container?
         | 
| 30 | 
            +
                  @container or false
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                def claim_value(vals, predecessors=nil)
         | 
| 34 | 
            +
                  if @claim_cb
         | 
| 35 | 
            +
                    @claim_cb.call(vals, predecessors)
         | 
| 36 | 
            +
                  else
         | 
| 37 | 
            +
                    vals.shift
         | 
| 38 | 
            +
                  end
         | 
| 39 | 
            +
                end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                def sizeof
         | 
| 42 | 
            +
                  self.size
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                private
         | 
| 46 | 
            +
                  # sets up a call back for claiming values out of an unpacked
         | 
| 47 | 
            +
                  # value array
         | 
| 48 | 
            +
                  def claim(&block)
         | 
| 49 | 
            +
                    @claim_cb = block
         | 
| 50 | 
            +
                  end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
              end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
            end
         | 
| 55 | 
            +
             | 
| @@ -0,0 +1,22 @@ | |
| 1 | 
            +
            module Rstruct
         | 
| 2 | 
            +
              class Field
         | 
| 3 | 
            +
                attr_reader :name, :typ_name, :typ, :args, :block
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                def initialize(name, typ, typ_name, args, block)
         | 
| 6 | 
            +
                  @name=name
         | 
| 7 | 
            +
                  @typ=typ
         | 
| 8 | 
            +
                  @typ_name = typ_name || typ
         | 
| 9 | 
            +
                  @args=args
         | 
| 10 | 
            +
                  @block=block
         | 
| 11 | 
            +
                end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                def respond_to?(arg)
         | 
| 14 | 
            +
                  super(arg) || @typ.respond_to?(arg)
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                def method_missing(name, *args, &block)
         | 
| 18 | 
            +
                  @typ.__send__(name, *args, &block)
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
              end
         | 
| 21 | 
            +
            end
         | 
| 22 | 
            +
             | 
| @@ -0,0 +1,66 @@ | |
| 1 | 
            +
            require 'set'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Rstruct
         | 
| 4 | 
            +
              class TypeConflictError < StandardError
         | 
| 5 | 
            +
              end
         | 
| 6 | 
            +
             | 
| 7 | 
            +
              class InvalidTypeError < StandardError
         | 
| 8 | 
            +
              end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
              class Registry
         | 
| 11 | 
            +
                attr_reader :name, :inherits
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                def initialize(name, *inherits)
         | 
| 14 | 
            +
                  @name = name.to_sym
         | 
| 15 | 
            +
                  @registry = Hash.new()
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                  @inherits = []
         | 
| 18 | 
            +
                  if @name != :default
         | 
| 19 | 
            +
                    @inherits << DEFAULT_REGISTRY 
         | 
| 20 | 
            +
                  end
         | 
| 21 | 
            +
                  @inherits.concat(inherits).uniq!
         | 
| 22 | 
            +
                end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                def typedef(p,t,opts={})
         | 
| 25 | 
            +
                  if (pt = get(p))
         | 
| 26 | 
            +
                    set(t, pt)
         | 
| 27 | 
            +
                  else
         | 
| 28 | 
            +
                    raise(InvalidTypeError, "unknown type: #{p}")
         | 
| 29 | 
            +
                  end
         | 
| 30 | 
            +
                end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                def get(typ)
         | 
| 33 | 
            +
                  if t=@registry[typ] 
         | 
| 34 | 
            +
                    return t
         | 
| 35 | 
            +
                  else
         | 
| 36 | 
            +
                    @inherits.each do |inc|
         | 
| 37 | 
            +
                      if t=inc.get(typ)
         | 
| 38 | 
            +
                        return t
         | 
| 39 | 
            +
                      end
         | 
| 40 | 
            +
                    end
         | 
| 41 | 
            +
                    return nil
         | 
| 42 | 
            +
                  end
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                alias [] get
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                def set(n,typ)
         | 
| 48 | 
            +
                  if n.nil?
         | 
| 49 | 
            +
                    raise(TypeConflictError, "can't register nil type")
         | 
| 50 | 
            +
                  elsif v=get(n)
         | 
| 51 | 
            +
                    raise(TypeConflictError, "type already registered: #{n} => #{v.inspect}" ) 
         | 
| 52 | 
            +
                  end
         | 
| 53 | 
            +
                  @registry[n]=typ
         | 
| 54 | 
            +
                end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                alias []= set
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                def register(typ, *names)
         | 
| 59 | 
            +
                  names.each do |n|
         | 
| 60 | 
            +
                    set(n.to_sym,typ)
         | 
| 61 | 
            +
                  end
         | 
| 62 | 
            +
                end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                DEFAULT_REGISTRY=new(:default)
         | 
| 65 | 
            +
              end
         | 
| 66 | 
            +
            end
         | 
| @@ -0,0 +1,30 @@ | |
| 1 | 
            +
            require 'rstruct/registry'
         | 
| 2 | 
            +
            require 'rstruct/field'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module Rstruct
         | 
| 5 | 
            +
             | 
| 6 | 
            +
              class StructBuilder
         | 
| 7 | 
            +
                attr_reader :__fields, :__registry
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                def initialize(registry=nil, &block)
         | 
| 10 | 
            +
                  @__fields = []
         | 
| 11 | 
            +
                  @__registry = registry || Registry::DEFAULT_REGISTRY
         | 
| 12 | 
            +
                  instance_eval &block
         | 
| 13 | 
            +
                end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                def field(name, typ, typ_name, *args, &block)
         | 
| 16 | 
            +
                  @__fields << Field.new(name,typ,typ_name,args,block)
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                def method_missing(typ_arg, *args, &block)
         | 
| 20 | 
            +
                  name = args.shift
         | 
| 21 | 
            +
                  unless typ = @__registry.get(typ_arg)
         | 
| 22 | 
            +
                    raise(InvalidTypeError, "invalid field type: #{typ_arg}")
         | 
| 23 | 
            +
                  end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                  field(name, typ, typ_arg, *args, &block)
         | 
| 26 | 
            +
                end
         | 
| 27 | 
            +
              end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
            end
         | 
| 30 | 
            +
             | 
| @@ -0,0 +1,59 @@ | |
| 1 | 
            +
            require 'rstruct/base_types'
         | 
| 2 | 
            +
            require 'rstruct/struct_builder'
         | 
| 3 | 
            +
            require 'rstruct/registry'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module Rstruct
         | 
| 6 | 
            +
              class StructError < StandardError
         | 
| 7 | 
            +
              end
         | 
| 8 | 
            +
             | 
| 9 | 
            +
              class Structure < ContainerType
         | 
| 10 | 
            +
                attr_reader :fields
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                def initialize(name, opts={}, &block)
         | 
| 13 | 
            +
                  lkupreg = (opts[:fields_from]) # allow a seperate reg for member types
         | 
| 14 | 
            +
                  builder = opts[:builder] || StructBuilder
         | 
| 15 | 
            +
                  reg = opts[:register]
         | 
| 16 | 
            +
                  reg=nil if reg==true
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  # pass a nil block to super to ensure we're claiming the caller's
         | 
| 19 | 
            +
                  super(name, opts, &(nil))
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                  @fields = builder.new((lkupreg || reg), &block).__fields
         | 
| 22 | 
            +
                  raise(StructError, "no fields were defined") if @fields.empty?
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                  # set up our internal struct container class
         | 
| 25 | 
            +
                  # we are taking the field name 'structure' 
         | 
| 26 | 
            +
                  # to reference ourselves
         | 
| 27 | 
            +
                  @mystruct = Struct.new(*self.field_names)
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                def instance(values=nil)
         | 
| 31 | 
            +
                  values ||= {}
         | 
| 32 | 
            +
                  vals = []
         | 
| 33 | 
            +
                  self.fields.each do |f|
         | 
| 34 | 
            +
                    v = values[f.name]
         | 
| 35 | 
            +
                    vals << (f.typ.respond_to?(:instance) ? f.typ.instance(v) : v)
         | 
| 36 | 
            +
                  end
         | 
| 37 | 
            +
                  s = @mystruct.new(*vals).extend(ContainerMixins)
         | 
| 38 | 
            +
                  s.rstruct_type = self
         | 
| 39 | 
            +
                  yield(s) if block_given?
         | 
| 40 | 
            +
                  return s
         | 
| 41 | 
            +
                end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                def claim_value(vals, obj=nil)
         | 
| 44 | 
            +
                  if @claim_cb
         | 
| 45 | 
            +
                    @claim_cb.call(vals, obj)
         | 
| 46 | 
            +
                  else
         | 
| 47 | 
            +
                    # create our struct container
         | 
| 48 | 
            +
                    s = instance()
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                    # iterate through the fields assigning values in the
         | 
| 51 | 
            +
                    # container and pass it along with values to each
         | 
| 52 | 
            +
                    # field's claim_value method.
         | 
| 53 | 
            +
                    self.fields.do {|f| s[f.name] = f.typ.claim_value(vals,s) }
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                    return s
         | 
| 56 | 
            +
                  end
         | 
| 57 | 
            +
                end
         | 
| 58 | 
            +
              end
         | 
| 59 | 
            +
            end
         | 
| @@ -0,0 +1,44 @@ | |
| 1 | 
            +
            require 'rstruct/base_types'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Rstruct
         | 
| 4 | 
            +
              Char = PackedType.new(:char, 1, "A", :alias => [:char, :char_t])
         | 
| 5 | 
            +
             | 
| 6 | 
            +
              Byte = PackedType.new(:byte, 1, "c", :alias => [:BYTE, :signed_byte])
         | 
| 7 | 
            +
             | 
| 8 | 
            +
              Ubyte = PackedType.new(:ubyte, 1, "C", :alias => [:UBYTE, :unsigned_byte])
         | 
| 9 | 
            +
             | 
| 10 | 
            +
              Int = PackedType.new(:int, [0].pack("i_").size, "i_", :alias => [:int_t, :bool, :BOOL, :signed_int])
         | 
| 11 | 
            +
             | 
| 12 | 
            +
              Uint = PackedType.new(:uint, [0].pack("I_").size, "I_", :alias => [:uint_t, :unsigned_int])
         | 
| 13 | 
            +
             | 
| 14 | 
            +
              Short = PackedType.new(:short, [0].pack("s_").size, "s_", :alias => [:short_t, :signed_short])
         | 
| 15 | 
            +
             | 
| 16 | 
            +
              Ushort = PackedType.new(:ushort, [0].pack("S_").size, "S_", :alias => [:ushort_t, :unsigned_short])
         | 
| 17 | 
            +
             | 
| 18 | 
            +
              Long = PackedType.new(:long, [0].pack("l_").size, "l_", :alias => [:long_t, :signed_long])
         | 
| 19 | 
            +
             | 
| 20 | 
            +
              Ulong = PackedType.new(:ulong, [0].pack("L_").size, "L_", :alias => [:ulong_t, :unsigned_long])
         | 
| 21 | 
            +
             | 
| 22 | 
            +
              Int16 = PackedType.new(:int16, 2, 's', :alias => [:int16_t, :i16, :i16_t, :signed_int16])
         | 
| 23 | 
            +
             | 
| 24 | 
            +
              Uint16 = PackedType.new(:uint16, 2, 'S', :alias => [:uint16_t, :u16, :u16_t, :unsigned_int16])
         | 
| 25 | 
            +
             | 
| 26 | 
            +
              Int32 = PackedType.new(:int32, 4, "l", :alias => [:int32_t, :i32, :i32_t, :signed_int32])
         | 
| 27 | 
            +
             | 
| 28 | 
            +
              Uint32 = PackedType.new(:uint32, 4, "L", :alias => [:uint32_t, :u32, :u32_t, :unsigned_int32])
         | 
| 29 | 
            +
             | 
| 30 | 
            +
              Int64 = PackedType.new(:int64, 8, "q", :alias => [:int64_t, :i64, :i64_t, :signed_int64])
         | 
| 31 | 
            +
             | 
| 32 | 
            +
              Uint64 = PackedType.new(:uint64, 8, "Q", :alias => [:uint64_t, :u64, :u64_t, :unsigned_int64])
         | 
| 33 | 
            +
             | 
| 34 | 
            +
              Uint16le = PackedType.new(:uint16le, 2, "v", :alias => [:uint16_le, :ul16, :le16])
         | 
| 35 | 
            +
             | 
| 36 | 
            +
              Uint32le = PackedType.new(:uint32le, 4, "V", :alias => [:uint32_le, :ul32, :le32])
         | 
| 37 | 
            +
             | 
| 38 | 
            +
              Uint16be = PackedType.new(:uint16be, 2, "n", :alias => [:uint16_be, :ub16, :be16])
         | 
| 39 | 
            +
             | 
| 40 | 
            +
              Uint32be = PackedType.new(:uint32be, 4, "N", :alias => [:uint32_be, :ub32, :be32])
         | 
| 41 | 
            +
             | 
| 42 | 
            +
              Pointer = PackedType.new(:pointer, [0].pack("L_").size , "L_", :alias => :pointer)
         | 
| 43 | 
            +
             | 
| 44 | 
            +
            end
         | 
    
        data/samples/fatparse.rb
    ADDED
    
    | @@ -0,0 +1,78 @@ | |
| 1 | 
            +
            #!/usr/bin/env ruby
         | 
| 2 | 
            +
            # A basic Rstruct example using Apple's FAT file structure.
         | 
| 3 | 
            +
            # To compare the structs to their C counterparts, see:
         | 
| 4 | 
            +
            #   http://fxr.watson.org/fxr/source/EXTERNAL_HEADERS/mach-o/fat.h?v=xnu-1228
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            $: << File.join(File.dirname(__FILE__), '..', 'lib')
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            require 'rstruct'
         | 
| 9 | 
            +
            extend Rstruct::ClassMethods
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            FAT_MAGIC = 0xcafebabe
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            fat_header = struct(:fat_header) {
         | 
| 14 | 
            +
              uint32be  :magic;      # FAT_MAGIC
         | 
| 15 | 
            +
              uint32be  :nfat_arch;  # number of structs that follow
         | 
| 16 | 
            +
            }
         | 
| 17 | 
            +
             | 
| 18 | 
            +
            typedef :uint32be, :cpu_type_t
         | 
| 19 | 
            +
            typedef :uint32be, :cpu_subtype_t
         | 
| 20 | 
            +
             | 
| 21 | 
            +
            fat_arch = struct(:fat_arch) {
         | 
| 22 | 
            +
              cpu_type_t     :cputype    # cpu specifier (int)
         | 
| 23 | 
            +
              cpu_subtype_t  :cpusubtype # machine specifier (int)
         | 
| 24 | 
            +
              uint32be       :offset     # file offset to this object file
         | 
| 25 | 
            +
              uint32be       :size       # size of this object file
         | 
| 26 | 
            +
              uint32be       :align      # alignment as a power of 2
         | 
| 27 | 
            +
            }
         | 
| 28 | 
            +
             | 
| 29 | 
            +
            typedef :uint32le, :cpu_type_le
         | 
| 30 | 
            +
            typedef :uint32le, :cpu_subtype_le
         | 
| 31 | 
            +
             | 
| 32 | 
            +
            mach_header = struct(:mach_header) { # little endian this time
         | 
| 33 | 
            +
              uint32le       :magic      # mach magic number identifier
         | 
| 34 | 
            +
              cpu_type_le    :cputype    # cpu specifier
         | 
| 35 | 
            +
              cpu_subtype_le :cpusubtype # machine specifier
         | 
| 36 | 
            +
              uint32le       :filetype   # type of file
         | 
| 37 | 
            +
              uint32le       :ncmds      # number of load commands
         | 
| 38 | 
            +
              uint32le       :sizeofcmds # the size of all the load commands
         | 
| 39 | 
            +
              uint32le       :flags      # flags
         | 
| 40 | 
            +
            }
         | 
| 41 | 
            +
             | 
| 42 | 
            +
            # a basic helper to produce textual dumps of fields
         | 
| 43 | 
            +
            def dump(struct)
         | 
| 44 | 
            +
              struct.each_pair.map {|k,v| "  #{k} = 0x%0.8x" % v}
         | 
| 45 | 
            +
            end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
            ARGV.each do |fname|
         | 
| 48 | 
            +
              File.open(fname,'r') do |f|
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                # Read and dump the FAT header from the file
         | 
| 51 | 
            +
                head = fat_header.read(f)
         | 
| 52 | 
            +
                unless head.magic == FAT_MAGIC
         | 
| 53 | 
            +
                  STDERR.puts "Error: #{fname} does not look like a FAT binary"
         | 
| 54 | 
            +
                  next
         | 
| 55 | 
            +
                end
         | 
| 56 | 
            +
                puts "File: #{fname}", "FAT header:", dump(head)
         | 
| 57 | 
            +
                puts
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                # parse the architectures located after the FAT header
         | 
| 60 | 
            +
                (head.nfat_arch).times do |i|
         | 
| 61 | 
            +
                  arch = fat_arch.read(f)
         | 
| 62 | 
            +
                  puts "  Architecture #{i}:"
         | 
| 63 | 
            +
                  puts "  " << dump(arch).join("\n  ")
         | 
| 64 | 
            +
                  puts
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                  # dump the mach header
         | 
| 67 | 
            +
                  opos = f.pos        # save our file position so we get the next arch
         | 
| 68 | 
            +
                  f.pos = arch.offset # jump to where the mach header should be
         | 
| 69 | 
            +
                  mach = mach_header.read(f)  # parse a mach_header struct
         | 
| 70 | 
            +
                  f.pos = opos # return to saved architecture position
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                  puts "    Mach Header:"
         | 
| 73 | 
            +
                  puts "    " << dump(mach).join("\n    ")
         | 
| 74 | 
            +
                  puts
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                end
         | 
| 77 | 
            +
              end
         | 
| 78 | 
            +
            end
         | 
| @@ -0,0 +1,64 @@ | |
| 1 | 
            +
             | 
| 2 | 
            +
            shared_examples_for "a basic Rstruct registry" do
         | 
| 3 | 
            +
              it "should have a name" do
         | 
| 4 | 
            +
                @registry.name.should == @reg_name
         | 
| 5 | 
            +
              end
         | 
| 6 | 
            +
             | 
| 7 | 
            +
              it "should have basic types already registered or inherited" do
         | 
| 8 | 
            +
                @registry.get(:byte).should == Rstruct::Byte
         | 
| 9 | 
            +
                @registry.get(:char).should == Rstruct::Char
         | 
| 10 | 
            +
                @registry.get(:int).should  == Rstruct::Int
         | 
| 11 | 
            +
                @registry.get(:short).should == Rstruct::Short
         | 
| 12 | 
            +
                @registry.get(:long).should  == Rstruct::Long
         | 
| 13 | 
            +
                @registry.get(:uint).should  == Rstruct::Uint
         | 
| 14 | 
            +
                @registry.get(:ushort).should == Rstruct::Ushort
         | 
| 15 | 
            +
                @registry.get(:ulong).should == Rstruct::Ulong
         | 
| 16 | 
            +
              end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
              it "should allow types to be retrieved with 'get' or '[]'" do
         | 
| 19 | 
            +
                (a=@registry[:byte]).should == Rstruct::Byte
         | 
| 20 | 
            +
                @registry.get(:byte).should == a
         | 
| 21 | 
            +
              end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
              it "should allow new types to be defined with 'register' or '[]='" do
         | 
| 24 | 
            +
                typ1 = :"create_#{@registry.name}_1"
         | 
| 25 | 
            +
                c1 = Class.new(Rstruct::Type)
         | 
| 26 | 
            +
                @registry[typ1]= c1
         | 
| 27 | 
            +
                @registry[typ1].should == c1
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                typ2 = :"create_#{@registry.name}_2"
         | 
| 30 | 
            +
                c2 = Class.new(Rstruct::Type)
         | 
| 31 | 
            +
                @registry.register(c2, typ2)
         | 
| 32 | 
            +
                @registry[typ2].should == c2
         | 
| 33 | 
            +
              end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
              it "should allow multiple names to be registered at once with 'register'" do
         | 
| 36 | 
            +
                typ1 = :"create_#{@registry.name}_3"
         | 
| 37 | 
            +
                typ2 = :"create_#{@registry.name}_4"
         | 
| 38 | 
            +
                c = Class.new(Rstruct::Type)
         | 
| 39 | 
            +
                @registry.register(c, typ1, typ2)
         | 
| 40 | 
            +
                @registry[typ1].should == c
         | 
| 41 | 
            +
                @registry[typ2].should == c
         | 
| 42 | 
            +
              end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
              it "should raise an exception when a type is defined which already exists" do
         | 
| 45 | 
            +
                typ = :"create_#{@registry.name}_5"
         | 
| 46 | 
            +
                c = Class.new(Rstruct::Type)
         | 
| 47 | 
            +
                @registry[typ] = c
         | 
| 48 | 
            +
                @registry[typ].should == c
         | 
| 49 | 
            +
                c2 = Class.new(Rstruct::Type)
         | 
| 50 | 
            +
                lambda{ @registry[typ] = c2 }.should raise_error(Rstruct::TypeConflictError)
         | 
| 51 | 
            +
              end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
              it "should allow type aliases to be defined with 'typedef'" do
         | 
| 54 | 
            +
                typ = :"typedef_#{@registry.name}_1"
         | 
| 55 | 
            +
                @registry.typedef :int, typ
         | 
| 56 | 
            +
                @registry[:int].should == @registry[typ]
         | 
| 57 | 
            +
              end
         | 
| 58 | 
            +
             | 
| 59 | 
            +
              it "should raise an exception when invalid types are aliased in 'typedef'" do
         | 
| 60 | 
            +
                typ = :"typedef_#{@registry.name}_2"
         | 
| 61 | 
            +
                lambda{ @registry.typedef :not_a_type, typ}.should raise_error(Rstruct::InvalidTypeError)
         | 
| 62 | 
            +
              end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
            end
         | 
| @@ -0,0 +1,61 @@ | |
| 1 | 
            +
            require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
         | 
| 2 | 
            +
            require 'registry_behaviors'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            describe Rstruct::Registry do
         | 
| 5 | 
            +
              before(:all) do
         | 
| 6 | 
            +
                @dflt_reg = Rstruct::Registry::DEFAULT_REGISTRY
         | 
| 7 | 
            +
              end
         | 
| 8 | 
            +
             | 
| 9 | 
            +
              it "should have a default registry" do
         | 
| 10 | 
            +
                @dflt_reg.should be_a(Rstruct::Registry)
         | 
| 11 | 
            +
              end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
              context "default registry" do
         | 
| 14 | 
            +
                before(:all) do
         | 
| 15 | 
            +
                  @registry = @dflt_reg
         | 
| 16 | 
            +
                  @reg_name = :default
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                it_should_behave_like "a basic Rstruct registry"
         | 
| 20 | 
            +
             | 
| 21 | 
            +
              end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
              context "creating a new registry" do
         | 
| 24 | 
            +
                before(:all) do
         | 
| 25 | 
            +
                  @reg_name = :rspec_test_registry
         | 
| 26 | 
            +
                  @preg = Rstruct::Registry.new(:parent_reg)
         | 
| 27 | 
            +
                  @registry = Rstruct::Registry.new(@reg_name)
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                it "should inherit types from the default registry" do
         | 
| 31 | 
            +
                  @registry[:byte].should == Rstruct::Byte
         | 
| 32 | 
            +
                  @registry[:int].should == Rstruct::Int
         | 
| 33 | 
            +
                  @registry.inherits.should == [Rstruct.default_registry]
         | 
| 34 | 
            +
                end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                it "should inherit types from arbitrary registries" do
         | 
| 37 | 
            +
                  begin
         | 
| 38 | 
            +
                    @registry.inherits.unshift(@preg)
         | 
| 39 | 
            +
                    c=Rstruct::Type.new(:some_parent_reg_type, :register => @preg)
         | 
| 40 | 
            +
                    @preg[:some_parent_reg_type].should == c
         | 
| 41 | 
            +
                    @registry[:some_parent_reg_type].should == c
         | 
| 42 | 
            +
                    Rstruct.default_registry[c].should be_nil
         | 
| 43 | 
            +
                  ensure
         | 
| 44 | 
            +
                    @registry.inherits.delete(@preg)
         | 
| 45 | 
            +
                  end
         | 
| 46 | 
            +
                    
         | 
| 47 | 
            +
                end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                it "should register types only to its own registry" do
         | 
| 50 | 
            +
                  typ = :"test_parent_reg_#{@registry.name}_1"
         | 
| 51 | 
            +
                  obj=Class.new(Rstruct::Type)
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                  @registry.register(obj,typ)
         | 
| 54 | 
            +
                  @registry[typ].should == obj
         | 
| 55 | 
            +
                  Rstruct.default_registry[typ].should be_nil
         | 
| 56 | 
            +
                end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                it_should_behave_like "a basic Rstruct registry"
         | 
| 59 | 
            +
             | 
| 60 | 
            +
              end
         | 
| 61 | 
            +
            end
         |