Main APIs
These types form the main API to RubyGateway.
Topics
class RbGateway
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
public final class RbGateway: RbObjectAccess, Sendable
class RbObject
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 RbObject
s 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 Hashable
Hashable
, Equatable
Equatable
, and Comparable
Comparable
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 SignedNumeric
SignedNumeric
protocol by forwarding the regular
arithmetic operators to the corresponding Ruby methods. This means you can
write let a = b + c
where all are RbObject
s 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
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
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 RbError
s 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:
- Call method by name or by symbol;
- Pass positional and/or keyword arguments;
- Optionally pass a block that can be expressed as a Swift function or a Ruby Proc.
- 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
public class RbObjectAccess
struct RbObjectCollection
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
public struct RbObjectCollection: RandomAccessCollection,
MutableCollection,
RangeReplaceableCollection,
RbObjectConvertible