Table of Contents Previous Next
Logo
Client-Side Slice-to-Ruby Mapping : 26.7 Mapping for User-Defined Types
Copyright © 2003-2010 ZeroC, Inc.

26.7 Mapping for User-Defined Types

Slice supports user-defined types: enumerations, structures, sequences, and dictio­naries.

26.7.1 Mapping for Enumerations

Ruby does not have an enumerated type, so the Slice enumerations are emulated using a Ruby class: the name of the Slice enumeration becomes the name of the Ruby class; for each enumerator, the class contains a constant with the same name as the enumerator (see Section 26.3 for more information on identifiers). For example:
enum Fruit { Apple, Pear, Orange };
The generated Ruby class looks as follows:
class Fruit
    include Comparable

    Apple = # ...
    Pear = # ...
    Orange = # ...

    def Fruit.from_int(val)

    def to_i

    def to_s

    def <=>(other)

    def hash

    # ...
end
The compiler generates a class constant for each enumerator that holds a corre­sponding instance of Fruit. The from_int class method returns an instance given its integer value, while to_i returns the integer value of an enumerator and to_s returns its Slice identifier. The comparison operators are available as a result of including Comparable, which means a program can compare enumer­ators according to their integer values.
Given the above definitions, we can use enumerated values as follows:
f1 = Fruit::Apple
f2 = Fruit::Orange

if f1 == Fruit::Apple   # Compare for equality
    # ...

if f1 < f2              # Compare two enums
    # ...

case f2
when Fruit::Orange
    puts "found Orange"
else
    puts "found #{f2.to_s}"
end
As you can see, the generated class enables natural use of enumerated values.

26.7.2 Mapping for Structures

Slice structures map to Ruby classes with the same name. For each Slice data member, the Ruby class contains a corresponding instance variable as well as accessors to read and write its value. For example, here is our Employee structure from Section 4.9.4 once more:
struct Employee {
    long number;
    string firstName;
    string lastName;
};
The Ruby mapping generates the following definition for this structure:
class Employee
    def initialize(number=0, firstName='', lastName='')
        @number = number
        @firstName = firstName
        @lastName = lastName
    end

    def hash
        # ...
    end

    def ==
        # ...
    end

    def inspect
        # ...
    end

    attr_accessor :number, :firstName, :lastName
end
The constructor initializes each of the instance variables to a default value appro­priate for its type. You can also declare different default values for members of primitive and enumerated types, as discussed in Section 4.9.2.
The compiler generates a definition for the hash method, which allows instances to be used as keys in a hash collection. The hash method returns a hash value for the structure based on the value of its data members.
The == method returns true if all members of two structures are (recursively) equal.
The inspect method returns a string representation of the structure.

26.7.3 Mapping for Sequences

Slice sequences map to Ruby arrays; the only exception is a sequence of bytes, which maps to a string (see below). The use of a Ruby array means that the mapping does not generate a separate named type for a Slice sequence. It also means that you can take advantage of all the array functionality provided by Ruby. For example:
sequence<Fruit> FruitPlatter;
We can use the FruitPlatter sequence as shown below:
platter = [ Fruit::Apple, Fruit::Pear ]
platter.push(Fruit::Orange)
The Ice run time validates the elements of a sequence to ensure that they are compatible with the declared type; a TypeError exception is raised if an incom­patible type is encountered.

Mapping for Byte Sequences

A Ruby string can contain arbitrary 8‑bit binary data, therefore it is a more effi­cient representation of a byte sequence than a Ruby array in both memory utiliza­tion and throughput performance.
When receiving a byte sequence (as the result of an operation, as an out parameter, or as a member of a data structure), the value is always represented as a string. When sending a byte sequence as an operation parameter or data member, the Ice run time accepts both a string and an array of integers as legal values. For example, consider the following Slice definitions:
// Slice
sequence<byte> Data;

interface I {
    void sendData(Data d);
    Data getData();
};
The interpreter session below uses these Slice definitions to demonstrate the mapping for a sequence of bytes:
> proxy = ...
> proxy.sendData("\0\1\2\3")   # Send as a string
> proxy.sendData([0, 1, 2, 3]) # Send as an array
> d = proxy.getData()
> d.class
=> String
> d
=> "\000\001\002\003"
The two invocations of sendData are equivalent; however, the second invocation incurs additional overhead as the Ice run time validates the type and range of each array element.

26.7.4 Mapping for Dictionaries

Here is the definition of our EmployeeMap from Section 4.9.4 once more:
dictionary<long, Employee> EmployeeMap;
As for sequences, the Ruby mapping does not create a separate named type for this definition. Instead, all dictionaries are simply instances of Ruby’s hash collection type. For example:
em = {}

e = Employee.new
e.number = 31
e.firstName = "James"
e.lastName = "Gosling"

em[e.number] = e
The Ice run time validates the elements of a dictionary to ensure that they are compatible with the declared type; a TypeError exception is raised if an incom­patible type is encountered.

Table of Contents Previous Next
Logo