- Ruby / Rails関連
週刊Railsウォッチ: Sidekiqが10歳に、BuildKiteのテストを高速化、フィーチャーフラグほか(20220131前編)
こんにちは、hachi8833です。
🔗Rails: 先週の改修(Rails公式ニュースより)
公式の更新情報をRailsウォッチが先回りしてしまったので、コミットリストの中からChangelogが変更されたものを中心に見繕いました。ドキュメントの修正が多いですね。
- 公式更新情報: Ruby on Rails — Rails 7.0.1, Webpacker retirement and more
- コミット: Comparing @{2022-01-20}...main@{2022-01-27} · rails/rails
🔗 Active Storageのリダイレクトモードでレスポンスをストリーミングしないようにした
従来はリダイレクトモードとプロキシモードのどちらもレスポンスがストリーミングされていたため、新しいスレッドが作成されてコネクションプールでコネクションがリークする可能性があった。しかし実際にはリダイレクトモードでデータを送信しないのでストリーミングは不要。
Luke Lau
同Changelogより
# activestorage/app/controllers/active_storage/base_controller.rb#L4
class ActiveStorage::BaseController < ActionController::Base
- include ActiveStorage::SetCurrent, ActiveStorage::Streaming
+ include ActiveStorage::SetCurrent
protect_from_forgery with: :exception
# activestorage/app/controllers/active_storage/blobs/proxy_controller.rb#L9
class ActiveStorage::Blobs::ProxyController < ActiveStorage::BaseController
include ActiveStorage::SetBlob
+ include ActiveStorage::Streaming
# activestorage/app/controllers/active_storage/representations/proxy_controller.rb#L9
class ActiveStorage::Representations::ProxyController < ActiveStorage::Representations::BaseController
+ include ActiveStorage::Streaming
+
つっつきボイス:「リダイレクトモードではデータを送信しないのでスレッドを作る必要はないと」「たしかに」「ActiveStorage::Streaming
は通常使わなくてもよいのでBaseControllerから外してProxyControllerに移動したんですね」
参考: §5.1 リダイレクトモード -- Active Storage の概要 - Railsガイド
参考: Rails API ActiveStorage::Streaming
🔗 YAML.load
をYAML.unsafe_load
に置き換えた
# activesupport/lib/active_support/encrypted_configuration.rb#L51
def deserialize(config)
- YAML.load(config).presence || {}
+ doc = YAML.respond_to?(:unsafe_load) ? YAML.unsafe_load(config) : YAML.load(config)
+ doc.presence || {}
end
つっつきボイス:「yamlパーサーであるpsych gemのバージョンアップに対応するためにYAML.load
がYAML.unsafe_load
に置き換えられた」「そういえば少し前にpsych 4.0.0でbreaking changeが入っていましたね」
参考: Ruby の Psych.safe_load(YAML.safe_load)の引数が Psych v4.0.0 から非互換になる - Secret Garden(Instrumental)
🔗 Object#instance_variable_names
を高速化
つっつきボイス:「これはActive Supportの最適化ですね」「instance_variable_names
って何に使うんでしょうか?」「文字どおりインスタンス変数のリストを返すメソッドですね」「あ〜なるほど」
# activesupport/lib/active_support/core_ext/object/instance_variables.rb#L27
- def instance_variable_names
- instance_variables.map(&:to_s)
+ if Symbol.method_defined?(:name) # RUBY_VERSION >= "3.0"
+ # Returns an array of instance variable names as strings including "@".
+ #
+ # class C
+ # def initialize(x, y)
+ # @x, @y = x, y
+ # end
+ # end
+ #
+ # C.new(0, 1).instance_variable_names # => ["@y", "@x"]
+ def instance_variable_names
+ instance_variables.map(&:name)
+ end
+ else
+ def instance_variable_names
+ variables = instance_variables
+ variables.map! { |s| s.to_s.freeze }
+ variables
+ end
+ end
end
「Ruby 3.0からSymbol#name
が使えるようになったので、String
のアロケーションをそれで避けたのか」「Ruby 3.1だと1.5倍ぐらい速くなっている!」
- Rails API:
Object#instance_variable_names
- Ruby 3.1 API:
Object#instance_variables
(Ruby 3.1 リファレンスマニュアル)
🔗 ActiveSupport::LoggerThreadSafeLevel#add
を削除
つっつきボイス:「不要になったので消したっぽい」「Ruby 2.7以上なら不要なメソッドで、現在のRailsがRuby 2.7以上が必須になったので消したんですね、なるほど」
参考: §1.2 Rubyバージョン -- Rails アップグレードガイド - Railsガイド
🔗 ガイド: カウンタキャッシュに追記
つっつきボイス:「これはドキュメントの更新ですね」「reset_counters
というメソッドがあるとは知らなかった」「あるモデルの主キーを更新すると、そのモデルの主キーを参照して計算しているカウンタキャッシュの値が実際の参照モデル数と一致しなくなってしまうので、reset_counters
で再計算させる」
# guides/source/association_basics.md#L1040
-Counter cache columns are added to the containing model's list of read-only attributes through `attr_readonly`.
+Counter cache columns are added to the owner model's list of read-only
+attributes through `attr_readonly`.
+If for some reason you change the value of an owner model's primary key, and do
+not also update the foreign keys of the counted models, then the counter cache
+may have stale data. In other words, any orphaned models will still count
+towards the counter. To fix a stale counter cache, use [`reset_counters`][].
+[`reset_counters`]: https://api.rubyonrails.org/classes/ActiveRecord/CounterCache/ClassMethods.html#method-i-reset_counters
🔗Rails
🔗 Railsのminitestを1分で終わらせる(Ruby Weeklyより)
つっつきボイス:「BuildKiteでテストが速くなったのかと思ったら、factory_botのファクトリーを修正して高速化したのね」
「そういえば、BuildKiteはRailsフレームワークのテストにも使われていますね↓」「そうそう、Railsで複数バージョンのテストを回すのに使っている: CIの設定をいろいろ変えてテストするときにこういうのを使いますね」「CircleCIの仲間みたいな感じですか」
参考: BuildKite -- Ruby on Rails/Rails
参考: 最先端の CI/CD ツール - CircleCI
「1分でテストが終わるってすごい」「個別のテストが高速ならやれるでしょうね」
#Frontend #Automated | Identify, Track & Fix Problematic Tests with Buildkite’s Test Analytics https://t.co/g3vQhQCP6y
— Yohan J. Rodríguez (@hasdid) January 27, 2022
🔗 Page ObjectでRailsのシステムテストをメンテ可能にする(Ruby Weeklyより)
つっつきボイス:「Page Objectってどこかで聞いたような」「こういうクラスを作ってテスト用のヘルパーにするという趣旨みたい↓」「TestPageとかRegistrationPageとかがそうなんですね」
# 同記事より
# test/pages/test_page.rb
class TestPage
include Rails.application.routes.url_helpers
attr_accessor :test, :page
def initialize(system_test, page)
@test = system_test
@page = page
end
def visit
@test.visit page_path
end
def page_path
raise "Override this method with the page path"
end
end
# test/pages/registration_page.rb
class RegistrationPage < TestPage
def register(user)
@test.fill_in "Email", with: user.email
@test.fill_in "Password", with: "12345671"
@test.fill_in "Password confirmation", with: "12345671"
@test.click_on "Sign up"
end
def page_path
new_user_registration_path
end
end
# test/pages/dashboard_page.rb
class DashboardPage < TestPage
def assert_logged_in
@test.assert_selector "h1", text: "Dashboard"
end
def page_path
dashboard_path
end
end
「ログインセッションを取るためのPage Objectぐらいはあってもいいけど、個人的にはあまり増やしたくない気持ちがありますね」「というと?」「テストコードにロジックを持つクラスを作ると、テストコードをテストしないといけなくなるので」「あ〜たしかに!」「そうなんですよ」「テストコードはベタがいいというヤツですね」
「もちろん、ログインセッションのように毎回同じことをやることがわかっている部分をこういうふうにPage Objectで自動化する↓のはありだと思いますし、実際にやります」
# 同記事より
# test/pages/registration_page.rb
class RegistrationPage < TestPage
def register(user)
@page.fill_in I18n.t("attributes.user.email"), with: user.email
@page.fill_in I18n.t("attributes.user.password"), with: user.password
@page.fill_in I18n.t("attributes.user.password_confirmation"), with: user.password
@page.click_on I18n.t("buttons.register")
end
def page_path
new_user_registration_path
end
end
🔗 Rails Best Practicesサイト
つっつきボイス:「お、年季の入ったサイト」「昔からあるRails Best Practicesというサイトで、rails_best_practices gemのチェック結果ではここを見るように言われるんですが、古くなっているのはあるかなと思って取り上げてみました」
「どれどれ、トップのエントリの内容↓がいきなり古い↓」「ありゃ」「今はTime.zone.now
ではなくTime.current
を使うべき」「そうそう、Time.current
ですよ〜」「一番新しいエントリが2014年か」「2010年のエントリが一番多いみたいですね」
# rails-bestpractices.comより
Time.zone.now
Time.zone.today
参考: When should DateTime.now.utc
vs. Time.current.utc
be used in Rails? - Stack Overflow
「rails_best_practices gemは今も更新されているみたいだけど」「名前のとおりRailsのベストプラクティスをチェックするという位置づけのようですが、今だとrubocop-railsと役割がちょっとかぶっていそう」「たしかに似てそうですね」「rubocop-rails↓は広く使われていて更新も盛んなので、自分だったらrubocop-railsを使うかな」「自分もrubocop-railsでいいかな」
「rails_best_practicesに限らず、この種のgemは深く考えずに適用するとハマる可能性があるので注意が必要ですね: 昔のベストプラクティスが今もそうとは限らないという話も含めて」「そうですね」
後で見ると、サイトは7年間更新されていませんでした↓
🔗 online_migrations: PostgreSQLマイグレーションをチェックするgem(Ruby Weeklyより)
つっつきボイス:「これは?」「PostgreSQLのマイグレーションで危険な操作があったら警告するそうです」
「このonline_migrationsと似たようなものを以前も見た気がする」「そういえばREADMEの末尾に書いてあるstrong_migrations↓は以前取り上げたことがあります(ウォッチ20170915)」
「結局マイグレーションで同じようなミスをする人は多いということでしょうね」「まあそうですよね」「strong_migrationsと同様、使いたい人が使えばよいと思います」
🔗 マイグレーションのドキュメントとして使う
「どちらのgemでもいいんですが、READMEに書かれている問題解説と修正方法を読んで参考にするといいんじゃないかな」「なるほど、マイグレーションをチェックするときのドキュメントとして使うんですね」「必要なことはだいたいREADMEに書かれているようです👍」
「これらのgemは警告のみのようですが、マイグレーションによっては自動修正しようがないものもあるんですよ: たとえばマイグレーション中にいったんデプロイをはさむとか」「あ、たしかに」「READMEにも書かれていますけど、たとえばカラムを変更するときなんかは、add_column
してからいったんデプロイして、データ移行が終わってからremove_column
する、といった操作が必要になりますし、カラムを削除するときのignored_columns
なんかもそう」
🔗 フィーチャーフラグを1時間で実装した話(Ruby Weeklyより)
# 同記事より
class Features
def self.configuration
Rails.configuration.features
end
def self.enabled?(feature)
configuration.fetch(feature.to_sym, false)
end
end
つっつきボイス:「フィーチャーフラグの実装は実際に簡単なので、やりたい人はどうぞ👍」
「ところで、自分はまだ使ったことはありませんが、フィーチャーフラグをWebインターフェイスで操作できるサービスがありますよね、たしかflipperあたりかな」「flipperは以前取り上げたことがありますね(ウォッチ20210330)↓」「エンジニアの手をわずらわせずにフィーチャーフラグを切り替えられたらいいなと思うときがあるので、そういうサービスを試してみたい気持ちがあります」「わかります」
🔗 その他Rails
つっつきボイス:「Sidekiqももう10年経つんですね🎉」「お〜そんなに」「まだ課金したことないけど」「お、有料版もあるんですか」「サイトにも記載があるように↓、Pro版以上でないと使えない機能もありますし、マルチプロセスはEnterprise版が必要」「Enterprise版は月2万か...」「Enterprise版の機能が必要になるほどの大規模サービスなら十分払えると思いますよ」
- サイト: Sidekiq
前編は以上です。
バックナンバー(2022年度第1四半期)
週刊Railsウォッチ: Rubyコンパイラの歴史動画、RubyのWebAssembly対応進む、ぼっち演算子の注意点ほか(20220126後編)
- 20220124前編 Webpackerが公式に引退宣言、『Everyday Rails』日本語版がRails 7に対応ほか
- 20220118後編 Ruby 2.5〜3.1ベンチマーク、Opal 1.4、JRubyが20歳に、2022年のCSSほか
- 20220117前編 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ウォッチタグ)