2025-12-06 (Sat) [長年日記]
_ String#stripで削除される文字を指定できるようにする
この記事は、Ruby/Rails Advent Calendar 2025の6日目の記事です。
とりあえず「何か書く」と登録したもののとくにネタはなく、どうしようかなと思ったらちょうど目に留まったポストが。
簡単なベンチマークを試すとこんな感じ。
voyager:tmp$ cat b.rb
require "benchmark"
TARGET = " \t\r\n\f\v\0" + "x" * 1024 + "\0 \t\r\n\f\v"
Benchmark.bmbm do |x|
x.report("strip") do
10000.times do
TARGET.strip
end
end
x.report("gsub") do
10000.times do
TARGET.gsub(/\A\s+|\s+\z/, "")
end
end
end
voyager:tmp$ ruby b.rb
Rehearsal -----------------------------------------
strip 0.004674 0.000938 0.005612 ( 0.005618)
gsub 0.284665 0.001060 0.285725 ( 0.285733)
-------------------------------- total: 0.291337sec
user system total real
strip 0.000953 0.001997 0.002950 ( 0.002950)
gsub 0.288242 0.000002 0.288244 ( 0.288261)
voyager:tmp$
ちなみに、\sがstripの削除対象からNUL文字を除いたものかどうか確認しようとドキュメントを見ていたら、たしかにそうだったが、ついでに正規表現ではなく文字列リテラルだと"\s" が" "(U+0020)と解釈されるということを知った。
Rubyとは30年近い付き合いだが、時折いまだに知らない一面を見せてくれる。
実用的には速度が問題になることはあまりない気もするがgsub(/\A\s+|\s+\z/, "")はちょっと読みにくいし、strip(" \t\r\n\f\v")と書けてもよさそうなものだ(ここでstrip("\s")と書いてしまうと前述のとおりstrip(" ")になってしまう)。
まず他の言語ではどうなっているか調べてみたところ、Pythonではstripに引数を指定すると削除対象の文字を指定できることがわかった。
>>> "---abc+++".strip("-+")
'abc'
他の言語に同様の機能があるということは一定のユースケースがあるという材料にもなるし、提案する時に言及すると導入されやすい(ただし、まつもとさんがあまり好きではなさそうな言語の場合はだまっておいた方がいいかもしれない)。
次にbugs.ruby-lang.orgに同様のissueがないか見てみる(こっちを先にやるべきだった)と、3か月前に[Feature #21552] allow String.strip and similar to take a parameter similar to String.deleteというissueが作成されていたことがわかった。
ここで解散してもよかったのだがそれだとまたネタを探さないといけない。実装はまだないようだったので、以前まつもとさんと話した時に「生成AIを使ったコードをRubyに受け入れてもいいと思っているし、実際に一部のコミッタは使っているらしい」という話を聞いて、「だったらCo-Authored-Byを付けるようにした方がいいんじゃないですかね」とか話してたのを思い出して、Claude Codeによる実装を試してみることにした。
「string.cで実装されているString#stripに省略可能な引数charsを追加して、charsが渡された場合は空白文字の代りにcharsに含まれる文字を除去するようにしてください」という雑な指示で最初に出て来た実装がこれ。
だいたい期待通りの動作だが、strip("0-9")のように範囲指定をすることができない。そんな指示してないんだから当然である。
tr_setup_table()という既存のヘルパー関数(trやdeleteで使われている)を使うように指示して出て来た実装がこれ。
これで範囲指定もできるようになった。
p " \t\r\n\f\vfoo \t\r\n\f\v".strip(" \t-\r") #=> "foo"
lstrip_offset()と別にlstrip_offset_chars()を追加しているせいで呼び出し側の分岐が増えて差分が多くなっていたり、ドキュメントの表現が他と比べてやや冗長だったりしたので、手で直した上でpull request #15400を作成した。
pull request作成後になかださんの指摘を受けてのドキュメントやテストの追加や、String#delete同様に複数引数を受け付ける修正を行った。
いいと思ったら、[Feature #21552]に👍️をお願いします。
2025-11-11 (Tue) [長年日記]
_ Mastodonアカウントの引っ越し
Mastodonはruby.socialを使っていたが、tDiaryのサーバをKAGOYA CLOUD VPSに移したついでにsocial.shugo.netにインスタンスを立ててアカウントを引っ越した。 Mastodonにはアカウントの引っ越し機能があるので、とりあえずruby.socialではじめて必要なら後で引っ越せばいいやと思っていたのだが、投稿データの引き継ぎが行われないのは誤算だった(データのアーカイブは取得できる)。 これからMastodonのアカウントを作る人はちゃんと考えた方がいいですよ。
CPU 2コア、メモリ2GBのプランのVPSにtDiaryも同居という構成なので、Dockerは使わずに公式ドキュメントのInstalling from sourceの手順でインストールした。
不要なデータの削除やバックアップなどの定期実行処理については上記の手順には書れていなかったので、適当に検索して出てきたcleanup.shやかゆいところに手が届くインスタンス運用の初級テクニック集を参考に、以下のスクリプトをcronで動かすことにした。
cleanup.sh:
#!/bin/bash
export PATH=/home/mastodon/.rbenv/shims:/home/mastodon/.rbenv/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/games:/usr/local/sbin:/usr/l
ocal/bin
set -eu
CACHE_RETENTION_DAYS=7
echo "=> Cleanup"
cd /home/mastodon/live
export RAILS_ENV=production
echo "=> Retention days set to ${CACHE_RETENTION_DAYS}"
echo "==> media cache ..."
./bin/tootctl media remove --days=${CACHE_RETENTION_DAYS}
echo "==> orphaned media ..."
./bin/tootctl media remove-orphans
echo "==> preview cards ..."
./bin/tootctl preview-cards remove --days=${CACHE_RETENTION_DAYS}
echo "==> prune profiles ..."
./bin/tootctl media remove --prune-profiles --days=${CACHE_RETENTION_DAYS}
echo "==> remove headers ..."
./bin/tootctl media remove --remove-headers --days=${CACHE_RETENTION_DAYS}
echo "==> clear cache ..."
./bin/tootctl cache clear
echo "==> remove unreferenced statuses ..."
./bin/tootctl statuses remove --days=${CACHE_RETENTION_DAYS}
echo "==> prune accounts ..."
./bin/tootctl accounts prune
backup.sh:
#!/bin/bash
set -euo pipefail
# Configurable variables
INSTANCE_DIR="/home/mastodon/live"
SYSTEM_DIR="/home/mastodon/live/public/system"
BACKUP_DIR="/home/mastodon/backup"
BACKUP_LIFETIME_DAYS=7
DATE_FORMAT="%Y%m%d"
DB_USER="mastodon"
DB_NAME="mastodon_production"
RCLONE_DB_DEST="r2:mastodon-db"
RCLONE_MEDIA_DEST="r2:mastodon-media"
# Error handling
trap 'echo "An error occurred. Exiting." && exit 1' ERR
backup_date="$(date +$DATE_FORMAT)"
# Database backup
echo -n "Backup DB... "
DB_BACKUP_FILE="$BACKUP_DIR/db_$backup_date.sql.gz"
pg_dump -Fc -U $DB_USER $DB_NAME | gzip -c >"$DB_BACKUP_FILE"
find "$BACKUP_DIR" -mtime +$BACKUP_LIFETIME_DAYS -name "db_*.gz" -exec rm -f {} \;
echo "Success!"
# Sync backup
echo -n "Backup to R2... "
rclone copy "$BACKUP_DIR" $RCLONE_DB_DEST
rclone copy --exclude="/cache/**" "$SYSTEM_DIR" $RCLONE_MEDIA_DEST
echo "Success!"
crontab -e:
35 3 * * * /home/mastodon/cleanup.sh > /dev/null
50 3 * * * /home/mastodon/backup.sh > /dev/null
11/18追記
cleanup.shのtootctl {media,preview-cards,statuses} removeは[管理] > [サーバ設定] > [コンテンツの保持] > [メディアキャッシュの保持期間]または[リモートコンテンツの保存期間]で設定しておけばSidekiqの定期実行ジョブで削除されそうなのと、他は消さなくてもよさそうなので、cleanup.shは実行しないように変更した。
2025-09-27 (Sat) [長年日記]
_ Kaigi on Rails 2025に参加した
はじめてKaigi on Railsに参加した。 関節リウマチの症状がつらくて行くかどうか迷っていたが、幸い薬で膝や足はだいぶ楽になったので何とか参加できた。 5時起きで1便に乗るのはつらかったが、諸橋さんの基調講演を聞いてがんばった甲斐があった。
2日目の昼はtakkanm氏に越前そばの店に連れて行ってもらったが、おろしそばがおいしかった。
Idempotency-Keyとか色々知らない話があって勉強になった。 RubyKaigiと違って業務に直接的に参考になりそうな話が多いので来年はもうちょっと会社の若者に参加してもらうといいかもしれない。
2025-06-28 (Sat) [長年日記]
_ 関西Ruby会議08に行ってきた
たまたま(?)前日に大阪でRubyアソシエーションのセミナーがあったので関西Ruby会議08に行ってきた。 関西Ruby会議はたぶん03以来? 会場の先斗町歌舞練場は桟敷席もあって、大江戸や名古屋の演芸ホールの回は行ったことなかったので、いつもと違う雰囲気でよかった。
トークは自作サービスやツールの開発の話が多くて、他の会議と違った雰囲気で新鮮だった。 東京Ruby会議も自作ソフトウェアの話が多かったけどあっちはもうちょっと技術よりだった気がする。 自作サービス作ってる人たちはお金かかって大変そうだなあ。TUIだとお金かからなくていいですよ。
ydahさんのクロージングもいい話だった。自分もまたがんばろう。来年の会場はyhara先生のふるさとですね。
今回も楽しかったです。みなさんありがとうございました。
にしんそば食べ損ねてにしんうどんだったので来年は函館でにしんそば食べたい。
2025-04-19 (Sat) [長年日記]
_ RubyKaigi 2025に参加した
Day 0
まつもとさんと羽角さんを松山に送り届けるというミッションを無事完了した。
Day 1
早朝に目が覚めたので道後温泉に行って来た。「霊の湯」が読めなかったが受付の人は気付かないフリをしてくれた。 個室料金だと皇室用の浴場を案内してくれるというので待っていたら結構時間がかかったが玉座をめでることができてよかった。
Ruby Taught Me About Encoding Under the Hood
メインフレームでEBCDIC触ったことがあるのうらやましいなと思って聞いていた。
Continuation is to be continued
shift/resetをcallccで実装したらcallccをそのまま使うより速くなったという話が興味深かった。
Goodbye fat gem 2025
fat gemつらいんですよねえという共感しかなかった。私は前回のトーク聴いてすぐにfat gemやめました。
Ruby's Line Breaks
Rubyの改行は無視されるところとそうでないところがあってほんとよくわかんないんだよな。
ISO/IEC 30170のBNFでは改行が表れてはいけないところに [no <line-terminator> here] と書いてあるけど、その後Rubyの方が変わってしまって改行を書けるようになってしまっていたり(メソッド呼び出しの . の前とか)。
Day 2
LT
Displaying "アパート" correctly on Textbringerという発表をした。全部は喋れないだろうと思ってたけど想像以上に喋れなくてごめんなさい。わりとima1zumiさんの基調講演と関連のある話ができたのはよかったんじゃないかと思う。
Day 3
Ruby Committers and the World
Twitterでは「Refinements使ってない」と言ってたことになってた気がしたけど、正確には「あんまり使ってない」です。 Textbringerでもちゃんと無駄に使っている。
宇和島風鯛めし
まだ食べられてなかったので一人でどっか行こうかなと思ってらたまたま通りかかったS.H.さんに案内してもらえてありがたかった。
Analyzing Ruby Code in IRB
Textbringerでも似たようなことをRipper.yylexでやってたのでPrism使うのよさそうと思ったけど、肝心のどう使っているかの話あたりは気絶してしまっていた。
Day 4
無事まつもとさんを松江まで送り届けた……はずだったがその後発熱して新型コロナ感染発覚。現在は一応回復しました(何かまだすっきりしないけど)。




