## Interfacing to Ruby

### Specifying a call to Ruby

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.

### Loading modules and setting the Ruby DLL search path

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.

### Conversion of Ruby data types to APL data

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.

### 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 (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.

### 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 Ś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 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:

        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$?

#### Example 1

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'
          ^

#### Example 2

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

Topic: APLX Help : Interfacing to other languages : Interfacing to Ruby

[Next | Previous | Contents | Index ]