Main APIs

These types form the main API to RubyGateway.


Topics

class RbGateway

Provides top-level Ruby services: information about the Ruby VM, evaluate expressions, access various kinds of Ruby objects, and define new Ruby classes, modules, and functions.

You cannot instantiate this type. Instead RubyGateway exports a public instance Ruby. Among other things this permits a dynamic member lookup programming style.

The Ruby VM is initialized when the object is first accessed and is automatically stopped when the process ends. The VM can be manually shut down before process exit by calling RbGateway.cleanup() but once this has been done the VM cannot be restarted and subsequent calls to RubyGateway services will fail.

The loadpath (where require looks) is set to the lib/ruby directories adjacent to the libruby the program is linked against and $RUBYLIB. RubyGems are enabled.

Accessing Ruby objects

The class inherits from RbObjectAccess which lets you look up constants or call functions as you would at the top level of a Ruby script, for example:

import RubyGateway

print("Ruby version is \(Ruby.version)")

do {
   try Ruby.require(filename: "rouge")
   let html = try Ruby.get("Rouge").call("highlight", args: ["let a = 1", "swift", "html"])
} catch {
}

If you just want to create a Ruby object of some class, see RbObject.init(ofClass:args:kwArgs:).

Running Ruby code

Use RbGateway.eval(ruby:) to evaulate a Ruby expression in the current VM. For example:

let result = try Ruby.eval(ruby: "Rouge.highlight('let a = 1', 'swift', 'html')")

Defining new Ruby classes

Use RbGateway.defineClass(_:parent:under:) and RbGateway.defineModule(_:under:) to define new classes and modules. Then add methods using RbObject.defineMethod(...) and RbObject.defineSingletonMethod(...).

Declaration
Swift
public final class RbGateway: RbObjectAccess, Sendable

class RbObject

A Ruby object.

All Ruby objects whatever their type or class are represented using this Swift type.

Use RbObject.init(ofClass:args:kwArgs:) to create a new Ruby object of some class:

let myObj = RbObject(ofClass: "MyModule::MyClass", args: ["arg1", 25.3])

See RbObjectAccess for ways to call methods, access properties, and find constants from an RbObject:

try myObj.set("name", "fred")  // explicit property set

let results = try myObj.call("process", args: ["arg1", 100])

let answer = try myObj.call("pose", kwArgs: ["questionNumber": 40])

See RbGateway and its global instance Ruby for access to the Ruby ‘top self’ object to get started finding constants or calling global functions.

Converting to and from Swift types

Convert RbObjects to Swift types using failable initializers or the throwing convert methods:

let height = Double(myObj)
let allHeights = Array<Double>(myObj)
let heightDb = Dictionary<String, Double>(myObj)

let width = try myObj.convert(to: Double.self)
model.field = try myObj.convert()

Check RbError.history to see the cause of failed initializations.

In the reverse direction, Swift types convert implicitly to RbObject when passed as arguments via the RbObjectConvertible protocol.

Collection protocols

The conversion example above converts the entire Ruby array to an independent Swift array. An alternative is to use RbObject.collection which provides a dynamic view onto the Ruby array that supports many Swift collection protocols so you can update a Ruby array like:

myArrayObj.collection.sort(4..<8)

Standard library conformances

RbObject conforms to HashableHashable, EquatableEquatable, and ComparableComparable protocols by forwarding to the corresponding Ruby methods. Beware though that it is easy to trigger Ruby errors here that currently cause RubyGateway to crash. For example this is poison:

RbObject(3) < RbObject("barney")

Future releases may add more control over what happens here.

Arithmetic operators

RbObject conforms to the SignedNumericSignedNumeric protocol by forwarding the regular arithmetic operators to the corresponding Ruby methods. This means you can write let a = b + c where all are RbObjects and get a valid value for a provided b and c are any of the Ruby numeric values or any Ruby class that happens to support those operators.

Again you must take ensure that your Ruby objects support these operators or the program will crash.

Defining methods

Use RbObject.defineMethod(...) and RbObject.defineSingletonMethod(...) to add methods implemented in Swift to an object or class. Use RbGateway.defineClass(_:parent:under:) to define entirely new classes.

Declaration
Swift
public final class RbObject: RbObjectAccess, Sendable

extension RbObject: CustomStringConvertible,
    CustomDebugStringConvertible,
    CustomPlaygroundDisplayConvertible

extension RbObject: ExpressibleByArrayLiteral

extension RbObject: ExpressibleByBooleanLiteral

extension RbObject: ExpressibleByDictionaryLiteral

extension RbObject: ExpressibleByFloatLiteral

extension RbObject: ExpressibleByIntegerLiteral

extension RbObject: ExpressibleByStringLiteral

extension RbObject: Hashable, Equatable, Comparable

extension RbObject: RbObjectConvertible

extension RbObject: SignedNumeric

class RbObjectAccess

Provides services to manipulate a Ruby object:

  • Call methods;
  • Access properties and instance variables;
  • Access class variables;
  • Access global variables;
  • Find constants, classes, and modules.

The class is abstract. You use it via RbObject and the global Ruby instance of RbGateway.

By default all methods throw RbErrors if anything goes wrong including when Ruby raises an exception. Use the RbObjectAccess.failable adapter to access an alternative API that returns nil on errors instead. You can still see any Ruby exceptions via RbError.history.

Calling methods

Ruby has a few different ways to call methods that are reflected in the various Swift methods here and their types. The degrees of freedom are:

  1. Call method by name or by symbol;
  2. Pass positional and/or keyword arguments;
  3. Optionally pass a block that can be expressed as a Swift function or a Ruby Proc.
  4. Method can either raise an exception or return a value.

From the simple:

try! obj.call("myMethod")

…to more baroque:

do {
    let result =
         try obj.call(symbol: myMethodSymbol,
                      args: [1, "3.5", myHash],
                      kwArgs: [("mode", RbSymbol("debug")]) { blockArgs in
                          blockArgs.forEach {
                              process($0)
                          }
                          return .nilObject
                      }
} catch RbError.rubyException(let exn) {
    handleErrors(error)
} catch {
    ...
}

When passing a Swift function as a block there are two methods to choose from. If the block is used only within the method execution you can provide a non-escaping, non-sendable function; if the block is persisted and used later then you must provide a retention rule and an escaping, sendable function.

Declaration
Swift
public class RbObjectAccess

struct RbObjectCollection

A view onto a Ruby array using Swift collection protocols.

This is an adapter type that wraps an RbObject and adopts Swift collection protocols for use with an underlying Ruby array (or any Ruby object that supports length, [], and []=.)

For example:

myObj.collection.replaceSubrange(lower..<upper, with: otherArray)

This is separate to RbObject to avoid dumping all the collection protocol members into its dynamic member lookup namespace.

Declaration