- Ruby / Rails関連
週刊Railsウォッチ: Active Recordのattribute更新系メソッド比較リスト、Railsリファクタリングガイドほか(20220425前編)
こんにちは、hachi8833です。RuboCopが10歳になりました🎉
- 元記事: RuboCop Turns 10 | Meta Redux(Hacklinesより)
- アニバーサリー版: Release RuboCop 1.28 (a.k.a. The 10th Anniversary Edition) · rubocop/rubocop
🔗Rails: 先週の改修(Rails公式ニュースより)
今回の改修はかなり件数が少なかったので範囲を広げて見繕いました。小粒揃いです。
🔗 config.enable_reloading
を追加
config.cache_classes
を設定するたびに、この設定が正しいかどうかいちいち考え込んでしまう。無駄に考えさせる設定。
一方、config.reload = true
という記述を見れば、アプリケーションがリロードされるであろうことは考えるまでもなく一瞬でわかる。
人は「リロードは有効なのか無効なのか?」と機能に沿って考えるものだ。しかもRubyプログラムでは、クラスが「キャッシュされる」という言い方はしない。必要なのは、カスタムのリロード機能を表す語だ。
このリファクタリングについて解説する。
unless config.cache_classes && config.eager_load ⇔ (unlessは「if !」と同じ)
if !(config.cache_classes && config.eager_load) ⇔ (!(A && B) は !A || !Bと同じ)
if !config.cache_classes || !config.eager_load ⇔ (!config.cache_classes は config.reload と同じ)
if config.reload || !config.eager_load
config.cache_classes
はずっと前からあるので、今さら非推奨化するのは実用的ではないと思う。それよりも、新しいアプリを生成するとこの新しいconfig.reload
を使うようにし、これが現在の推奨フラグであることをドキュメントに書いて推奨しつつ、config.cache_classes
を非推奨化せずに引き続きサポートすることを提案したい。Rails 8になったら非推奨サイクルを開始してもいいかもしれないが、この点は議論の余地がある。
同PRより
つっつきボイス:「config.cache_classes
というコンフィグの意味がわかりにくいので、config.enable_reloading
というコンフィグを追加したらしい」「!config.cache_classes
と同等なんですね」「たしかにconfig.enable_reloading
の方がいい名前👍」
より直感的な名前にするために、
config.enable_reloading
(!config.cache_classes
と同じ)が定義された。今後はconfig.enable_reloading
とconfig.reloading_enabled?
が推奨されるが、後方互換性のためconfig.cache_classes
も引き続きサポートする。
Xavier Noria
同Changelogより
🔗 config.enable_dependency_loading
が非推奨化
config.enable_dependency_loading
を非推奨化。このフラグは(Zeitwerk以前の)classic
オートローダーの制約を正すためのものだが現在は無効。この非推奨化メッセージを修正するために、この参照を削除して欲しい。
Xavier Noria
同PRより
# railties/lib/rails/application/configuration.rb#L300
+ ENABLE_DEPENDENCY_LOADING_WARNING = <<~MSG
+ This flag addressed a limitation of the `classic` autoloader and has no effect nowadays.
+ To fix this deprecation, please just delete the reference.
+ MSG
+ private_constant :ENABLE_DEPENDENCY_LOADING_WARNING
+
+ def enable_dependency_loading
+ ActiveSupport::Deprecation.warn(ENABLE_DEPENDENCY_LOADING_WARNING)
+ @enable_dependency_loading
+ end
+
+ def enable_dependency_loading=(value)
+ ActiveSupport::Deprecation.warn(ENABLE_DEPENDENCY_LOADING_WARNING)
+ @enable_dependency_loading = value
+ end
つっつきボイス:「上に続いてZeitwerkの@fxnさんによるコミットです」「これもRailsのオートロード関連ですね」
参考: Classic から Zeitwerk への移行 - Railsガイド
🔗 ドキュメント: host_configuration
をhost_authorization
に修正
# guides/source/configuring.md#L509
You can exclude certain requests from Host Authorization checks by setting
-`config.host_configuration.exclude`:
+`config.host_authorization.exclude`:
# Exclude requests for the /healthcheck/ path from host checking
-Rails.application.config.host_configuration = {
+Rails.application.config.host_authorization = {
exclude: ->(request) { request.path =~ /healthcheck/ }
}
When a request comes to an unauthorized host, a default Rack application
will run and respond with `403 Forbidden`. This can be customized by setting
-`config.host_configuration.response_app`. For example:
+`config.host_authorization.response_app`. For example:
-Rails.application.config.host_configuration = {
+Rails.application.config.host_authorization = {
response_app: -> env do
[400, { "Content-Type" => "text/plain" }, ["Bad Request"]]
end
}
つっつきボイス:「英語版のRails Guidesの設定パラメータが間違っていたのが修正されたのか」「host_configuration
というコンフィグは見たことないですね」「早速Railsガイドにも修正プルリクを投げました↓」
参考: configuring.mdを修正 by hachi8833 · Pull Request #1233 · yasslab/railsguides.jp -- マージ済み
🔗 ドキュメント: primary_key
に関する不要な記述を削除
- PR: Remove unnecessary note for primary_key in the guide by pocke · Pull Request #44911 · rails/rails
# guides/source/active_record_migrations.md#L354
By default, `create_table` will create a primary key called `id`. You can change
-the name of the primary key with the `:primary_key` option (don't forget to
-update the corresponding model) or, if you don't want a primary key at all, you
-can pass the option `id: false`. If you need to pass database specific options
-you can place an SQL fragment in the `:options` option. For example:
+the name of the primary key with the `:primary_key` option or, if you don't
+want a primary key at all, you can pass the option `id: false`. If you need to
+pass database specific options you can place an SQL fragment in the `:options`
+option. For example:
つっつきボイス:「これもドキュメント修正で、@pockeさんが英語と合わせて日本語↓も修正してくださいました🙇」「ありがたい🙏」
🔗 issue: マルチプルDBでnon-primary DBのマイグレーションのステータスがdownでもActiveRecord::PendingMigrationError
が発生しない
再現手順
マルチプルDBでprimary以外のDBマイグレーションのステータスがdownでもActiveRecord::PendingMigrationError
が発生しない。
# config/database.yml
development:
backbone:
<<: *default
database: backbone_development
library:
<<: *default
database: library_development
migrations_paths: db/library_migrate
以下を実行してhttp://localhost:3000/users か http://localhost:3000/books を開く。
bin/rails g scaffold User title
bin/rails g scaffold Book title --db library
bin/rails db:create db:migrate
bin/rails s
# 別のコンソール
bin/rails g migration add_published_at_to_books published_at:datetime --db library
期待される動作
add_published_at_to_books
マイグレーションのステータスがdownなのでActiveRecord::PendingMigrationError
が発生する。
$ bin/rails db:migrate:status
database: backbone_development
Status Migration ID Migration Name
--------------------------------------------------
up 20191022014341 Create users
database: library_development
Status Migration ID Migration Name
--------------------------------------------------
up 20191022023152 Create books
down 20191022023755 Add published at to books
$ rails db:abort_if_pending_migrations
You have 1 pending migration:
20191022023755 AddPublishedAtToBooks
Run `rails db:migrate` to update your database then try again.
実際の動作
エラーが発生しない。
システム構成
Rails 6.0.0
Ruby 2.6.5
同issueより
つっつきボイス:「あ〜、たしかにnon-primaryのDBでマイグレーションがpendingなのにPendingMigrationError
エラーが出ないのはつらい」「まだ修正プルリクは出ていないみたい」「ちなみに以下のマイルストーンで見つけたissueです↓」
参考: 7.0.2 Milestone
「このissueは発生の条件が少し厳しくて、マルチプルデータベースでprimaryとnon-primaryの両方を同じRailsリポジトリで管理しているときに発生するっぽい」「なるほど」「マルチプルデータベースを本格的に使っていないとなかなか踏まないバグかも」
参考: Active Record で複数のデータベース利用 - Railsガイド
「DBのロールバックって普段あまりやらないかも」「マイグレーションがどのぐらいちゃんと書かれているか調べないとロールバックするのは怖いです」「ロールバックする可能性がありそうな箇所なら時間をかけてでも検証しますけどね」
🔗Rails
🔗 GitHubが行ったRailsのパフォーマンス改善(Ruby Weeklyより)
Latest from the GitHub Engineering Blog... learn how we made 50ms+ savings in response times by moving telemetry code out of the critical path. https://t.co/LfZaexljT3
— GitHub (@github) April 11, 2022
つっつきボイス:「へ〜、rack.after_reply
なんてのがあるのね」「rack.after_reply
は元々Pumaの拡張で、リクエストが完了したあとでコールバックを呼び出す機能らしい」
「GitHubはWebサーバーにPumaではなくUnicornを使ってると書かれてる」「そういえばGitHubはUnicornをマルチスレッド・マルチプロセスで使っているという話を昔見たような気がしますけど、今もUnicornを使ってるんですね」「Pumaのrack.after_reply
を使いたいけどUnicornには実装されていなかったので、Unicornにパッチを投げたそうです」「その気持わかる」「GitHubは技術ブログを着実に更新していていいですね👍」
# 同記事より(改行を削減)
class AfterResponse
def initialize(env)
env[“rack.after_reply”] ||= []
env[“rack.after_reply”] << -> do
self.call
end
@to_perform = []
end
# Calls each callable defined via #perform
def call
@to_perform.each do |block|
begin
block.call(self)
rescue Object => e
Rails.logger.error(e)
end
end
end
# Adds given block to the array of callables that will
# be called after the user has received the response.
def perform(name, &block)
@to_perform << block
end
end
🔗 Rails 7版: Active Recordのさまざまなattribute更新系メソッドの比較リスト(Ruby Weeklyより)
つっつきボイス:「お、定番のActive Record attribute更新系メソッドの挙動比較表」「コールバックを呼ぶものや呼ばないものかあったりしますね」
「upsert_all
はタイムスタンプがセットされないみたいな点は注意が必要」「そうそう、コールバックとタイムスタンプ更新は連動するので、コールバックが呼ばれないメソッドではActiveRecord::Timestamp
が呼ばれない」「たしかに」「この表を丸覚えするよりは、挙動を理解する方が覚えやすいでしょうね: いい情報👍」
「Rails 6以前向けの記事リンクも記載されていました↓」「こういう記事はこまめに更新しないといけないのが大変」「たまに記事が間違ってたり古かったりするとハマりますよね」
- 元記事: Different Ways to Set Attributes in ActiveRecord (Rails 6)
- 元記事: Different Ways to Set Attributes in ActiveRecord (Rails 5)
- 元記事: Different Ways to Set Attributes in ActiveRecord (Rails 4) -- 別サイト記事
- 元記事: Different Ways to Set Attributes in ActiveRecord (Rails 3) -- 別サイト記事
「assign_attributes
はattributes=
のエイリアスなのね」
参考: ActiveModel::AttributeAssignment
「そういえばQiitaにも以前似たような記事があった気がします」「そうそう、これですね↓」
参考: ActiveRecord の attribute 更新方法まとめ - Qiita
そういえばupdate_attributes
とupdate_attributes!
はRails 6.1でそれぞれupdate
とupdate!
に移行したのでした↓。
参考: Deprecate update_attributes
and update_attributes!
by elebow · Pull Request #31998 · rails/rails
参考: Rails 6 deprecates update_attributes and update_attributes! | Saeloun Blog
🔗 Railsリファクタリングガイド
Ruby - Zenn 脱Rails初心者のためのリファクタリングガイド https://t.co/BzhyESAF4A
— tomi rss (@tomirss) April 17, 2022
つっつきボイス:「リファクタリング記事は定番ですね」「慣れていない人は読んでおくといいと思います👍」
「『リファクタリング時にspecも一緒に作る』、その通り!」「そうそう」「何をするにもspecを揃えてから作業すべき」
「テストがないコードは、そもそもテストしにくかったりできなかったりすることもよくある」「その場合は、まずテストを書いて正しく動くようにコードを改修するところから始めないといけない」「ああ、それつらいヤツです」
「この記事を書いた方はフリーランスのエンジニアなのか」「フリーランスの場合、プロパーとしてチームに常駐する人とはアプローチが異なってくる面もありますね: プロジェクトのメンバーや特性などでも変わってきますが」
「フリーランスの場合、最初の1か月である程度の成果を出すことを求められることもよくあると思いますけど、足りないテストを増やすというタスクは、そういうときに少しずつでも成果を出せるのが嬉しい」「少なくともテストを増やすことで既存のコードが悪くなる心配はないですよね」「既存プロジェクトへの途中参加案件で直近取り組める手頃なサイズのタスクが無い場合に、進捗を刻んで開発の流れに乗っていくためにまずテストを書くことを提案することもあります」
🔗 環境変数でテストが壊れやすくなる問題(Ruby Weeklyより)
つっつきボイス:「環境変数で挙動が変わる部分のテストを書いたら思わぬ挙動をしたという記事だそうです」「近年は環境変数でアプリの挙動を変えることが増えていますけど、どこまで環境変数でやるのかは悩ましいところ」「そうそう」
「記事では、問題を起こす環境変数をメソッドで渡すように変えることで、環境変数によって挙動が変わらないようにしたんですね」
「たとえば外部APIに依存するアプリで、APIをスタブモードに切り替えるのに環境変数を使ったりしますよね」「APIが有料だったり、顧客のプライベートなネットワークでしかアクセスできないような場合は、そうやって環境変数で切り替わるようにしないとローカルで開発できない」「ついこの間まさにそういうコードを書きました」「おそらくダミーのAPIサーバーを立ててそちらにアクセスする方がいいんでしょうけど、工数が増える」「そうなんですよ」「環境変数で挙動が変わる部分はテストを書いておきたい」
「ちなみに今やっているプロジェクトの環境変数は、開発環境向けのものなので数は抑えているんですが、それでもかなり多い」「そうなりますよね」
追いかけボイス: 「RSpecのbefore do
の中でENV['hoge'] = 'piyo'
のようにENVを設定してしまうと、ENVの値はafter
で元の値に復元されたりunsetされたりしないので、テストの実行順序が変わると落ちるようになってしまうことがあります: これを回避するにはbefore
で環境変数を一時退避してafter
で戻すなどの対応が必要になりますが、マルチスレッドが絡むとさらにおかしくなることもあります」
🔗 Webpackerからimportmap-railsへの移行
つっつきボイス:「Webpackerからimportmap-rails、大変そう」「疲れさまです」「ぼくも同じような記事書いたんでした↓」「こういう知見はありがたい👍」
「大規模なRailsアプリでimportmapを使っているところはまだ少なそう」「Webpackerはやめたけどwebpackは残しているところも多いんじゃないかな」「webpackを剥がすのは大変だと思います」
🔗 その他Rails
もうすぐリリースされる Ubuntu 22.04 には OpenSSL 3.0 しか存在しないので公式パッケージのみだと Ruby 3.1 以降しかビルドできません。 https://t.co/uz1toC051C
— Hiroshi SHIBATA (@hsbt) April 13, 2022
つっつきボイス:「古いRubyを使わないといけないときとかでつらいヤツだ」「compat_openssl的なものは出ないんだろうか」「そういうのがないとRuby以外の言語でも困りそうですし、公式でなくても誰か作ると思いたい」「自分たちがUbuntu 22.04にアップデートするのはもう少し先になると思いますが、あって欲しい」
参考: Ubuntu 22.04 その3 - OpenSSL 3.0への移行計画 - kledgeb
「Dockerを使うときにUbuntuのバージョン指定をlatest
にしている人は要注意ですね」「う、latest
普通に使っちゃってます😅」「latest
にするとアップデートで死ぬのでバッドプラクティスです、マジで」「そうでした、ちゃんとバージョン指定しなきゃ...」「この分だとしばらくの間あちこちで悲鳴が上がるかも」
参考: Ubuntu - Official Image | Docker Hub -- latest
は既に22.04になっています
前編は以上です。
バックナンバー(2022年度第2四半期)
週刊Railsウォッチ: RubyのGCコンパクション改修、jemalloc、ReDoSの自動検出修正ほか(20220419後編)
- 20220418前編 RailsConf 2022が5月17〜19日開催、認可機能解説記事ほか
- 20220412後編 HashieでRubyのハッシュを強化、最近のRubyコア解説記事ほ
- 20220411前編 Turbo Railsチュートリアル、Active Recordの「Leaky Abstraction」を削減ほか
- 20220406後編 RBS関連記事、Ruby formatterプロジェクト、Google Cloud Runほか
- 20220404前編 Ruby 3.2.0 Preview 1リリース、Rails向けDocker環境ジェネレータ、scientist gemほか
今週の主なニュースソース
ソースの表記されていない項目は独自ルート(TwitterやはてブやRSSやruby-jp SlackやRedditなど)です。
週刊Railsウォッチについて
TechRachoではRubyやRailsなどの最新情報記事を平日に公開しています。TechRacho記事をいち早くお読みになりたい方はTwitterにて@techrachoのフォローをお願いします。また、タグやカテゴリごとにRSSフィードを購読することもできます(例:週刊Railsウォッチタグ)