Swiftpack.co -  johnfairh/RubyGateway as Swift Package
Swiftpack.co is a collection of thousands of indexed Swift packages. Search packages.
johnfairh/RubyGateway
Embed Ruby in Swift: load Gems, run scripts, call APIs seamlessly in both directions.
.package(url: "https://github.com/johnfairh/RubyGateway.git", from: "v5.1.0")

RubyGateway

CI codecov Carthage compatible Pod Platforms License

Embed Ruby in Swift: load Gems, run Ruby scripts, invoke APIs seamlessly in both directions.

RubyGateway is a framework built on the Ruby C API that lets Swift programs running on macOS or Linux painlessly and safely run and interact with Ruby programs. It's easy to pass Swift values into Ruby and turn Ruby objects back into Swift types.

RubyGateway lets you call any Ruby method from Swift, including passing Swift closures as blocks. It lets you define Ruby classes and methods that are implemented in Swift.

See CRuby if you are looking for a low-level Ruby C API wrapper.

Examples

Services

Rouge is a code highlighter. In Ruby:

require 'rouge'
html = Rouge.highlight("let a = 3", "swift", "html")
puts(html)

In Swift:

import RubyGateway

try Ruby.require(filename: "rouge")
let html = try Ruby.get("Rouge").call("highlight", args: ["let a = 3", "swift", "html"])
print(html)

Calling Ruby

// Create an object.  Use keyword arguments with initializer
let student = RbObject(ofClass: "Academy::Student", kwArgs: ["name": "barney"])!

// Acess an attribute
print("Name is \(student.get("name"))")

// Fix their name by poking an ivar
try! student.setInstanceVar("@name", newValue: "Barney")

// Get a Swift version of `:reading`
let readingSubject = RbSymbol("reading")

// Call a method with mixed Swift data types
try! student.call("add_score", args: [readingSubject, 30])
try! student.call("add_score", args: [readingSubject, 35])

// Get a result as floating point
let avgScoreObj = try! student.call("mean_score_for_subject", args: [readingSubject])
let avgScore = Double(avgScoreObj)!
print("Mean score is \(avgScore)")

// Pass Swift code as a block
let scores = student.all_scores!
scores.call("each") { args in
    print("Subject: \(args[0]) Score: \(args[1])")
    return .nilObject
}

// Convert to a Swift array
let subjects = Array<String>(student.all_subjects!)
subjectsPopularityDb.submit(subjects: subjects)

Calling Swift

Bound class definition:

class Cell {
    init() {
    }

    func setup(m: RbMethod) throws {
        ...
    }
    
    func getContent(m: RbMethod) throws -> String {
        ...
    }
}

let cellClass = try Ruby.defineClass("Cell", initializer: Cell.init)

try cellClass.defineMethod("initialize",
        argsSpec: RbMethodArgsSpec(mandatoryKeywords: ["width", "height"])
        method: Cell.setup)

try cellClass.defineMethod("content",
        argsSpec: RbMethodArgsSpec(requiresBlock: true),
        method: Cell.getContent)

Called from Ruby:

cell = Cell.new(width: 200, height: 100)
cell.content { |c| prettyprint(c) }

Global variables:

// epochStore.current: Int

Ruby.defineGlobalVar("$epoch",
        get: { epochStore.current },
        set: { epochStore.current = newEpoch })

Global functions:

let logArgsSpec = RbMethodArgsSpec(leadingMandatoryCount: 1,
                                   optionalKeywordValues: ["priority" : 0])
try Ruby.defineGlobalFunction("log",
                              argsSpec: logArgsSpec) { _, method in
    Logger.log(message: String(method.args.mandatory[0]),
               priority: Int(method.args.keyword["priority"]!))
    return .nilObject
}

Calls from Ruby:

log(object_to_log)
log(object2_to_log, priority: 2)

Documentation

Requirements

  • Swift 5.4 or later, from swift.org or Xcode 12.5+
  • macOS (tested on 11.3) or Linux (tested on Ubuntu Bionic/18.04 on x86_64) with Clang 6.
  • Ruby 2.6 or later including development files:
    • For macOS, these come with Xcode.
    • For Linux you may need to install a -dev package depending on how your Ruby is installed.
    • RubyGateway requires 'original' MRI/CRuby Ruby - no JRuby/Rubinius/etc.

Installation

For macOS, if you are happy to use the system Ruby then you just need to include the RubyGateway framework as a dependency. If you are building on Linux or want to use a different Ruby then you also need to configure CRuby.

If you are using Ruby 3 then you need to set the -fdeclspec Clang flag, either on the Swift PM command line (swift build -Xcc -fdeclspec) or in Xcode's Other Swift Flags settings.

Getting the framework

Carthage for macOS:

github "johnfairh/RubyGateway"

Swift package manager for macOS or Linux:

.package(url: "https://github.com/johnfairh/RubyGateway", from: "5.0.0")

CocoaPods for macOS:

pod 'RubyGateway'

Configuring CRuby

CRuby is the glue between RubyGateway and your Ruby installation. It is a separate github project but RubyGateway includes it as submodule so you do not install or require it separately.

By default it points to the macOS system Ruby. Follow the CRuby usage instructions to change this. For example on Ubuntu 18 using rbenv Ruby 3:

mkdir MyProject && cd MyProject
swift package init --type executable
vi Package.swift
# add RubyGateway as a package dependency (NOT CRuby)
# add RubyGateway as a target dependency
echo "import RubyGateway; print(Ruby.versionDescription)" > Sources/MyProject/main.swift
swift package update
swift package edit CRuby
Packages/CRuby/cfg-cruby --mode rbenv --name 3.0.0
PKG_CONFIG_PATH=$(pwd)/Packages/CRuby:$PKG_CONFIG_PATH swift run -Xcc -fdeclspec

Contributions

Welcome: open an issue / johnfairh@gmail.com / @johnfairh

License

Distributed under the MIT license.

GitHub

link
Stars: 81
Last commit: 4 weeks ago

Ad: Job Offers

iOS Software Engineer @ Perry Street Software
Perry Street Software is Jack’d and SCRUFF. We are two of the world’s largest gay, bi, trans and queer social dating apps on iOS and Android. Our brands reach more than 20 million members worldwide so members can connect, meet and express themselves on a platform that prioritizes privacy and security. We invest heavily into SwiftUI and using Swift Packages to modularize the codebase.

Release Notes

5.1.0
4 weeks ago

Update to require Swift 5.4 to build, Ruby 2.6 to run, and support Ruby 3 too.

Breaking

  • Removed RbGateway.taintChecks -- $SAFE removed in Ruby 3
  • Internal modules CRuby and RubyGatewayHelpers are now imported as @_implementationOnly

Enhancements

  • Support Ruby 3 - check README notes on -fdeclspec, see CI for an example
  • Support building cleanly with Xcode 13
  • Add kwArgs parameter to RbMethod.yieldBlock(...)

Swiftpack is being maintained by Petr Pavlik | @ptrpavlik | @swiftpackco | API