- Ruby / Rails関連
週刊Railsウォッチ: Active Modelのパターンマッチングがいったん取り消し、Ruby技術者認定試験が10月3日から3.xに対応ほか(20220719)
こんにちは、hachi8833です。
🔗Rails: 先週の改修(Rails公式ニュースより)
今回は以下の中から取り上げていなかったものを中心に見繕いました。
🔗 in_batches
を最適化
従来の
in_batches
(編注: 原文each_batch
を修正)の実装では、特定のidをpluck
してから、それらをWHERE IN (...ids...)
で用いてバッチを生成していた。
このリストはすぐ大きくなりがちで(デフォルトは1000件)、そうなるとクエリの効率が低下したりログや監視ツールのSQLクエリが切り詰められたりすることがある。きっと誰しも画面サイズの半分を占める怪しいクエリを見たことがあるはず。
このプルリクは、クエリをrange(WHERE id >= num1 AND id < num2
)でイテレートすることでクエリの効率を高めてコンパクトにする実装を行う。
User.in_batches(of: 3) do |relation|
puts relation.to_sql; nil
end; nil
--改修前
SELECT "users".* FROM "users" WHERE "users"."id" IN (1, 2, 3)
SELECT "users".* FROM "users" WHERE "users"."id" IN (4, 5, 6)
SELECT "users".* FROM "users" WHERE "users"."id" IN (7, 8, 9)
SELECT "users".* FROM "users" WHERE "users"."id" IN (10, 11, 12)
SELECT "users".* FROM "users" WHERE "users"."id" IN (13, 14)
--改修後
SELECT "users".* FROM "users" WHERE "users"."id" < 4 ORDER BY "users"."id" ASC LIMIT 3
SELECT "users".* FROM "users" WHERE "users"."id" >= 4 AND "users"."id" < 7 ORDER BY "users"."id" ASC LIMIT 3
SELECT "users".* FROM "users" WHERE "users"."id" >= 7 AND "users"."id" < 10 ORDER BY "users"."id" ASC LIMIT 3
SELECT "users".* FROM "users" WHERE "users"."id" >= 10 AND "users"."id" < 13 ORDER BY "users"."id" ASC LIMIT 3
SELECT "users".* FROM "users" WHERE "users"."id" >= 13 ORDER BY "users"."id" ASC LIMIT 3
この手法は自分のgemで既に使っていて非常にうまくいっている。
このパッチを実装した後で、これと似たような#42695があることに気がついたが、そこまでうまく動いていない様子("改修前"のようなクエリがまだ生成される)。個人的には自分の実装はよりシンプルにできたと思っている。そういった流れでこのプルリクをオープンした。
同PRより
つっつきボイス:「先週も見たような気がするプルリクだけど、先週のはin_batches
にuse_ranges: true
を指定できる改善でしたね(ウォッチ20220711): こちらは従来のin_batches
やfind_in_batches
がSQLのINでidのリストを渡す方式だったのをWHERE id >= num1 AND id < num2
のようなrangeに変えている」「先週と同じ方がこのプルリクも出してるんですね」
参考: Rails API in_batches
-- ActiveRecord::Batches
参考: Rails API find_in_batches
-- ActiveRecord::Batches
🔗 app:update
実行時に機能がconfig/application.rbで無効になっているかをチェックするようになった
つっつきボイス:「RailsでActive MailboxやAction Textのような機能をrequire
していないならapp:update
の対象にする必要はない、たしかに」「ですよね」
🔗 in_batches
にブロックを渡さずに降順を指定できるようになった
#30590の続き。
#30590ではバッチを降順にする機能が導入されたが、in_batches
にブロックを渡さずに同じことをする機能がサポートされていなかった(おそらくコレクションの省略がサポートされていなかったためと思われる)。
in_batches
にブロックを渡す場合: 問題なく動作する
# Post Pluck (0.1ms) SELECT "posts"."id" FROM "posts" ORDER BY "posts"."id" DESC LIMIT ? [["LIMIT", 1000]]
Post.in_batches(order: :desc) {}
in_batches
にブロックを渡さない場合
# 変更前: DESCを期待したがASCになる
# Post Pluck (0.1ms) SELECT "posts"."id" FROM "posts" ORDER BY "posts"."id" ASC LIMIT ? [["LIMIT", 1000]]
Post.in_batches(order: :desc).each {}
# 変更後
# Post Pluck (0.1ms) SELECT "posts"."id" FROM "posts" ORDER BY "posts"."id" DESC LIMIT ? [["LIMIT", 1000]]
Post.in_batches(order: :desc).each {}
同PRより
つっつきボイス:「これもin_batches
の改善」「ブロックを渡さないとorder: :desc
が効かなかったのを効くようにしたんですね」「今までASCになっちゃってたのか」「気づかなかったら事故りかねないヤツ」
🔗 空のデータベースが存在していてもdb:prepare
でスキーマを読み込み可能にした
db:prepare
タスクを更新して、初期化されていないデータベースが存在する場合にスキーマを読み込み、マイグレーションの後でスキーマをダンプするようにした。
Ben Sheldon
同PRより
# activerecord/lib/active_record/tasks/database_tasks.rb#L188
def prepare_all
seed = false
each_current_configuration(env) do |db_config|
ActiveRecord::Base.establish_connection(db_config)
begin
- # Skipped when no database
- migrate
-
- if ActiveRecord.dump_schema_after_migration
- dump_schema(db_config, ActiveRecord.schema_format)
- end
+ database_initialized = ActiveRecord::SchemaMigration.table_exists?
rescue ActiveRecord::NoDatabaseError
create(db_config)
+ retry
+ end
+ unless database_initialized
if File.exist?(schema_dump_path(db_config))
load_schema(
db_config,
ActiveRecord.schema_format,
nil
)
- else
- migrate
end
-
seed = true
end
+
+ migrate
+ dump_schema(db_config) if ActiveRecord.dump_schema_after_migration
end
つっつきボイス:「この機能が欲しいのはわかる」「空のデータベースがあってもdb:prepare
したいときがあるんでしょうか?」「プルリクにもあるように、HerokuなどのPaaS環境を使う場合には事前に空のDBだけが作られていて、db:create
相当の操作をRails側から行えないケースがあるので、そういうときに欲しい機能でしょうね」
🔗 credentialの改良2つ
- PR: Support custom credentials templates by jonathanhefner · Pull Request #45544 · rails/rails
- PR: Generate
secret_key_base
for all new credentials by jonathanhefner · Pull Request #45543 · rails/rails
つっつきボイス:「2つのプルリクはどちらもcredential関連でした」
「1つ目は、credentialのカスタムテンプレートをアプリに置けるようになったんですね: 通常だとcredentialにはsecret_key_base
ぐらいしか含まれなかったと思うけど、そのアプリで使うcredential項目が他にもいろいろある場合はテンプレート化したい気持ちはわかる」「プルリクにもあるように、オープンソースのRailsアプリのセットアップがやりやすくなりそうですね」
このコミットは、アプリ内でカスタムcredentialテンプレートのサポートを追加する。credentialファイルが存在しない場合は
rails credentials:edit
でlib/templates/rails/credentials/credentials.yml.ttでcredentialファイルの生成を試みてからデフォルトのテンプレートにフォールバックする。
これにより、たとえばオープンソースのRailsアプリ(リポジトリにcredentialファイルが含まれていない)にcredentialのテンプレートを含められるようになり、そのアプリをインストールするユーザーがcredentials:edit
を実行するとカスタムの記入済みcredentialファイルを得られるようになる。
#45544より
「2つ目はそれに関連して、credentialを生成するときにsecret_key_base
を常に含めるようにしたようですね」
現在は
CredentialsGenerator
によってconfig/credentials.yml.encが生成されるときに利便性のためにsecret_key_base
が含まれる。しかしconfig/credentials/#{environment}.yml.encは別のジェネレータ(EncryptedFileGenerator
)が生成するのでsecret_key_base
が含まれなかった。
このコミットはCredentialsGenerator
をよりジェネレータ的に改修してrails credentials:edit
でconfig/credentials.yml.encとconfig/credentials/#{environment}.yml.encを両方生成するようにすることで、常にどちらにもsecret_key_base
が含まれるようになる。
#45543より
🔗 Active Modelのパターンマッチングのマージがいったん取り消された
- PR: Revert "Provide pattern matching for ActiveModel" by gmcgibbon · Pull Request #45553 · rails/rails
つっつきボイス:「今週のRailsのChangelogをチェックしていて、珍しく削除されている部分があったので見てみると、みんな大好きActive Modelのパターンマッチング(ウォッチ20220516)がいったん取り消されていました」「ありゃ〜残念」「パターンマッチングの書き方はあまりに便利なので、一度リリースされたら相当乱用されそうではある」「言われてみれば、一度使われてしまったら機能の修正が必要なときに大変そう」
「パターンマッチを使うときれいに書けるケースが多いと思うので、きっと広く使われるでしょうね」「強力な機能だけに慎重にやるのがいいのかも」「パターンマッチングでやんちゃされる前に議論を尽くしておくのはいい方向: 英断だと思います👍」
「#45553のコメントを見ると戻り値周りなんかが懸念されているらしい↓」「150%って凄い」
このあたりを考えるうえでのコンテキストを@kddnewtonに提供しておく。
このAPIを正しいものにするチャンスは1度きりしかなく、ひとたび使えるようになったら変更しようがない。非推奨サイクルで戻り値を変更することもできないし、Ruby組み込み機能なのでリネームや置き換えサイクルも適用できない。
そういうわけで、特定の振る舞いを固める前に150%正しい値を返せるようにしておく必要がある。
自分は、この機能で期待される動作についてはまだコミュニティで共通認識を得られる段階ではないと思うし、私たちがこの機能を選択することでこの感情を定義できる立場にいるわけでもないと思う。率直に言えば、私たちの立場を利用してRubyの新しい言語機能を成功させたい気持ちはあるものの、一度決定したAPIを変えられないことを考慮すると、早期に飛びつくのはデメリットが大きいと思う。
具体的には、この機能で_read_attribute
や#[]
などを、あるいはsend
を使うべきなのだろうか?モデルのアクセサをオーバーライドする機能は、ドキュメント化されているpublic APIに含まれている。オーバーライドを尊重する実装とオーバーライドをバイパスする実装のどちらもあり、それだけで検討が難しくなるが、#45553で提案されている属性と関連付けのミックスとマッチは、いずれにしろ成り立たなさそうに思える。
https://github.com/rails/rails/pull/45553#issuecomment-1179568855(by matthewd)より
「別プルリクの#45070(現在オープン)にあるこのコードみたいなことをやりたくなるんでしょうね↓: Active ModelでパターンマッチできるようになればActive Recordでもパターンマッチできるように実装されるでしょうけど、通常のattributes APIであれば特に問題のないようなコードでも、Active Recordとパターンマッチが結合したときに何か面倒が起きそうな予感はする」「たしかに」
# #45070より
class Post < ActiveRecord::Base
has_many :comments
end
class Comment < ActiveRecord::Base
belongs_to :post
end
post = Post.new(title: "Welcome!", comments: [Comment.new(body: "Thanks!")])
post => { title: "Welcome!", comments: [Comment[body:]] }
body # => "Thanks!"
「ところで今回パターンマッチがrevertされたのは、そろそろRails 7.1のリリースを検討し始めているからかも」「今のところ7-1-stableブランチやv7.1.0タグは見当たりませんけど、検討していそうですね」
以下の翻訳記事にも、マージがいったん取り消された件を追記しました↓。
🔗Rails
🔗 rails-template-inspector: ブラウザ画面クリックでRailsソースコードを直接開ける
何これすごい https://t.co/cD4KhuRsGG
— すろっくさん (@srockstyle) July 11, 2022
つっつきボイス:「ぼくのツイートだ😆」「なるほど、RailsのビューにJavaScriptコードを追加する形でやっているんですね↓」「これは便利そう」「便利ですよ〜」
<!DOCTYPE html>
<html>
<head>
<!-- ... -->
</head>
<body>
<%= yield %>
<% if Rails.env.development? %>
<script type="module" src="https://cdn.skypack.dev/@aki77/rails-template-inspector@^0.3.0"></script>
<rails-inspector url-prefix="vscode://file" root="<%= Rails.root %>" combo-key="command-shift-v"></rails-inspector>
<% end %>
</body>
</html>
「最近Storybookなどを仕事でやむを得ず触っているんですけど、今どきのフロントエンドはこういう風に画面をクリックしてソースコードを開くみたいな機能がとても充実していることを実感しましたね」「お〜」「こういう便利機能がもっとRailsにあっていいと思う👍」
参考: Storybook: UI component explorer for frontend developers
「なお、上の記事をjnchitoさんが頑張って英語版も出したそうです↓」「英語圏でも広く使われるようになったらさらに改良が進みそう」
I translated the original post into English. rails-template-inspector might help Rails developers in the world!
"Introducing rails-template-inspector: Open your view files by clicking browser elements" #DEVCommunity https://t.co/xK4zUYnBb4
— Junichi Ito (伊藤淳一) (@jnchito) July 12, 2022
🔗 Deserialization on Rails
つっつきボイス:「お〜、Railsのデシリアライズという切り口でセキュリティ上の注意点などをまとめた記事ですね: 目の付け所がうまい」「みっちり書かれていて凄いですね」「デシリアライズは気をつけたい点が多いので、読んでおきたい記事👍」
この記事は、先週のRailsセキュリティ修正について調べていて見つけました↓。
🔗 「Hotwireかんたん入門」
「Hotwireかんたん入門」https://t.co/ENvX1uIdpu
Hotwireについてまだ詳しく知らない方向けに、概要をわかりやすく説明します。#万葉note #Hotwire
— 株式会社万葉 (@everyleaf) July 12, 2022
つっつきボイス:「万葉さんがHotwire宣言したとおりにHotwireの入門記事を書いてくれた🎉」
🔗 スライド『クックパッドマートの失敗したデータ設計 Before / After 大放出』
つっつきボイス:「これはいいスライド👍」
「営業日と定休日の扱い↓あたりは経験者がいれば避けられた可能性はあるかもしれないけど、よく起きる問題なんですよね」「そうそう、定休日はまず規則的になってくれない」「おおむね規則性はあっても、きっと例外的なことが起きる」
🔗 その他Rails
[Explore Companies that use Ruby around the world | Top Ruby Companies](https://t.co/wsBKivtRhT) Rubyの経済効果スゴイ pic.twitter.com/f3UH0Z1Q7m
— _ko1 (@_ko1) July 14, 2022
つっつきボイス:「Rubyの経済効果、1位はAirbnb、2位はShopifyか」「言われてみればAirbnbはRubyだった」
イベントページ上は定員オーバーですが、増枠の予定があるみたいです。「満席か〜」って諦めてた人もまだチャンスがあるかも?
てか、オンラインイベントとはいえ現時点の申込みが500人オーバーって!めちゃくちゃプレッシャー感じてきました😅 https://t.co/UJ6m5EriSk— Junichi Ito (伊藤淳一) (@jnchito) July 14, 2022
「jnchitoさんが7/27(水)に開催する"リーダブルなテストコードについて考えよう"の参加者が既に500人超えです」「リーダブルなテストコードのように答えが1つではない問題はプロジェクトや会社によっても変わってきますけど、こんなふうにどんどん発表してもらって答えが煮詰められていくといいですね👍」
以下はつっつき後に見つけたツイートです↓。
スライド、だいたいできました。話したいことはたくさんあるけど、補足説明的なネタはなるべく省いて内容をギュッと濃縮した結果、ギリ15分ぐらい?みなさんお楽しみに!!
リーダブルなテストコードについて考えよう - connpass https://t.co/T5ZnDE10wq
— Junichi Ito (伊藤淳一) (@jnchito) July 18, 2022
🔗Ruby
🔗 Ruby技術者認定試験がRuby 3.xへの対応を発表
正式に発表になりました。Ruby3.x対応は10月3日から。
Link: Ruby技術者認定試験改訂のお知らせ
https://t.co/amz0zNWEbp— Yukihiro Matz (@yukihiro_matz) July 12, 2022
つっつきボイス:「そうそう、Ruby技術者認定試験が10月からついに改訂されますね🎉」「3.xに対応するなら勉強がてら受けてみようかな〜」「それならパターンマッチ覚えないと」「う😅」
「2.xの旧試験は2022/09/30に配信終了するんですって」「お別れの季節ですね👋」
🔗 Rubyの定数探索スタイル(Ruby Weeklyより)
つっつきボイス:「このどらちのスタイルにするか問題、ありますよね↓」「自分はafter派かも」「自分は途中に何も置かないならbefore派かな: 定数のネストが必要以上に深いのは好きじゃないので」
# 同記事より
# Before
class Product::Operation::Create
# ...
end
# After
module Product
module Operation
class Create
# ...
end
end
end
🔗 その他Ruby
Rails Girls Japanでは、Rails Girlsイベントの参加者、オーガナイザー、スタッフの中からRubyKaigiへの参加支援を行っています。
RubyKaigi 2022 参加支援の募集を開始しました!
締め切りは、2022/07/31 10:00までとなります。詳細は、下記サイトをご確認ください。🦐🦐🦐https://t.co/fBE3SfHgmA
— Rails Girls Japan (@RailsGirlsJapan) July 11, 2022
つっつきボイス:「Rails Girls Japanが今年もRubyKaigiへの参加を支援してくれているんですね」「ありがたい🙏」
今週は以上です。
バックナンバー(2022年度第3四半期)
週刊Railsウォッチ: RubyのGCが高速化、RuboCopのストレスを減らす4つの方法、Defensive CSSほか(20220712後編)
- 20220711前編 AR::RelationにCTEを利用できるwithメソッドが追加、Propshaftアップグレードガイドほか
- 20220705後編 6月のRubyコア動向、Stack Overflowアンケート結果ほか
- 20220704前編 マイグレーションをStrategyパターンで拡張可能にほか
今週の主なニュースソース
ソースの表記されていない項目は独自ルート(TwitterやはてブやRSSやruby-jp SlackやRedditなど)です。
週刊Railsウォッチについて
TechRachoではRubyやRailsなどの最新情報記事を平日に公開しています。TechRacho記事をいち早くお読みになりたい方はTwitterにて@techrachoのフォローをお願いします。また、タグやカテゴリごとにRSSフィードを購読することもできます(例:週刊Railsウォッチタグ)
今週は月曜日が祝日のため短縮版でお送りします。