Ruby 3.2 リリースパーティー presented by アンドパッドに参加した

Ruby 3.2 リリースパーティー presented by アンドパッドに参加しました。とても楽しい時間で、新しいRubyのたくさんの機能が楽しみになりました。

andpad.connpass.com

WASM

irb.wasmのReline対応がアツい!PRを見てもどうトライすればその修正になるのか、何でそうすれば動くのかわかりません。元気に動いていてかっこいいです。

デモでの表示崩れが気になってデバッグしてました。端末(ブラウザ)が表示できる幅を79文字以下にすると折返しがうまく描画できなくなっていたので、画面の大きさ取得がうまくいってなさそうとわかりました。

Relineでいうとこのあたりです。

github.com

irb.wasmではこのあたりでIO.winsizeを上書きしているようです。

github.com

横幅を79文字になるようにしてirb.wasmをリロードしてもやはり折返しがうまくいっていなかったので、ブラウザの端末幅を取得できていないのかも?と思いました。 あと縦幅も19行以下?にすると補完を出したときの強制スクロールがうまくなさそうでした。これも固定行で扱っているからなのかもしれません。

WASM について何からどう触っていいか自分はよくわかっていないと感じました。なにかしら触ってみたいけど何からやればいいのか〜な状態なので、ちょっと遊んでみたいです。ビンゴゲームとか郵便ゲームとか楽しそうです。TryRuby的な入力を受け付けて実行する簡単なものを書いてみるといいのかなとも考えています。

debugger

debuggerどんどん便利になっていてすごいです。

irb/debuggerにビジュアライズしたイメージを出すにはRelineでSixel対応が必要そうな気がしました。

以前Sixel対応の話をどこかで聞いたときは、何に使えるんだろう?と思っていたのですが、debuggerとの連携で需要があるのかもしれません。あるいは、作ってみてから用途を考えるでもいいのかもしれません。

そのほか

ReDOS対応、バックトレースの対応、型の話などじっくり聞けてよかったです。当事者から語ってもらえる場がとてもありがたいです。

終わった後少し参加者とお話して、Relineのレビューについて直接フィードバックをもらったり、キーボードの話をしたりとRubyistとの交流もできてとても楽しかったです。フィードバックが嬉しくてやっていきエネルギーが高まりました。自分もどんどんフィードバックしていきたいです。 今年はRubyKaigiやRWCがあったものの、関東で大きい集まりができる機会はあまりなかった気がして新鮮でした。話し足りませんでした。

やっていき

最近 IRB と Reline の committer になりました。 Reline は @hasumikin と同時に、IRB は @hasumikin @st0012 と同時に committer になりました。ちなみに Ruby の committer ではないです。

Ruby 3.2 の明るい話を聞きながら、自分が来年こういう場にいたらどんな話を持っていけるだろうかと思いました。とりあえず、楽しくメンテナンスを続けることが第一の目標です。あわよくば新機能も考えていきたい。そんな感じで今後ともよろしくお願いします。

Ruby ビルド時の parse.tmp.y:12.10-14: require bison 3.0, but have 2.3 を解決する

macOS に入っている bison のバージョンが 2.3 のため Ruby 3.2 がビルドできなくなっていた。

❯ asdf install ruby 3.2.0-dev
Downloading ruby-build...
Cloning into '/Users/mi/.asdf/plugins/ruby/ruby-build-source'...
remote: Enumerating objects: 13397, done.
remote: Counting objects: 100% (2096/2096), done.
remote: Compressing objects: 100% (374/374), done.
remote: Total 13397 (delta 1876), reused 1828 (delta 1704), pack-reused 11301
Receiving objects: 100% (13397/13397), 2.70 MiB | 2.33 MiB/s, done.
Resolving deltas: 100% (9114/9114), done.
Already on 'master'
To follow progress, use 'tail -f /var/folders/7g/c0g_krhn7zg7q4jzpntm6ylw0000gn/T/ruby-build.20221214004540.19944.log' or pass --verbose
Downloading openssl-3.0.7.tar.gz...
-> https://dqw8nmjcqpjn7.cloudfront.net/83049d042a260e696f62406ac5c08bf706fd84383f945cf21bd61e9ed95c396e
Installing openssl-3.0.7...
Installed openssl-3.0.7 to /Users/mi/.asdf/installs/ruby/3.2.0-dev

Cloning https://github.com/ruby/ruby.git...
Installing ruby-master...
ruby-build: using readline from homebrew
ruby-build: using gmp from homebrew

BUILD FAILED (macOS 12.4 using ruby-build 20221206)

Inspect or clean up the working tree at /var/folders/7g/c0g_krhn7zg7q4jzpntm6ylw0000gn/T/ruby-build.20221214004540.19944.xS24dl
Results logged to /var/folders/7g/c0g_krhn7zg7q4jzpntm6ylw0000gn/T/ruby-build.20221214004540.19944.log

Last 10 log lines:
generating parse.c
copying lex.c
compiling proc.c
compiling process.c
making ractor.rbinc
compiling random.c
compiling range.c
parse.tmp.y:12.10-14: require bison 3.0, but have 2.3
make: *** [parse.c] Error 63
make: *** Waiting for unfinished jobs....


❯ bison -V
bison (GNU Bison) 2.3
Written by Robert Corbett and Richard Stallman.

Copyright (C) 2006 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Ruby 3.2 からは bison 3.0 以上が必要になるため、インストールしてPATHを通す。

homebrew を使っている場合は、

brew install bison

で bison をインストールする。

インストール時に以下のように通すべきPATHが書いてあるためここにPATHを通す。

以下の例は Intel mac で、M1 mac だとおそらくパスが異なる。

If you need to have bison first in your PATH, run:
  echo 'export PATH="/usr/local/opt/bison/bin:$PATH"' >> ~/.zshrc

.zshrc に PATH を書いて、シェルを再起動するか source ~/.zshrc する。

# bison
export PATH="/usr/local/opt/bison/bin:$PATH"

bison -V でバージョンが変わっていたらOK

❯ bison -V
bison (GNU Bison) 3.8.2
Written by Robert Corbett and Richard Stallman.

Copyright (C) 2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.


❯ asdf install ruby 3.2.0-dev
Downloading ruby-build...
Cloning into '/Users/mi/.asdf/plugins/ruby/ruby-build-source'...
remote: Enumerating objects: 13397, done.
remote: Counting objects: 100% (2084/2084), done.
remote: Compressing objects: 100% (392/392), done.
remote: Total 13397 (delta 1864), reused 1798 (delta 1674), pack-reused 11313
Receiving objects: 100% (13397/13397), 2.70 MiB | 4.38 MiB/s, done.
Resolving deltas: 100% (9114/9114), done.
Already on 'master'
To follow progress, use 'tail -f /var/folders/7g/c0g_krhn7zg7q4jzpntm6ylw0000gn/T/ruby-build.20221214010049.68309.log' or pass --verbose
Downloading openssl-3.0.7.tar.gz...
-> https://dqw8nmjcqpjn7.cloudfront.net/83049d042a260e696f62406ac5c08bf706fd84383f945cf21bd61e9ed95c396e
Installing openssl-3.0.7...
Installed openssl-3.0.7 to /Users/mi/.asdf/installs/ruby/3.2.0-dev

Cloning https://github.com/ruby/ruby.git...
Installing ruby-master...
ruby-build: using readline from homebrew
ruby-build: using gmp from homebrew
Installed ruby-master to /Users/mi/.asdf/installs/ruby/3.2.0-dev

perfでCRubyのプロファイリングができる環境を作る

みなさん、こんにちは。ima1zumi です。

これは Ruby Advent Calendar 2022 6日目の記事です。

本日は macOS で perf が動く仮想環境を作って CRuby のプロファイリングできる環境構築をしていきます。

目次

macOS で perf は使えない

私は普段 macOS で開発しているのですが、perf は Linux 用の計測ツールなので macOS では利用できません。

macOS には Instrument という計測ツールがありますが、情報が少なく調査に難儀するためここでは利用者の多い perf を使います。

というわけで Ubuntu の環境を作っていきますが、 Docker for mac は perf が使えません。そのため、 Virtualbox & Vagrant仮想マシンが動く環境を作ります。Linux 環境の方は「perf をインストールする」まで飛ばしてください。

github.com

Virtualbox & VagrantUbuntu が動く環境を作る

Virtualbox をインストール

brew install --cask vitrualbox または Downloads – Oracle VM VirtualBoxVirtualbox をインストールし、セットアップウィザードを実行します。

Vagrant をインストール

brew install vagrant または Install | Vagrant | HashiCorp DeveloperVagrant をインストールし、セットアップウィザードを実行します。

Box ファイルを入手

次に仮想マシンのベースとなる Box ファイルを入手します。任意のディレクトリで、

vagrant box add ubuntu/jammy64

を実行し、Box ファイルをインストールします。

以下のサイトで様々な Box ファイルが配布されています。

入手した Box ファイルは vagrant box list で確認できます。

❯ vagrant box list
ubuntu/bionic64 (virtualbox, 20220530.0.0)
ubuntu/bionic64 (virtualbox, 20220810.0.0)
ubuntu/jammy64  (virtualbox, 20220810.0.0)
ubuntu/jammy64  (virtualbox, 20221201.0.0)

Vagrantfile を作成

Vagrantfile は仮想マシンの設定ファイルで、メモリやOSなどをどう使うか指定することができます。

Vagrant を起動したいディレクトリで vagrant init ubuntu/jammy64 を実行します。実行後に Vagrantfile ができていればOKです。今回はデフォルトのまま起動します。

Vagrant を起動

vagrant up仮想マシンを起動します。起動後、vagrant status で状態を確認できます。

❯ vagrant status
Current machine states:

default                   running (virtualbox)

The VM is running. To stop this VM, you can run `vagrant halt` to
shut it down forcefully, or you can run `vagrant suspend` to simply
suspend the virtual machine. In either case, to restart it again,
simply run `vagrant up`.

Vagrant に接続

vagrant sshVagrant に接続します。以下のように、Ubuntu にログインできていれば OK です。

❯ vagrant ssh
Welcome to Ubuntu 22.04.1 LTS (GNU/Linux 5.15.0-56-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  System information as of Mon Dec  5 13:18:02 UTC 2022

  System load:  0.16455078125     Processes:               108
  Usage of /:   3.6% of 38.70GB   Users logged in:         0
  Memory usage: 20%               IPv4 address for enp0s3: 10.0.2.15
  Swap usage:   0%


0 updates can be applied immediately.


vagrant@ubuntu-jammy:~$

perf をインストール

ここでは apt でインストールします。 perf が含まれる linux-tools は Kernel のバージョンに依存するため、以下のコマンドでインストールします。

sudo apt install linux-tools-`uname -r`

Ruby をビルドする

プロファイリングをする際にビルドオプションを変えたりコードに変更を加えたくなるので、手でビルドできるようにします。

依存ライブラリのインストール

ビルドのために

git ruby autoconf bison gcc(or clang, etc) make

が必須です。gcc は clang など他のコンパイラでもOKです。

この環境には Ruby がインストールされていなかったため、Ruby をインストールします。

sudo apt install ruby

次に、拡張ライブラリのためのライブラリをインストールします。必要なライブラリはビルドする Ruby のバージョンによって変わります。rbenv の Wiki に詳しくまとまっています。

Home · rbenv/ruby-build Wiki

ここでは以下のライブラリをインストールします。

sudo apt install autoconf bison patch build-essential rustc libssl-dev libyaml-dev libreadline6-dev zlib1g-dev libgmp-dev libncurses5-dev libffi-dev libgdbm6 libgdbm-dev libdb-dev uuid-dev

Ruby をビルド

作業用ディレクトリを作ってビルドしていきます。まず RubyソースコードGitHub からクローンします。

mkdir workdir
cd workdir
git clone https://github.com/ruby/ruby.git

github.com

Ruby がクローンできたら ruby ディレクトリに移動して autogen.sh を実行して build ディレクトリを作成します。

cd ruby
./autogen.sh
cd ..
mkdir build
cd build

configure を実行します。configure には色々なコンパイルオプションを渡すことができますが、一旦これでビルドします。

../ruby/configure --prefix=$PWD/../install --enable-shared

make./ruby を生成します。 -j オプションは仮想環境のメモリに余裕がないとコンパイルに失敗しがちだったため、メモリをあまり割り当てていない場合はつけないほうが良さそうです。

make

make install でインストールディレクトリを作成します。

make install

./ruby -v が実行できれば OK です。

./ruby -v
ruby 3.2.0dev (2022-12-06T11:24:02Z master 14074567ea) [x86_64-linux]

perf を実行する

さて、 String#split のプロファイリングをしてみます。性能データを採取するには perf report コマンドを使います。バックトレース情報も含めて採取したいため --cal-graph dwarf をつけます。

String#split の実行時間が短すぎるとうまく採取できず、長すぎると実行結果のファイルが大きくなりすぎるため適当に String#split を実行してその結果を採取します。

sudo perf record --call-graph dwarf -g ~/workdir/install/bin/ruby -e '("あ,い,う,え,お"*100000).split(",")'

実行結果はデフォルトで perf.data ファイルに出力され、 perf report で確認できます。CPU使用率が表示されています。

sudo perf report

Ruby のメソッドに相当する CRuby の関数は rb_ で始まることが多いです。String#split に対応するCRubyの関数は rb_str_split で、その中で rb_str_split_m を実行しています。

rb_str_split_m の中の関数にどれくらいCPUを使用しているか見てみます。見たい行にカーソルをあわせて + キーを押します。

このように詳細が見られます。

また、採取した perf.data を使って flamegraph を出力することもできます。計測において可視化はとても強力なので、ぜひ使ってみてください。

github.com

参考

RubyKaigi2022で "String meets Encoding" というタイトルで話しました

RubyKaigi2022 Day3で "String meets Encoding" というタイトルで話しました。

無事話すことができて良かったです。

スライド

後日YouTubeに動画がアップロードされると思うので、アップロードされたらそちらも貼ります。

きっかけ

本編中に話したとおり、RubyKaigi Takeout 2021 後に見たすとうさんのツイートです。ですが、読み違えていてすとうさんの元ツイは「String#encode高速化してほしい」だったのに「KEN_ALL.CSVを読み込むのが遅いのか…つまりCSV.readか?それを調査するか…よっしString#splitが30%近くかかってるな!これでプロポーザル出しちゃおう!」と読み違えたままその勢いでプロポーザル出しました。その後プロポーザルはアクセプトされ、全く気づかずにString#splitの速度改善してました。なんとかなったので良かったです。

困ったこと

RubyKaigi1週間前まで速度改善できず、見切り発車でプロポーザル出したことを大変後悔していました。職場の同僚にRubyKaigiの進捗を聞かれるたび「やばいです」「だめです」「全然わからなくて困ってます」と答えていました。実際RubyKaigi1週間前の金曜日の徹底解説で話したときには「速度改善できなかったんですけど何か話します…」と言っていました。 そもそもプロポーザル出した時点でC言語読めなくて苦しんで覚えるC言語を読んでました。今もあんまり読めないです。perfがmacOSで使えないということも知りませんでした。何も知らない私にいろいろアドバイスをくださったudzuraさんには本当に感謝しています。

なかなか速度改善できなかった理由はString#splitが何をしているか理解していなかったからでした。それまでperfで何かわかりやすいボトルネックが取れないかとあれこれやってみてはいたんですが、結局perfで見てるだけじゃだめでした。改善したい箇所、今回でいうとString#splitの処理の流れをlldbでステップ実行しながら全体的に把握して、その上でperfを見ることでperfが何を伝えようとしているのか分かるようになりました。perfは注目すべき点を可視化してくれますが、それがどんな意味を持っているかはコンテキストを把握してないとなかなか難しいものだな、と思いました。この辺の話もうまく発表内に組み込めると生生しくてよかったんですが、何分時間がなかったのできれいな一本道っぽく仕立てることにしました。

慣れないツールをガンガン使ったのでバグ踏むと解決に業務後の貴重な調査時間が吸い取られてしんどかったです。特にperfでcallgraph dwarfをつけてperf.dataが1MB以上になるような測定を行うとperfがクラッシュする問題1に苦しみました。結局なんとかtoolsのバージョンの問題を踏んでいて、Ubuntuのバージョンをjammyに上げないと解決しなかったです。

途中でmacOSのバージョンを上げてしまったのでXCodeやら手で入れたGCCが吹っ飛んで入れ直したりということもありました。面倒な手順を踏んでGCC入れてGDBデバッグできるようにしても、なぜかinlineの関数に入れなくて全然ステップ実行できなくて困ったりしました。その問題はGDBやめてlldbでデバッグすることで回避しました。

あとrebaseするとmacOSRubyがビルドできなくなったりもしました。これはどうやって解決したのか覚えていません。make clean などで一旦綺麗にして再実行した気がします。

時間なさすぎてノーレビューでぶっつけ本番に突入したので、反応を見るのが怖かったです。好意的な反応が多くてホッとしました。物理会場でお話するのは初めてでしたが、ふーがさんから聞いていた通り「発表前が一番緊張するけど、始まってしまえばもうあとは話すだけ」でした。

発表時間管理にキーノートのストップウォッチをあてにしていましたが、事前のセッティングでストップウォッチが始まってしまい、発表開始時に戻せず時間管理ができなくなっていたのはわりと困りました。別でストップウォッチは用意しておこうと思いました。また、発表中に適当なキーを触ったらページ数が大きく表示されてしまったのも焦りました。本番では余計なキーを触らないよう気をつけます。

良かったこと

発表前に使えなかった stackprof, perf, lldb が使えるようになりました。CRuby も単純なメソッドなら読めるようになりました。Kaigi駆動開発で自分のやれることをガッと増やす、をここ2年間やって無理やり自分を強くしていましたが、今年のように困ったことになるのでもっと普段からやろうと思いました…

また、発表後に「姿勢を見習いたい」のようなコメントをいくつかいただいたのは私にとって思ってもいないところでした。参考になる点があったなら幸いです。また、対面やオンラインで感想を言ってもらえるのはとてもうれしく励みになりました。ありがとうございました。

発表が終わった後、舞台裏で次の発表の準備をされていたznzさんに少し挨拶していつもるりまのPRを見てくださっているお礼を伝えられて良かったです。その瞬間のことが、なんだか心に焼き付いています。

話足りなかったこと

上にあげた困ったことも話したかったですが、これはいずれブログにでもまとめて昇華させてあげたいです。perfは大変便利なので、macOSを使っていてもわざわざLinux環境を作って入れる価値はあるツールだと思います。(macOSならインストゥルメンタルでもいいはずなんですが、使い方の情報がperf以上に少なくてたいへん)

あとは String のデータ構造や rb_encoding もとい OnigEncodingTypeST という巨大構造体2 も調べて話したかったです。いつかの機会に話せるといいなと思います。

まとめ

プロポーザルを出してからとにかく苦しい生活でした。去年はプレッシャーとの戦いでしたが、今年は成果が出ないことにずっと苦しんでいました。もう見切り発車でプロポーザル出すようなことはしたくないので、362日(今年はもっと短いですが)のRubyistとしての生活に気合をいれて、日々の成果をRubyKaigiにぶつけられるようになりたいです。

あとは PR がまだ draft なのでちゃんと仕上げたいですし、Stringまとめて生成すればオーバーヘッド少なくなりそうというのも見たいですし、宿題にしたStirng#encodeの高速化も見たいのでまだまだやりたいことがたくさんあります。楽しみです。

RubyKaigi Takeout 2021 で文字コードの話をしました

2022年になって今更2021年の話です。

2021年当時にRubyKaigi参加ブログを書こうとしていた下書きを見つけたので、書きかけですがそのままリリースします。

下書きなので途中から箇条書きです。





2021-09-11 (Sat) の RubyKaigi Takeout 2021 Day3 で "Dive into Encoding" というタイトルで、文字コードRuby文字コードの話をしました。

動画はこちら。

youtu.be

この記事ではプロポーザルを提出するきっかけや準備や登壇してみての気持ちなど、感想をつらつらと綴っていきます。

プロポーザルを提出するまで

RubyKaigi Takeout 2021 のプロポーザルの募集が始まった時点では RubyKaigi にプロポーザルを提出するつもりはありませんでした。私は何かを作る人ではなく、RubyKaigi は何かを作る人の発表の場だと思っていたからです。

2021-06-11 時点ではこんな気持ちでした。

2021-06-22 に Fukuoka.rb ランチ会の Nishitetsu.rb に参加しました。その場で複数名がしおいさんに RubyKaigi にプロポーザルを出すよう熱いエールを送っていました。それを聞いていて感じるところがあり、私も RubyKaigi にプロポーザルを出すとしたらどんなことを話せるかな?と考え始めました。合わせて知り合いからima1zumiさんプロポーザル出さないの?というお便りが届いて覚悟を決めてネタ出しをはじめました。

ネタは Unicode Property の話や Emoji の話などいくつか考えてましたが、一番面白そうな「Rubyにオレオレ文字コードを実装してみる」にしました。ネタを考えた時点では本当に実装できるのかよくわかっていませんでしたが、調べるうちに去年CRubyに新たな文字コードが追加されたことを思い出しました。そのPRを見ると意外と変更量が小さかったので自分にも出来るかも?と思いました。 ちなみにこのPRを知ったのはるりま1Ruby 3.0 対応 · Issue #2458 · rurema/doctree をまとめていたおかげでした。この作業をやっていなければ自分に文字コードを追加することが可能だと判断できず、このテーマでプロポーザルを出すことは出来なかったと思います。どこで何がつながるか分からないものだと思いました。

翌日に会社の人に RubyKaigi にプロポーザル出します〜ということをなにかの折に話し、社内レビュー会を開催してもらえることになり 2021-06-29 に社内レビューを受け 2021-06-30 に提出しました。出すと決めてから7日で提出でした。

採択後

しばらくどきどきしながら待っていましたが7月中旬頃に採択されたという連絡がありました。

  • 7月
  • 8月
    • IROHAをビルドできるようになった
    • CRubyのコードリーディング
    • スライドづくり
      • 裏とりが大変
      • 正しい言葉を使う、不確定なことは話さない(もしくは分からないということを話す)
    • 英語化
      • 適当な日本語をそのままDeepLにかけると長すぎる
      • Day1, Day2の他の人のスライドを見て簡潔な英語にしたほうがいいなと思っていろいろ直していた
    • slidev
  • 9月
    • ワクチン2回目の接種で2.5日ほど準備から離れる
      • 休日なしでずっとRubyKaigiのこと考えてたので副反応で寝込む期間がいい休暇になった
    • 9/1にスライド完成してホッとする
    • 英語化
    • 構成の見直し
    • 図を作る

当日

  • 通しで練習できたのは5回くらい?
  • 練習時間はあまり取れなかった
  • めちゃくちゃ緊張した
  • 緊張してチャットは見れなかった
    • 後で会社の人がチャットのログをくれたので見れてよかった
  • 堂々と発表してたというフィードバックを見れてよかった
    • (自信がなかったとしても)自信があるような振る舞いで話すのが大事、とどこかで見かけたので意識していた
    • 裏とりにめちゃくちゃ時間を使ったので、言っていることがほぼ間違っていないだろうということには自信があった
    • UTF-16はじゅうろくで良かった
  • 後でチャットみて楽しんでもらえてよかった…と思った

やってみて

  • RubyKaigi 面白い!プレッシャーは半端ないけどやってよかった
    • 半年以上たって思いかえしても、準備は大変だったけど楽しかったなー!という気持ちになる
  • スライドを git 管理にしたのは安心感があってよかった
    • 戻すことはほぼなかったけど、いざとなったら戻せるという安心感

やっておけばよかったこと

  • 図は仮置でいいから早く作る。後回しにすると自分でも何の図を入れたいのかよくわからなくなる
  • 逆に裏とりは後でもいい
  • 通訳さんがつく場合は原稿があったほうが親切
  • 直前までスライド弄るのは精神と通訳さんによくない
  • 早めに仕上げてレビュー受けるの高速回転すればよかった…
    • 自分が言いたいことはスライドを作ってからでないと見えてこないことがある
  • 英語スライド特有の難しさがある
    • 簡潔に書かないと自分でも読めない
    • 図が多いほうがわかりやすい
    • 日本語より短く簡潔に文の構造を簡単に、を意識する
  • zoomとスライドとプレゼンターモードの配置とか事前に考えておけばよかった
    • カメラ使う場合あんまり下を見て喋りたくないなと思っていたが、そうするとプレゼンターモードの画面を置ける位置が限られるので困った
  • スケジュール管理
  • 家族になるべく負担かけない(だいじ)

配信関連

  • zoom を使って画面共有する形だったので慣れていて戸惑わなかった。よかった
  • プロンプターすごい。
  • チャットのログを遡れなかったのは残念
  • なぜかRubyKaigi teamからのメールがGmailの「プロポーション」に振り分けられ、見落とすことが多かった
  • 字幕の精度がすごい。
  • SpeakerやCommiterやRubyKaigiTeamに色や絵文字がついてたのよかった

今年もやっていくぞ


  1. Rubyリファレンスマニュアル https://docs.ruby-lang.org/ja/

-bash: warning: setlocale: LC_CTYPE: cannot change locale (UTF-8): No such file or directory を解消する

まとめ

(1) .zshrc などで

export LC_CTYPE="ja_JP.UTF-8"

日本語ローカライズに非対応の場合は、

export LC_CTYPE="en_US.UTF-8"

(2) /etc/ssh/ssh_configSendEnv LANG LC_*コメントアウトする

経緯

ssh しようとして以下のエラーが出ることがある。

-bash: warning: setlocale: LC_CTYPE: cannot change locale (UTF-8): No such file or directory

locale を見るとこのように LC_CTYPEUTF-8 となっている。エラーメッセージは UTF-8 という locale がないと言っている。

$ locale
LANG=""
LC_COLLATE="C"
LC_CTYPE="UTF-8"
LC_MESSAGES="C"
LC_MONETARY="C"
LC_NUMERIC="C"
LC_TIME="C"
LC_ALL=

macOS では Terminal.app 起動時に以下の設定がオンな場合、LC_CTYPE="UTF-8" に設定される。

ssh する際に ssh_configSendEnv LANG LC_* が有効で接続先の AcceptEnv LANG LC_* も有効な場合、LC_CTYPE="UTF-8"ssh 先にも送られる。接続先に UTF-8 がない場合は -bash: warning: setlocale: LC_CTYPE: cannot change locale (UTF-8): No such file or directory とエラーになる。

このため解消するには、

  • .zshrc などで export LC_CTYPE する
  • ssh_configSendEnv LANG LC_* を無効にする
  • Terminal.app を使わない
  • Terminal.app の Set locale environment variables on startup をオフにする

という対策が考えられる。なお Terminal.app の設定を変えると日本語入力ができなくなるので注意。

私はたまに Terminal.app を使うのと、日本語ローカライズに対応している場合はその設定を使ってほしいので .zshrcexport LC_CTYPE="ja_JP.UTF-8" を書くことにした。

参考

String#force_encodingは文字コードの強制変換ではない

RubyString#force_encoding は String の encoding を変更するだけで、文字コードの変換を行うわけではありません。そのため、バイト列は変換されません。

force_encoding はどんなメソッドか

String の encoding を変更します。ただし、バイト列は変更せず、encoding に対しバイト列が正しいかどうかは確認しません。

例えば EUC-JP に変換して encode と比較します。

''.encode(Encoding::EUC_JP).encoding
# => #<Encoding:EUC-JP>

''.force_encoding(Encoding::EUC_JP).encoding
# => #<Encoding:EUC-JP>

どちらも encoding は EUC-JP です。

String#valid_encoding? で正当なバイト列かどうか確認できます。

''.encode(Encoding::EUC_JP).valid_encoding?
# => true

''.force_encoding(Encoding::EUC_JP).valid_encoding?
# => false

force_encodingfalse になり、バイト列が正しくないことがわかります。

encodeforce_encoding の返り値を見てみると、

''.encode(Encoding::EUC_JP)
# => "\x{A4A2}"

''.force_encoding(Encoding::EUC_JP)
# => "\xE3\x81\x82"

encodeforce_encoding で結果が異なっています。

どちらも のバイト列ですが、encode の返り値の \xA4\xA2EUC-JP のバイト列で、 force_encoding のほうの \xE3\x81\x82UTF-8 のバイト列です。

''.encode.bytes.map { _1.to_s(16) }
# => ["e3", "81", "82"]

encode は encoding とバイト列を変更しますが、 force_encoding は encoding のみ変更してバイト列は変更しません。

force_encoding はどんなときに使うか

バイト列を変えずに encoding だけを変換したいときに使います。

Array#pack の返り値に対して encoding を設定する場合

Array#pack の返り値の encoding は ASCII-8BIT だったりして人間には読みにくく、また encoding に UTF-8 を期待している場合に使いにくいので、UTF-8 (もしくはお使いの環境の文字コード)の文字列として扱いたい場合 force_encoding をつけると使いやすくなります。

["e38182"].pack("H*")
# => "\xE3\x81\x82"

["e38182"].pack("H*").force_encoding(Encoding::UTF_8)
# => "あ"

バイト列から文字を組み立てる場合

バイト列だけが分かっていて、encoding を後から設定したい場合です。

例えば以下の記事に書いたような、未定義文字に対する変換先の文字を定義する場合です。これを encode で同じことをすると少しややこしくなるので force_encoding したほうがすっきりします。

U+301C from UTF-8 to Windows-31J (Encoding::UndefinedConversionError) に対応する - esm アジャイル事業部 開発者ブログ

str = "\u{2014 301C 2016 2212 00A2 00A3 00AC}"

undefined_signs = {
  "\u2014" => "\x81\x5C".force_encoding(Encoding::Windows_31J), # — EM DASH
  "\u301C" => "\x81\x60".force_encoding(Encoding::Windows_31J), # 〜 WAVE DASH
  "\u2016" => "\x81\x61".force_encoding(Encoding::Windows_31J), # ‖ DOUBLE VERTICAL LINE
  "\u2212" => "\x81\x7C".force_encoding(Encoding::Windows_31J), # − MINUS SIGN
  "\u00A2" => "\x81\x91".force_encoding(Encoding::Windows_31J), # ¢ CENT SIGN
  "\u00A3" => "\x81\x92".force_encoding(Encoding::Windows_31J), # £ POUND SIGN
  "\u00AC" => "\x81\xCA".force_encoding(Encoding::Windows_31J), # ¬ NOT SIGN
}

p str.encode(Encoding::Windows_31J, fallback: undefined_signs)

この場合は String.new でも同じことができます。

String.new("\x81\xCA", encoding: Encoding::CP932)

DB に入れるデータをバイナリ扱いしたい場合

普通にやると別の文字コードに変換されてしまう文字を DB の設定を変えずに DB に格納したいことがあり、そのために encoding に Encoding::ASCII_8BIT をつけてバイナリ扱いして encoding を変換させずに入れるために使ったことがあります。

まとめ

force_encoding では encoding のみを変更し、バイト列は変更しません。また、バイト列が正しいかどうかのチェックも行いません。

encoding に対してバイト列が不正な場合は文字を正しく扱えないため、どうしても force_encoding したい場合は valid_encoding? とセットで正しいバイト列かチェックして使う方が安全だと思います。

参考

Ruby文字コード対応については以下の記事がとても詳しいので、興味のある方はぜひこちらを参照ください。

Ruby M17N の設計と実装