Ruby::Box Hacking Guide

2026-03-17 13:09:14 +0900

Ruby::Box と戦っているので Ruby::BoxCodexRuby::Box Hacking Guide としてまとめてもらいました。

ここから本文

1. Ruby::Box とは何か

Ruby::Box は、同じ Ruby VM の中で、box ごとのロード状態や class / module に紐づく内部状態の一部を切り分けるための仕組みです。Ruby::Box.new で新しい box を作ると、$LOAD_PATH$LOADED_FEATURES、global variable table、class / module に紐づく一部の内部状態が box ごとに分かれます。

Ruby::Box の目的は、同じ VM の中で box ごとの状態分離を実現することです。そのためには、どの状態を共有し、どの状態を box ごとに保持し、どこで解放するのかという ownership を正しく管理する必要があります。

2. まず読むファイル

Ruby::Box を追うときは、次の順で読むと流れを掴みやすいです。

  • box.c
  • class.c
  • internal/class.h
  • test/ruby/test_box.rb

まず box.c で Ruby 側 API と box の lifecycle を見て、次に class.cinternal/class.h で class / module 周りの内部状態の扱いを追います。最後に test/ruby/test_box.rb を読むと、外部からどのような振る舞いが期待されているかと、既存のテストの流儀をまとめて確認できます。

3. 各ファイルの責務

Ruby::Box を追うときは、まず「どのファイルが何を担当しているか」を押さえた方が読みやすくなります。

box.c は Ruby 側 API と box 自体の lifecycle を担当します。Ruby::Box.newRuby::Box.currentRuby::Box#requireRuby::Box#eval といった入口に加えて、box-local な load path、loaded features、global variable table、local extension cleanup もここで扱います。

class.c は class / module に紐づく Ruby::Box の内部実装の中心です。rb_class_duplicate_classextclass_duplicate_iclass_classextrb_class_classext_free など、box ごとの class/module 状態を複製・管理・解放する処理があります。

internal/class.hrb_classext_t の定義と、その読み書きを行う macro / helper を持ちます。box ごとの classext lookup がどのように切り替わるかを見るにはここが基準になります。

test/ruby/test_box.rb は Ruby::Box に期待されている外部挙動を確認する場所です。API の使い方だけでなく、どのような regressions が既に認識されているかを把握するうえでも重要です。

4. rb_classext_t と box ごとの状態

Ruby::Box を理解するうえで重要なのは、class object 自体を box ごとに丸ごと複製しているわけではない、という点です。実際に box ごとの差分を支えているのは rb_classext_t です。

概念的には、次のような構造をイメージすると分かりやすいです。

                +----------------------+
                | class / module obj   |
                |       (shared)       |
                +----------------------+
                           |
          +----------------+----------------+
          |                                 |
          v                                 v
  +----------------------+        +----------------------+
  | prime classext       |        | box classext         |
  +----------------------+        +----------------------+
  | m_tbl                |        | m_tbl                |
  | const_tbl            |        | const_tbl            |
  | cvc_tbl              |        | cvc_tbl              |
  | subclasses / caches  |        | subclasses / caches  |
  +----------------------+        +----------------------+

Ruby::Box の class/module 周りの挙動は、「どの class object を見るか」ではなく「どの box 用 classext を使うか」で切り替わります。したがって、method table、constant table、class variable cache table、subclass 関連の情報、一部の cache 類が、shared のままよいのか、box ごとに持つべきかを常に意識する必要があります。

include が絡むと ICLASS 用の classext も登場しますが、それは基本構造の上に乗る特殊ケースです。まずは「shared な class/module object に対して、box ごとに classext がぶら下がる」という見方を固めるのが先です。

5. Box の基本操作はどう実装されているか

Ruby 側から見える操作の大半は box.c にあります。Ruby::Box.new は root box の状態を土台にして、新しい box-local 状態を初期化します。Ruby::Box.currentRuby::Box.rootRuby::Box.main は、現在どの box 文脈で実行しているかを表す入口です。

Ruby::Box#requireRuby::Box#loadRuby::Box#require_relative は、box ごとの load path と loaded features を使って feature を解決します。現状の実装では、新しい box は root 側の $LOAD_PATH$LOADED_FEATURES を初期値として複製し、その後は独立して変化します。そのため、box 作成後に main 側で行った path の変更や gem activation は box 側に自動では反映されず、require の解決結果が一致しないことがあります。

Ruby::Box#eval は、指定した box の文脈でコードを評価する入口です。再現コードの切り分けや、box ごとの定数・メソッド解決を確認するときに重要です。

また、Ruby::Box は Ruby-level API だけで完結していません。class/module 周りの状態は class.cinternal/class.h が支えており、必要な classext は読むタイミングや書くタイミングで作られます。新しい box を作った瞬間に全てが eager に複製されるわけではありません。

6. Box 拡張時の設計上の論点

以下は、現状の Ruby::Box 実装から読み取れる範囲で、box ごとの状態分離を考えるうえで整理が必要になる点をまとめたものである。

新しい機能や内部状態を Box 対応させるときは、まず次の 4 点を整理する必要があります。

  1. その状態は shared でよいのか
  2. box-local にするならどこに持つのか
  3. いつ複製するのか
  4. どこで解放するのか

box-local な load path や loaded features のように rb_box_t にぶら下がっているものもあれば、class/module に紐づくので rb_classext_t に置かれているものもあります。新しい state を追加するときは、まずその所有単位が box なのか class/module なのかを見定める必要があります。

次に必要なのは duplication の方針です。eager に複製するのか、最初の write や最初の lookup まで遅延させるのかで実装が変わります。Ruby::Box は copy-on-write に寄せた構造が多いので、複製の入口と invalidation の入口を分けて考えた方が追いやすくなります。

最後に、mark、write barrier、free の対応も確認対象になります。Ruby object への参照が増えるなら GC の mark/update が必要になりますし、table entry を heap 上に積むなら cleanup 側で誰が free するのかを決める必要があります。Ruby::Box の拡張では、機能追加そのものより ownership と lifecycle の整合性が問題になりやすいです。

7. テストの書き方

Ruby::Box のテスト入口は test/ruby/test_box.rb です。ここには enabled?currentrootmainrequireload、nested box、ICLASS 周りの回帰テストがまとまっています。

通常の機能追加なら、まず既存のテストと同じ粒度で setup_box を使ったテストを書けば十分です。一方で、exit 時 crash や process-global な副作用が絡むなら subprocess 系 assertion を使います。

  • assert_separately
  • assert_ruby_status
  • assert_in_out_err

exit status だけ見たいなら assert_ruby_status が軽く、標準出力や標準エラーも検証したいなら assert_in_out_err を使います。Box 内部の regression test では、可能な限り pure Ruby の再現に寄せ、stdlib native extension 依存は避ける方が無難です。

8. Box 拡張時の実装確認項目

実装を入れた後は、少なくとも次を確認した方がよいです。

  • root / main / user box で見え方が変わるか
  • require / load / eval をまたいで状態が期待通りに見えるか
  • class/module に紐づく状態なら classext duplication と cleanup が対応しているか
  • GC mark/update と write barrier が不足していないか
  • process 終了時や box teardown 時にだけ壊れないか

特に class/module に紐づく state は、「作る場所」と「free する場所」をセットで確認した方がよいです。複製側だけを見ていると shallow copy の見落としをしやすく、cleanup 側だけを見ていると shared state を壊しやすくなります。

9. Appendix: 壊れやすい箇所

Ruby::Box の不具合は、表面的には NameErrorLoadError、stale cache、終了時 crash のように見えても、内部では ownership mismatch に収束することが多いです。特に class/module 系では、次の 3 点を一本の線で追うと整理しやすくなります。

  1. box 作成時に何が複製されるか
  2. 何が shared のまま残るか
  3. 終了時に何が free されるか

特に class/module 系では、次の関数から読むと追いやすいです。

  • rb_class_duplicate_classext
  • class_duplicate_iclass_classext
  • rb_class_classext_free

class variable 系なら variable.crb_cvar_setrb_cvar_class_tbl_entry を作り、vm_insnhelper.c がそれを cache として使います。allocation と free が本当に 1 対 1 になっているかを見ると、exit 時 crash の切り分けがしやすくなります。

10. Appendix: build / run の落とし穴

Ruby::Box の調査では、VM の問題と build/run 環境の問題が混ざりやすいです。

  • source tree Ruby に別 install の native extension を混ぜると ABI mismatch で壊れる
  • 新しい box は root 側の $LOAD_PATH$LOADED_FEATURES を初期値として複製し、その後は独立して変化する
  • extconf.rb を変な場所で叩くと top-level Makefile を壊すことがある

再現コードを削るときは、まず unpatched build だけで確実に落ちるケースを作り、その後で patched build に切り替えて差分を見る方がよいです。最初から両方を並行で追うと、再現条件が曖昧なまま比較してしまいやすくなります。

Ruby::Boxを使っただけなのに

2026-03-13 20:55:22 +0900

ruby-4.0 出ましたね。

待望の新機能、 Ruby::Box を使っていますか? 個人プロジェクトで利用しているのでその利用方法について書いていきますよ。

Testで安全にFFI(fiddle)を使いたい!!!!

Ruby::Box はかつて Namespace と呼ばれていたものです。ということは コンパイラー 書いてる人にとってテストが安全にできるじゃないですか。 コンパイラーで生成したバイナリを安全に分離できるじゃないですか。 テスト 向けのロード用ファイルを作ります。

require "fiddle/import"

module Fibonacci
  extend Fiddle::Importer
  dlload "./libhoge.so"
  extern "int fibo(int)"
end

テスト側では以下のようにしますね。

require "test/unit"
class TestFibo < Test::Unit::TestCase
  def test_load_fiddle
    omit('"Ruby::Box" needs RUBY_BOX=1') unless ENV["RUBY_BOX"] == "1"
    box = Ruby::Box.new
    box.require "./load_so"
    assert_equal(box::Fibonacci.fibo(10), 55)
  end
end

では実行してみましょう

$ RUBY_BOX=1 ruby test_fibo.rb
ruby: warning: Ruby::Box is experimental, and the behavior may change in the future!
See https://docs.ruby-lang.org/en/4.0/Ruby/Box.html for known issues, etc.
Loaded suite test_fibo
Started
Finished in 0.007523671 seconds.
-----------------------------------------------------------------------------------------------
1 tests, 1 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications
100% passed
-----------------------------------------------------------------------------------------------
132.91 tests/s, 132.91 assertions/s

テスト通りましたね!やった!!便利!!! Ruby::Box つかうには RUBY_BOX=1 が必要ですね。なので無い場合のガードとして omit... を入れています。 この確認もしますかね。

$ ruby test_fibo.rb
Loaded suite test_fibo
Started
O
===============================================================================================
Omission: "Ruby::Box" needs RUBY_BOX=1 [test_load_fiddle(TestFibo)]
test_fibo.rb:5:in 'TestFibo#test_load_fiddle'
===============================================================================================
Finished in 0.000660511 seconds.
-----------------------------------------------------------------------------------------------
1 tests, 0 assertions, 0 failures, 0 errors, 0 pendings, 1 omissions, 0 notifications
0% passed
-----------------------------------------------------------------------------------------------
1513.98 tests/s, 0.00 assertions/s

そうしたら omit しないようにしますかね

$ ruby test_fibo.rb
Loaded suite test_fibo
Started
E
===============================================================================================
Error: test_load_fiddle(TestFibo): RuntimeError: Ruby Box is disabled. Set RUBY_BOX=1 environment variable to use Ruby::Box.
test_fibo.rb:5:in 'Ruby::Box#initialize'
test_fibo.rb:5:in 'Class#new'
test_fibo.rb:5:in 'TestFibo#test_load_fiddle'
     2:
     3: class TestFibo < Test::Unit::TestCase
     4:   def test_load_fiddle
  => 5:     box = Ruby::Box.new
     6:     box.require "./load_so"
     7:     assert_equal 55, box::Fibonacci.fibo(10)
     8:   end
===============================================================================================
Finished in 0.000693731 seconds.
-----------------------------------------------------------------------------------------------
1 tests, 0 assertions, 0 failures, 1 errors, 0 pendings, 0 omissions, 0 notifications
0% passed
-----------------------------------------------------------------------------------------------
1441.48 tests/s, 0.00 assertions/s

とこのようになりますね。omit 入れることで error にならなくなりますね。便利!!!! ということで Ruby::Box の便利な面をみてきました。こうやって安全に利用できるようになりましたね。

Ruby::Boxでネームスペースを区切ろう

コンパイラーつくってると生成したバイナリをロードして読み込みたいんですよ。 でもテストはプロセス1つじゃないですか? libbinA.solibbinB.so を同時に読みたいということはとくになく、 libbinA.so は一つで読みとりたい。 libbinB.solibbinA.so とは別に読みとりたいことがあるじゃないですか。 このとき、 Ruby::Box でそれぞれ読めばいいとおもうじゃん?ということでやってみますね。 といってもいきなり分けてやるよりもどうせネームスペースがわかれるので同じファイルで実験してみましょう。

require "test/unit"
class TestFibo < Test::Unit::TestCase
  def test_load_fiddle
    omit('"Ruby::Box" needs RUBY_BOX=1') unless ENV["RUBY_BOX"] == "1"
    box = Ruby::Box.new
    box.require "./load_so"
    assert_equal(box::Fibonacci.fibo(10), 55)
  end
  
  def test_load_fiddle_2
    omit('"Ruby::Box" needs RUBY_BOX=1') unless ENV["RUBY_BOX"] == "1"
    box = Ruby::Box.new
    box.require "./load_so"
    assert_equal(box::Fibonacci.fibo(10), 55)
  end
end

実行〜〜

$ RUBY_BOX=1 ruby test_fibo.rb
ruby: warning: Ruby::Box is experimental, and the behavior may change in the future!
See https://docs.ruby-lang.org/en/4.0/Ruby/Box.html for known issues, etc.
Loaded suite test_fibo
Started
E
===============================================================================================
Error: test_load_fiddle_2(TestFibo)
===============================================================================================
Error: test_load_fiddle_2(TestFibo)Finished in 0.011718944 seconds.
-----------------------------------------------------------------------------------------------
1 tests, 1 assertions, 0 failures, 2 errors, 0 pendings, 0 omissions, 0 notifications
100% passed
-----------------------------------------------------------------------------------------------

WTF 落ちるなんで!!!????よくみたら 2 errors なのに 100% passed ってなんぞ!? そもそも表示も崩壊してんじゃねーか。 ということでエラー内容みますかね。

load_so.rb:1:in 'Kernel#require': cannot load such file -- fiddle/import (LoadError)

WTF 標準ライブラリーが見えねえってどういうことだよ!!!

これから真相を探るため Ruby::Box を探す旅がはじまります。

老害からのひとこと

2025-12-01 00:00:00 +0900

やんちゃハウス Advent Calendar 2025 1日目の記事となります。

やんちゃハウスとは?

@yancya が主催する RubyKaigi/Regional RubyKaigi用泊り込み施設です。 RubyKaigi 2017からやんちゃハウスは開催されています。 今年のRubyWorld Conference開催時のやんちゃWorldハウス含み計15回開催(コロナ情勢により2つのイベントがキャンセル)されています。 うちやんちゃハウス6回(2018-2025)、やんちゃWorldハウス1回(2024)に参加しています。 やんちゃ首都圏メガロポリスハウス(2025)は参加申し込みはして料金も払ったけど体調不良で不参加でした。

よいてん

よい点として以下のような点があります。

  • 宿を予約しなくていい
  • わいわいできる

宿を予約しなくていい

あらかじめやんちゃさんが予約してくれているので私にとってはこの点は非常に大きい点です。 予約して時間通りに行くというのが嫌な人間としてはこの点は非常にいいですね。 それくらいか。

わいわいできる

同じ目的を持ったひとたちで集まるので、自然とわいわいできます。 とくに RubyKaigi の感想とかで深夜まで話こんだりしてしまいますね。 今年のハウスではRubik’s Cubeで盛り上がってたようです。

どちらともいえないてん

いいともわるいともいえない点としては以下のようなものがあります。

  • 大人数での宿泊
  • 場所

大人数での宿泊

どうしても大人数での宿泊となりますので、深夜までうるさくなりがちです。 私はそんな環境でも寝れるのでたいして問題にはなりませんが、気になる人は注意かも。

場所

毎回毎回近い場所で開催できるわけもなく、遠い回もあります。一番遠かったのは三重かな。 次回の函館は一番近いです。道中みんなでわいわいしながら行くという話もあるので そんなに大変かというとそこまでないです。

もっていくとよさそうなもの

とりあえずうるさくてもまぶしくてもあまり気にならない人間なのであるとよさそうなものを

安眠にあるとよさそうなもの

  • アイマスク
  • 耳栓
  • ネックピロー

やんちゃハウス 2019 のときはリビングルームみたいなところで寝てたのでアイマスクや耳栓はあるとよいと思いますね。 そのときはまあそんなところだとはおもっていなかったけど、どこでも寝れるので問題なかったですね。 あと枕は気になるというひとは予めネックピローなんてものを買ってもっていくといいんじゃないかな。

みんなでわいわいするためのもの

  • 酒、お土産
  • ボードゲーム
  • PC

わいわいするために酒やお土産を持っていってもいいとおもいますね。 ボードゲームで盛り上げるのもありだとおもいます。 やんちゃハウス 2025 ではやんちゃさんがいろいろ持ってきていました。 熱燗用のお酒や、Rubik’s Cubeを大量に持ってきてくれてました。 PCはRubyKaigiのセッション内容の復習とかであるといいとおもいますし、 復習時に動かしたりコード読んだりすることができるのであるといいですね。 みんな当然持ってきてるとおもうけどね。 やんちゃハウス開催期間中、二次会でいそがしく実はみんなで集る機会はすくないのですが……

まとめ

主催者以外で自称最多参加のおじさんからのやんちゃハウス参加への心得を書いてみました。 来年の函館やんちゃハウスも参加予定ですのでよろしくおねがいします。

身分証明書を引き出すための方法

2025-08-14 00:00:00 +0900

去る 2025/08/03 14:00 ごろに財布を紛失したのでやったことをまとめます。

財布紛失

普段行かない原宿で買い物してたら、財布がないのに気付く。 とりあえず気付いた店で「財布落ちてなかったか?」と店員に聞きまわる。 そこではみつからなかったので、一旦立ち寄った店でも聞きまわり、ないということが分かった。 ないことが分かったので、近く警察に紛失した旨を申告、遺失物届けを提出。

カード類停止

原宿という場所柄、クレジットカードが利用されていないかスマートフォンで確認。 調べた時点では利用されていないので、帰宅後クレジットカード、キャッシュカードを止め、クレジットカードは再発行手続きを実施。 幸い買い物行く直前にカードから suica へ入金していたので got 事なき。 現金、カードが無いのでその日の夕食はスタ丼屋ヘ。 紛失した日は一旦これで終わり。

ブートストラップ身分証明書

次の日、仕事休んで身分証明書の発行へ。

まず現金が手元にないのでキャッシュカードの再発行手続きへ銀行に行く。 ここでキャッシュカードの再発行に必要なものとして病院の診察券などの本人確認書類の提示が求められた。 財布を紛失したことでそもそもそのような身分証明書類になるようなものを所持しておりません。 正直になにも所持していないことを銀行の窓口に伝える。これ以上は銀行ではできないことがわかりました。 キャッシュカードの再発行は後回しにして、身分証明書の再発行へ。

まずは、マイナカードの再発行。マイナカードの再発行に居住地区の役所へ、すると役所でのマイナカードの再発行手続きにはどうやら本人確認書類となる運転免許証が必要とわかった。

ということで運転免許証の再発行へ近くの警察署へ。警察署署員に聞くとここでは運転免許証の再発行は行なわれていないことを告げられる。 運転免許試験場で再発行が行なわれていることを署員さんに教えてもらったので運転免許試験場へ。 運転免許試験場へ着いて再発行手続きをすると本人確認書類として住民票の提出を求められました。 そんなものは持っていない状態なので一旦帰宅。

帰宅後、キャッシュカード再発行手続きで必要といわれた身分証明書類を探したが、みつからず。 住民税の通知書を発見したので手に再び役所へ。 役所では、身分証明書類を紛失した旨を伝え、住民票の発行手続きへ。 どうやら住民税支払い通知書だけではダメだったらしく1、本人しか知り得ない情報を捻り出してなんとか発行。 住民票を得たので、今度こそ運転免許証の発行へ最寄りの運転免許試験場に出発。 運転免許試験場の再発行手続きでは、とくになにも問題なくスムーズに再発行手続きがおわり、再発行できました。 運転免許証の再発行自体が完了したのは16:00すぎだったのでその他カード類の再発行は手続きは無理そうだったのでこの日は終了。

カード類復活

身分証明書のブートストラップは解決したので今度は止めてあるカード類の復活を行います。 まずは銀行のキャッシュカードです。住民票と運転免許証を所持しているのでなにも気にせず銀行へ。 銀行に到着したら怒られが発生。 銀行員に「今、ここでキャッシュカードの再発行は予約いっぱいでできないかもしれない。どうしてもやりたいなら予約が空くまでここで待機しておいてください」と。 朝ごはん食べていなかったので昼食をとりに行きたかったので、何時ごろ空くのか聞いてみたら突然予約が空いたらしくその日の午後に手続きができるようになった2。 午後、再訪問し再発行完了!!!

この勢いのままマイナカード再発行へ。今度は身分証明書をいろいろ所持しているので大丈夫やろ。 手続きに入って最終の再発行する際に発行手数料がかかりました。平気平気、この役所は電子マネー使えるから!と思ってたら、 役所の人に「あ、現金のみです」と告げられました。 キャッシュカード再発行できたとはいえ、電子マネー使えるので現金を降ろさずに来てます、なので当然現金なんて持っていないです。 ということで一旦現金降ろしにコンビニへ。

コンビニで現金を引き出そうとすると謎にエラーになります。当然です。カード類停止時にキャッシュカードロックを行なっています。 引き出せないです。どうしようもなくなったので3一旦その日の手続きは終わらして、後日やるということを窓口で伝えました。

帰宅後、キャッシュカードロックの解除を行なおうと銀行のサイトで操作してたら、秘密の質問とかいう謎のパスワードを聞かれた4。 案の定謎パスワードも揮発しているので繰り返し入力してたらそれもバンされて詰み。現金はこの日は引き出せないことが確定。 そんなことなので再設定をするには銀行に行く必要がでてきた。この日は銀行を予約しておわり。

予約した銀行

前日に予約したと思ってた銀行は実際には一週間先で失意で仕事へ。 昼食へ外出する際にポスト覗くとなにかお手紙が。このお手紙読むとなんと財布が発見されたようです。 仕事は中抜けして財布を回収へ。回収した財布はなにひとつ欠けることなく現金まで入ってました5。 ということで急いで現金の引き出しが性急にする必要はなくなったので、銀行の予約は一旦そのままにしておきます。

予約した銀行へ訪問し、キャッシュカードロックを解除させるためにアプリのパスワードリセットします。 といっても銀行の人がリセットしてくれるので眺めておくだけで、こちらはパスワードと秘密のことばをその場で設定しておわりです。

このあと、発見された財布に入ってた旧運転免許証を破棄するために警察署6へ訪問して一連の作業は終了。 つかれた。

まとめ

財布を紛失した顛末を書いた。 財布は失くすといろいろと面倒というのはもともと理解してたつもりだったが、いざ失くなると何が必要とか焦ってしまって調べるとかできないこともわかった。

今回の一番面倒だなと思ったのがマイナンバーカードを紛失した際の手続き。 こいつ自体も強い本人確認証だしほぼすべての本人確認書類が集まっている。 申請自体は多分そこまでおかしい点はなさそうだが、一時的に利用できなくなる期間が1-2週間かかりそう。 今回は幸い盗まれたとかではなかったので再発行しなくてよかったけど。

身分証明書と本人確認書類という言葉が出てきますが、基本同じです。 本人確認書類は相手方(役所、銀行など)として審査、確認する際ときに使う意味として使っています。 身分証明書は自分が相手方に提出する書類としての意味として使っています。 区役所のウェブページで「本人確認書類」と掲示されていたため、意識的に分けています。

おまけ: いろいろなもの再発行フローチャート

  1. 台東区では そんなことはなく、住民税の通知でよさそう。 

  2. この日の気温は37℃越え。そりゃあキャンセルされるわな。 

  3. 近所の人に財布なくしたことは伝えていたので現金化方法はあったが、面倒になったのと窓口の時間も終了が迫っていたので。 

  4. ホントウは設定してあったソフトウェアトークンの再設定をするために秘密の質問が必要。 

  5. こういうとき何故か大量の現金が入ってたりした。 

  6. 見つけた場所の近所の警察で良いとおもうけど、ついでだったので管轄の警察署にした。 

Gentoo のバイナリ配布公式レポを発見したので対応してみたら大失敗した

2025-05-31 23:59:59 +0900

[Gentoo Linux] でバイナリが公式配っていたのを発見したので、 退役したデスクトップをサーバー用途として利用してみます。

設定

設定自体は発見したブログを参考に設定することで問題はないです。

インストール

ではインストールをしてみましょう。 ブログを参考にリポジトリなどを設定後、以下のコマンドでインストールします。

emerge -uDN world

そして失敗へ……

インストールが終わり、動いているのを確認しましょう。 動いていますね。OK, OK では不要なパッケージを片づけましょうかね。

emerge --depclean

一部バイナリになって便利になりましたねー。便利ー。

Zellij のセッションが残っている間は問題が特にわからなかったです。 次の日あたり、クライアントから Samba がなぜか繋がらなくなっています。 原因を探るため、 ssh でログインしてみましょう。 これもログイン失敗します。しかたないのでユーザーを替えてログインしてみます。こいつはログインできます。 ログインしたアカウントから doas してみますが、 doas できる権限を与えていないので当然できません。 sudosudo 自体を削除しているのでできないですね。 また su も権限がないので別ユーザーへ変更できないです。 ということで何かまずいことが起きているのでログインできるようにサーバーの復旧を行ないます。

いつも通り OS インストールと同様にインストールディスクを起動して必要なディレクトリをマウントします。 そうするとなぜかいつも利用しているはずのコマンド eixgitzsh が無いのです!!!

ということでインストールします。

emerge --sync # リポジトリを git で読むようにしているので失敗
emerge git # デフォルトリンカを mold にしているので失敗
# mold 使わないように修正
emerge git
emerge eix
eix-sync # ようやく成功
emerge zsh

というようなコマンドを実施してなんとか復旧。

おわり

バイナリの配布リポジトリを設定できるのは薄々知ってはいたのですが、 動いているのを変更するの面倒だったので今回替えてみました。 カーネルもバイナリでインストールできるようになったので便利になりました(たぶん)。 あとうかつに emerge --depclean はするもんじゃない。

RubyKaigi 2025 at Matz-yama

2025-04-25 23:00:00 +0900

今年も RubyKaigi 2025 に行ってきました。

今年みたセッション一覧を日毎に。

Day 1

  1. Ruby Taught Me About Encoding Under the Hood
  2. Bringing Linux pidfd to Ruby
  3. The Evolution of the CRuby Build System
  4. Deoptimization: How YJIT Speeds Up Ruby by Slowing Down
  5. Ruby’s Line Breaks
  6. State of Namespace
  7. TRICK 2025: Episode I

初日のキーノートは、 @ima1zumi さんで文字コードの話してくれました。 つぎは @mensfeldpidfd の話を聞きました。 これは server 動かしているとはまる問題、 pid の使い回しによるバグの対処方法として pidfdRuby に対応したという話でした。 オチは、そもそも overkill で不要だったねという。 午前の終りは @kateinoigakukun さんのビルドシステムの話でした。 Ruby のビルドシステムのリプレースを考えているようでした。 午後一発目はお馴染 ShopifyYJIT チームの @k0kubun さんの話。 YJIT を速くするために、如何に deoptimization するかという話で、速くするための方法を話していました。 パーサーギャング 一味のドン、@yi-knk さんの話で今年楽しみにしてた発表。 Namespace の話は @tagomoris さんがやってくれました。 今年のうちに入りそうな雰囲気を感じた。 いったん休憩して trick へ。

Day 2

  1. Performance Bugs and Low-level Ruby Observability APIs
  2. Dissecting and Reconstructing Ruby Syntactic Structures
  3. ZJIT: Building a Next Generation Ruby JIT
  4. Bazel for Ruby
  5. The Implementations of Advanced LR Parser Algorithm
  6. Lightning Talks

dd-trace-rb の作者 @ivoanjoobservability の話。 もう同じ目的をもった @osyoyuスマートバンクブログで解説しているのでそちらを参照。 新米 Ruby Commiter@ydah です。 lramaRubyparse.y をどう読み込むのか、どう構造化するのかの話でした。 Shopify YJIT チームの親分、 maximecb の発表。 新しい JIT 、今年一番の衝撃 ZJIT の提案。詳細は Proposal to upstream ZJIT を見てくれ。 パーサーギャング 一味のトリ @junk0612 の発表。 lramaIELR を適用してみる話。まあ遅かったりするようです。 最後に

Day 3

  1. Ruby Committers and the World
  2. Improving my own Ruby
  3. The Ruby One-Binary Tool, Enhanced with Kompo
  4. Toward Ractor local GC
  5. Modular Garbage Collectors in Ruby
  6. Matz Keynote

@sisshiki1969 さんで、rustruby 処理系を作ってる人で、速くした話でした。 @ahogappa さんが作ってる kompo というツールの話で、この kompo はバイナリー1つにまとめてくれる便利ツールです。 今回は Rails アプリが動くようになった話をしていました。 @peterzhu2118 さんが GC (Garbage Collection) をモジュール化して入れ替えれるような話をしてました。 さいごに @matz のキーノートで Ruby のこれからの話、AI の話などなど。

おわり

おそくなったけど、一旦聞いたセッションをまとめてみました。来年は函館で東京から電車一本で行けるので楽そうですね。 おわり。

Hello, ZOLA を失敗した話

2025-04-12 00:00:00 +0900

HELLO, ZOLA World!!!!! としたかったけど、 移行が面倒すぎたので諦めた話。 以下は作業ログです。一応終りは見えたけど、

ELECTIONS

Jekyll でサイト作るの飽きてきたので、 新しい SSG(Static Site Generator) に変更することにした。

選定必須基準として、以下の条件を。

  1. markdown 形式1 で書けること
  2. Jekyll と同じ URL が生成されること

いまどき直接 HTML で書く人はあまりいないし、いままでの記事があるので markdown で書けることは当然ですね。 もうひとつの必須条件として、 Jekyll で設定した URL で記事が書けることとなります。 これは現在出している記事の URL を変更しないために必須です。

移行に苦労しないようにするために以下の条件をとることにした。

  1. Jekyll と同じ様に markdown の先頭に frontmatter でいろいろと設定できること
  2. Jekyll と同様にテーマが選択できること
  3. バイナリーで配ってること

frontmatter については移行を楽にするためで多少の書き替えはしかたがないとしています。 デザイン変更を楽にするためテーマが選択できる SSG だといいなあと思い。 バイナリーで配ってあると楽なんですよ2

以上の条件を踏まえいくつか検討してみましたが、 zola にしました。

getzola/zola - GitHub

Jekyll to Zola

katsyoshi/jekyll2zola - GitHub

やったこと

まず、ファイル名が .markdown と混在してので .md へ統一。 つぎに markdown 先頭の frontmatterYAML から TOML へ変換する。 これは大変な話ではなく、 frontmatter 部分を読み込んで変更するだけです。 でこの変更部分なのですが、大抵の部分は問題にならないです。 大抵の部分は……

URL の設定

URL の設定は、参考サイト の設定を利用して変換します。 ここで問題となるのが、Jekyll の設定で、 /blog/%Y/%m/%d/file-name となっていることです。 Zola では config.toml で設定する slugify に設定する方法があるようですが、イマイチ使えず (そもそも slugify を勘違いしてた)。 ディレクトリを利用し設定する方法frontmatterpath設定する方法があります。 今回は frontmatterpath 指定して URL を設定することにしました。 変換はファイル名から取得すればよいので問題は特になく変換できました。 ただ、変換する際にこのままだと挿入するには注意が必要で、挿入する必要があります。 これは Array#insert を利用すれば 解決 ですね。

テーマ

テーマ はあるのですがプラグイン形式というわけではなく、 git clone or git submodule add して利用する点が慣れない。 気になる部分は template/ に修正ファイルを全部作る必要があり面倒でした。 Cargo でインストールできればいいのに…… ということでもっと面倒臭い自分のテーマを作ることでこの気になる点を解決。 作成方法は 公式ドキュメント を参考にすすめれば問題なく作成できます。 テーマというか、普通にテンプレート化するだけです。 テーマ化 も実際は簡単にでき、 theme.toml を書いておけば大丈夫なようです。 個人的に利用しているだけなのでテーマ化はしないですが。

FEED.XML

RSS Feed 自体はサポートされていますが、 URL のパスが /atom.xml/rss.xml になってしまうため 旧来のパス /feed.xml は新規にテンプレートを作る必要があります。 これは追い追い考える。現状 Jekyll にあった feed.xml を作るのは大変すぎる。

こまかい修正

いくつか気になる部分があったがこまかいので手で修正

  1. 脚注の順番が脚注を書いた順に出力
  2. Syntax Highlighting のサポート言語
  3. 画像の変更
  4. rake task の整理

脚注は脚注で引用した順番ではなく、脚注を書いた順になっているので修正してます。 Syntax Highlighting ではサポートされていない書きかた(console)をやめてサポートされている言語(bash)へ修正。 画像の変更はとくに zola は関係なく、大きいファイルが多いので変更しています。 rake task では、いままでデプロイやら記事作成やらなんだかんだやっていたので整理を行いました。 今は一旦、記事作成だけにしております。そのうちデプロイを行なえるようになると思います

とここまで書いたけどデプロイやめた

というのが、 feed.xml の移行とスタイルの修正が思った以上に大変で中止。 この今 Jekyll のテーマのスタイルがあれば、そのうち変更するかもね。


  1. RFC(Request For Comment) になっててびびってる 

  2. Jekyll は依存解決とかあって最新の Ruby が使えなかったりしたのでストレスたかかった 

KaigiOnRails 2024 に行ってきました

2024-10-27 11:09:14 +0900

Kaigi on Rails 2024 にはじめて現地参加しました。

day 1

day 1 before

kaigiが始まるまえに場所が有明だったので朝寿司へ。 1年くらい前はよく朝寿司に行っていたので余裕だろうとおもい、朝9時集合に設定した。 普段行ってる店ではなく新規開拓ということで tabelog の評価が高い、寿司処 やまざき さんにしました。 食べたあと丁度いい時間に会場へ入れそうだったので歩いて向いました。

会議

聞いたセッションは以下のとおり

  1. rails-way-or-the-highway
  2. RailsのPull requestsのレビューの時に私が考えていること
  3. そのカラム追加、ちょっと待って!カラム追加で増えるActiveRecordのメモリサイズ、イメージできますか?
  4. モノリスでも使える!OpenTelemetryでRailsアプリのパフォーマンス分析を始めてみよう
  5. cXML という電子商取引のトランザクションを支えるプロトコルと向きあっている話
  6. リリース8年目のサービスの1800個のERBファイルをViewComponentに移行した方法とその結果
  7. [ActionCableなら簡単? 生成 AIの応答をタイピングアニメーションで表示。実装、コスト削減、テスト、運用まで。]
  8. 現実のRuby/Railsアップグレード

感想

キーノートの @palkan の話はすごくよくて、Rails Wayについて話をしてくれました。 @yahonda は、 Rails の The Committers の一員で Rails でレビューしている際に気を付けている部分について話してくれてました。 @asayamakk は DB にカラムを増やすとどれだけ ActiveRecord のオブジェクトが増え、メモリの消費量が増えるかの話でした。 @katty0324 は ViewComponent に移行するために自動化した話でした。この話は大変興味深く聞いてました。 ViewComponent に対応するために問題の整理、自動化するための準備など丁寧に仕事している印象でした。 @takeyuweb ではアップデートするためになにをしたのか、していくのかという話をしてくれていました。

day 1 after

この日はオフィシャルの懇親会があり、わいわいと話してました。このとき @tagomoris が Ruby でやっている Namespace のビルドが通ったらしく皆でお祝い。 懇親会後に @palkan や @yahonda たちと二次会で新橋へ。私は早々に帰ったのですが会は1:30ごろ解散だったようです。

day 2

day 2 before

この日の 10:00 のセッションに間に合うはずだったのですが、出る時間の予定が狂ってしまった。 出る直前に近所のおじさんが向っていることに気がついたので有楽町で落ちあい、タクシーで向かうことに。

Kaigi

  1. 作って理解する RDBMSのしくみ
  2. 入門『状態』
  3. Hotwire光の道とStimulus
  4. Data Migration on Rails
  5. Identifying User Identity
  6. WHOLENESS, REPAIRING, AND TO HAVE FUN: 全体性、修復、そして楽しむこと

Kansou

最初のセッションは前日いっしょに飲んでた @ydah で、RDBの中身の基本的な話をしてくれました。 その後はちょくちょくづつ聞く感じになりました。 @snoozer05 のキーノートはいい話でした。

day 2 after

会議後の懇親会は mov Drinkup と pixiv RubyMusicMixin へ。 この日は前日の疲れが取れていないので終ったあとすぐに帰宅。

おわり

たのしい二日間でした。ありがとうございました。

今日、私が学んだこと

2024-05-30 23:59:59 +0900

ここ1年ほど Ruby のパーサーがもりあがっているので、 lrama の作者を講師にした 通称ドラゴンブックの「コンパイラ」読書会 に参加しています。

4.8.1 節の「競合解消のための優先順位と結合性」を読み感想を言い合う場で、 以下のような話になった。(細かいとろこは本を読んでくれ

  • id + id * id
    • +* より優先度が低いため shift
  • id * id + id
    • ** より優先度が高いため reduce
  • id + id + id
    • + は左結合なので reduce
  • id = id = id
    • = は右結合なので shift

ここでそういえば Ruby ではどう動くのだろうかとおもい、次の式をirbで書いてみた 0 - i = 1 + j = 2 * 3 #=> -7 とまあ普通(?)の答えが返ってきた。 でも上のパーサーの条件考えてみるとそもそもこのコードはほかのプログラミング言語で パースできるのだろうかと思い、やってみた。

$ python -c '0 - j = 1 + j = 2 * 3'
  File "<string>", line 1
    0 - j = 1 + j = 2 * 3
    ^^^^^
SyntaxError: cannot assign to expression
$ perl -e '0 - j = 1 + j = 2 * 3'
Can't modify addition (+) in scalar assignment at -e line 1, at EOF
Execution of -e aborted due to compilation errors.
$ lua -e '0 - j = 1 + j = 2 * 3'
lua: (command line):1: unexpected symbol near '0'
$ crystal eval '0 - j = 1 + j = 2 * 3'
syntax error in eval:1
Error: unexpected token: "="
$ ruby -e '0 - j = 1 + j = 2 * 3'

$

:thinkingface:???とにかく Ruby は通るが他のプログラミング言語で通らないことがわかった。

さいごに RubyAST がどうなってるのかとりあずみてみた。

$ ruby -e 'pp RubyVM::AbstractSyntaxTree.parse "0 - j = 1 + j = 2 * 3"'
(SCOPE@1:0-1:21
 tbl: [:j]
 args: nil
 body:
   (OPCALL@1:0-1:21 (LIT@1:0-1:1 0) :-
      (LIST@1:4-1:21
         (LASGN@1:4-1:21 :j
            (OPCALL@1:8-1:21 (LIT@1:8-1:9 1) :+
               (LIST@1:12-1:21
                  (LASGN@1:12-1:21 :j (OPCALL@1:16-1:21 (LIT@1:16-1:17 2) :* (LIST@1:20-1:21 (LIT@1:20-1:21 3) nil)))
                  nil))) nil)))

まあわかるような木構造になってますね。 それはそうとパーサーどうなってるんですかねえという話でおわり。

Ruby を KOMPO してみた

2024-05-29 23:59:59 +0900

RubyKaigi2024 の発表、It’s about time to pack Ruby and Ruby scripts in one binary で話されていた kompo を試してみた。

じゅんびというかとらしゅーというかうごかすまでのきろく

とりあえず動かしてみましょう!!

$ gem install kompo
$ mkdir hello; cd hello;
$ echo puts \"hello, world\" > hello.rb
$ kompo
which: no brew in (/home/katsyoshi/.rbenv/versions/3.3.1/bin:/home/katsyoshi/.rbenv/libexec:/home/katsyoshi/.rbenv/plugins/ruby-build/bin:/home/katsyoshi/.local/share/zinit/plugins/rust/bin:/home/katsyoshi/.local/share/zinit/plugins/sk/bin:/home/katsyoshi/.local/share/zinit/plugins/hs/bin:/home/katsyoshi/.local/share/zinit/plugins/fd/bin:/home/katsyoshi/.rbenv/shims:/home/katsyoshi/.rbenv/bin:/home/katsyoshi/.pyenv/plugins/pyenv-virtualenv/shims:/home/katsyoshi/.pyenv/shims:/home/katsyoshi/.pyenv/bin:/home/katsyoshi/.local/share/zinit/polaris/bin:bin:/home/katsyoshi/.go/bin:/home/katsyoshi/bin:/home/katsyoshi/.bin:/usr/lib/llvm/17/bin:/usr/lib/llvm/18/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin)
which: no brew in (/home/katsyoshi/.rbenv/versions/3.3.1/bin:/home/katsyoshi/.rbenv/libexec:/home/katsyoshi/.rbenv/plugins/ruby-build/bin:/home/katsyoshi/.local/share/zinit/plugins/rust/bin:/home/katsyoshi/.local/share/zinit/plugins/sk/bin:/home/katsyoshi/.local/share/zinit/plugins/hs/bin:/home/katsyoshi/.local/share/zinit/plugins/fd/bin:/home/katsyoshi/.rbenv/shims:/home/katsyoshi/.rbenv/bin:/home/katsyoshi/.pyenv/plugins/pyenv-virtualenv/shims:/home/katsyoshi/.pyenv/shims:/home/katsyoshi/.pyenv/bin:/home/katsyoshi/.local/share/zinit/polaris/bin:bin:/home/katsyoshi/.go/bin:/home/katsyoshi/bin:/home/katsyoshi/.bin:/usr/lib/llvm/17/bin:/usr/lib/llvm/18/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin)
/home/katsyoshi/.rbenv/versions/3.3.1/lib/ruby/gems/3.3.0/gems/kompo-0.2.0/lib/kompo.rb:110:in `valid?': kompo-cli not found. Please install 'kompo-cli'. (RuntimeError)
        from /home/katsyoshi/.rbenv/versions/3.3.1/lib/ruby/gems/3.3.0/gems/kompo-0.2.0/lib/kompo.rb:120:in `block in cd_work_dir'
        from /home/katsyoshi/.rbenv/versions/3.3.1/lib/ruby/3.3.0/tmpdir.rb:99:in `mktmpdir'
        from /home/katsyoshi/.rbenv/versions/3.3.1/lib/ruby/gems/3.3.0/gems/kompo-0.2.0/lib/kompo.rb:118:in `cd_work_dir'
        from /home/katsyoshi/.rbenv/versions/3.3.1/lib/ruby/gems/3.3.0/gems/kompo-0.2.0/exe/kompo:8:in `<top (required)>'
        from /home/katsyoshi/.rbenv/versions/3.3.1/bin/kompo:25:in `load'
        from /home/katsyoshi/.rbenv/versions/3.3.1/bin/kompo:25:in `<main>'

「なるほどなるほど、 brew が必要なのか。そっかー。」 とおもったのだが、そもそも今 Linux だぞ?となり、リポジトリを見ることに。 リポジトリを見ると kompo-vfs をインストールしろと書いてあるな。 Building に書いてあるように、ダンロードして手元でビルドして、環境変数を設定してみる。

$ git clone git@github.com:ahogappa0613/kompo-vfs
$ cd kompo-vfs && cargo build --release
$ export KOMPO_CLI=$(realpath target/release/kompo-cli)
$ export LIB_KOMPO_DIR=$(realpath target/release)
$ kompo
which: no brew in (/home/katsyoshi/.rbenv/versions/3.3.1/bin:/home/katsyoshi/.rbenv/libexec:/home/katsyoshi/.rbenv/plugins/ruby-build/bin:/home/katsyoshi/.local/share/zinit/plugins/rust/bin:/home/katsyoshi/.local/share/zinit/plugins/sk/bin:/home/katsyoshi/.local/share/zinit/plugins/hs/bin:/home/katsyoshi/.local/share/zinit/plugins/fd/bin:/home/katsyoshi/.rbenv/shims:/home/katsyoshi/.rbenv/bin:/home/katsyoshi/.pyenv/plugins/pyenv-virtualenv/shims:/home/katsyoshi/.pyenv/shims:/home/katsyoshi/.pyenv/bin:/home/katsyoshi/.local/share/zinit/polaris/bin:bin:/home/katsyoshi/.go/bin:/home/katsyoshi/bin:/home/katsyoshi/.bin:/usr/lib/llvm/17/bin:/usr/lib/llvm/18/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin)
which: no brew in (/home/katsyoshi/.rbenv/versions/3.3.1/bin:/home/katsyoshi/.rbenv/libexec:/home/katsyoshi/.rbenv/plugins/ruby-build/bin:/home/katsyoshi/.local/share/zinit/plugins/rust/bin:/home/katsyoshi/.local/share/zinit/plugins/sk/bin:/home/katsyoshi/.local/share/zinit/plugins/hs/bin:/home/katsyoshi/.local/share/zinit/plugins/fd/bin:/home/katsyoshi/.rbenv/shims:/home/katsyoshi/.rbenv/bin:/home/katsyoshi/.pyenv/plugins/pyenv-virtualenv/shims:/home/katsyoshi/.pyenv/shims:/home/katsyoshi/.pyenv/bin:/home/katsyoshi/.local/share/zinit/polaris/bin:bin:/home/katsyoshi/.go/bin:/home/katsyoshi/bin:/home/katsyoshi/.bin:/usr/lib/llvm/17/bin:/usr/lib/llvm/18/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin)
/home/katsyoshi/.rbenv/versions/3.3.1/lib/ruby/gems/3.3.0/gems/kompo-0.2.0/lib/kompo.rb:112:in `valid?': Entrypoint not found: '/home/katsyoshi/hello/main.rb'. Please specify the entry file path with '-e' or '--entrypoint' option. (RuntimeError)
        from /home/katsyoshi/.rbenv/versions/3.3.1/lib/ruby/gems/3.3.0/gems/kompo-0.2.0/lib/kompo.rb:120:in `block in cd_work_dir'
        from /home/katsyoshi/.rbenv/versions/3.3.1/lib/ruby/3.3.0/tmpdir.rb:99:in `mktmpdir'
        from /home/katsyoshi/.rbenv/versions/3.3.1/lib/ruby/gems/3.3.0/gems/kompo-0.2.0/lib/kompo.rb:118:in `cd_work_dir'
        from /home/katsyoshi/.rbenv/versions/3.3.1/lib/ruby/gems/3.3.0/gems/kompo-0.2.0/exe/kompo:8:in `<top (required)>'
        from /home/katsyoshi/.rbenv/versions/3.3.1/bin/kompo:25:in `load'
        from /home/katsyoshi/.rbenv/versions/3.3.1/bin/kompo:25:in `<main>'

まだ駄目みたですね。エラーメッセージを見ると -e でエントリーポイントを指定しろとあるな。 ということで kompo -e hello.rb と入力してみますね。

$ kompo -e hello.rb
...snip...
gcc -O3 -Wall -I/tmp/d20240530-1369030-t2mico/dest_dir/include/ruby-3.3.0/x86_64-linux -I/tmp/d20240530-1369030-t2mico/dest_dir/include/ruby-3.3.0 -L/tmp/d20240530-1369030-t2mico/dest_dir/lib -L/home/katsyoshi/Program/Ruby/kompo/kompo-vfs/target/release  main.c -Wl,--start-group  fs.o /tmp/d20240530-1369030-t2mico/ruby/ext/extinit.o /tmp/d20240530-1369030-t2mico/ruby/enc/encinit.o /tmp/d20240530-1369030-t2mico/ruby/ext/bigdecimal/bigdecimal.a /tmp/d20240530-1369030-t2mico/ruby/ext/cgi/escape/escape.a /tmp/d20240530-1369030-t2mico/ruby/ext/continuation/continuation.a /tmp/d20240530-1369030-t2mico/ruby/ext/coverage/coverage.a /tmp/d20240530-1369030-t2mico/ruby/ext/date/date_core.a /tmp/d20240530-1369030-t2mico/ruby/ext/digest/bubblebabble/bubblebabble.a /tmp/d20240530-1369030-t2mico/ruby/ext/digest/digest.a /tmp/d20240530-1369030-t2mico/ruby/ext/digest/md5/md5.a /tmp/d20240530-1369030-t2mico/ruby/ext/digest/rmd160/rmd160.a /tmp/d20240530-1369030-t2mico/ruby/ext/digest/sha1/sha1.a /tmp/d20240530-1369030-t2mico/ruby/ext/digest/sha2/sha2.a /tmp/d20240530-1369030-t2mico/ruby/ext/erb/escape/escape.a /tmp/d20240530-1369030-t2mico/ruby/ext/etc/etc.a /tmp/d20240530-1369030-t2mico/ruby/ext/fcntl/fcntl.a /tmp/d20240530-1369030-t2mico/ruby/ext/fiddle/fiddle.a /tmp/d20240530-1369030-t2mico/ruby/ext/io/console/console.a /tmp/d20240530-1369030-t2mico/ruby/ext/io/nonblock/nonblock.a /tmp/d20240530-1369030-t2mico/ruby/ext/io/wait/wait.a /tmp/d20240530-1369030-t2mico/ruby/ext/json/generator/generator.a /tmp/d20240530-1369030-t2mico/ruby/ext/json/parser/parser.a /tmp/d20240530-1369030-t2mico/ruby/ext/monitor/monitor.a /tmp/d20240530-1369030-t2mico/ruby/ext/nkf/nkf.a /tmp/d20240530-1369030-t2mico/ruby/ext/objspace/objspace.a /tmp/d20240530-1369030-t2mico/ruby/ext/openssl/openssl.a /tmp/d20240530-1369030-t2mico/ruby/ext/pathname/pathname.a /tmp/d20240530-1369030-t2mico/ruby/ext/psych/psych.a /tmp/d20240530-1369030-t2mico/ruby/ext/pty/pty.a /tmp/d20240530-1369030-t2mico/ruby/ext/rbconfig/sizeof/sizeof.a /tmp/d20240530-1369030-t2mico/ruby/ext/ripper/ripper.a /tmp/d20240530-1369030-t2mico/ruby/ext/socket/socket.a /tmp/d20240530-1369030-t2mico/ruby/ext/stringio/stringio.a /tmp/d20240530-1369030-t2mico/ruby/ext/strscan/strscan.a /tmp/d20240530-1369030-t2mico/ruby/ext/syslog/syslog.a /tmp/d20240530-1369030-t2mico/ruby/ext/zlib/zlib.a /tmp/d20240530-1369030-t2mico/ruby/enc/libenc.a /tmp/d20240530-1369030-t2mico/ruby/enc/libtrans.a -lkompo -lruby-static -Wl,-Bstatic -lz -lrt -lgmp -lcrypt -lffi -lssl -lcrypto -lyaml -Wl,-Bdynamic -ldl -lm -lpthread -Wl,--end-group -o hello
/usr/lib/gcc/x86_64-pc-linux-gnu/13/../../../../x86_64-pc-linux-gnu/bin/ld: -lgmp が見つかりません: そのようなファイルやディレクトリはありません
/usr/lib/gcc/x86_64-pc-linux-gnu/13/../../../../x86_64-pc-linux-gnu/bin/ld: -lffi が見つかりません: そのようなファイルやディレクトリはありません
/usr/lib/gcc/x86_64-pc-linux-gnu/13/../../../../x86_64-pc-linux-gnu/bin/ld: -lssl が見つかりません: そのようなファイルやディレクトリはありません
/usr/lib/gcc/x86_64-pc-linux-gnu/13/../../../../x86_64-pc-linux-gnu/bin/ld: -lcrypto が見つかりません: そのようなファイルやディレクトリはありません
/usr/lib/gcc/x86_64-pc-linux-gnu/13/../../../../x86_64-pc-linux-gnu/bin/ld: -lyaml が見つかりません: そのようなファイルやディレクトリはありません
collect2: エラー: ld はステータス 1 で終了しました
...snip...

いったとおもったら、今度はライブラリ(libgmplibffiopenssllibcryptlibyaml)のリンクができないようですね。 必要なライブラリはインストールされているのか見てみましょう。

$ eix -ICU 'dev-libs' -s 'gmp|libcrybp|libffi|libyaml|openssl'
[I] dev-libs/gmp
     Available versions:  6.3.0-r1(0/10.4)^d {+asm +cpudetection +cxx doc pic static-libs ABI_MIPS="n32 n64 o32" ABI_S390="32 64" ABI_X86="32 64 x32"}
     Installed versions:  6.3.0-r1(0/10.4)(02時44分45秒 2024年05月30日)(asm cpudetection cxx -doc -pic -static-libs ABI_MIPS="-n32 -n64 -o32" ABI_S390="-32 -64" ABI_X86="64 -32 -x32")
     Homepage:            https://gmplib.org/
     Description:         Library for arbitrary-precision arithmetic on different type of numbers

[I] dev-libs/libffi
     Available versions:  3.4.4-r4(0/8)^t ~3.4.6(0/8)^t {debug exec-static-trampoline pax-kernel static-libs test ABI_MIPS="n32 n64 o32" ABI_S390="32 64" ABI_X86="32 64 x32"}
     Installed versions:  3.4.4-r4(0/8)^t(02時43分18秒 2024年05月30日)(-debug -exec-static-trampoline -pax-kernel -static-libs -test ABI_MIPS="-n32 -n64 -o32" ABI_S390="-32 -64" ABI_X86="64 -32 -x32")
     Homepage:            https://sourceware.org/libffi/
     Description:         Portable, high level programming interface to various calling conventions

[I] dev-libs/libyaml
     Available versions:  0.2.2^t 0.2.5^t {doc static-libs test}
     Installed versions:  0.2.5^t(02時44分57秒 2024年05月30日)(-doc -static-libs -test)
     Homepage:            https://github.com/yaml/libyaml
     Description:         YAML 1.1 parser and emitter written in C

[I] dev-libs/openssl
     Available versions:  [M]1.0.2u-r1^td [M]1.1.1w(0/1.1)^t 3.0.11(0/3)^t 3.0.12(0/3)^t 3.0.13(0/3)^t ~3.0.13-r1(0/3)^t 3.0.13-r2(0/3)^t ~3.1.5-r1(0/3)^t ~3.1.5-r2(0/3)^t ~3.2.1-r1(0/3)^t ~3.2.1-r2(0/3)^t **3.3.0(0/3)^t {+asm bindist fips gmp kerberos ktls rfc3779 sctp sslv2 (+)sslv3 static-libs test tls-compression (+)tls-heartbeat vanilla verify-sig weak-ssl-ciphers ABI_MIPS="n32 n64 o32" ABI_S390="32 64" ABI_X86="32 64 x32" CPU_FLAGS_X86="sse2"}
     Installed versions:  3.0.13-r2(0/3)^t(02時43分56秒 2024年05月30日)(asm -fips -ktls -rfc3779 -sctp -static-libs -test -tls-compression -vanilla -verify-sig -weak-ssl-ciphers ABI_MIPS="-n32 -n64 -o32" ABI_S390="-32 -64" ABI_X86="64 -32 -x32" CPU_FLAGS_X86="sse2")
     Homepage:            https://www.openssl.org/
     Description:         Robust, full-featured Open Source Toolkit for the Transport Layer Security (TLS)

入ってますねこれ…もういちどよくコンパイルしてエラーとなった行をよくみてみましょう。 どのファイルも .a でおわってますね。 ということはこれらのファイルに静的コンパイルされたファイルがなさそうですね。 Gentoo Linux のパッケージマネージャー portage では static-libs というオプションをつけてあげることにより静的コンパイルされます。

$ doas emerge -uDN world
$ kompo -e hello.rb
...snip...
$ ./hello
`RubyGems' were not loaded.
`error_highlight' was not loaded.
`did_you_mean' was not loaded.
`syntax_suggest' was not loaded.
hello, world

yatta!!ugoita!!!

まとめ

kompo を動かしてみました。 コンパイルに時間がかかりますが、これで Ruby のプログラムを簡単に配ることができそうですね。