在 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)
// 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)
绑定类定义
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。
从 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 是 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 许可证下分发。