Ruby Mapping for Parameters and Return Values
In Parameters
All parameters are passed by reference in the Ruby mapping; it is guaranteed that the value of a parameter will not be changed by the invocation.
Here is an interface with operations that pass parameters of various types from client to server:
struct NumberAndString
{
int x;
string str;
}
sequence<string> StringSeq;
dictionary<long, StringSeq> StringTable;
interface ClientToServer
{
void op1(int i, float f, bool b, string s);
void op2(NumberAndString ns, StringSeq ss, StringTable st);
void op3(ClientToServer* proxy);
}
The Slice compiler generates the following proxy for this definition:
module ClientToServerPrx_mixin
def op1(i, f, b, s, context=nil)
...
end
def op2(ns, ss, st, context=nil)
...
end
def op3(proxy, context=nil)
...
end
end
class ClientToServerPrx < Ice::ObjectPrx
include ClientToServerPrx_mixin
end
Given a proxy to a ClientToServer interface, the client code can pass parameters as in the following example:
p = ... # Get proxy...
p.op1(42, 3.14, true, "Hello world!") # Pass simple literals
i = 42
f = 3.14
b = true
s = "Hello world!"
p.op1(i, f, b, s) # Pass simple variables
ns = NumberAndString.new()
ns.x = 42
ns.str = "The Answer"
ss = [ "Hello world!" ]
st = {}
st[0] = ns
p.op2(ns, ss, st) # Pass complex variables
p.op3(p) # Pass proxy
Out Parameters and Return Values
The return value of a mapped method depends on how many values the corresponding Slice operation returns, including out parameters and a non-void return value:
Zero values
The corresponding Ruby method returnsvoid. For the purposes of this discussion, we're not interested in these operations.One value
The client receives a single return value, regardless of whether the Slice definition of the operation declared it as a return value or as an out parameter.Two or more value
The client receives them in the form of a result array. A non-voidreturn value, if any, is always the first element in the result array, followed by theoutparameters in the order of declaration.
Here again are the same Slice definitions we saw earlier, but this time with all parameters being passed in the out direction:
struct NumberAndString
{
int x;
string str;
}
sequence<string> StringSeq;
dictionary<long, StringSeq> StringTable;
interface ServerToClient
{
int op1(out float f, out bool b, out string s);
void op2(out NumberAndString ns,
out StringSeq ss,
out StringTable st);
void op3(out ServerToClient* proxy);
}
The Ruby mapping generates the following code for this definition:
def op1(context=nil)
def op2(context=nil)
def op3(context=nil)
Given a proxy to a ServerToClient interface, the client code can receive the results as in the following example:
p = ... # Get proxy...
i, f, b, s = p.op1()
ns, ss, st = p.op2()
stcp = p.op3()
The operations have no in parameters, therefore no arguments are passed to the proxy methods. Since op1 and op2 return multiple values, their result arrays are unpacked into separate values, whereas the return value of op3 requires no unpacking.
Parameter Type Mismatches
Ice validates the arguments to a proxy invocation at runtime and reports any type mismatches as a TypeError exception.
Nil Parameters
Some Slice types naturally have "empty" or "not there" semantics. Specifically, sequences, dictionaries, and strings all can be nil, but the corresponding Slice types do not have the concept of a null value. To make life with these types easier, whenever you pass nil as a parameter or return value of type sequence, dictionary, or string, the Ice runtime automatically sends an empty sequence, dictionary, or string to the receiver.
This behavior is useful as a convenience feature: especially for deeply-nested data types, members that are sequences, dictionaries, or strings automatically arrive as an empty value at the receiving end. This saves you having to explicitly initialize, for example, every string element in a large sequence before sending the sequence in order to avoid a run-time error. Note that using null parameters in this way does not create null semantics for Slice sequences, dictionaries, or strings. As far as the object model is concerned, these do not exist (only empty sequences, dictionaries, and strings do). For example, it makes no difference to the receiver whether you send a string as nil or as an empty string: either way, the receiver sees an empty string.
Optional Parameters
Optional parameters use the same mapping as required parameters. The only difference is that Ice::Unset can be passed as the value of an optional parameter or return value. Consider the following operation:
optional(1) int execute(optional(2) string p, out optional(3) float value);
A client can invoke this operation as shown below:
i, v = proxy.execute("--file log.txt")
i, v = proxy.execute(Ice::Unset)
if v != Ice::Unset
puts "value = " + v.to_s
end
A well-behaved program must always compare an optional parameter to Ice::Unset prior to using its value. Keep in mind that the Ice::Unset marker value has different semantics than nil. Since nil is a legal value for certain Slice types, the Ice runtime requires a separate marker value so that it can determine whether an optional parameter is set. An optional parameter set to nil is considered to be set.