- Ruby / Rails関連
週刊Railsウォッチ(20210406前編)GitHubが修正したRailsセッションハンドリングの競合、erb/haml/slimの速度比較ほか
こんにちは、hachi8833です。
🔗Rails: 先週の改修(Rails公式ニュースより)
今回は以下のコミットリストから見繕いました。Changelog更新はほとんどありませんでした。
つっつきボイス:「先週mimemagicの件があったせいか今週の変更は少なめでした」
週刊Railsウォッチ(20210329前編)特集: Rails更新版の臨時リリースとmimemagic gemのGPL問題
🔗 マイグレーション生成の--pretend
オプションのエラーを修正
- PR: Fix create migration generator with `--pretend` option by euxx · Pull Request #41255 · rails/rails
# railties/lib/rails/generators/actions/create_migration.rb#L22
def invoke!
+ return super if pretend?
+
invoked_file = super
File.exist?(@destination) ? invoked_file : relative_existing_migration
end
つっつきボイス:「マイグレーションに--pretend
オプションがあるとは知らなかった」「dry run的なものかな?」
後でbin/rails g migration --help
して確認しました。
Runtime options:
-f, [--force] # Overwrite files that already exist
-p, [--pretend], [--no-pretend] # Run but do not make any changes
-q, [--quiet], [--no-quiet] # Suppress status output
-s, [--skip], [--no-skip] # Skip files that already exist
また、手元のRails 5.2.5ではマイグレーションの--pretend
はエラーにならず、Rails 6.1.3.1だとこのプルリクにかかれているのと同じエラーになりました。
🔗 ActiveSupport::NumericWithFormat#to_s
を最適化
- PR: Optimize ActiveSupport::NumericWithFormat#to_s by casperisfine · Pull Request #41801 · rails/rails
つっつきボイス:「to_s
のnil
チェックを移動して早期脱出させたようですね」
# activesupport/lib/active_support/core_ext/numeric/conversions.rb#L109
def to_s(format = nil, options = nil)
+ return super() if format.nil?
+
case format
- when nil
- super()
when Integer, String
super(format)
when :phone
ActiveSupport::NumberHelper.number_to_phone(self, options || {})
when :currency
ActiveSupport::NumberHelper.number_to_currency(self, options || {})
when :percentage
ActiveSupport::NumberHelper.number_to_percentage(self, options || {})
when :delimited
ActiveSupport::NumberHelper.number_to_delimited(self, options || {})
when :rounded
ActiveSupport::NumberHelper.number_to_rounded(self, options || {})
when :human
ActiveSupport::NumberHelper.number_to_human(self, options || {})
when :human_size
ActiveSupport::NumberHelper.number_to_human_size(self, options || {})
when Symbol
super()
else
super(format)
end
end
case format when nil
は最終的にNilClass === nil
を呼ぶので非常に効率がよい。これはObject#==
のエイリアスなので、基本的には最終的にnil == nil
となる。
しかしformat.nil?
は専用のopコードのおかげで著しく速い。
この場合Integer#to_s
は引数なしで呼ばれることが非常に多いので、頻度が最も高いケースについて最適化する価値がある。
同PRより大意
🔗 issue: Rails 5.2.5とCSRFトークンフォーマット
- issue: ArgumentError: invalid base64 when update Rails v5.2.5 · Issue #41783 · rails/rails -- 現在はオープン
mimemagic に依存しなくなった rails 5.2.5 に CSRF トークンのフォーマットが Urlsafe Base64 になる変更が(意図せず?)入っているようで 5.2.5 で生成されたセッションが 5.2.4.x 以下 or 6.0.x で読めなくなってしまっているようなので rails 5.2.5 と 6.1.x 以外の rails を組み合わせて使っている人は要注意です。
hackmd.io『mimemagicの最新動向』より
つっつきボイス:「mametterさんやruby-jp Slackの有志がまとめてくださった上のmimemagic動向記事に追記されていたのを見て知りました」「Rails 5.2.5でRailsがmimemagicに依存しなくなったときにCSRFトークンフォーマットが変わってしまったということですね」
「Railsサーバーコンテナを複数動かして段階deployする場合は、deployが完了するまで旧フォーマットで動くRailsサーバーと新フォーマットで動くRailsサーバーが混在してエラーレートが跳ね上がる可能性があるので、規模の大きなサービスほど問題になるでしょうね(#41783コメント)」「なるほど」「mimemagicの件でアップグレードする時にチェックしておく必要はあると思います」「Rails 5.2向けに#41797がオープンしてますね↓」
- PR: [5-2-stable] Backport Upgrade-safe URL-safe CSRF tokens #39076 by kamipo · Pull Request #41797 · rails/rails - 現在オープン中
- PR: [6-0-stable] Backport Upgrade-safe URL-safe CSRF tokens #39076 by kamipo · Pull Request #41806 · rails/rails -- マージ済み
🔗Rails
🔗 rails_multisite: Discourseから切り出されたRailsマルチサイトgem(Ruby Weeklyより)
つっつきボイス:「DiscourseのRailsから切り出されたマルチサイトgemだそうです」
参考: Discourse - Civilized Discussion
「以下のようにホスト名ベースでデータベースを切り替えられるようですね↓」
# config/multisite.yml
db_one:
adapter: ...
database: some_database_1
db_two:
adapater: ...
database: some_database_2
参考: Active Record で複数のデータベース利用 - Railsガイド
「いわゆるマルチテナントのRailsアプリで、データベースもテナントごとに分けたいというときに使うのかな?案件の事情によってはこういう設計にしないといけない場合もあるのかもしれないけど、マイグレーションやデプロイが大変そうですし、このようにひとつのRailsアプリでデータベースをテナントごとに分けるよりもRailsアプリのインスタンスを分ける方が一般的かなと思いました」「それもそうですね」「自分たちが運用することを考えるとアプリを分ける方が楽だと思います」
参考: Apartment でマルチテナントサービスを作成する - Qiita
🔗 GitHubが発見・修正したRailsセッションハンドリングの競合(Ruby Weeklyより)
つっつきボイス:「GitHubの技術ブログです」「この図がポイントかな↓」
「Railsのセッションcookieでレアなrace conditionが発生したということらしい」「race conditionの解決は大変そう」「記事冒頭を見ると、少し前にGitHubからユーザーが突然ログアウトさせられた件について書かれていますね: そのときの修正内容の解説なのか」「あ、そういえば1か月ほど前にそんなことがありましたね」
参考: GitHub security update: A bug related to handling of authenticated sessions - The GitHub Blog
GitHub 突然ログアウトしたなーと思ったらこれか!
GitHub security update: A bug related to handling of authenticated sessions - The GitHub Blog https://t.co/OgnI52oJsB
— Kosuke Ogawa🌤️エンジニア🏝宮崎 (@koogawa) March 9, 2021
「これは読んでみてもよさそう👍」「もう少ししたらGitHubブログの日本語版にも公開されるかもしれませんね↓」
参考: GitHubブログ - 製品アップデートや開発に関するアイディアやインスピレーションなど、エンジニアの皆さんに役立つ情報を発信します。
🔗 erbとhamlとslimの速度を比較してみた
つっつきボイス:「単純に出力するならerbが一番速いんじゃないかな?」「条件を変えたりして測定してますね」
「ループの中で毎回new
しないで、以下のように最初にnew
したインスタンスを使い回せば速くなる↓、たしかに」
# 同記事より
erb_engine = ERB.new(erb_example, 0, '-', '__result')
slim_engine = Slim::Template.new { slim_example }
haml_engine = Haml::Engine.new(haml_example)
Benchmark.bmbm(10) do |bcmk|
bcmk.report("erb_test") { (1..2000).each { erb_engine.result binding } }
bcmk.report("slim_test") { (1..2000).each{ __result = slim_engine.render(context) } }
bcmk.report("haml_test") { (1..2000).each { __result = haml_engine.render(binding) } }
end
「ただ、CMSなどでキャッシュを一括生成するような場合なら上のように書けるでしょうけど、通常のWebレンダリングではこういう書き方はできないと思います」「あ、それもそうか」
「記事末尾にある現実的な例だと予想通りerbが一番速い↓」「そんなに大きくは違わなそうですね」
# 同記事より
$ hey -n 1200 http://localhost:3000/notes_erb/index
Summary:
Total: 52.2586 secs
Slowest: 19.2837 secs
Fastest: 0.0389 secs
Average: 0.6960 secs
Requests/sec: 22.9627
$ hey -n 1200 http://localhost:3000/notes_haml/index
Summary:
Total: 61.7637 secs
Slowest: 18.5290 secs
Fastest: 0.0442 secs
Average: 0.8557 secs
Requests/sec: 19.4289
$ hey -n 1200 http://localhost:3000/notes_slim/index
Summary:
Total: 63.1625 secs
Slowest: 19.9744 secs
Fastest: 0.0874 secs
Average: 0.7959 secs
Requests/sec: 18.9986
🔗 Railsのジェネレータを改造する(Ruby Weeklyより)
つっつきボイス:「Railsのジェネレータを自分で作ったことあったのを思い出した」「ジェネレータを作るのって大変そうですけど」「既存のジェネレータのコードを見ながらやれば作れますよ: erbが二重になっている箇所があってそこは少し面倒ですが」
「自分はジェネレータを手作りまでしようとはあまり思わないかな」「ジェネレータに手を加えたらRailsをアップデートしたときに困ったりしませんか?」「既存のジェネレータを上書きするならともかく、独自のジェネレータを作るなら影響は小さいと思います」「なるほど」「単純なジェネレータなら一度自分で作ってみると勉強になりますよ🎓」
🔗 RailsでキャッシュをクリアしたらSidekiqジョブが吹っ飛んた話(RubyFlowより)
つっつきボイス:「あ〜、redisのキャッシュをクリアしたらSidekiqジョブが飛んだのか、結構でかい事故」「これはキツそう」
「redisはたしかデフォルトで16個のデータベースが使えて、キャッシュ用やジョブ用でデータベースを使い分けるものなんですが、それを同じデータベースに保存したらキャッシュをクリアした瞬間にジョブも消えるでしょうね」「う〜む」
参考: RedisのDB番号を増やす - tsunokawaのはてなダイアリー
# 同記事より: Active Supportのコード
# Redisサーバーのすべてのキャッシュをクリアする。
# キャッシュが名前空間化されていれば共有サーバーでも安全。
#
# Failsafe: Raises errors.
def clear(options = nil)
failsafe :clear do
if namespace = merged_options(options)[:namespace]
delete_matched "*", namespace: namespace
else
redis.with { |c| c.flushdb }
end
end
end
「元記事ではredisを名前空間化して修正したとあるけど↓、Redisのdatabase idを分ける方がいいんじゃないかな...」
# 同記事より
# config/application.rb
config.cache_store = :redis_cache_store, { url: ENV["REDIS_URL"], namespace: "rails" }
🔗 その他Rails
ActiveSupport::BacktraceCleaner ってのがあるのか。https://t.co/KtbcciZ8WY
backtraceをフィルタリングするコードを書けるぽい。
これ2008年に入ってる。Rails2.3あたり。知らなかった・・・。— igaiga (@igaiga555) March 31, 2021
つっつきボイス:「お〜、Rubyのバックトレースをフィルタできるんですね」「add_silencer
はどこかで見たことあったかも」
# api.rubyonrails.orgより
bc = ActiveSupport::BacktraceCleaner.new
bc.add_filter { |line| line.gsub(Rails.root.to_s, '') } # strip the Rails.root prefix
bc.add_silencer { |line| /puma|rubygems/.match?(line) } # skip any lines from puma or rubygems
bc.clean(exception.backtrace) # perform the cleanup
前編は以上です。
バックナンバー(2021年度第1四半期)
週刊Railsウォッチ(20210330後編)Active Recordモデル属性暗号化が標準で入る可能性、Flipper Cloud、awesome_printほか
- 20210329前編 特集: Rails更新版の臨時リリースとmimemagic gemのGPL問題
- 20210323後編 GitHub Actionsで使えるruby/setup-ruby、中高生国際Rubyプログラミングコンテスト2020ほか
- 20210322前編 Active Recordのstrict loadingの修正、セキュリティリリースのポリシー追加、N+1チェッカーprosopite gemほか
- 20210316後編 testdouble/standard gem、DockerfileベストプラクティスとDockerfileのlintツールhadolintほか
- 20210315前編 Active Recordのenum関連改修、Active SupportのEnumerableでpluckが使えるほか
- 20210309後編 RubyのIRBに隠れているイースターエッグ、Power Automate Desktop、SQLクエリのありがちなミス6つほか
- 20210303後編 Bundlerのセキュリティ修正、Rubyのガベージコレクション記事、Rubyが2/24に誕生日ほか
- 20210222 ActiveRecord::Relationの新メソッドload_asyncとexcluding、Active Jobのperform_laterの改善ほか
- 20210209後編 Rubyでミニ言語処理系を作る、Kernel#getsの意外な機能、CSSのcontent-visibilityほか
- 20210208前編 Rails次期リリースがバージョン7に決定、thoughtbotのアプリケーションセキュリティガイドほか](/hachi8833/2021_02_08/103801)
- 20210202後編 Ruby 3 irbのmeasureコマンド、テストを関数型言語のマインドセットで考えるほか
- 20210201前編 Webpackerのガイドがマージ、RailsはRuby 3でどのぐらい速くなったかほか
- 20210126後編 Google Cloud FunctionsがRubyをサポート、Ruby 3のパターンマッチングでポーカーゲームほか
- 20210125前編 Railsリポジトリのデフォルトブランチがmainに変更、Rails 6.1はMySQLのENUM型に対応済みほか
- 20210120後編 Ruby 3.0の新機能で遊ぶ、RubyスニペットをJSに変換するRuby2JS、rspec-parameterized gemほか
- 20210113後編 Ruby 3.0 Ractor解説記事、Vercelホスティングサービス、教育用OS xv6ほか
- 20210112前編 Active Recordの範囲指定バリデーション改善、soleとfind_sole_byメソッド、AlgoliaとRailsほか
今週の主なニュースソース
ソースの表記されていない項目は独自ルート(TwitterやはてブやRSSやruby-jp SlackやRedditなど)です。
週刊Railsウォッチについて
TechRachoではRubyやRailsなどの最新情報記事を平日に公開しています。TechRacho記事をいち早くお読みになりたい方はTwitterにて@techrachoのフォローをお願いします。また、タグやカテゴリごとにRSSフィードを購読することもできます(例:週刊Railsウォッチタグ)