Interfacing to Ruby
Specifying a call to Ruby
You can interface to Ruby by supplying 'ruby' as the environment string (left argument) for
Loading modules and setting the Ruby DLL search path
Ruby classes are placed in Modules. When you use
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
'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
Conversion of Ruby data types to APL data
APLX by default applies the following data conversion rules to data returned from Ruby:
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
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.
Supplying Boolean arguments to Ruby methods
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 (
Using the Ruby interface from multiple APL tasks
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.
Evaluating Ruby expressions
Because Ruby is an interpreted language, it is possible to use
'ruby' ⎕EVAL 's=String.new "Hello there"' Hello there 'ruby' ⎕EVAL 's.length' 11 'ruby' ⎕EVAL 'Math.sqrt(9)' 3
Ruby naming conventions
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:
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:
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
Copyright © 1996-2010 MicroAPL Ltd