- Ruby / Rails関連
週刊Railsウォッチ: 英国政府サイトで使われるRailsアプリ、pg-oscとPercona Toolkitほか(20220308前編)
こんにちは、hachi8833です。
🔗Rails: 先週の改修(Rails公式ニュースより)
以下のコミットログのうち、Changelogに反映されているものから見繕いました。
🔗 CSPの修正
content_security_policy
が無効なディレクティブを返すことがある問題を修正。
lambda呼び出しの結果によるディレクティブで、self
やunsafe-eval
などが一重引用符で囲まれないことがあった。
content_security_policy do |policy|
policy.frame_ancestors lambda { [:self, "https://example.com"] }
end
この修正によって、上で生成されるポリシーが有効になるようになった。
同Changelogより
つっつきボイス:「CSPのヘッダーで'self'
がself
になってしまうことがあったのを修正したんですね: 明らかにバグ」「frame_ancestors
という属性初めて知りました」
参考: CSP: frame-ancestors
- HTTP | MDN
🔗 skip_forgery_protection
の挙動を修正。
- PR: Allow skip_forgery_protection if no protection set by brtrick · Pull Request #44537 · rails/rails
フォージェリー保護が有効になってない状態で
skip_forgery_protection
を実行してもエラーにならないよう修正。
この修正により、Rails 7.0でdefault_protect_from_forgery
をfalseにした場合にWelcomeページ(/
)でArgumentError
が発生しなくなる。
Brad Trick
同Changelogより
つっつきボイス:「Action Mailboxに修正が入っていますね↓」「ほんとだ」「でもそれ以外にも関連していそう: たまたまActionController::Base
を継承してかつskip_forgery_protection
を使っていたのがここだけだったんでしょうね」「Welcome画面でArgumentError
が出たらびっくり」
# actionmailbox/app/controllers/action_mailbox/base_controller.rb#L3
module ActionMailbox
# The base class for all Action Mailbox ingress controllers.
class BaseController < ActionController::Base
- skip_forgery_protection if default_protect_from_forgery
+ skip_forgery_protection
before_action :ensure_configured
🔗 リダイレクトのレスポンスからbodyコンテンツを削除
つっつきボイス:「リダイレクトでbodyコンテンツが返されてたんですか?」「言われてみれば"You are being redirected"みたいなbodyはありましたね↓: テストでrequest specを書いたりするとこういうのが入ってくる」「そういえばあったかも」
# actionpack/lib/action_controller/metal/redirecting.rb#L88
self.status = _extract_redirect_to_status(options, response_options)
self.location = _enforce_open_redirect_protection(_compute_redirect_to_location(request, options), allow_other_host: allow_other_host)
- self.response_body = "<html><body>You are being <a href=\"#{ERB::Util.unwrapped_html_escape(response.location)}\">redirected</a>.</body></html>"
+ self.response_body = ""
end
「これが削除されると表示が変わるんでしょうか?」「リダイレクトレスポンスなので、ブラウザの挙動上ユーザーに表示されることはありませんね: curl
とかでアクセスしたりすると表示される」「たしかに」「テストの修正は必要になるのかな?」「テストでリダイレクトのbodyをチェックしていない限り大丈夫だと思います: リダイレクトのテストでチェックするのは基本的にステータスコードとロケーションですね」「なるほど」「むしろこの修正でテストが落ちるとしたら、そのテストが的はずれな部分をチェックしている可能性があるかも」
「プルリクによると、リダイレクトのレスポンスにbodyがあるとW3Cバリデータ↓でエラーになるんですって」「今のままでも問題は起きなさそうですけど、W3Cバリデータに怒られるなら削除しようという流れなんでしょうね」
# 同PRより
Warning: Consider adding a lang attribute to the html start tag to declare the language of this document.
Error: Start tag seen without seeing a doctype first. Expected <!DOCTYPE html>.
Error: Element head is missing a required instance of child element title.
参考: The W3C Markup Validation Service
🔗 ActionContoller::Live
のIsolatedExecutionState
を修正
- PR: Copy over the IsolatedExecutionState in AC::Live by casperisfine · Pull Request #44499 · rails/rails
短命なスレッドでIsolatedExecutionStateをコピーするよう
ActionContoller::Live
を修正。
ActionContoller::Live
は当初から、ミドルウェアで設定されたCurrentAttributes
などをコントローラのアクションで保持するためにスレッドのローカル変数をコピーするようになっていた。
7.0でIsolatedExecutionState
が導入され、ActionContoller::Live
コントローラで一部のグローバルステートが失われていた。
Jean Boussier
同Changelogより
つっつきボイス:「ACってAction Controllerだったのか」「Live
はストリーミング系のコントローラで、例のCurrentAttributes
とも関連しているらしい」「IsolatedExecutionState
の一部が共有されていたのをちゃんと分けるようにしたようですね」
後で調べると、IsolatedExecutionState
はpublicではないようです↓。
参考: rails/isolated_execution_state.rb at 7-0-stable · rails/rails
🔗 フィクスチャのメモリフットプリントを削減
フィクスチャアクセサのメモリフットプリントを削減。
従来のフィクスチャアクセサはdefine_method
でeagerに定義されていたため、メモリ使用量がフィクスチャやテストスイートの個数に直接依存していた。
フィクスチャアクセサをmethod_missing
で実装したことで、メモリやCPUのオーバーヘッドを大幅に削減できた。
Jean Boussier
同Changelogより
つっつきボイス:「これはメモリ使用量の最適化か」「コードもだいぶ減りましたね↓」「チリも積もればというヤツで、こういう改修が積み重なって全体のパフォーマンスがよくなる👍」
# activerecord/lib/active_record/test_fixtures.rb#L57
def setup_fixture_accessors(fixture_set_names = nil)
fixture_set_names = Array(fixture_set_names || fixture_table_names)
- methods = Module.new do
+ unless fixture_set_names.empty?
+ self.fixture_sets = fixture_sets.dup
fixture_set_names.each do |fs_name|
- fs_name = fs_name.to_s
- accessor_name = fs_name.tr("/", "_").to_sym
-
- define_method(accessor_name) do |*fixture_names|
- force_reload = fixture_names.pop if fixture_names.last == true || fixture_names.last == :reload
- return_single_record = fixture_names.size == 1
- fixture_names = @loaded_fixtures[fs_name].fixtures.keys if fixture_names.empty?
-
- @fixture_cache[fs_name] ||= {}
-
- instances = fixture_names.map do |f_name|
- f_name = f_name.to_s if f_name.is_a?(Symbol)
- @fixture_cache[fs_name].delete(f_name) if force_reload
-
- if @loaded_fixtures[fs_name][f_name]
- @fixture_cache[fs_name][f_name] ||= @loaded_fixtures[fs_name][f_name].find
- else
- raise StandardError, "No fixture named '#{f_name}' found for fixture set '#{fs_name}'"
- end
- end
-
- return_single_record ? instances.first : instances
- end
-
- private accessor_name
+ key = fs_name.match?(%r{/}) ? -fs_name.to_s.tr("/", "_") : fs_name
+ key = -key.to_s if key.is_a?(Symbol)
+ fs_name = -fs_name.to_s if fs_name.is_a?(Symbol)
+ fixture_sets[key] = fs_name
end
end
- include methods
end
🔗 #attach
した添付ファイルの保存に成功するとblobを返すようになった
添付ファイルをレコードに保存するとblobオブジェクトを返すようになる。
従来は、添付ファイルを保存してもblobオブジェクトは返されなかった。
現在は、#attach
メソッドで添付ファイルを追加してレコードを保存すると、レコードにアタッチされたblobまたはblobの配列を返すようになった。添付ファイルの保存に失敗した場合はfalse
を返す。
Ghouse Mohamed
同Changelogより
つっつきボイス:「これはActive Storageですね」「今まではrecord.save
の結果を返していたのでtrueかfalseしか返さなかったけど、改修後は成功時にblob(binary large object)を返すようになったんですね: この方が扱いやすくてよさそう👍」「Rubyの条件式はnil
とfalse
以外はtrue
になるので、既存のロジックも変わらずに済みますね」
# activestorage/lib/active_storage/attached/one.rb#L57
def attach(attachable)
if record.persisted? && !record.changed?
record.public_send("#{name}=", attachable)
- record.save
+ if record.save
+ record.public_send("#{name}")
+ else
+ false
+ end
else
record.public_send("#{name}=", attachable)
end
end
🔗 パス名が空(""
)の場合にのみPathname.blank?
がtrueを返すよう修正
- PR:
Pathname.blank?
only returns true forPathname.new("")
by byroot · Pull Request #44485 · rails/rails
つっつきボイス:「言われてみれば、スペース文字だけのファイル名は作ろうと思えば作れますね」「ファイル名にスペースを混ぜるのがありなんだから、スペースだけのファイル名もありということになりますね」「で、今までは" "
も""
もPathname.blank?
がfalseになっていたけど、" "
ならfalseを返して""
ならtrueを返すのが正しい、たしかに」「これはバグ修正ですね」
# activesupport/test/core_ext/pathname/blank_test.rb
# frozen_string_literal: true
require_relative "../../abstract_unit"
require "active_support/core_ext/pathname/blank"
class PathnameBlankTest < ActiveSupport::TestCase
def test_blank
assert_predicate Pathname.new(""), :blank?
assert_not_predicate Pathname.new("test"), :blank?
assert_not_predicate Pathname.new(" "), :blank?
end
end
「それにしても、名前がスペースだけのファイルを作るという発想が今までなかった」「それに気づくようなコードを書いていた人がいるということなのかな?」「issue #44452を見ると、Pathname.presence
の挙動で驚いたのが修正のきっかけだったらしい」「なるほど、名前がスペースだけのファイルを作りたかったわけではなかった」
- Rails API:
Object#presence
🔗Rails
🔗 英国政府のサイトの多くはRailsで構築されている(Ruby Weeklyより)
つっつきボイス:「英国政府の公式サイトは70個ほどあって、そのうちRailsアプリがかなりたくさん使われているそうです」「へー、知りませんでした」「上の記事では政府がアプリを採用するときにどういうテストを行うかという基準を示しているそうです」「なるほど、こういうふうに事前に割と細かく仕様を示すところはありますね」「Railsが使われていると聞くと何となくイギリスに親近感を抱いちゃいました」「アプリをオープンソースベースで作っている公的機関は世界的に見れば結構あります」
🔗 pg-osc: PostgreSQLスキーマ変更ツール(Ruby Weeklyより)
つっつきボイス:「PostgreSQLのスキーマをゼロダウンタイムで変更する、よく話題になるヤツですね」「このpg-online-schema-change
というコマンドでやれるらしい↓」「略してoscなんですね」
# 同記事より
pg-online-schema-change perform \
--alter-statement 'ALTER TABLE books ADD COLUMN "purchased" BOOLEAN DEFAULT FALSE; ALTER TABLE books RENAME COLUMN email TO new_email;' \
--dbname "production_db" \
--host "localhost" \
--username "jamesbond" \
--password "" \
--drop
「このpg-oscは、pt-online-schema-changeにインスパイアされたと記事にあります」「お、pt-online-schema-changeってPercona Toolkitじゃないですか↓」「コマンド名のptをpgに変えたということなのかな」
参考: pt-online-schema-change -- Percona Toolkit
「Percona Toolkitといえば、これまでもよく話題に出てきた優秀なツールですね(ウォッチ20201020)」「元々MySQL向けでしたが今はPostgreSQL向けにもなりつつある、歴史も実績もあるツールです: なお昔はmaatkitという名前でした」「2008年からあるみたいなので歴史長いんですね」「Percona Toolkitにはかなり信頼を寄せています👍」
参考: Percona Toolkit (ペルコナツールキット) | MySQLチューニング/保守サポート/コンサルティングのスマートスタイル
「このツールにはMySQLのチューニングでかなりお世話になりました: かつてのMySQLはスロークエリを秒でしか指定できなかった時代があったんですよ」「秒だけだとキツい...」「あの頃Percona Toolkitがなかったらmsecで指定するのは無理でしたね」
🔗 その他Rails
How are you deploying Rails apps nowadays?
Capistrano? Containers? Serverlessly? Heroku? Dokku? EC2? Using GitHub Actions? Bare metal? It'd be interesting to see a thread of various approaches that are working for Rubyists as of 2022!
— Ruby Inside (@RubyInside) February 21, 2022
つっつきボイス:「ツイートのスレッドを追うと、Railsのデプロイにどんなものを使っているか人それぞれで楽しい」「Herokuだったり、GitHub Actionsだったり、DockerコンテナからFargateだったり、ssh経由でgit pullしたり、いろいろ」「いったんCDを設定したら後は気にしなくなりますけどね」「そうそう」
参考: CD(継続的デリバリー)とは? » CloudBees|テクマトリックス
前編は以上です。
バックナンバー(2022年度第1四半期)
週刊Railsウォッチ: Ruby標準のCSVライブラリは優秀、if代入のコーディングスタイル、rambulanceほか(20220301後編)
- 20220228前編 dartsass-railsがリリース、webpack-mergeツール、Rubyが29歳にほか
- 20220222後編: 端末文字幅とRubyのreline、SQLのプリペアドステートメント、Terraformほか
- 20220221前編 orderでコレーション指定をサポート、awesome_nested_set、GitHub Copilotほか
- 20220216後編 Bundler自身のバージョンロック機能、gem署名メカニズムの提案ほか
- 20220214前編 Rails 7.0.2の改修内容、receipts gemでレシートを作成ほか
- 20220209後編 Rubygems.orgのAPIキーに権限スコープが追加、RailsのDBパフォーマンス改善ほか
- 20220208前編 Rails 6.1を7.0にアップグレードしてみた、PostgreSQLでジョブキューほか
- 20220201後編 Rubygems Adoptionフォームが開設、JetBrains Gateway、NGINX Unitほか
- 20220131前編 Sidekiqが10歳に、BuildKiteのテストを高速化、フィーチャーフラグほか
- 20220126後編 Rubyコンパイラの歴史動画、RubyのWebAssembly対応進む、ぼっち演算子の注意点ほか
- 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ウォッチタグ)