Tantiny 、ろくにドキュメントを読んでいなかったので知らなかったのだけど、リアルタイムで検索インデックスの更新ができるようだった。これまで一時間に一度バッチ処理を動かして、更新された記事があればすべての記事を読み直してインデックスを更新するような富豪的なことをやっていた。アホだった。
Docker コンテナまで含めて完全 Ruby 3 化
バッチ処理を動かしている Docker コンテナ含めてすべての環境を Ruby 3 で動かせるようになった。以下の点に難儀した。
- MeCab が Google Drive からダウンロードできなくなっているので代替を探した
- https://github.com/shogo82148/mecab のやつを参照することにした
- Tantiny が依存する rutie という Rust と Ruby をブリッジする gem が新しめの Rust に対応しておらず、 Rust のバージョンを 1.77 に固定する必要があった
- ActiveRecord が v6 に上がったことにより、
DATABASE_URL
を環境変数で渡すことで DB 接続設定を上書きできなくなってしまった- 設定ファイルの方を優先して読み込むようだった
ついでにキャッシュも効くように修正した。 sinatra-cache がおかしかったのは Haml の挙動が変わって - form_tag
としていたところを = form_tag
とする必要があるのと同様に、 - cache_fragment
を = cache_fragment
にする必要があった。再びキャッシュが効くようになって高速になったが、一部 HTML タグが混ざって表示されることがある。 sinatra-cache.gem が依存する sinatra-outputbuffe.gem の方に問題がありそう。この gem は 16 年以上更新されていない。どこかでキャッシュ依存はやめないといけないかもしれない。
Ruby 3 化
先週末と今日ガチャガチャやって、ようやく Ruby 3 にアップグレードすることができた。 Ruby 2.7.3 → Ruby 3.1.6 。ただ Ruby 3.1 は今年の 5 月に EOL を迎えるみたいなのでこちらもさっさと新しいバージョンの Ruby に上げないといけない。
やったことは一つ前の記事に加えて以下。
kaminari-sinatra の SinatraHelpers が Ruby 3 & ActionView v6 対応していなかったのでちょこちょこと修正した。
次に padrino-helpers が Ruby 3 と Haml v6 に対応していないのを対応させた。具体的には form_tag
の中身のタグが過剰に escape されてしまうので、あんまり良くないかもだが capture_html
したやつを html_safe
した。 form_tag
の内側に来るものはユーザー投稿コンテンツではないはずなのでエスケープはサイト管理者側でできるはず。
ドキュメントでは
= form_tag
を
!= form_tag
にしろとは言われているが、 form_tag
の中身で concat_contet してる片方( capture_html(&block)
の結果)が html_safe? => false
になるので、 View テンプレート側で何かやっても意味がない( Buffer が汚染されると View で html_safe
しても汚染された部分の文字列はエスケープ済みになっている)。
kaminari-sinatra も padrino-helpers も本家にパッチを送ると良いのだろうが、職業プログラマーではなくなったのでなかなか腰が重い。 kaminari-sinatra はテストが通らないし、 padrino-helpers は git clone で submodule の clone に失敗するのでテストが実行すらできないかもしれない。
心の余裕ができたらやってみる。
ちなみに Ruby 3 化するにあたり sinatra-cache を完全に捨てたので負荷が上がるかも。オリジナルの gem は 15 年くらいコミットされてなくて fork して使い続けてきたけど Sinatra や Haml の変更に追従できる気がしないのでいったん捨ててみる。
Ruby 3 未遂
このブログは Ruby 2.7 でずっと動かしていた。コミットログをたどると 2020 年の 1 月から Ruby 2.7 のようだ。 Ruby 2.7 は 2023 に EOL を迎えている。
さすがにまずいと思ったので Ruby 3 にしようと一日頑張ってみたが、なかなかうまくいかない。Ruby 3 の キーワード引数の仕様変更はかなり対応がきつい。どこで ArgumentError が起こっているのかが極めて追いかけづらい。他のメソッドに委譲している場合などは特に。
ガチャガチャやってトップページと個別記事ページまでは Ruby 3 化できたので Ruby 3 でデプロイしてみたが、動かない画面があることに気がついたので Ruby 2.7 に戻した。 Kaminari がちゃんと動かない(具体的には kaminari-sinatra と actionview v6 系の互換性がない)のが原因でページネーションするページがちゃんと動かなそうだったので Ruby 3 化は諦めた。 kaminari-sinatra の ActionViewTemplateProxy#initialize
を actionview v6 対応させないと無理っぽい。
kaminari のような有名な gem の派生 gem ならきちんとメンテされてるかなーと思っていたが、 kaminari-sinatra の最終コミットは 4 年前だった。
Ruby で View まで作る人たちはほとんどいなくなってるのだろう。
なお動かないところのデバッグは ChatGPT と対話しながらやった。めっちゃ便利。一人だと気がつかないような部分のコードを見てみろと ChatGPT が言ってくれて、そこにデバッグコードを入れてみるとビンゴだったりする。便利な世の中になった。
忘れないようにやったこと・気づいたことをメモっておく。
- sinatra は v4 にあげないといけないのでパスの正規表現から
^
と$
は消さないといけない - better_errors の REPL がちゃんと動かないので Backtrace を見たいときはログを開くか better_errors を使うのをやめる
- fork していた sinatra-cache は sinatra 4 では動かないので外した(キャッシュできない部分をどうするかは要検討)
- capistrano-puma も Ruby 3 対応させないといけない(期待される systemd のフォーマットが変わっているので単にデプロイするだけではだめで一部手作業が必要)
- tilt は v2.1.0 に固定(
Tilt::ErubisTemplate
クラスが消えるため、 padrino-helper がエラーを出す) - concurrent-ruby は 1.3.5 未満に固定
- compass は 1.0.3 に固定
- SESSION_SECRET は 64 文字以上にする
- haml の過剰な escape を抑制
- kaminari-sinatra の
ActionViewTemplateProxy#initialize
を actionview v6.1.7.8 のActionView::Base#initialize
に対応させる
Can AI replace me?
人気記事集約用のシェルスクリプトは自己流で書いてたのであまり自信がなかったので、ものは試しにと ChatGPT にリファクタリングしてもらった。今日の集計と昨日の集計と全期間の集計で似たようなコードがあるのにコードを共通化できていなかったので共通化を提案されたが、そのまま適用するとちゃんと動かない。重要な細かい仕様をなかったことにしたりもする。 Copilot も試してみたが似たような感じだった。生成 AI はチャットでベストプラクティスを聞いたり、細かい作法を尋ねたりするのには向いているかもだが、ソフトウェア開発者を丸々置き換えることはできないと思う。少なくとも現状は。実際に動かしてみて期待通りに動くかのチェックは絶対に必要だし、人間が細かいところで楽をすることはできるけど、完全に ChatGPT とか Copilot だけでシステムを構築するのは難しいだろう。 AI は人間が知ってることしかできない。人間が知らないことは人間にしかできない。
過去の任意の日付の人気記事を確認できるようにした
人気記事を確認できるようにしたという記事を以前書いている。もう 7 年も前のことのようだ。
調べればこういうログ集約・集計系のサービスはあるようだったが、個人ブログなのでなるべくお金はかけたくない。
しばらくの間は集計後のログを捨てていたが、 2 年半前からログローテートするタイミングで日付ごとのファイルを書き出し保存するようにしていた。日付ごとのログファイルは Amazon の S3 にアップロードしてさくらの VPS サーバーからは待避させている。現状、 2022 年の 6 月 13 日のログから S3 上にデータが残っているので、その日以後の日ごとの人気記事を確認できるようにした。こんな感じ。
日付の選択は最初 React のライブラリを使って作ろうかと思ったが、そういや最近のブラウザーは <input type="date" />
とすると結構ちゃんとしたカレンダー UI と日付選択の機能を提供してくれるので楽をすることにした。
max と min を指定すると選べない日付は非活性にされるし、無理に選択すると form を submit したときにバリデーションしてくれる。


ブログの閲覧者からしたらいつどの記事が読まれていたかなんて興味ないだろうけど、著者である自分には興味深い情報になる。セルフホストのブログだからできる機能だ。
YouTube の OGP 読み込み
YouTube の OGP が読み込めない問題があって、回避策をいろいろ考えていた。
YouTube は未ログインで bot っぽい User Agent でアクセスすると OGP のタグが入ってないページを返すようだった。
検索すると facebookexternalhit/1.1 (+http://www.facebook.com/externalhit_uatext.php)
という UA でリクエストすれば OGP タグ入りのページを返してくるようだった。
このやり方を試してみようとしたが、使っている gem が UA の上書きに対応していないので面倒くさそうだった。
追加でいろいろ調べてみると、そもそも YouTube は埋め込み用の HTML 片を返す API を用意しているようだった。
動画の ID がわかっているなら https://www.youtube.com/oembed?url=動画のURL
という風に GET リクエストを投げると、動画のメタ情報に加えて埋め込み用の iframe スニペットを返してくれる。こんな感じ。
{
"title": "Ride - Vapour Trail (Live on KEXP)",
"author_name": "KEXP",
"author_url": "https://www.youtube.com/@kexp",
"type": "video",
"height": 113,
"width": 200,
"version": "1.0",
"provider_name": "YouTube",
"provider_url": "https://www.youtube.com/",
"thumbnail_height": 360,
"thumbnail_width": 480,
"thumbnail_url": "https://i.ytimg.com/vi/9bVS9j8NoZ0/hqdefault.jpg",
"html": "<iframe width=\"200\" height=\"113\" src=\"https://www.youtube.com/embed/9bVS9j8NoZ0?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen title=\"Ride - Vapour Trail (Live on KEXP)\"></iframe>"
}
結果はこんな感じ。
こういう仕組みは標準化されていて oEmbed というっぽい。そういえば一昔前に聞いたことがあるような気がする。
OGP カードのようにしようかなと思ったけど、 YouTube 動画ならその場で再生できた方が便利かなと思ったので、 YouTube 動画の URL はすべて埋め込みとして表示することにした。
ちなみに YouTube にまつわる何かを検索しようとするとなかなか知りたい情報にたどり着けなくて困った。 YouTube OGP
みたいなキーワードで検索すると、大量に OGP について解説している YouTube 動画がヒットする。技術寄りの内容というよりマーケターっぽい人向けの情報。これは地獄みがある。 Google だけでなく DuckDuck Go でも同じような結果だった。相変わらずインターネットは不便になってきている。