Project

cordis

0.0
The project is in a healthy, maintained state
Revertible effects with LIFO disposal, a plugin/fiber lifecycle state machine, reactive coeffects (service provide/inject), events, and service isolation — the core mechanisms of Cordis (cordiverse/cordis), reimplemented on the async gem. Based on the paper "A Programming Paradigm for Spatiotemporal Composability".
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies

Runtime

~> 2.35
 Project Readme

cordis-rb

English | 繁體中文 | 日本語


English

⚠️ Experimental — a learning/research project, not production-ready. APIs change without notice.

cordis-rb is a Ruby reimplementation of the core mechanisms of Cordis (a TypeScript plugin/lifecycle meta-framework), based on the paper "A Programming Paradigm for Spatiotemporal Composability". It is built on the async gem, using Fiber-based cooperative concurrency in place of the event loop that JavaScript gets for free.

What's here so far

Aligned with upstream packages/core (4.0 naming: Fiber, formerly EffectScope):

  • Revertible effectsctx.effect applies a side effect and registers its inverse (single or multi-step); disposal is strict LIFO (Theorem 16)
  • Plugin systemctx.plugin applies a plugin as a revertible effect on the parent fiber, so the whole plugin tree is one nested effect tree; loading is deferred one tick, load/unload transitions are serialized per fiber (inertia lock)
  • Reactive coeffectsctx.provide / ctx.inject: consumers load when their dependencies are satisfied, reload when a provider is swapped, and are torn down before their provider finishes unloading
  • Eventsctx.on / once / emit / bail / waterfall (sync) and ctx.parallel / serial (async); listeners are effects, removed automatically on fiber disposal
  • Isolation & interceptctx.isolate puts a service name in its own realm (provides/injects no longer cross the boundary; share a realm by passing the same label); ctx.intercept / Hash inject configs carry per-caller config, resolved with ctx.resolve_config
  • Service base class — subclass Cordis::Service, declare provide :name (+ optional inject), and pass the class to ctx.plugin: the instance provides itself, and #init runs as the load body (blocking there keeps the service invisible to dependents — pending inject)
ctx = Cordis::Context.new

Sync do
  provider = ctx.plugin(lambda { |c, _config|
    db = Database.connect
    c.effect { -> { db.close } } # registered before provide → runs after all dependents are gone
    c.provide(:db, db)
  })

  ctx.inject([:db]) do |c, _config|
    c.on('request') { |req| c.db.query(req) } # loads only once :db is active
  end

  provider.await   # wait for the deferred load
  provider.dispose # dependents tear down first, then the provider, LIFO
end

Roadmap

  • Revertible effects with LIFO disposal
  • Plugin/fiber lifecycle (epoch + inertia state machine, via async)
  • Reactive coeffects (ctx.provide / ctx.inject)
  • Event system (ctx.on, waterfall included)
  • Isolation & intercept (ctx.isolate / ctx.intercept)
  • Cordis::Service base class
  • Loader / hot-reload reconciliation — maybe, later

Installation

gem 'cordis', github: '5xRuby/cordis-rb' # not yet published to rubygems.org

Development

bundle install && bundle exec rspec
bundle exec ruby examples/demo.rb   # lifecycle / coeffect walkthrough
ruby examples/webapp.rb             # the same story with Sinatra + Falcon + SQLite

License

Apache-2.0


繁體中文

⚠️ 實驗性專案 — 練習/研究用途,不求生產可用,API 隨時會變。

cordis-rb 用 Ruby 重新實作 Cordis(TypeScript 的 plugin/生命週期 meta-framework)的核心機制,理論基礎來自論文 "A Programming Paradigm for Spatiotemporal Composability"。以 async gem 為併發基底 —— JavaScript 有 event loop 免費提供協作式併發,Ruby 沒有對應物,這裡用 Fiber-based 的 async 補上這塊。

目前完成

對接上游 packages/core(4.0 命名:Fiber,即原本的 EffectScope):

  • Revertible effectctx.effect 套用副作用並登記 inverse(單步或多步);撤除嚴格 LIFO(Theorem 16)
  • Plugin 系統ctx.plugin 把 plugin 當成一個 revertible effect 掛在 parent fiber 上,整棵 plugin tree 就是巢狀 effect tree;載入延後一個 tick,每個 fiber 的 load/unload 由 inertia lock 序列化
  • Reactive coeffectctx.provide / ctx.inject:依賴滿足才載入、provider 被換掉就 reload、provider 卸載前依賴者先 teardown
  • 事件系統ctx.on / once / emit / bail / waterfall(同步)與 ctx.parallel / serial(非同步);listener 就是 effect,fiber 卸載時自動移除
  • Isolation 與 interceptctx.isolate 讓某個 service name 進入獨立 realm(provide/inject 不再跨界;傳同一個 label 可共用 realm);ctx.intercept 與 Hash 形式的 inject config 攜帶 per-caller 設定,用 ctx.resolve_config 解析
  • Service base class — 繼承 Cordis::Service、宣告 provide :name(可加 inject),把 class 直接丟給 ctx.plugin:instance 會 provide 自己,#init 就是載入本體(在裡面 block 住,service 對依賴者就不可見 —— pending inject)
ctx = Cordis::Context.new

Sync do
  provider = ctx.plugin(lambda { |c, _config|
    db = Database.connect
    c.effect { -> { db.close } } # 註冊在 provide 之前 → 所有依賴者卸載後才執行
    c.provide(:db, db)
  })

  ctx.inject([:db]) do |c, _config|
    c.on('request') { |req| c.db.query(req) } # :db active 之後才載入
  end

  provider.await   # 等延後的載入完成
  provider.dispose # 依賴者先卸載,再撤 provider,LIFO
end

Roadmap

  • Revertible effect 與 LIFO 撤除
  • Plugin/fiber 生命週期(epoch + inertia 狀態機,基於 async)
  • Reactive coeffect(ctx.provide / ctx.inject)
  • 事件系統(ctx.on,含 waterfall)
  • Isolation 與 intercept(ctx.isolate / ctx.intercept)
  • Cordis::Service base class
  • Loader / hot-reload reconciliation — 骨架穩了再說

安裝

gem 'cordis', github: '5xRuby/cordis-rb' # 尚未發佈到 rubygems.org

開發

bundle install && bundle exec rspec
bundle exec ruby examples/demo.rb   # 生命週期 / coeffect 導覽
ruby examples/webapp.rb             # 同一個故事,換成 Sinatra + Falcon + SQLite

授權

Apache-2.0


日本語

⚠️ 実験的プロジェクト — 学習・研究目的であり、プロダクション利用は想定していません。API は予告なく変わります。

cordis-rb は、Cordis(TypeScript 製の plugin / ライフサイクル meta-framework)のコア機構を Ruby で再実装するプロジェクトです。理論的基盤は論文 "A Programming Paradigm for Spatiotemporal Composability" にあります。並行処理の基盤には async gem を採用 —— JavaScript ではイベントループが協調的並行性を無償で提供しますが、Ruby には相当物がないため、Fiber ベースの async でその役割を担います。

現状

上流 packages/core に整合(4.0 命名:Fiber、旧 EffectScope):

  • Revertible effectctx.effect が副作用を適用して inverse を登録(単段・多段);巻き戻しは厳密な LIFO(Theorem 16)
  • Plugin システムctx.plugin は plugin を revertible effect として parent fiber に掛けるため、plugin ツリー全体が入れ子の effect ツリーになる;ロードは 1 tick 遅延、fiber ごとの load/unload は inertia lock で直列化
  • Reactive coeffectctx.provide / ctx.inject:依存が満たされたらロード、provider が入れ替われば reload、provider のアンロード前に依存側が先に teardown
  • イベントシステムctx.on / once / emit / bail / waterfall(同期)と ctx.parallel / serial(非同期);listener は effect であり、fiber の破棄時に自動で外れる
  • Isolation と interceptctx.isolate は service name を独立した realm に隔離(provide/inject は境界を越えない;同じ label を渡せば realm を共有);ctx.intercept と Hash 形式の inject config は per-caller 設定を運び、ctx.resolve_config で解決
  • Service base classCordis::Service を継承し provide :name(必要なら inject も)を宣言、class をそのまま ctx.plugin に渡す:instance が自身を provide し、#init がロード本体になる(そこで block すれば service は依存側から不可視のまま —— pending inject)
ctx = Cordis::Context.new

Sync do
  provider = ctx.plugin(lambda { |c, _config|
    db = Database.connect
    c.effect { -> { db.close } } # provide より先に登録 → 依存側が全て消えた後に実行
    c.provide(:db, db)
  })

  ctx.inject([:db]) do |c, _config|
    c.on('request') { |req| c.db.query(req) } # :db が active になってからロード
  end

  provider.await   # 遅延ロードの完了を待つ
  provider.dispose # 依存側が先に teardown、その後 provider、LIFO
end

Roadmap

  • Revertible effect と LIFO 巻き戻し
  • Plugin/fiber ライフサイクル(epoch + inertia ステートマシン、async ベース)
  • Reactive coeffect(ctx.provide / ctx.inject)
  • イベントシステム(ctx.on、waterfall 含む)
  • Isolation と intercept(ctx.isolate / ctx.intercept)
  • Cordis::Service base class
  • Loader / hot-reload reconciliation — 骨格が安定してから

インストール

gem 'cordis', github: '5xRuby/cordis-rb' # rubygems.org には未公開

開発

bundle install && bundle exec rspec
bundle exec ruby examples/demo.rb   # ライフサイクル / coeffect ウォークスルー
ruby examples/webapp.rb             # 同じストーリーを Sinatra + Falcon + SQLite で

ライセンス

Apache-2.0