おもしろwebサービス開発日記

Ruby や Rails を中心に、web技術について書いています

ginza.rb 第83回を開催してRails7.2のマイナーフィーチャーを学んだ

Ginza.rb 第83回 - Rails7.2のマイナーフィーチャーを学ぶぞ - connpass

第83回のお題は第82回に引き続いてRails7.2。メジャーフィーチャー以外の気になった変更についてgistにまとめたものを見ながらみんなで「これはいいですねえ」とか「これはどうなんだろうなあ」とか話していました。

gistはこちら。 Rails7.2のマイナーフィーチャーで気になったところ

gistははてなブログと違ってh2ヘッダのアンカーURLが取得しやすいので、あとでこの機能について誰かに話したい、となったときに便利。

時間がちょっと余ったので、やぎぬまさんの用意したgistも見てワイワイしました。'debug/prelude'の件とかconnectionの扱いが変わる件とかそういえばありましたね。

gistはこちら。 Ginza.rb 第83回向けの何か.

個人的な印象に残ったポイント

  • 7.1ではいったstrict localsについて、パースするのが大変じゃんという発想がなかった
  • render @postsのようにして部分テンプレートを描画するときに自動でpost_counter, post_iterationというローカル変数が生える、というのは知らない人が多そう

次回

次回は10月11日(金)に、Campfireのソースコードをみんなで眺める会をやる予定です。ご都合の合う人はお気軽にご参加ください(\( ⁰⊖⁰)/)

大阪Ruby会議04でRailsの関連について話してきました

表題の通り、先日開催された大阪Ruby会議04で「どうしてこうなった?から理解するActive Recordの関連の裏側」というタイトルで登壇させていただきました。

スライドはこちら。

登壇するまで

僕は常々なにかしらRails関連のテックな話がしたいな〜と思っています。以前ブログに書いたActive Recordで関連先を保存するときに気をつけることは自分的にはかなりの力作だったのですが、あんまり読まれてなさそうだったのでこの機会に解説をモリモリ足して発表するともう少し幅広い人にリーチできるかな、と思いプロポーザルを出してみました。そこまでキャッチーな話ではないので通る自信はなかったですが、結果として無事採択されてよかった。今回の発表は大阪Ruby会議04がなければ日の目を見なかった可能性は高いです。オーガナイザの皆様、貴重な発表の場をありがとうございます。

登壇資料はブログをベースにすればいいからすぐできるやろ、と甘く見ていたら発表にあたって調査が必要な箇所が複数あり結局ギリギリまでスライドを調整していました。プロポーザルを通すのはいいけど、通ると期限内にスライドを作らなければいけないという制約*1が毎回辛い><

登壇

前回の大阪Ruby会議03ではうっかり10分の発表枠を選択してしまい時間が足りずに大変だったので今回は即30分の発表枠を選びました。おかげで当日じっくり話すことができてよかった。前回に限らず最近は「時間が足りないので急いで話す」ことが多くなにも意識しないと急いで話してしまうため「ここで水を飲む」スライドをあらかじめ複数配置して強制的に一息つくようにしていました。一定の効果があったので次回もやりたい。

登壇内容は上でも書いたようにRailsの関連についてでした。Railsの関連は「普段とくに内部実装を意識することなく頻繁に使っているけど、意識し始めると無限に疑問が出てくるし内部実装について言及しているドキュメントはほぼないしコードを読んで理解するのも難易度が高い」機能になっています。30分ですべてを説明することは難しいのですが、関連実装の中でも特に難しいと個人的に思っている関連先の自動保存と双方向の関連付けについて噛み砕いて説明することで、仮に昔の自分がこれを聞いたらめっちゃ満足するであろう話になりました。

しかし当日ちゃんとスケジュールを見てみるとスポンサーLTを除いてRailsの話をするのは僕だけ*2で参加者の需要に合った話だったかな…?と心配になりましたが、登壇後に役に立つ話だったと(はすみさん以外からも)何人か言及いただいたので安心しました。

今回は家庭の事情により次の日の朝すぐに東京に戻らないといけなくて大阪観光はほぼできず、という感じでしたが会場あたりの街並みがたぶん大阪に住んでいた頃に何度か通った場所で、懐かしい気持ちになりました。来年はもうちょっと余裕を持ってこれるといいな。

SaveChainInspector

関連先の自動保存がどのような順番で起きるのか調査するときに使えるsave_chain_inspectorというgemを紹介しましたが、現時点では出力を標準出力決め打ちにしていたりとだいぶ素朴な実装になっています。個人的には現状の仕様で困っていないのですが、需要があったらログファイルに出力もできるようにするなどしてもいいかな、と思っているので需要があったらその旨教えて下さい :pray:

次回予告

次の登壇はKaigi on Rails 2024で「Sidekiq vs SolidQueue」というタイトルで話す予定です。これから登壇準備を頑張ります。

*1:制約ではない

*2:強いて言えばkinoppydさんがRails周辺の話であるHotwireの話をしている

ginza.rb 第82回を開催した

Ginza.rb 第82回 - connpass 第82回のお題はもうじきリリースされるはずのRails7.2。やぎぬまさんがRails7.2メジャーフィーチャについてのスライドを作ってくれていたのでこれに沿ってワイワイしました。

メジャーフィーチャーという建付けだけど機能というよりは「rails newのときに新しいなにかが生成されるようになった」というものが多く、これはもしかしたらサラッと30分くらいで終わるかもしれないな〜と思ったので マイナーフィーチャー用の資料を用意していたのだけど、いい感じに時間いっぱいまで盛り上がって終了。マイナーフィーチャーに関しては来月持ち越し。

メジャーフィーチャー中のトランザクション中でエンキューしないという機能は個人的には最高便利!と思っていたのだけど、「トランザクション中でエンキューが失敗したらロールバックする」ができなくなるというデメリットについて話が出て「その発想はなかったなあ」となりました*1

来月はRails7.2のマイナーフィーチャーについてワイワイする予定なので都合合う方はご参加ください。

*1:sidekiq pro以上を使っていれば Reliability Client でエンキューの失敗をサポートできる気もしないでもないけど

ginza.rb 第81回を開催した

コロナ禍から休止状態だったginza.rbを4年ぶりに復活させました。

Ginza.rb 第81回 - connpass

コロナ禍以降リモートで働くひとが増えたので都内でオフラインミートアップをしても集まらないんじゃないかな?と思っていたのですが、蓋を開けてみたら定員オーバーするレベルで嬉しい誤算でした。開催してみた感想はオフラインミートアップからしか取れない栄養素あるな〜でした。復活させてよかった。

今回のミートアップの内容は久しぶりの開催なのでみんなの近況を聞きたいな、と思い自己紹介拡大版としました。みなさんかわらず元気にやっているようでよかった。

自己紹介タイムのあと、僕のプライベートが忙しくなったこともあり再開にあたってなるべく省エネでミートアップできる方法ないかなあ…というのを相談させてもらいました。ただginza.rbらしさをキープしつつ省エネするのはなかなか難しいですねという感触。ありがたいことにこれまでのginza.rbの内容を評価してくれる声もあったのでしばらく従来通り開催しつつ様子を見ようかな、と思っています。

次回はRails7.2をお題にして7月26日(金)に開催する予定です。お題がキャッチーなときは公開してすぐ枠が埋まりがちなので、参加するモチベが高い人はginza.rbのslackやconnpass*1などに登録しておいて通知を受け取れるようにしておくとよいかと思います。

*1:再開にあたってdoorkeeperから移行しています

savanna.io をRails 7.2にアップグレードした

お仕事SNSsavanna.ioの開発を空いた時間でやっています。先日Rails 7.2のbetaが出たので試しにCIを回してみたらそれほど問題なくアップグレードできそうなのでサッと対応してアップグレードしました。以下対応した内容を書いています。

bulletを外す

bulletの依存でbundle installができなかったので、一旦bulletを外しました。 PRは出ているので取り込まれたら戻すのを検討します。

Support active record 7.2.0 by hatsu38 · Pull Request #707 · flyerhzm/bullet

enumの形式変更に対応

Rails7.0からenumの引数の渡し方が新しくなりました。7.2からは古い渡し方はdeprecatedになり、8.0からは新しい方式だけになる模様です。

# 旧
enum status: { unlooked: 0, looked: 10, read: 20 }
# 新
enum :status, unlooked: 0, looked: 10, read: 20

Allow new syntax for enum to avoid leading _ from reserved options by kamipo · Pull Request #41328 · rails/rails

savanna.ioではconfig/environments/test.rbで次のようにしてdeprecation warningのときに例外を発生させるようにしているので、Rails7.2にアップグレードしたことでCIが失敗しました。

config.active_support.deprecation = :raise

Rails7.2にアップグレードする際に必須の修正ではないですが、早く対応するに越したことはないので一緒に修正しました。

prepared statementの実行でエラー

Rails7.2にアップグレードすると、次のように文字列でクエリを渡している箇所でActiveRecord::StatementInvalidエラーになりました(コードは簡略化しています)

def not_todays_target
  User.joins(:conditions).where(
    'conditions.transfer_on - interval :duration = :today',
    today: Time.zone.today,
    duration:
  )
end

コードを追いかけて調査したところ、Rails7.1まで上記のクエリはprepared statementの対象外だったのがRails7.2から対象になったことが原因のようでした。intervalリテラルの部分をプレースホルダとして利用することはできないらしいので、クエリの書き方が悪いと判断してdurationの部分を文字列展開させることで対応しました*1

def not_todays_target
  User.joins(:conditions).where(
    'conditions.transfer_on - interval #{duration} = :today',
    today: Time.zone.today,
    duration:
  )
end

prepared statementの対象が変更になったのはこのPRが原因のようです。

Relation#where build BoundSqlLiteral rather than eagerly interpolate · rails/rails@8e6a5de

このPRは、DBのconnectionを使う機会をなるべく減らす一環で作られたものです。

これまでは例えばwhereメソッドの実行時にActiveRecord::Base.sanitize_sqlを利用してプレースホルダの置換を行っていました。このとき、利用するDBによるクォートの付け方の差異をconnectionを利用することで解決しています。このPRではプレースホルダの置換、つまりconnectionの利用を遅延させて、prepared statementを利用する設定であればそれを利用するようにコードを修正しています。結果として、これまでprepared statementを利用していなかったクエリが、prepared statementの対象になっています。

所感

思っていたよりも簡単にアップグレードできました。次はconfig.load_defaults 7.2にチャレンジしようと思います。

*1:durationはユーザ入力文字列ではないのでセキュリティの問題はありません

RubyKaigi 2024に行ってきました

  • ちゃんと書こうとすると一生書けなさそうなのでざっくりとまとめています
  • めっちゃ楽しかったです
  • 5/11~19まで沖縄を満喫しました
  • 前入りで旅行をしてからRubyKaigiに臨むと体力が0に近い状態からのスタートになるので、懇親会は欲張らずに1次会だけの参加にしてサッとホテルへ戻り体力温存を優先するのが良いな、という知見を得ました
  • 発表はだいたい一番大きい会場にいました
    • 3階席が空いていて居心地が良かった
    • こばじゅんさんやydahさんのパーサ関連の発表だけそれぞれB、C会場で聞きました
      • 僕もいちおうドラゴンブック読書会の末席にいるので、仲間を応援する気持ちでした
  • 観た中ではモリスさんのNamespaceの発表が一番ワクワクしました
    • Namespaceによって僕ら(Railsアプリケーションをつくる人)の生活がまた一段階便利になるんじゃないかな〜
  • 久しぶりのひとと直接会って「RubyKaigiは同窓会みたいですねえ」みたいな話をしていました
    • RubyKaigiは同窓会感もあるんだけど、通信簿感もある
      • この一年なにをしてきたか、が反映される
    • 各発表内容で「なんもわからん」というのは(最初のぺんさんの発表を除いて)なかったのでインプットはそれなりにできている
    • が、アウトプットはそれほどない
      • 仕事の中ではちょこちょこやっているのだけど、それをもっと広く公開する手間を掛けられていない
    • 英語もリスニング、スピーキングがまだまだ
    • 昔ほどは自分の時間が取れないんだけど、来年の4月をもっと楽しむためにがんばっていこ(\( ⁰⊖⁰)/)という気持ちになりました
  • 松山も楽しみにしています!!

gimei v1.5.0をリリースしました

先月にgimei のv1.4.0を、昨日にv.1.5.0をリリースしていました。それぞれの変更についてまとめてみます。

v1.4.0

gimeiはランダムに生成した名前を返すライブラリです。姓や名だけがほしいときはそれぞれ次のようにlastメソッドとfirstメソッドが使えます。

Gimei.last.to_s #=> "藤田"
Gimei.first.to_s #=> "太志"

また、それらのエイリアスとしてfamily, givenもあります。

Gimei.family.to_s #=> "二宮"
Gimei.given.to_s #=> "徹二"

gimeiを普通に使うと、あらかじめ用意している配列からsampleで名前を選ぶので、同じ名前が続けて二回出力されることは確率的にはゼロではありません。それを避けたいときに使えるuniqueというメソッドを用意しています。

Gimei.unique.name.to_s #=> "堀内 七虹"

uniqueを使い続けて、gimeiが用意している名前が尽きるとエラーになります。なので適切なタイミングでGimei.unique.clearをして、「これまで利用した名前のリスト」をクリアしてあげると便利です。

clearに引数を渡さないとすべてのリストを消去しますが、次のように引数を渡すと特定の「これまで利用した名前のリスト」に絞って消去することができます。

Gimei.unique.clear(:first) # Gimei.unique.first の結果を消去

と、ここまでが変更に関する前提知識の紹介でした。

Gimei.unique.givenのように名前を生成したときにそれだけをクリアしたいときは、これまでGimei.unique.clear(:first)のようにエイリアス元を指定しないと意味がない状態だったのを、Gimei.unique.clear(:given)でもよいようにしたのがv1.4.0の変更です。

PRはこれ:family, :givenのaliasをclearでも使えるようにする by izumitomo · Pull Request #67 · willnet/gimei

v1.5.0

v1.5.0は内部的な改善です。きっかけは開発版RubyでCIを動かすと失敗していたことでした。

Ruby 3.4のテストが失敗する · Issue #68 · willnet/gimei

gimeiは漢字、フリガナ、ふりがな以外にもローマ字による出力が可能です。

Gimei.name.romaji #=> "Hitomi Ooba"

これまで、これはromajiというgemを利用してふりがなを変換して生成していました。そしてromajiはnkfに依存しており、nkfがRuby3.4からdefault gemではなくなることからromajiのrequireに失敗するようになっていたのでした。

そもそも漢字、フリガナ、ふりがなはYAMLでデータを持っているのにローマ字だけ動的に生成するのが一貫性がない、と感じていたところだったのでこの期にromajiの依存をなくしたいな、と思っていたところそのものズバリのPRを頂いたのでマージしてv1.5.0にしました。

PRはこれ Romajiによる変換後のデータをymlに追加 by atolix · Pull Request #69 · willnet/gimei

というわけで

ちょっとずつですが改善していっています。どうぞご利用ください。