- Ruby / Rails関連
週刊Railsウォッチ: Devise 4.9のHotwire/Turbo統合に対応する、英国政府のViewComponentほか(20230314前編)
こんにちは、hachi8833です。Ruby 2.7のメンテナンス終了が近づいていますね。
🔗Rails: 先週の改修(Rails公式ニュースより)
🔗 複数ジョブを一度にエンキューするperform_all_later
が追加
複数のジョブやジョブの配列を以下のように渡すことで、コールバックを実行せずにジョブをバルクエンキューする機能が追加される。
ActiveJob.perform_all_later(MyJob.new("hello", 42), MyJob.new("world", 0)) user_jobs = User.pluck(:id).map { |id| UserJob.new(user_id: id) } ActiveJob.perform_all_later(user_jobs)
これにより、キューデータストアへのラウンドトリップ回数を大幅に削減できる。
新しいenqueue_all
メソッドを実装していないキューアダプタでは、個別にジョブをエンキューする方法にフォールバックする。なお、Sidekiqアダプタはpush_bulk
でenqueue_all
を実装している。このメソッドは既存の
enqueue.active_job
イベントを利用せず、enqueue_all.active_job
という新しいイベントを追加する。
Sander Verdonschot
同Changelogより
つっつきボイス:「これはActive Jobの改修ですね」「多数のマイクロジョブみたいなものを一度にエンキューする機能はいい👍: この改修ではSidekiqのアダプタにenqueue_all
メソッドを追加してpush_bulk
を呼んでいるけど、おそらくpush_bulk
の中でRedisにJobを保存する部分でRedisのbulk put系命令を使うようになっているんでしょうね」
# activejob/lib/active_job/queue_adapters/sidekiq_adapter.rb#L35
def enqueue_all(jobs) # :nodoc:
jobs.group_by(&:class).each do |job_class, same_class_jobs|
same_class_jobs.group_by(&:queue_name).each do |queue, same_class_and_queue_jobs|
immediate_jobs, scheduled_jobs = same_class_and_queue_jobs.partition { |job| job.scheduled_at.nil? }
if immediate_jobs.any?
Sidekiq::Client.push_bulk(
"class" => JobWrapper,
"wrapped" => job_class,
"queue" => queue,
"args" => immediate_jobs.map { |job| [job.serialize] },
)
end
if scheduled_jobs.any?
Sidekiq::Client.push_bulk(
"class" => JobWrapper,
"wrapped" => job_class,
"queue" => queue,
"args" => scheduled_jobs.map { |job| [job.serialize] },
"at" => scheduled_jobs.map { |job| job.scheduled_at }
)
end
end
end
end
「この翻訳記事↓でも書かれているように、Active Job経由だとActive Jobがサポートする機能しか使えないので、バックエンドで使っているSidekiqの機能をすべて使えるわけではありませんが、こうした改善によってより効率的な処理をサポートするための機能が少しずつ増えてきている感じですね」
🔗 デフォルトのカラムシリアライザを定義可能になった
- PR: Allow to define the default column serializer by casperisfine · Pull Request #47463 · rails/rails
YAMLは何かと自分の足を撃ち抜きがちなので、他の何かで代替可能にしておくか、いっそシンプルにあらゆるシリアライズドカラムで明示的にシリアライザをユーザーに強制定義させるぐらいの方が望ましい。
このプルリクは#47422の縮小版で、デフォルトのシリアライザを設定可能にするもの。デフォルトのシリアライザを変更すべきかどうか(そしてデフォルトのシリアライザをどれにすべきか)についてはその後で議論可能。
同PRより
つっつきボイス:「割と変更が多いですね」「YAMLがカラムシリアライザのデフォルトなのは良くないという話はその通りだと思います」「この改修の前に#47422でYAMLをデフォルトから外してたんですね」
「#47422ではデフォルトのカラムシリアライザを7.1からnil
に変更している↓: 最終的にはデフォルトのカラムシリアライザをJSONに変更したいのかなと思ったけど、#47422のプルリクメッセージにはJSONも難しいだろうと書かれているので、7.1では先にYAMLをデフォルトから外してシリアライザを明示的に指定する形にしたということなんでしょうね」
# #47422より
# guides/source/configuring.md#L1272
#### `config.active_record.default_column_serializer`
The serializer implementation to use if none is explicitly specified for a given
column.
-`serialize` and `store` while allowing to use alternative serializer
-implementations, use `YAML` by default, but it's not a very efficient format
+Historically `serialize` and `store` while allowing to use alternative serializer
+implementations, would use `YAML` by default, but it's not a very efficient format
+and can be the source of security vulnerabilities if not carefully employed.
As such it is recommended to prefer stricter, more limited formats for database
serialization.
+Unfortunately there isn't really any suitable defaults available in Ruby's standard
+library. `JSON` could work as a format, but the `json` gems will cast unsupported
+types to strings which may lead to bugs.
+
+The default value depends on the `config.load_defaults` target version:
+
| Starting with version | The default value is |
| --------------------- | -------------------- |
| (original) | `YAML` |
+| 7.1 | `nil` |
「今回の#47463の改修は、従来の書き方にdeprecation warningを出して7.1のbreaking changeに備えつつ、デフォルトをカスタマイズ可能にしたようですね: serialize
にcoder:
というキーワード引数を追加して、デフォルトのカラムシリアライザをconfig.active_record.default_column_serializer
コンフィグでカスタマイズ可能にする」「ところでYAMLの場合は元々yaml:
オプションも渡せるようになってたんですね」
# activerecord/lib/active_record/attribute_methods/serialization.rb#L156
# class User < ActiveRecord::Base
- # serialize :preferences, yaml: { permitted_classes: [Symbol, Time] }
+ # serialize :preferences, coder: YAML, yaml: { permitted_classes: [Symbol, Time] }
# end
🔗 deliver_later
のジョブキュー名をメーラーごとにカスタマイズ可能になった
deliver_later_queue_name
は既にActionMailer::Base
でカスタマイズ可能。その値がすべてのサブクラスに継承される。クラスの継承可能な属性を代わりに使うことでサブクラスでオーバーライド可能にする。これは、コンカレンシー制御で名前付きクエリに名前を使っている場合に便利。たとえば、多数のワーカーで処理中のキューは大量のメールを同時配信可能だが、それが望ましくない場合もあるだろう。多数のメールの同時配信数が多過ぎると、メールプロバイダで速度を制限されたりブロックされたりする可能性がある。これを防止する方法のひとつは、ワーカー数を減らすか1個にすること。
このプルリクは、デフォルトの配信ジョブが利用するキューを以下のようにメーラーごとに設定可能にすることで、これをもっと手軽に行えるようにする。class AnnouncementsMailer < ApplicationMailer self.deliver_later_queue_name = :throttled_mailer vend
参考: Allow configuration of ActionMailer queue name #18587 (comment)
同PRより
つっつきボイス:「今まではdeliver_later
のジョブキュー名をconfig.action_mailer.deliver_later_queue_name
で設定していたのでサブクラスごとに変えられなかったんですね: 使いたい機能👍」
参考: § 3.13.15 config.action_mailer.deliver_later_queue_name
-- Rails アプリケーションを設定する - Railsガイド
🔗 database.ymlのshared
設定を改善
動機/背景
従来のshared
コンフィグハッシュは、database.ymlで定義されているすべてのデータベース設定とマージされるようになっていた。詳細
このコミットは、shared
ハッシュを以下のように3階層に対応させ、設定名が一致する場合にのみ環境間で設定を共有できるようにした。shared: one: migrations_path: "db/one" two: migrations_path: "db/two" development: one: adapter: sqlite3 two: adapter: sqlite3 production: one: adapter: mysql2 two: adapter: mysql2
これによって、development環境とproduction環境の両方で、設定
one
とtwo
のmigration_paths
が正しく設定されるようになる。追加情報
修正: #47367cc @eileencodes
cc @matthewd(#28896のコメントでこれを思いついていたので)
同PRより
つっつきボイス:「そもそもRailsにはRails::Application#config_for
にYAMLを読み込む機能が実装されているんですが、ここにshared
をルート要素のキーとするエントリがあると、全環境にデフォルトでその設定をmergeするという機能があります」
参考: Rails API config_for
-- Rails::Application
「database.ymlも同じ思想で、shared
キーを持っていると全環境の共通設定として書けるんですが、これが3階層以上(各環境内の設定値としては2階層以上)になると、個別環境で設定を書くとmergeではなく上書きされてしまうという問題があったようです」
「これは、database.ymlの処理はRails::Application#config_for
の処理とは別のコードとして実装されていた(#database_configuration
)ことに起因していて、これまではRails::Application#config_for
とdatabase.ymlの読み込み仕様において、shared
キーの扱いが異なる状態だったようです」
参考: database_configuration
-- Rails::Application::Configuration
「このプルリクではこれを解消して、database.ymlのshared
についても階層のある設定を上書きではなくmergeするようにすることで解決した、ということのように見えます」
「なお、なぜこのshared
のようなキーが存在するかについては、恐らくはYAMLパーサーであるpsychのデフォルトのsafe_load
がYAMLエイリアス機能(&defaults
などで使う)が無効化されてしまったことが背景にあるのではないかと思われます」
「環境別の設定は共通項目も多いので、エイリアス機能がないと大量の同じ行をコピペすることになってしまうため不便です: そのため、YAMLエイリアスは使わないけどshared
という特別なキーを導入し、config読み込み機能側で特別な処理をさせることでセキュリティと利便性のバランスを取ったのだろうと想像しています(出典までは探れなかったので間違っていたらごめんなさい)」
🔗 新規アプリのテンプレートにconfig.hosts
とconfig.host_authorization
がコメントアウト状態で追加されるようになった
5c830a8で
/up
エンドポイントが追加されてロードバランサーやアップタイムモニタで便利に使えるようになったが、たまにDNSリバインディングに邪魔されることがある。hosts
とhost_authorization
設定で対応できることを示しておけばスムーズに対応しやすいだろう。
同PRより
つっつきボイス:「設定を探さずに対応できるようになるのは便利👍」
# railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt#L103
# DNSリバインディングやその他の`Host`ヘッダー攻撃からの保護を有効にする設定:
# config.hosts = [
# "example.com", # Allow requests from example.com
# /.*\.example\.com/ # Allow requests from subdomains like `www.example.com`
# ]
# デフォルトのヘルスチェックエンドポイントでDNSリバインディング保護をスキップする設定:
# config.host_authorization = { exclude: ->(request) { request.path == "/up" } }
end
参考: §3.5.1 ActionDispatch::HostAuthorization
-- Rails アプリケーションを設定する - Railsガイド
🔗 Rails Application Templatesガイド
つっつきボイス:「改修そのものはRails Guides Webサイトの目次追加だけど、Rails Application Templatesガイドの作成が進行中なんですね」「なるほど」
# guides/source/documents.yaml#L289
+ -
+ name: Rails Application Templates
+ url: rails_application_templates.html
+ description: >
+ Application templates are simple Ruby files containing DSL for adding
+ gems, initializers, etc. to your freshly created Rails project or an
+ existing Rails project.
+ work_in_progress: true
「Edgeガイドで進行中のガイドが読めますね↓」
参考: Rails Application Templates — Ruby on Rails Guides
🔗 番外: コピーライト表記の年号を削除
Railsも、curlみたいにライセンスから日付を削除すべき。何かに役立つわけでもないし、毎年この日付を更新するためにプルリクを作成しないといけない。
例: #46866、#44039、#40991、#34831、#31606、#27523、とまだまだ続く(これで多くのプログラマーが何年間助かるだろうか...)😂
つっつきボイス:「著作権表記の年号ってそういえば法的な意味がよくわからないですね」「ネットにもいろいろ情報はあるけど、不安であれば著作権法に詳しい弁護士などの専門家に相談してみるべきでしょうね」
🔗Rails
🔗 Devise 4.9をインストールしてRails 7.0 (Hotwire/Turbo)に対応する
つっつきボイス:「jnchitoさんがDevise 4.9でのHotwire/Turbo対応を少し前から調べていて記事を公開していたのですが、4.9の正式リリースに合わせて記述を更新したそうです」「これはありがたい!」「元々以下の呼びかけ↓に応えた記事だそうです」
Looking for some more volunteers to test Devise + Turbo integration: https://t.co/YTl47n5k0B
Check the readme/changelog, and report back if you run into any issues. (please don't run this in production! at least not yet, you've been warned haha.)
❤️💜💛💚💙
— ❤️💚💙💛🧡 (@heartcombo) February 3, 2023
「ちなみにjnchitoさんは4.9へのアップグレードガイドも翻訳しています↓」
参考: 【日本語訳】How To: Upgrade to Devise 4.9.0 [Hotwire Turbo integration]
🔗 Active Record関連付けのループはfind_each
で(Ruby Weeklyより)
つっつきボイス:「これは定番ですね: primary_keyであるidカラム昇順以外でのORDER BYが効かない点には気をつける必要がありますが(追記 2023/03/16: #30590でdesc
も指定できるようになっています)、基本的にeach
よりもfind_each
で回す方がいい」
# 同記事より
post.comments.each do |comment|
# Do stuff with each comment: enqueue a job
end
post.comments.find_each do |comment|
# Do stuff with each comment: enqueue a job
end
参考: Rails API find_each
-- ActiveRecord::Batches
🔗 英国政府が公開しているViewComponentベースのデザインシステム
- サイト: GOV.UK Components
つっつきボイス:「以下のViewComponent公式ドキュメントで知りました↓」「公的機関がデザインシステムを公開するのは理にかなってますね: ViewComponentコンポーネントの実装まで公開するのは英国政府のWebサイトが公式にRailsを採用しているからできることですが」
つっつき後に、日本のデジタル庁もデザインシステムを公開していることを教わりました↓
参考: デザインシステム|デジタル庁
🔗 その他Rails
GitHub 上にある #Railsガイド のコンテンツが更新されると自動的に本番環境にも反映されるデプロイパイプラインができたー!🚀✨ https://t.co/2PKC2Tz0f7
色々と事情があってこれまでは手動でデプロイしてました... 😂💦
GitHub Actions メッチャ便利...!! 😻🆒✨
— 安川要平/Yohei Yasukawa (@yasulab) March 4, 2023
つっつきボイス:「Railsガイドでマージ後に自動デプロイできるようになったので、コントリビュートの反映がやりやすくなりました」
「GitHub Actionsは本当に便利ですよね: organizationの切り方を間違えると大変ですが」「というと?」「GitHub Actionsをプロダクトごとにorganizationを分けて無料枠で運用していると、無料枠ではデフォルトのキャッシュ容量に上限があるので、大きなものをデプロイするとキャッシュが足りなくなって困ったことがあります」「ありゃ〜」
参考: Actions | GitHub
前編は以上です。
バックナンバー(2023年度第1四半期)
週刊Railsウォッチ: Ruby30周年記念イベント、37signalsのデプロイツールmrskほか(20230308後編)
- 20230307前編 Action Mailerプレビューで全メールヘッダーを表示可能に、rubocop-graphqlほか
- 20230222後編 Ruby 3.2のData#initializeの設計、ruby-openai gemほか
- 20230221前編 Ruby30周年記念イベント、ActiveRecord APIクイズほか
- 20230215後編 Bundler 2.4リリース、RubyKaigi 2023参加募集開始ほか
- 20230214前編 AssumeSSLミドルウェア追加、Fly.ioとRails 7.1のDocker対応ほか
- 20230202後編 ShopifyのYJIT記事、RubyGemsのgem execコマンドほか
- 20230201後編 Ruby 3.2のベンチマーク記事、dry-cliで高度なCLIを作るほか
- 20230131前編 Evil Martiansが使っているgem、JavaScriptガイドが更新ほか
- 20230125前編 2022年のRails振り返り記事、RailsにDocker関連ファイルが追加ほか
ソースの表記されていない項目は独自ルート(TwitterやはてブやRSSやruby-jp SlackやRedditなど)です。
週刊Railsウォッチについて
TechRachoではRubyやRailsなどの最新情報記事を平日に公開しています。TechRacho記事をいち早くお読みになりたい方はTwitterにて@techrachoのフォローをお願いします。また、タグやカテゴリごとにRSSフィードを購読することもできます(例:週刊Railsウォッチタグ)