- Ruby / Rails関連
週刊Railsウォッチ: Webpackerが公式に引退宣言、『Everyday Rails』日本語版がRails 7に対応ほか(20220124前編)
こんにちは、hachi8833です。
🔗Rails: 先週の改修(Rails公式ニュースより)
今回は以下の中からChangelogが更新されているものを中心に見繕いました。久しぶりに更新が少ないですね。
🔗 atomic_write
の競合状態の挙動を修正
production環境で見かけた
atomic_write
関連のバグがある(他にもさまざまなところで見た)。スタックトレースによれば、FileUtils.touch
はそのbasedirが存在しなくなったパスを操作していて、それが原因でErrno::ENOENT
例外が発生していた。どこかで競合条件が発生しているのかもしれない。いずれにしろFile.probe_stat_in
はデバッグには使えそうにないので、atomic_write
が爆発したとしても爆発させるべきではないだろう。
同PRより
- Rails API:
atomic_write
--File
つっつきボイス:「privateメソッドprobe_stat_in
がENOENT
例外に対応していなかったのを修正したようですね↓」
# activesupport/lib/active_support/core_ext/file/atomic.rb#L56
def self.probe_stat_in(dir) # :nodoc:
basename = [
".permissions_check",
Thread.current.object_id,
Process.pid,
rand(1000000)
].join(".")
file_name = join(dir, basename)
FileUtils.touch(file_name)
+ rescue Errno::ENOENT
+ file_name = nil
ensure
FileUtils.rm_f(file_name) if file_name
end
「ときどき見かけるENOENT
ってどう読むのか謎だったんですが、Error No Entoryの略だったんですね」「Eで始まるのがいかにもPOSIXのエラーっぽいので探してみるとやはりそうだった↓」「POSIXにこんなにたくさんエラーがあるとは知らなかった〜」
ENOENT
そのようなファイルやディレクトリはない (POSIX.1-2001)
通常は、このエラーは、指定されたパス名が存在しないか、パス名のディレクトリプレフィックスの構成要素のいずれかが存在しないか、指定されたパス名が壊れた (dangling) シンボリックリンク、の場合に発生する。
linuxjm.osdn.jpより
🔗 preload_link_tag
がrel: 'modulepreload'
を使うよう修正
- PR: Ensure preload_link_tag preloads modules correctly by ElMassimo · Pull Request #44198 · rails/rails
このプルリクは、
preload_link_tag
がmodule
スクリプトのプリロードをサポートするように修正する。
type: 'module'
が指定されると、rel: 'preload'
ではなくrel: 'modulepreload'
を使うようになる。
背景
従来は、preload_link_tag
でのタグのレンダリングとレスポンスのearly hintの両方について、rel=preload
が(module
スクリプトを含む)すべてのスクリプトで使われていた。
型が合わない(スクリプトはプリロードされるがモジュールはインクルードされていない)ので、ブラウザはこのプリロードされたリソースを無視する。
同PRより
つっつきボイス:「お、今まではrel
属性にpreload
を指定してJavaScriptファイルをプリロードしていたのをmodulepreload
でやるように修正されたのね↓」「JavaScript用のリンク種別なのか」
# actionview/lib/action_view/helpers/asset_tag_helper.rb#L319
def preload_link_tag(source, options = {})
href = path_to_asset(source, skip_pipeline: options.delete(:skip_pipeline))
extname = File.extname(source).downcase.delete(".")
mime_type = options.delete(:type) || Template::Types[extname]&.to_s
as_type = options.delete(:as) || resolve_link_as(extname, mime_type)
crossorigin = options.delete(:crossorigin)
crossorigin = "anonymous" if crossorigin == true || (crossorigin.blank? && as_type == "font")
integrity = options[:integrity]
nopush = options.delete(:nopush) || false
+ rel = mime_type == "module" ? "modulepreload" : "preload"
link_tag = tag.link(**{
- rel: "preload",
+ rel: rel,
href: href,
as: as_type,
type: mime_type,
crossorigin: crossorigin
}.merge!(options.symbolize_keys))
- preload_link = "<#{href}>; rel=preload; as=#{as_type}"
+ preload_link = "<#{href}>; rel=#{rel}; as=#{as_type}"
preload_link += "; type=#{mime_type}" if mime_type
preload_link += "; crossorigin=#{crossorigin}" if crossorigin
preload_link += "; integrity=#{integrity}" if integrity
preload_link += "; nopush" if nopush
send_preload_links_header([preload_link])
link_tag
end
参考: リンク種別: modulepreload
- HTML: HyperText Markup Language | MDN
🔗 ServerTimingミドルウェアのドキュメント更新
- 0547b16で入った
ActionDispatch::ServerTiming
のAPIドキュメントを追加- rails_on_rack.mdガイドで抜けていたミドルウェアや解説を更新
同PRより
つっつきボイス:「これはドキュメントの更新もれの修正ですね👍」「そういえばServerTimingミドルウェアは少し前に追加されていましたね(ウォッチ20211011)」「そうそう、Server-Timing
はパフォーマンス測定用にレスポンスに追加できるヘッダー」
参考: rails/server_timing.rb at 0547b1646d09a80d0685a03c932fb0ba09c3e614 · rails/rails
参考: Server-Timing - HTTP | MDN
後で調べると、Rails 7のproduction環境とtest環境ではデフォルトでServerTimingがオフになっていました↓(development環境ではオンでした)。
$ RAILS_ENV=production dip rails middleware
Creating enno_docker_backend_run ... done
use ActionDispatch::HostAuthorization
use Rack::Sendfile
use ActionDispatch::Static
use ActionDispatch::Executor
use Rack::Runtime
use RailsSameSiteCookie::Middleware
use Rack::MethodOverride
use ActionDispatch::RequestId
use ActionDispatch::RemoteIp
use Rails::Rack::Logger
use ActionDispatch::ShowExceptions
use ActionDispatch::DebugExceptions
use ActionDispatch::Callbacks
use ActionDispatch::Cookies
use ActionDispatch::Session::CookieStore
use ActionDispatch::Flash
use ActionDispatch::ContentSecurityPolicy::Middleware
use ActionDispatch::PermissionsPolicy::Middleware
use Rack::Head
use Rack::ConditionalGet
use Rack::ETag
use Rack::TempfileReaper
use Rack::Attack
use Rack::Attack
run App::Application.routes
Rails 7のdevelopment環境でChrome DevToolsの[Network]タブを見ると、こんな感じで出力されていました↓。DevToolsの[Timing]タブをクリックするとグラフも表示されます。
Server-Timing: cache_read.active_support;dur=0.063720703125, cache_write.active_support;dur=0.029296875, start_processing.action_controller;dur=1.03662109375, sql.active_record;dur=4.445556640625, !render_template.action_view;dur=27.783203125, render_partial.action_view;dur=16.796875, render_template.action_view;dur=12.628173828125, render_layout.action_view;dur=22.005126953125, process_action.action_controller;dur=68.377197265625
🔗Rails
🔗 Webpackerが引退
RETIREMENT: Webpacker has served the Rails community for over five years as a bridge to compiled and bundled JavaScript. This bridge is no longer needed for most people in most situations following the release of Rails 7.https://t.co/vIp3DeG82L
— Ruby on Rails (@rails) January 19, 2022
つっつきボイス:「とうとうWebpackerが引退とは」「Rails 7リリースやここ最近のRailsの動きから、うすうす予感していた人は多いでしょうね」「Twitterを見ていてもそんな印象でした」
今後メンテナが開発継続するけどRailsチームオフィシャルではなくなるのか。 https://t.co/8PzGgLQ9du
— masa寿司 (@masa_iwasaki) January 19, 2022
つっつき後に、Webpackerは以下のshakapackerに引き継がれると教わりました↓。
「Rails 7で入ったjsbundling-railsを使えば↓引き続きJavaScriptのバンドリングはできるので、TypeScriptなどを使う人はjsbundling-railsに移行してくれということでしょうね」「移行ガイドも出ていますね」
参考: Webpacker 5->jsbundling-rails移行ガイド jsbundling-rails/switch_from_webpacker.md at main · rails/jsbundling-rails
「ちなみにHotwireのdiscussionサイトで見つけたんですが、UJSはRails 7では削除されていないそうです↓」「互換性についてだけturbo-railsのREADMEに書いておいたのね: rails-ujsはTurboと共存できるように名前空間が別になっていますね👍」
We stopped removing UJS, and instead just document the compatibility: https://github.com/hotwired/turbo-rails#compatibility-with-rails-ujs
discuss.hotwired.devよりDHHのコメント
参考: Rails UJS/Turbolinks -> Turboアップグレードガイド turbo-rails/UPGRADING.md at main · hotwired/turbo-rails
なお、Rails 7でbin/rails new --help
すると以下のようにJavaScriptビルドツールの選択肢が表示されます。importmap、webpack、esbuild、rollupから選択できます。なおデフォルトのimportmapの場合はjsbundling-railsはインストールされず、importmap-railsがインストールされます。
-j, [--javascript=JAVASCRIPT] # Choose JavaScript approach [options: importmap (default), webpack, esbuild, rollup]
なお私はesbuildが速いという評判を見かけたのでRails 7でjsbundling-rails+esbuildにしてみました。
参考: Node.js のビルドツール「esbuild」について!
参考: [Web フロントエンド] esbuild が爆速すぎて webpack / Rollup にはもう戻れない | Kabuku Developers Blog
🔗 メモリリークとmemory bloat(Ruby Weeklyより)
memory bloatの日本語定訳が見当たらないので英ママとしました。
つっつきボイス:「メモリリークとmemory bloatの違いを解説するのかな?: メモリリークは完全にバグですが、memory bloatはメモリが非効率な状態になることですね」「なるほど」「実際はmemory bloatであるものをメモリリークと呼ぶのは、仕様をバグと呼ぶようなものかもしれませんね」
後で探すとQuoraで見つかりました↓。
参考: (4) プロダクションの Rails サーバーの利用メモリがひたすら増加していくような挙動を観測したとき、どう対応するのがよいですか? - Quora
これは使われないメモリへの参照が残るバグとしてのmemory leakではなくて、ちゃんと開放してるにもかかわらずメモリ使用量が減らないmemory bloatだからです。
jp.quora.comより
「実際、Railsに限らず大規模なデータを扱うサーバーサイドアプリケーションではmemory bloatは多かれ少なかれ生じるものなので、これはもう仕方ないとも言えます」「そうなんですね...」「もちろんメモリ使用量を監視していればmemory bloatに気づけますが」
「記事ではいくつかmemory bloat検出用ツールが紹介されていますね↓」
「ちなみに3つ目のoinkは英語でブタの鳴き声を表す擬音ですね」「へ〜」「もしかするとmemory bloatを監視・検出するツールの動作を、トリュフを見つけるブタの鳴き声になぞらえているのかなと想像してみました」「あ〜それありそう」
参考: oinkの意味・使い方・読み方|英辞郎 on the WEB
🔗 rails_same_site_cookie: RailsのSameSite cookieを自動設定
参考: SameSite cookies - HTTP | MDN
参考: いまさら聞けないSameSite CookieとGoogle Chrome 80 | ecbeing
つっつきボイス:「私のRailsアプリ(5.2)でSameSite cookieを自力で設定するのが面倒だったんですが、以下の記事でこのrails_same_site_cookieを知って、作り中のRails 7アプリでこれを入れてみようかなと検討中です」「ほほぉ〜」
恐らくこの gem を使うのが一番簡単で確実ですね👍
rails_same_site_cookie gemで、RailsアプリにChrome 80向けのSameSite属性を指定する https://t.co/djoSgbKZei #Qiita
— サトウリョウスケ (@ryosuke_sato) March 3, 2020
「このgemを入れたらすぐ使える感じですね」「はい、これといったコンフィグなしで導入できて、期待どおりにローカル環境ではSameSite=None
のcookieでsecure
がオンになってくれました」「なるほど、Rackの機能を使わずに独自にcookieを設定しているようですね: この機能がRailsに公式に入ってもよさそうな気もしますが、この種のcookieのデフォルト設定は注意が必要なので、Rails本家にはすぐには入らないんじゃないかな🤔」「あ〜そうなのかも」
「Qiita記事でもcookieのテストをUser-Agentごとに書いてる↓: テストを書かないと振る舞いがよくわからなくて困りそうなcookieみたい」「う〜む、もう少し調べます」
# https://qiita.com/jnchito/items/1290a7b1dbf0ccf40c7b より
require 'rails_helper'
RSpec.describe 'Cookies', type: :request do
describe 'Cookie の SameSite 属性' do
before do
user = create :user
login_as user
end
it 'User-Agent指定無しの場合 SameSite=None がつく' do
get new_user_session_path
expect(response.headers['Set-Cookie']).to match /_your_app_session=.*SameSite=None/
end
it 'SameSite=Lax がデフォルトになる Chrome 80 では SameSite=None がつく' do
mac_chrome_80 = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.42 Safari/537.36 '
win_chrome_80 = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.16 Safari/537.36'
get new_user_session_path, headers: { 'User-Agent' => mac_chrome_80 }
expect(response.headers['Set-Cookie']).to match /_your_app_session=.*SameSite=None/
get new_user_session_path, headers: { 'User-Agent' => win_chrome_80 }
expect(response.headers['Set-Cookie']).to match /_your_app_session=.*SameSite=None/
end
it 'SameSite=Noneの扱いにバグがある iOS12 Safari では SameSite=None がつかない' do
iphone_ios12_user_agent = 'Mozilla/5.0 (iPhone; CPU iPhone OS 12_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0 Mobile/15E148 Safari/604.1'
get new_user_session_path, headers: { 'User-Agent' => iphone_ios12_user_agent }
expect(response.headers['Set-Cookie']).to include '_your_app_session='
expect(response.headers['Set-Cookie']).not_to include 'SameSite'
end
end
end
後で探すと、Railsへのプルリクを1件見つけましたがcloseしていました。上のテストにもあるようにSafariでSameSite=None
が期待どおりに動かない問題があったようです。
参考: Add SameSite to Cookies by cfabianski · Pull Request #28297 · rails/rails -- closed
参考: cookieのSameSite属性 おぼえがき - kasei_sanのブログ -- Safariのバグについて書かれています
🔗 bullet gemがRails 7に対応
つっつきボイス:「bullet gemがRails 7で動くようになった🎉」「この間まで動きませんでしたが、さっきGemfileでコメント解除したら無事インストールできました」
🔗 mrujs: TypeScriptで書かれたujs
- サイト: Mrujs
参考: Form submit with turbo-streams response, without redirect - General - Hotwire Discussion -- このコメントでmrujsを知りました
つっつきボイス:「Rails 7のフォームで確認ダイアログを出す方法を調べていて見つけました」「モダンujsと書かれているので、ujsの機能をTypeScriptで書き直した感じかな: 確認ダイアログのようなujsの機能をひととおり使えるらしい」「ちなみにjnchitoさんの記事↓のおかげで最終的にTurboで確認ダイアログを表示できたので、mrujsはとりあえず使わずに済みました」
参考: Rails 7.0 + Ruby 3.1でゼロからアプリを作ってみたときにハマったところあれこれ - Qiita
🔗 その他Rails
信じられますか?
Everyday Railsは、最新版へのアップデートは無料なんですよ
だいたい書籍買ったら情報が古くなり、最新版は追加購入しないといけませんが、Everyday Railsは無料本当にありがたいです https://t.co/bhVdu94qAr pic.twitter.com/YYvSkyEsoa
— teyama3 (@_teyama3) January 17, 2022
つっつきボイス:「RSpecによるテスト入門書『Everyday Rails』がRails 7に対応したそうです」「お〜、継続的にアップグレードされているんですね」「無料アップグレードできるのか〜」「期間限定で割引セールですって」「いつ買ったか思い出せないけど電子書籍で買ってたはず...(探す)あった、2014年にLeanpubで買ってた」
「電子書籍をいろんなサイトで買うと後から探すのが大変ですよね」「PDFやepubをどこからダウンロードしたかわからなくなる問題、あるある」
前編は以上です。
バックナンバー(2022年度第1四半期)
週刊Railsウォッチ: Ruby 2.5〜3.1ベンチマーク、Opal 1.4、JRubyが20歳に、2022年のCSSほか(2022018後編)
- 2022017前編 rails-ujs->Turboアップグレードガイド、RubyとWeb Componentsほか
- 20220112 Rails 7をRuby 3.1で動かす、クックパッドのRuby 3.1解説記事、Rails 6->7更新ほか
今週の主なニュースソース
ソースの表記されていない項目は独自ルート(TwitterやはてブやRSSやruby-jp SlackやRedditなど)です。
週刊Railsウォッチについて
TechRachoではRubyやRailsなどの最新情報記事を平日に公開しています。TechRacho記事をいち早くお読みになりたい方はTwitterにて@techrachoのフォローをお願いします。また、タグやカテゴリごとにRSSフィードを購読することもできます(例:週刊Railsウォッチタグ)