RubyGateway

CI codecov Carthage compatible Platforms License

在 Swift 中嵌入 Ruby:加载 Gems、运行 Ruby 脚本、在两个方向无缝调用 API。

RubyGateway 是一个基于 Ruby C API 构建的框架,它让运行在 macOS 或 Linux 上的 Swift 程序能够轻松且安全地运行 Ruby 程序并与之交互。可以很容易地将 Swift 值传递到 Ruby 中,并将 Ruby 对象转换回 Swift 类型。

RubyGateway 允许你从 Swift 调用任何 Ruby 方法,包括将 Swift 闭包作为块传递。 它允许你定义在 Swift 中实现的 Ruby 类和方法。

如果您正在寻找一个低级的 Ruby C API 包装器,请参阅 CRuby

示例

服务

Rouge 是一个代码高亮器。 在 Ruby 中

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

在 Swift 中

import RubyGateway

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

调用 Ruby

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

// Access 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)

调用 Swift

绑定类定义

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)

从 Ruby 调用

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

全局变量

// epochStore.current: Int

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

全局函数

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
}

从 Ruby 调用

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

文档

要求

安装

对于 macOS,如果您乐于使用系统 Ruby,则只需将 RubyGateway 框架作为依赖项包含即可。 如果您在 Linux 上构建或想使用不同的 Ruby,则还需要配置 CRuby。

Linux

从 Swift 6 开始,Apple 破坏了 Swift PM,因此您必须传递 "-Xcc -fmodules" 才能构建项目。 请查看 CI 调用示例。

获取框架

macOS 的 Carthage

github "johnfairh/RubyGateway"

macOS 或 Linux 的 Swift 包管理器

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

配置 CRuby

CRuby 是 RubyGateway 和您的 Ruby 安装之间的粘合剂。 它是一个 独立的 github 项目,但 RubyGateway 将其作为子模块包含,因此您无需单独安装或要求它。

默认情况下,它指向 macOS 系统 Ruby。 按照 CRuby 使用说明 更改此设置。 例如,在 Ubuntu 18 上使用 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 -fmodules

贡献

欢迎:提出问题 / johnfairh@gmail.com / @johnfairh@mastodon.social

许可证

在 MIT 许可证下分发。