Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails関連

週刊Railsウォッチ: RailsチュートリアルがRails 7対応版をリリース、ViewComponentで使えるLookbookほか(20221115前編)

こんにちは、hachi8833です。Ruby 3.2.0 Preview 3がリリースされました。ReDoS対策も追加されています。

週刊Railsウォッチについて

  • 各記事冒頭には🔗でパーマリンクを置いてあります: 社内やTwitterでの議論などにどうぞ
  • 「つっつきボイス」はRailsウォッチ公開前ドラフトを(鍋のように)社内有志でつっついたときの会話の再構成です👄
  • お気づきの点がありましたら@hachi8833までメンションをいただければ確認・対応いたします🙏

TechRachoではRubyやRailsなどの最新情報記事を平日に公開しています。TechRacho記事をいち早くお読みになりたい方はTwitterにて@techrachoのフォローをお願いします。また、タグやカテゴリごとにRSSフィードを購読することもできます(例:週刊Railsウォッチタグ)

🔗Rails: 先週の改修(Rails公式ニュースより)

🔗 check_box_tagradio_button_tagchecked:キーワード引数が利用可能になった

このAPIをFormHelperの類似メソッドにより近づける。以下のように、checkedを位置引数(通常の引数)でもキーワード引数でも指定できる。

= check_box_tag "admin", "1", false
= check_box_tag "admin", "1", checked: false
= radio_button_tag 'favorite_color', 'maroon', false
= radio_button_tag 'favorite_color', 'maroon', checked: false

Alex Ghiculescu
同Changelogより

参考: Rails API check_box_tag -- ActionView::Helpers::FormTagHelper
参考: Rails API radio_button_tag -- ActionView::Helpers::FormTagHelper


つっつきボイス:「この改修はありがたい👍」「位置引数でfalseを単体で渡すよりキーワード引数でchecked: falseと書ける方が全然わかりやすいですよね」「つい最近もこのパラメータを渡し間違えました」

🔗 DBアダプタの#executeメソッドにallow_retryオプションを渡せるようになった

アダプタの#executeメソッドにallow_retryオプションを指定可能になった。trueに設定すると、コネクション関連のエラーが発生したときに、データベース設定のconnection_retries値に達するまでSQLステートメントがリトライされる。
Adrianna Chang
同Changelogより


動機と背景
リトライの振る舞いをオプトインするには#executeメソッド全体の再実装が必要。もしその代わりに#executeallow_retryオプションを受け取れる形にすれば、アプリが#executeにパッチを当ててsuperをallow_retry: trueで呼び出せるようになる。
追加情報
リトライをすべてのクエリで有効にするコンフィグを追加することを検討したが、ShopifyやGitHubのアプリケーションではさほど深く考えずにすべてのクエリをリトライするようになったという歴史はあるものの、これはRailsに導入する設定としてはリスクが大きすぎると判断した。#executeを変更するのは妥協点としてはよさそうに思える。アプリがリトライの振る舞いを得るには引き続き#executeに意図的にパッチを当てなければならないが、しかしこの方法なら私たちのパッチがメソッド全体を再実装せずに済むので、パッチがもろくなりにくい。
同PRより


つっつきボイス:「SQLのリトライをallow_retryオプションで指定できるようになったのはうまい方法かも」「デフォルトではオフなんですね」「Multi-AZ failoverで接続先のRDSインスタンスが切り替わるときなどは最初に一度失敗してから再接続することになるけど、そういうときのコネクション再接続も含めてリトライできるなら純粋に嬉しい👍」「この機能が必要な理由がわかりますね」

参考: Amazon RDS(マネージドリレーショナルデータベース)| AWS

🔗 リクエストのparameter_filterがpublicメソッドになった

これは、リクエストと同じパラメータフィルタに基づいて独自のハッシュをフィルタしたい場合に便利。
例: 例外の通知やログ出力でヘッダーやカスタム値をリクエストから読み込む必要があり、なおかつそれらをフィルタしたい場合。
同PRより


つっつきボイス:「Railsのログで値を出力しないときに使うconfig.filter_parametersメソッドを公式APIとして使えるようにしたんですね」「ログでFILTEREDにするときのヤツですね」「例外通知やログをカスタム処理するときにこのメソッドで処理したりできる、なるほど」

# actionpack/test/dispatch/request_test.rb#1289
  test "parameter_filter returns the same instance of ActiveSupport::ParameterFilter" do
    request = stub_request(
      "action_dispatch.parameter_filter" => [:secret]
    )

    filter = request.parameter_filter

    assert_kind_of ActiveSupport::ParameterFilter, filter
    assert_equal({ "secret" => "[FILTERED]", "something" => "bar" }, filter.filter("secret" => "foo", "something" => "bar"))
    assert_same filter, request.parameter_filter
  end

参考: § 3.1.21 config.filter_parameters -- Rails アプリケーションを設定する - Railsガイド

🔗 schema.rbの外部キーやCHECK制約にvalidate: falseを追加するようになった(PostgreSQL)

従来は、外部キーやCHECK制約を追加するときにvalidate: falseを指定してもschema.rbに記録していなかった。そのため、このスキーマからデータベースを復元すると外部キーやCHECK制約の検証ができなくなる可能性があった。
Tommy Graves
同Changelogより


動機/背景
現在は、validate: falseは外部キーやCHECK制約(validate機能をサポートするデータベースの場合)についてschema.rbでトラッキングできない。つまり、このスキーマをリストアすると、すべての外部キーやCHECK制約が(それまで一度も検証されたことがなくても)常に検証されることになる。
詳細
このプルリクは、外部キーやCHECK制約が正しくない場合にschema.rbのadd_foreign_keyadd_foreign_keyvalidate: falseを追加する。これらが正しい場合はvalidate: trueにしない(これがデフォルトなので)。
同PRより


つっつきボイス:「このプルリク自体は、従来マイグレーションで外部キーやCHECK制約にvalidate: falseを指定してもschema.rbに反映されていなかったのを反映するようにしたということですね」「APIドキュメントを見ると、add_foreign_keyadd_check_constraintvalidate:オプションはPostgreSQLのみの機能だそうです↓」「ホントだ」

参考: Rails API add_foreign_key -- ActiveRecord::ConnectionAdapters::SchemaStatements
参考: Rails API add_check_constraint -- ActiveRecord::ConnectionAdapters::SchemaStatements


「ところでこのvalidateオプションって何だろう?」「制約そのものと別に検証のオプションがあるってよくわからない」「マイグレーションで使う機能なのはたしかだけど」(しばし調べる)「どうやらこのVALIDATE CONSTRAINTのようですね↓」

参考: PostgreSQL 14ドキュメント ALTER DOMAIN

VALIDATE CONSTRAINT
この構文は、以前にNOT VALIDとして追加された制約を検証します。 つまり、そのドメイン型のテーブル列の値すべてが指定された制約を満たすかどうかを検証します。
www.postgresql.jpより

「ALTERを使って後付けで制約を加えるときに、今入っている既存のデータに対してバリデーションするかどうかを指定するのがVALIDATE CONSTRAINTということみたい」「あ〜、なるほど」「制約は加えたいけど既存の古いデータは事情があって変えたくないのでバリデーションしたくないことってありそうですね」「これは知らなかった」「デフォルトはtrueなんですね」「ぽすぐれは新しい機能の発見がいつもありますね」

🔗 ActiveRecord::Baseオブジェクトのクエリでカラムのリストを指定可能になった

ActiveRecord::Baseオブジェクトの更新、削除、再読み込み時のSQLクエリビルドに利用できるカラムリストを指定可能になった。

class Developer < ActiveRecord::Base
  query_constraints :company_id, :id
end
developer = Developer.first.update(name: "Bob")
# => UPDATE "developers" SET "name" = 'Bob' WHERE "developers"."company_id" = 1 AND "developers"."id" = 1

Nikita Vasilevsky
同Changelogより


つっつきボイス:「"composite primary keys"とあるので、複合主キーに関連している機能追加らしい」「プルリクにも、今後Railsで複合主キーをサポートするための準備の一環とも書かれていますね」「お〜期待しちゃいます」「query_constraintsはもっといい名前にできそうな気はするけど今後に期待したい」

参考: 複合主キーとは - 意味をわかりやすく - IT用語辞典 e-Words

🔗 夏時間が終わるときのTime#change#advanceを修正

従来は、Time#changeTime#advanceが夏時間(DST: Daylight Saving Time)の最後のストレッチの中で時刻を構築すると、ローカル時刻では常にDSTでないオフセットが選択されていた。

# DSTはUS/Easternで2021-11-07 2:00:00 AMの直前に終了する
ENV["TZ"] = "US/Eastern"

time = Time.local(2021, 11, 07, 00, 59, 59) + 1
# => 2021-11-07 01:00:00 -0400
time.change(day: 07)
# => 2021-11-07 01:00:00 -0500
time.advance(seconds: 0)
# => 2021-11-07 01:00:00 -0500

time = Time.local(2021, 11, 06, 01, 00, 00)
# => 2021-11-06 01:00:00 -0400
time.change(day: 07)
# => 2021-11-07 01:00:00 -0500
time.advance(days: 1)
# => 2021-11-07 01:00:00 -0500

そしてTimeZoneオブジェクトの時刻に対しては常にDSTオフセットが選択されてしまうことになる。

Time.zone = "US/Eastern"
time = Time.new(2021, 11, 07, 02, 00, 00, Time.zone) - 3600

# => 2021-11-07 01:00:00 -0500
time.change(day: 07)
# => 2021-11-07 01:00:00 -0400
time.advance(seconds: 0)
# => 2021-11-07 01:00:00 -0400

time = Time.new(2021, 11, 8, 01, 00, 00, Time.zone)
# => 2021-11-08 01:00:00 -0500
time.change(day: 07)
# => 2021-11-07 01:00:00 -0400
time.advance(days: -1)
# => 2021-11-07 01:00:00 -0400

修正後は、Time#changeTime#advanceは可能な場合に元の時刻のオフセットと一致するオフセットを選択するようになる。

ENV["TZ"] = "US/Eastern"

time = Time.local(2021, 11, 07, 00, 59, 59) + 1
# => 2021-11-07 01:00:00 -0400
time.change(day: 07)
# => 2021-11-07 01:00:00 -0400
time.advance(seconds: 0)
# => 2021-11-07 01:00:00 -0400

time = Time.local(2021, 11, 06, 01, 00, 00)
# => 2021-11-06 01:00:00 -0400
time.change(day: 07)
# => 2021-11-07 01:00:00 -0400
time.advance(days: 1)
# => 2021-11-07 01:00:00 -0400

Time.zone = "US/Eastern"

time = Time.new(2021, 11, 07, 02, 00, 00, Time.zone) - 3600
# => 2021-11-07 01:00:00 -0500
time.change(day: 07)
# => 2021-11-07 01:00:00 -0500
time.advance(seconds: 0)
# => 2021-11-07 01:00:00 -0500

time = Time.new(2021, 11, 8, 01, 00, 00, Time.zone)
# => 2021-11-08 01:00:00 -0500
time.change(day: 07)
# => 2021-11-07 01:00:00 -0500
time.advance(days: -1)
# => 2021-11-07 01:00:00 -0500

Kevin Hall, Takayoshi Nishida, and Jonathan Hefner
同Changelogより


つっつきボイス:「こちらはバグ修正です」「夏時間の境界条件で起きるバグはつらそう...」「そういえば米国で夏時間やめようみたいな話が持ち上がってましたよね↓」

参考: 「夏時間を恒久化する法案」が全会一致でアメリカ連邦議会上院を通過 - GIGAZINE

🔗Rails

🔗 RailsチュートリアルがRails 7対応版をリリース

つっつきボイス:「7対応版ついに出た🎉」「先行リリースのWeb版に加えて電子書籍版(EPUB/PDF)もリリースされたので、今後は解説動画もリリースされるそうです」「こういう教科書を日本語で読めるのは貴重」「訳文の改定・改善もすごく進んでいます」「実際に教育や研修の現場で使いながら継続的に修正を加えていく手法は、多くの人に使われるほど改善がはかどるのが強いですよね」

参考: Ruby on Rails チュートリアル:プロダクト開発の0→1を学ぼう
参考: 電子書籍でプロダクト開発を学ぼう - Railsチュートリアル

「こちらは11/11(金)のRubyWorld Conferenceでも発表される予定です↓(注: イベントは終了しました)」「Railsチュートリアルを使った授業の受講者評点が学内最高スコア、すごい」

以下はRubyWorld Conference 2022 Day2の当該動画です(4:49:29)。

参考: YouTubeチャンネル RubyWorld Conference

🔗 Railsアプリをスケールさせる方法


つっつきボイス:「AppSignalの記事です」「キャッシュを始めとする定番の手法をまとめた感じですね」「スケールするときは記事のような順序で検討する感じでしょうか?」「キャッシュして効果が出るかどうかなどはリクエストによっても違ってくるので一概には言えないでしょうね」「なるほど」

🔗 Primer ViewComponents

primer/view_components - GitHub


つっつきボイス:「GitHubが作ったものだそうです」「以下の"Primer Design System"の一環としてViewComponent用のコンポーネント集をGitHubが作ったんですね」

参考: Primer Design System

「おそらくGitHub自身もRailsのViewComponentでこれを使っているんでしょうね: Design Systemはコンポーネントと相性がいいので実際に使っていそう」「GitHubなら自動生成してるかもしれませんね」「Design Systemとして整備されていてよさそう👍」

「ところでこのOcticonはGitHub独自の用語でしょうね↓」「GitHubが定義したアイコン集をこう呼んでいるんですね」

参考: Octicon | Primer ViewComponents

🔗 Lookbook: ViewComponent向けのストーリーブック

ViewComponent/lookbook - GitHub


つっつきボイス:「Evil MartiansのViewComponent記事で、Reactとかでよく使われているStorybook.js↓のRuby版として、GitHubが作ったこのLookbookが紹介されていました」「Railsエンジンの形になっていて、これでストーリーブックを見られるようになっているんですね: たしかにViewComponentを使うならストーリーブック的なものが欲しくなるし、ないと困る」

参考: Storybook: Frontend workshop for UI development
参考: ViewComponent in the Wild II: supercharging your components—Martian Chronicles, Evil Martians’ team blog

🔗 ストーリーブック

「ストーリーブックはここ10年ぐらいで登場した概念で、コンポーネントをそこで直接動かしてプレビューできるのが重要」「ふむふむ」「各コンポーネントがどんな外観でどんなふうに動作するかを、アプリ全体を動かさなくてもその場で確かめられるし、コード変更も即座に反映されるので、コンポーネント単位のデザイン修正などがとてもやりやすくなります」「なるほど!」

「たとえば会員登録や決済などのフローで、最後の画面デザインだけ修正しようとすると、表示するためにデータを用意するなどの準備や多くの操作が必要になりますけど、ストーリーブックならデータすら不要で即座に確認・修正できる」「これは欲しくなるヤツ」「ストーリーブックは一度体験するととてもいいものだとわかるので、Lookbookも期待できそう👍」「GitHubはLookbookをもっと宣伝してもいいと思います」

🔗 HerokuのEco DynoとPostgres Miniプラン


つっつきボイス:「jnchitoさんの記事です」「Eco DynoとPostgres Miniが低額で提供されるようになったんですね」「月5ドルならまあまあかな」「PostgreSQL Miniの"最大1万行"はかなりつらそう」「普通に使ってたら1万行なんてすぐですよね」「かといって外部DBに置くと遅くなるだろうし統合性も落ちるんですよね」

🔗 Sidekiq 7.0(Ruby Weeklyより)


つっつきボイス:「Sidekiq 7.0ではグラフ表示が改善されてきれいになったんですって↓」「ほほ〜前よりリッチになってますね」「見た目は大事」「JSONにシリアライズできないオブジェクトをジョブ登録時に引数として渡すとジョブの実行に失敗するので、perform_asyncにそういうものを渡すとエラーになるようになったとも書かれていますね」


同記事より


前編は以上です。

バックナンバー(2022年度第4四半期)

週刊Railsウォッチ: 書籍『Programming Ruby 3.2 (5th Edition)』、ReDoSチェックサイトほか(20221102後編)

ソースの表記されていない項目は独自ルート(TwitterやはてブやRSSやruby-jp SlackやRedditなど)です。

Rails公式ニュース

Ruby Weekly


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。