You can interface to Ruby by supplying 'ruby' as the environment string (left argument) for ŚNEW, ŚGETCLASS, or ŚCALL. These system functions will allow you to create an instance of a Ruby class, or to call a Ruby static mathod.
Ruby classes are placed in Modules. When you use ŚNEW (or ŚGETCLASS or ŚCALL) to refer to a Ruby class, the system searches through the modules which have been loaded into the Ruby interpreter to resolve the class name. If the class is not found, you will get an error:
dt„'ruby' Śnew 'DateTime' #<NameError: uninitialized constant DateTime> DOMAIN ERROR dt„'ruby' Śnew 'DateTime' ^
The above example has failed because DateTime is a class defined in the Date module, which has not been loaded. (In a Ruby script, you would get the same error if you had not loaded the Date module by using the Ruby 'require' statement). You can tell the Ruby interpreter to load a module by using the system function ŚSETUP and the 'require' keyword. For example:
'ruby' ŚSETUP 'require' 'Date' dt„'ruby' Śnew 'DateTime' dt [ruby:DateTime]
The effect of using the 'require' keyword is to add a given Ruby module (or script) to the list in which Ruby will search for class definitions. The parameter is a character vector containing the module name. This can be specified either as a full path name, or as just a file name, in which case Ruby will search in its current search path for the script:
'ruby' ŚSETUP 'require' 'c:\ruby\myapp.rb'
You can use the 'addpath' keyword to add one or more directories to Ruby's current search path for modules:
'ruby' ŚSETUP 'addpath' 'c:\rubyapps' 'c:\rubylibs\version2'
See the documentation on ŚSETUP for more options for controlling the Ruby environment.
APLX by default applies the following data conversion rules to data returned from Ruby:
Ruby integer ("Fixnum") values are converted to APL integers.
Ruby Float and Bignum values are converted to APL floating-point numbers.
Ruby Booleans (true or false) are converted to APL binary values 1 or 0
Ruby Strings are converted to APL character arrays, translated to APLX internal representation.
Ruby nil values are converted to APL Null objects
Simple Ruby arrays are converted to APL arrays, with individual elements converted as above.
Anything else is left as an object in the Ruby environment, and a reference to the object is returned to APL.
There are some special cases to consider. The data might not be convertible at all, or it might lose precision in the conversion. For example, a Ruby Bignum might have a higher precision than an APL double-precision floating point can represent. To handle cases like this, APLX provides the ŚREF system method. This forces the data to remain as a Ruby object. You can then call Ruby methods appropriate to the Bignum or other data type, to manipulate the data without losing precision.
An example which cannot be represented at all is where a Ruby Double contains a NaN (Not A Number). APL does not handle NaNs, so it cannot be converted to an APL floating-point value. Instead, NaNs are left as Objects. If you try to use the data in an APL expression, you will get a DOMAIN ERROR, but you can see that it is a NaN and use Ruby methods on it.
Ruby methods which expect true or false as arguments have to be handled in a special way, because Ruby does not allow 1 and 0 as equivalents to Booleans. To work around this, pass a one element matrix (1 1˝1 or 1 1˝0) as the argument; the interface will recognize this as a special case and convert the data to a Ruby true or false value.
Because it is not safe to call the Ruby interpreter from multiple threads, you cannot use the Ruby interface from more than one APL task at a time. If you try to do so, you will get an error message and a FILE LOCKED error:
dt„'ruby' Śnew 'Date' This interface cannot be used by more than one APL task at a time FILE LOCKED dt„'ruby' Śnew 'Date' ^
The lock will be cleared when the APL task which has been accessing Ruby executes a )CLEAR, )LOAD, or )OFF.
Because Ruby is an interpreted language, it is possible to use ŚEVAL to run lines of Ruby code, and for setting up variables in the Ruby environment:
'ruby' ŚEVAL 's=String.new "Hello there"' Hello there 'ruby' ŚEVAL 's.length' 11 'ruby' ŚEVAL 'Math.sqrt(9)' 3
Ruby allows a method names to include various characters (such as = and ?) which are not valid in APL names. Indeed by convention in Ruby, methods which return a Boolean often end with a question mark. For example, the method leap? of the Ruby DateTime class returns a Boolean indicating whether the date falls in a leap year, and the method responds_to? is a standard way of finding out whether a Ruby object supports a given method call (message).
The problem with this is that the APL parser has a different view of what constitutes a valid name. So if you write:
is_leap_year„RUBYDATE.leap?
then the APL interpreter will think the rightmost token of the line is the APL ? primitive, separate from the compound identifier RUBYDATE.leap. It will therefore give an error.
To work around this problem, the $ character can be used as an escape character in external names. It has the effect of treating the next character as part of the name. So the valid way of calling the is_leap? method is:
is_leap_year„RUBYDATE.leap$?
This example shows the use of the Ruby Hash class, which maintains a list of Key - Value pairs.
h„'ruby' ŚNEW 'Hash' h.length 0 h.store 'France' 'Paris' Paris h.store 'UK' 'London' London h.store 'Italy' 'Rome' Rome h.store 'Germany' 'Berlin' Berlin h.length 4 h.to_a UK London France Paris Italy Rome Germany Berlin h.sort © Sort by key values, return array France Paris Germany Berlin Italy Rome UK London h.key$? 'France' © Does key 'France' exist? (Note use of $ escape character) 1 h.key$? 'USA' © Does key 'USA' exist? 0 h.fetch 'France' Paris h.fetch 'USA' #<IndexError: key not found> DOMAIN ERROR h.fetch 'USA' ^
This example shows the use of the Ruby Complex class, for manipulating complex numbers:
'ruby' Śsetup 'require' 'complex' compclass„'ruby' ŚGETCLASS 'Complex' a„ŚNEW compclass 3 4 a.ŚDS 3+4i b„ŚNEW compclass 2 Ż1 b.ŚDS 2-1i b.conjugate [ruby:Complex] b.conjugate.ŚDS 2+1i b.polar 2.236067977 Ż0.463647609 c„a.$* b © Complex multiplication. Note use of $ escape char c.ŚDS 10+5i