rails
Ruby on Rails
Topics
- Rails 8.0からRails 8.1への移行(アップデート、アップグレード)で必要な作業
- Railsモデルのクラスメソッドはメソッドチェーンでも呼べる ― 注意点とscopeへ整理する方針
- RailsのAction MailerとAction Mailboxはmail gemを利用している
- minitest6にアップデートするとrails testがエラーになる
- rails 7.2からrails 8.0への移行(アップデート、アップグレード)で必要な作業
- rails 7.2で追加されたGitHubワークフローの設定ファイルci.ymlの内容について
- 既存のRailsアプリへのDev Containerの導入
- 既存のRailsアプリへのRuboCopの導入
- 既存のRailsアプリでのDependabotへの対応
- 既存のRailsアプリへのBrakemanの導入
Railsモデルのクラスメソッドはメソッドチェーンでも呼べる ― 注意点とscopeへ整理する方針
Railsでモデルにクラスメソッドを定義すると、メソッドの内容に関わらず、メソッドチェーン(ActiveRecord::Relation)経由でもそのメソッドを呼び出せます。 これが便利な場合がある一方で、エラーにはならないものの、意味的に不正確なコードが書けてしまったり、気を付けないとバグの温床になったりすることもあります。 特に、リレーションを返す用途のクラスメソッドと、そうでないクラスメソッドが混在している場合に混乱が生じやすくなります。 本記事ではその仕組みと注意点、そして実務上の整理方針を解説します。
モデルのクラスメソッドがActiveRecord::Relation経由でも呼べる仕組み
以下のようにPenクラスにクラスメソッドを定義したとします。
class Pen < ApplicationRecord
def self.with_color(color)
if color.present?
where(color: color)
else
all
end
end
end
定義したこのメソッドは以下のように呼べます。これはRubyにおける通常のクラスメソッド呼び出しです。
test(dev):000> Pen.with_color("red")
Pen Load (0.1ms) SELECT "pens".* FROM "pens" WHERE "pens"."color" = 'red' /* loading for pp */ LIMIT 11
=>
[#<Pen:0x00007b3619af2060
id: 1,
color: "red",
price: 80,
created_at: "2026-02-20 05:52:53.460900000 +0000",
updated_at: "2026-02-20 05:52:53.460900000 +0000">,
#<Pen:0x00007b361a437f80
id: 3,
color: "red",
price: 150,
created_at: "2026-02-20 05:53:10.752331000 +0000",
updated_at: "2026-02-20 05:53:10.752331000 +0000">]
では、メソッドチェーンでの呼び出しはどうなっているでしょうか。
まず、whereなどのスコープメソッドを呼び出した結果は、以下で確認できる通り、
PenクラスではなくPen::ActiveRecord_Relationのインスタンスになります。
test(dev):000> Pen.where(created_at: 1.year.ago..).class
=> Pen::ActiveRecord_Relation
このリレーションインスタンスに対しても、クラスメソッドを以下のように呼び出せます。 つまり、モデルに定義したクラスメソッドは、そのモデルに対応したActiveRecord::Relationクラスのインスタンスからも呼べる仕組みになっているのです。この仕組みが、クラスメソッドをメソッドチェーンから呼べるようにしています。
test(dev):000> Pen.where(created_at: 1.year.ago..).with_color("red").count
Pen Count (0.5ms) SELECT COUNT(*) FROM "pens" WHERE "pens"."created_at" >= '2025-02-21 07:00:00.454690' AND "pens"."color" = 'red'
=> 2
メソッドチェーンで呼べてしまうからこそ、モデルのクラスメソッドは責務と名前を明確にする
with_colorのようにクエリを組み立てるメソッドであれば、メソッドチェーンで呼び出せても違和感がないと思います。
しかし、データベースクエリと関係のないメソッドでもクラスメソッドであれば同様にリレーション経由で呼び出せてしまいます。
その結果、クラスメソッドであれば、その処理内容に関わらず、クエリを組み立てるメソッドを間に挟んだメソッドチェーンにおいて、エラーにならず実行できてしまいます。
具体例を見てみましょう。export_to_csvがクラスメソッドだとすると以下のようにwhereを間に挟んでもエラーにならずに実行できます。
Pen.where(created_at: 1.year.ago..).export_to_csv(1, 2)
ではexport_to_csvメソッドは、countのように「条件に合ったレコードに対する処理をするメソッド」なのでしょうか?
それとも、クエリ構築とは関係がない「ユーティリティメソッド」で、このメソッドチェーンで呼び出すコードは何かしらのミスの結果なのでしょうか?
この例から分かるのは、モデルクラスに定義するクラスメソッドの名前はより慎重に付けなくてはならないということです。 名前だけではありません。メソッドの責務についても、クエリ用途であるか否かなど、明確さがあることが望まれます。
そのため、モデルのクラスメソッドの責務と名前に一定の規則性を持たせることも1つのアイデアです。
例えば、「findやcreateなど、Active Recordのクエリメソッドで使われる単語から名前が始まる場合は、必ずクエリ関連の責務とする」といったルールが考えられます。
また、名前だけでなくコードドキュメントにおいても、他のメソッド以上に使い方をはっきりと示すことが望まれます。
クエリ構築用クラスメソッドでelse節を省くとメソッドチェーンの途中でエラーを招く
クエリ構築用のクラスメソッドの場合には、条件分岐のelse節を省いてしまうと、メソッドチェーンの途中で予期せぬエラーを引き起こす原因になるという注意点もあります。
以下のように、先ほどのwith_colorメソッドからelse節を削除してみましょう。
class Pen < ApplicationRecord
def self.with_color(color)
if color.present?
where(color: color)
end
end
end
引数が有効値のとき(color.present?が真になるとき)はエラーになりませんが、
以下のように引数が空文字列で条件を満たさない場合はNoMethodErrorが発生してしまいます。
test(dev):000> Pen.where(created_at: 1.year.ago..).with_color("").count
(test):0:in '<main>': undefined method 'count' for nil (NoMethodError)
これは、以下のように条件を満たさなかった場合の戻り値がnilになってしまうためです。
nilに対してcountメソッドを呼び出そうとしたため、エラーになったのです。
test(dev):000> Pen.where(created_at: 1.year.ago..).with_color("")
=> nil
クエリ構築用メソッドとしてクラスメソッドを設計するなら、 常にActiveRecord::Relationインスタンス(またはそれに準ずるオブジェクト)を返す設計にしておくことが求められます。
リレーションを返す場合はscopeに統一する
クエリ構築の一環としてリレーションを返すことを意図したメソッドは、 クラスメソッドではなくscopeでの定義に統一するのが有効です。 これは、前述したモデルのクラスメソッドに関する困難さを和らげる、シンプルなアプローチの1つです。
1つのプロジェクト内でクラスメソッドでの定義とscopeでの定義を混在させることは、 前述のクラスメソッドの注意点とscopeの注意点を足し合わせることになり、チーム開発における理解コストを上げてしまいます。 では、混在をやめてどちらに統一するのが良いかと言えば、scopeに統一することが有力な戦略です。以下にその理由を3つ述べます。
理由1: scopeとクラスメソッドでは挙動に違いがある
まず大きな違いは、nilまたはfalseを返したときの挙動です(この点については理由3で詳しく後述します)。
また、レシーバとしてモデル名を書いた際にも明確な挙動の違いが確認できます(下記「参考」を参照)。
これらに限らず、他の挙動の差異が潜んでいる可能性もあります。
このように仕様や挙動に差異がある場合、二通りのやり方が混在していると「片方で問題ない書き方がもう片方でバグになる」という厄介さを抱えることになります。また、両方の仕様・挙動に対する知識が求められるため、コードベースの理解コストも上がってしまいます。
(参考)モデルクラス名を明示的にレシーバとした場合の挙動の違い
クエリ構築を行うscopeとクラスメソッドでは、モデルクラス名を明示的にレシーバとして書いてしまうと、 前の条件を引き継ぐか否かに違いが出てしまいます。
具体的には、まず以下のように、Pen.where(color: color)とだけ記述したscopeとクラスメソッドをそれぞれ定義します。
class Pen < ApplicationRecord
scope :with_color_s, ->(color) { Pen.where(color: color) }
def self.with_color_m(color)
Pen.where(color: color)
end
end
次に、それぞれを実行してみると、以下のような違いが出ます。
test(dev):000> Pen.where(price: 80).with_color_m("red")
Pen Load (0.2ms) SELECT "pens".* FROM "pens" WHERE "pens"."price" = 80 AND "pens"."color" = 'red' /* loading for pp */ LIMIT 11
=>
[#<Pen:0x00007ad76b175648
id: 1,
color: "red",
price: 80,
created_at: "2026-02-20 05:52:53.460900000 +0000",
updated_at: "2026-02-20 05:52:53.460900000 +0000">]
test(dev):000> Pen.where(price: 80).with_color_s("red")
Pen Load (0.2ms) SELECT "pens".* FROM "pens" WHERE "pens"."color" = 'red' /* loading for pp */ LIMIT 11
=>
[#<Pen:0x00007ad76ae02d08
id: 1,
color: "red",
price: 80,
created_at: "2026-02-20 05:52:53.460900000 +0000",
updated_at: "2026-02-20 05:52:53.460900000 +0000">,
#<Pen:0x00007ad76ae02bc8
id: 3,
color: "red",
price: 150,
created_at: "2026-02-20 05:53:10.752331000 +0000",
updated_at: "2026-02-20 05:53:10.752331000 +0000">]
クラスメソッドのwith_color_mは前の条件where(price: 80)が適切にマージされているのに対し、
scopeのwith_color_sはprice: 80の条件が無視されています(where(color: color)のようにレシーバPenを消せば正しくマージされます)。
なお、この違いの確認には執筆時の最新版であるRails 8.1.2を用いました。
理由2: scopeには「リレーションを返す」という共通認識がある
クラスメソッドは、用途が極めて広い汎用的な仕組みです。そのため、メソッド名だけでは「チェーン可能なクエリメソッドなのか」が判断しづらくなります。
一方、scopeは「クエリを組み立ててActiveRecord::Relationを返すためのもの」という開発者間の共通認識があります。 また、scopeは通常のクラスメソッド定義方法とは異なるため、メソッドチェーンで利用できることも自然に受け入れられます。
以上から、scopeの方が、コードの読み書きに必要な認知的負荷が低いと言えます。
理由3: scopeでは、else節を省いた簡潔な記載が可能
RailsのAPIドキュメントによれば、
nilまたはfalseが返された場合にその時点までに構築済みのクエリ条件(allに相当)を返すというのがscopeの仕様です。
そのため、以下のscope定義にはelse節がありませんが、メソッドチェーンの途中で呼び出されてもエラーを引き起こしません。
class Pen < ApplicationRecord
scope :with_color, ->(color) do
if color.present?
where(color: color)
end
end
end
これにより、先ほど述べたクラスメソッドが持つ「nilを返してチェーンが壊れる」問題を回避しつつ、条件付きクエリを簡潔に表現できます。
さらにRubyでは、全く同じ処理を以下の一行に書き直せます。
class Pen < ApplicationRecord
scope :with_color, ->(color) { where(color: color) if color.present? }
end
このようにscopeを使うことで、意図が明確かつ簡潔なコードを書くことが出来ます。
まとめ
- クエリ用途ではないクラスメソッドを定義するときには、メソッドチェーンで呼び出されることが不自然であると伝わるように、責務や名前の明確さが重要です。
- リレーションを返すメソッドをscopeでの定義に統一することで、
nil返却エラーを防いだり、コードの読み書きを容易にしたり出来ます。
RailsのAction MailerとAction Mailboxはmail gemを利用している
RailsでMailクラスに遭遇したら、それはmail gemのものです。ですので、そのAPI情報などは、Railsの情報源ではなく、以下のようなmail gemの情報源を見ると得られます。
- GitHub: https://github.com/mikel/mail
- APIドキュメント: https://www.rubydoc.info/gems/mail/
minitest6にアップデートするとrails testがエラーになる
現象
2025/12/18にリリースされたminitest 6.0.0のgemにアップデートしたところ、
bin/rails testの実行で以下のエラーが発生して止まりました。ちなみに、テストが1つも無い状態ではこのエラーは発生しませんでした。
Running 1 tests in a single process (parallelization threshold is 50)
Run options: --seed 32547
# Running:
/x/vendor/bundle/ruby/3.4.0/gems/railties-8.0.4/lib/rails/test_unit/line_filtering.rb:7:in 'run': wrong number of arguments (given 3, expected 1..2) (ArgumentError)
from /x/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.0/lib/minitest.rb:467:in 'block (2 levels) in Minitest::Runnable.run_suite'
from /x/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.0/lib/minitest.rb:463:in 'Array#each'
from /x/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.0/lib/minitest.rb:463:in 'block in Minitest::Runnable.run_suite'
from /x/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.0/lib/minitest.rb:505:in 'Minitest::Runnable.on_signal'
from /x/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.0/lib/minitest.rb:492:in 'Minitest::Runnable.with_info_handler'
from /x/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.0/lib/minitest.rb:462:in 'Minitest::Runnable.run_suite'
from /x/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.0/lib/minitest.rb:355:in 'block in Minitest.run_all_suites'
from /x/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.0/lib/minitest.rb:355:in 'Array#map'
from /x/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.0/lib/minitest.rb:355:in 'Minitest.run_all_suites'
from /x/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.0/lib/minitest.rb:310:in 'Minitest.run'
from /x/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.0/lib/minitest.rb:84:in 'block in Minitest.autorun'
とりあえずの回避策
Gemfileのgroup :testのところに以下の記述を追加してminitestのバージョンを6よりも下に設定したところエラーは出なくなりました。
group :test do
gem "minitest", "< 6"
1/9にリリースされたRails v8.1.2は、minitest6で問題なさそうです。簡単にテストした分には、正常にテストが走りました。
ちなみにv8.1.1は、minitest6にすると、エラーにはならないものの、テストが全く拾われず0 runs, 0 assertions, 0 failures, 0 errors, 0 skipsになっていました。
rails 7.2からrails 8.0への移行(アップデート、アップグレード)で必要な作業
基本的にはRailsガイドの手順に従えば良いと思います。
ガイドの手順にもありますが、ぜひbin/rails app:updateコマンドを活用しましょう。
rails 8.0への移行で対応が必要そうな個別の作業について、以下のコメントでそれぞれ取り上げますので、ご参考になれば幸いです。
不要であればアイコン画像の削除
bin/rails app:updateコマンドを実行すると以下の2つのアイコン画像ファイルがpublic/に作成されますが、
これら2つのファイルは、rails newで生成される新規railsアプリ用のファイルであるため、既存アプリでは単純に削除してしまい、
従来通りのアイコン画像とアイコン設定(faviconやapple-touch-icon)を用いれば大丈夫です。
(もちろん、既存アプリにおいてアイコン設定が適切に行われている場合の話です。)
- icon.png
- icon.svg
より詳細な情報はrails 7.2への移行におけるアイコン画像のコメントをご覧ください。
Rails 8.0への移行におけるActive Storageのmigration
bin/rails app:updateコマンドを実行すると以下の3つのファイルがdb/migrate/に作成されることがありますが、
Active Storageをまだ使用したことがないアプリケーションでは、
これら3つのファイルは単純に削除してしまっても良いはずです。
- xxx_add_service_name_to_active_storage_blobs.active_storage.rb
- xxx_create_active_storage_variant_records.active_storage.rb
- xxx_remove_not_null_on_active_storage_blobs_checksum.active_storage.rb
詳しくは、「Rails 7.1への移行におけるActive Storageのmigration」のコメントをご覧ください。
Rails 8.0ではfilter_parametersに:cvvと:cvcが追加された
rails newで生成されるconfig/initializers/filter_parameter_logging.rbにおいて、
Rails 8.0でconfig.filter_parametersに:cvvと:cvcが追加されました。
この追加に関するプルリクエストはこちらです。
セキュリティの観点からは、確かにクレジットカードのCVCとCVVをフィルタすることは適切であるように思いますので、既存アプリにおいても:cvvと:cvcを追加すると良さそうです。
Rails 8.0におけるdeprecation関連の設定の削除
rails newで生成されるconfig/environments/test.rbと同development.rbにおいて、
7.2以前は以下の設定用コードが存在していましたが、
このプルリクエストのマージによって削除されました。
# Raise exceptions for disallowed deprecations.
config.active_support.disallowed_deprecation = :raise
# Tell Active Support which deprecation messages to disallow.
config.active_support.disallowed_deprecation_warnings = []
削除された理由は、当該プルリクエストによると、アプリが新規生成された直後は非推奨の問題と無縁であるということからのようです。 ですので、非推奨の問題が出てくる可能性のある既存アプリにおいては適切な設定をしたままにしておくのが良さそうです。
Rails 8.0におけるpublic_file_server.headersの設定の変更
rails newで生成されるconfig/environments/test.rbと同development.rbにおいて、
このプルリクエストのマージによって、
config.public_file_server.headersのキーが以下のように小文字になりました。
- config.public_file_server.headers = { "Cache-Control" => "public, max-age=#{2.days.to_i}" }
+ config.public_file_server.headers = { "cache-control" => "public, max-age=#{2.days.to_i}" }
変更された理由はRack 3への対応のためですので、既存アプリでも設定を変更するのが良さそうです。
Rails 8.0におけるquery_log_tags_enabled
config.active_record.query_log_tags_enabledは、SQLクエリのログに実行時情報のコメントを付加する機能を有効にするかどうかの設定です。
rails newで生成されるconfig/environments/development.rbにおいて、
8.0からは以下のようにこの設定が有効になるコードになりました(この変更のPR)。
ちなみに、default値は7.2から変わらずfalseですので、
development.rb内の当該設定を意図的に変更しなければ、既存アプリの挙動が勝手に変わることはありません。
# Append comments with runtime information tags to SQL queries in logs.
config.active_record.query_log_tags_enabled = true
確かにdevelopment環境において便利そうな機能ですので、8.0へのアップデートを機に、有効にしても良いかもしれません。
Rails 8.0におけるassets.quiet
config.assets.quietは、アセット関連リクエストのログ出力を無効にするかどうかの設定です。後述のように、Rails 8.0.0でtrueにする以下の設定が削除されましたが、復活させるPRが出ていますので、既存アプリで削除するかには慎重な判断が必要かもしれません。
# Suppress logger output for asset requests.
config.assets.quiet = true
rails newで生成されるconfig/environments/development.rbにおいて、
8.0.0と8.0.1ではこの設定が削除されています(この変更のPR)。
しかし、この設定を削除するとターミナル出力がとっちらかるので設定を復活させるというPRがマージ済みですので、今後のバージョンでは設定が復活します。
Rails 8.0におけるaction_controller.perform_cachingとaction_mailer.perform_caching
config.action_controller.perform_cachingとconfig.action_mailer.perform_cachingは、両方ともキャッシュ機能を有効にするかどうかの設定です。
rails newで生成されるconfig/environments/test.rbにおいて、
8.0からは以下のようにこれらの設定を無効にするコードが削除されました(この変更のPRとこの変更のCommit)。
- # Show full error reports and disable caching.
+ # Show full error reports.
config.consider_all_requests_local = true
- config.action_controller.perform_caching = false
config.cache_store = :null_store
- # Disable caching for Action Mailer templates even if Action Controller
- # caching is enabled.
- config.action_mailer.perform_caching = false
公式として8.0からはテスト環境ではキャッシュが効いた状態を標準とするということだと思いますので、既存アプリでも同じ設定変更、つまり、設定の削除をしても良いかもしれません。
Rails 8.0におけるassets.compile
config.assets.compileは、動的なSprocketsコンパイルを有効にするかどうかの設定です。
rails newで生成されるconfig/environments/production.rbにおいて、
8.0からは以下のようにこの設定が削除されているのですが(この変更のPRとこの変更のCommit)、
これは8.0からrails newで生成されるアプリのアセットパイプラインのデフォルトがPropshaftになったことに起因するものです。
- # Do not fall back to assets pipeline if a precompiled asset is missed.
- config.assets.compile = false
ですので既存アプリは、Propshaftへ移行する前はこの設定は削除しない方が良いと思われます。
Rails 8.0におけるpublic_file_server.headersとassume_ssl
rails newで生成されるconfig/environments/production.rbにおいて、
8.0からは以下の設定2つが追加されているのですが(この変更のPRとこの変更のCommit)、
これは8.0からrails newで生成されるアプリでKamal 2対応が行われていることに起因するものです。
+ # Cache assets for far-future expiry since they are all digest stamped.
+ config.public_file_server.headers = { "cache-control" => "public, max-age=#{1.year.to_i}" }
# Assume all access to the app is happening through a SSL-terminating reverse proxy.
- # Can be used together with config.force_ssl for Strict-Transport-Security and secure cookies.
- # config.assume_ssl = true
+ config.assume_ssl = true
ですので既存アプリでは、Kamal 2対応をしないのであればこれらの設定の追加はしなくても良いと思われます。
Rails 8.0におけるsilence_healthcheck_path
config.silence_healthcheck_pathは、ログ出力を抑制すべきヘルスチェックのパスを指定する設定です。
rails newで生成されるconfig/environments/production.rbにおいて、
8.0からは以下のようにこの設定を行うコードが追加されました(この変更のPRとこの変更のCommit)。
# Prevent health checks from clogging up the logs.
config.silence_healthcheck_path = "/up"
ヘルスチェックでログが詰まる問題が起きている場合や見やすさなどの理由でヘルスチェックのログを抑制をしたい場合は既存アプリでも同じ設定変更、つまり、設定の追加をしても良いかもしれません。
Rails 8.0におけるaction_mailer.perform_caching
config.action_mailer.perform_cachingは、メーラーテンプレートでフラグメントキャッシングを行うかどうかの設定です。
rails newで生成されるconfig/environments/production.rbにおいて、
8.0からは以下のようにこの設定を行うコードが削除されました(この変更のPRとこの変更のCommit)。このキャッシングを無効にするコードの削除により、メーラーテンプレートでのフラグメントキャッシングが有効になります。
- # Disable caching for Action Mailer templates even if Action Controller
- # caching is enabled.
- config.action_mailer.perform_caching = false
既存アプリでもメーラーテンプレートでのフラグメントキャッシングを有効にしたい場合は、このコードの削除により可能です。
Rails 8.0におけるactive_record.attributes_for_inspect
config.active_record.attributes_for_inspectは、Active Recordオブジェクトのinspectに関わる設定です。
rails newで生成されるconfig/environments/production.rbにおいて、
8.0からこの設定を行う以下のコードが追加されました(この変更のPR)。このコードの追加により、production環境ではinspectの結果に基本的にidのみが含まれるようになります。
# Only use :id for inspections in production.
config.active_record.attributes_for_inspect = [ :id ]
この設定はproduction環境でのパフォーマンスの悪化をさけるための変更に端を発していますので、既存アプリでもこのコードを追加すると良いかもしれません。
Rails 8.0におけるlogger
config.loggerは、Railsアプリで用いるロガーの設定です。
rails newで生成されるconfig/environments/production.rbにおいて、
8.0からこの設定について以下の変更が行われました(この変更のPRその1とPRその2)。この変更により、Kamralを用いたproduction環境ではタイムスタンプが二重に表示されなくなります。
- # Log to STDOUT by default
- config.logger = ActiveSupport::Logger.new(STDOUT)
- .tap { |logger| logger.formatter = Logger::Formatter.new }
- .then { |logger| ActiveSupport::TaggedLogging.new(logger) }
-
- # Prepend all log lines with the following tags.
+ # Log to STDOUT with the current request id as a default log tag.
config.log_tags = [ :request_id ]
+ config.logger = ActiveSupport::TaggedLogging.logger(STDOUT)
既存アプリでも、production環境でタイムスタンプが二重になっている場合には、この変更を行ってタイムスタンプが1つになるようにすると良いかもしれません。
rails 7.2で追加されたGitHubワークフローの設定ファイルci.ymlの内容について
rails 7.2では、新規アプリに対してデフォルトでGitHubワークフローの設定ファイルであるci.ymlが生成されるようになりました。そこで、このci.ymlの内容について簡単に解説します。
なお、ci.ymlの最新のテンプレートはこちらでご覧になれます。
scan_ruby
bin/brakemanコマンドを実行するジョブです。
Railsの一般的なセキュリティ脆弱性がないかどうかチェックします。
scan_js
bin/importmap auditコマンドを実行するジョブです。
利用しているJavaScriptパッケージにセキュリティ上の脆弱性がないかどうかチェックします。
lint
bin/rubocopコマンドを実行するジョブです。
Rubyの静的コード解析を行い設定されているルールに準拠しているかどうかチェックします。
test
bin/rails testコマンドを実行するジョブです。なお、skip_system_testオプションが無効であれば、システムテストも実行します。
既存のRailsアプリへのDev Containerの導入
Rails 7.2においてDev Container設定を生成する機能が追加されました。 例えば既存アプリでは、以下のコマンドで既存アプリ用のDev Container設定を生成できるようになりました。
rails devcontainer
生成した設定は、Visual Studio Codeで利用でき、Dev Containerを利用した既存アプリの開発が可能となります。 必要となるソフトウェアのインストール等、Dev Containerでの開発を開始するための手順についてはDev Containerでの開発ガイドが参考になります。
なお、Visual Studio Code以外のエディタでもDev Containerで開発できるようにするべくDev Container CLIというツールの開発が進んでいますが、2024年12月現在では、ポートフォワーディングに対応していないなど、今のところはまだまだ開発途中のツールであり、実開発に投入するにはまだ早い段階である印象です。
既存のRailsアプリへのRuboCopの導入
Rails 7.2からRuboCopが新規アプリケーションでデフォルトで有効になりました。 このTopicでは、7.1以前で作成した既存RailsアプリにRuboCopを後から導入して、RuboCopに関して7.2の新規アプリ相当の状態にセットアップする方法をご紹介します。
RuboCop (Omakase Ruby styling for Rails) のインストール
基本的にはrubocop-rails-omakaseの公式ドキュメントのインストール手順に従います。
まず以下のように、Gemfileのgroup :development, :testのところにrubocop-rails-omakaseを追加します。
group :development, :test do
# Omakase Ruby styling [https://github.com/rails/rubocop-rails-omakase/]
gem "rubocop-rails-omakase", require: false
end
次にbundle installを実行して、RuboCop等をインストールします。
bundle install
さらに、必須ではありませんが、bin/rubocopでRuboCopを実行できるように、以下のコマンドを実行します。
bundle binstubs rubocop
最後に.rubocop.ymlという名前でファイルを作成し、以下の内容を記述します。
# Omakase Ruby styling for Rails
inherit_gem:
rubocop-rails-omakase: rubocop.yml
以上でRuboCop (Omakase Ruby styling for Rails) のセットアップが出来ました。
ローカル環境でのbin/rubocopの実行とその結果に対する対応の進め方
以下のコマンドでbin/rubocopを実行でき、omakaseのチェックを行えます。
bin/rubocop
このチェックでの指摘事項が多かった場合、以下のオプションを付けて実行することで、
無視設定を記述した.rubocop_todo.ymlというファイルが作成され、
.rubocop.ymlにもこの無視ファイルを参照する設定が追加されますので、
とりあえず全ての指摘事項を無視するように設定が出来ます。
bin/rubocop --auto-gen-config
設定が出来たら、bin/rubocopを実行して、とりあえず指摘事項の数が0となることを確認します。
1つずつ指摘事項へ対応
ここから先は、以下の手順を繰り返します。
-
.rubocop_todo.ymlから1項目を削除 -
bin/rubocopを実行して指摘内容を確認して対応bin/rubocop -aもしくはbin/rubocop -Aを実行して自動修正できるものは自動修正し、その修正内容で問題ないことを確認- 自動修正できなかったものは手作業で修正・確認
- 特定のファイルのみチェックを回避する場合は、
.rubocop.ymlにてExclude:を用いて設定 - ソースコードの特定の箇所(行)のみチェックを回避する場合は、ソースファイルのコメントに
# rubocop:disable等を用いて設定 - 指摘内容に対応する規則を完全に採用しない場合には、
.rubocop.ymlにてEnabled: falseの記述を用いて規則を無効にする
1項目ずつ対応を進め、.rubocop_todo.ymlの中身が無くなったら、.rubocop_todo.ymlを削除すると共に、.rubocop.yml内で.rubocop_todo.ymlを参照している設定を削除します。ここまで終わればRuboCopの諸規則への対応は完了となります。お疲れ様でした。
GitHubワークフロー(CI)でのrubocopの実行
.github/workflows/ci.ymlに相当するファイルがなければ作成します。 この.ymlファイルを編集して、以下のようにjobsの下にlintジョブを追加します。
注意点
timeout-minutes:の設定は、実行時間に対して十分余裕を持たせて下さい。ruby-version: .ruby-versionという設定は、プロジェクトルートにある.ruby-versionという名前のファイルで指定されているrubyのバージョンという意味になります。この設定について詳しくはこちらのTopicをご覧下さい。-f githubオプションは、出力フォーマットをGitHub Actionsに適したものにするオプションです。
jobs:
lint:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: .ruby-version
bundler-cache: true
- name: Lint code for consistent style
run: bin/rubocop -f github
公式情報
- RuboCopのその他の詳細についてはRuboCop公式ドキュメントをご覧ください。
- Omakase Ruby styling for RailsのRuboCop設定の最新の内容はGitHubで確認出来ます。なお、最新ではなくインストールしたバージョンのRuboCop設定内容をご覧になりたい場合は、GitHubの操作でTagsから当該バージョンを選択するようにして下さい。
既存のRailsアプリでのDependabotへの対応
Rails 7.2から新規アプリケーションにおいてDependabotがデフォルトで有効になりました。
具体的には、rails newで生成される新規アプリにおいて、Dependabotの設定ファイルである.github/dependabot.ymlが生成されるようになりました。
Dependabotとは
Dependabotとは、GitHubのサービスであり、リポジトリで使用しているソフトウェアを最新の状態に保つことをサポートしてくれるサービスです。 具体的なDependabotの機能としては、以下の3つがあります。
- Dependabot alerts — リポジトリで使っている依存関係に内在する脆弱性について通知します。
- Dependabot security updates — 使っている依存関係のうち、既知のセキュリティ脆弱性があるものを更新するための pull request を自動的に生成します。
- Dependabot version updates — 依存関係を最新に保つための pull request を自動的に発行します。
Dependabotの詳細な理解にはクイックスタート ガイドなどをご利用ください。
既存のRailsアプリでのDependabotへの対応方法
脆弱性の通知機能(Dependabot alerts)は、GitHubのWebページから設定を変更するだけで利用できます。
脆弱性に関するpull requestの自動生成機能(Dependabot security updates)も、GitHubのWebページから設定を変更するだけで利用できますが、詳細な設定が必要な場合はdependabot.ymlを通して行います。
脆弱性対応以外も含めて最新の状態にするためのpull requestを自動生成する機能(Dependabot version updates)は、dependabot.ymlを通した設定が必要です。
(参考)デフォルトのdependabot.ymlの内容
このリンク先で閲覧できるrails newで生成される新規アプリのdependabot.ymlの設定内容は、既存アプリでも参考になるかもしれません。
このdependabot.ymlには、Rails 7.2の時点では、railsアプリで利用しているgemとGitHub Actions(GitHubのCI)で利用しているアクションを最新に保つための設定が記述されています。
既存のRailsアプリへのBrakemanの導入
Rails 7.2から新規アプリケーションにおいてBrakemanがデフォルトで有効になりました。 このTopicでは、7.1以前で作成した既存RailsアプリにBrakemanを後から導入して、Brakemanに関して7.2の新規アプリ相当の状態にセットアップする方法をご紹介します。
Brakemanのインストール
まず以下のように、Gemfileのgroup :development, :testのところにbrakemanを追加します。
group :development, :test do
# Static analysis for security vulnerabilities [https://brakemanscanner.org/]
gem "brakeman", require: false
end
次にbundle installを実行します。
bundle install
以上でBrakemanのインストールが出来ました。
さらに、必須ではありませんが、bin/brakemanでBrakemanを実行できるように、以下のコマンドを実行します。
bundle binstubs brakeman
ローカル環境でのBrakemanの実行
以下のコマンドでBrakemanを実行でき、デフォルトのチェックを行えます。
bin/brakeman
全てのチェックを実行する場合には、以下のオプション付けて実行します。
bin/brakeman -A
警告の無視に関する設定と管理を行う場合は、以下のオプション付けて実行します。
bin/brakeman -I
以下のように、これら2つのオプションを同時に指定して実行することも出来ます。
bin/brakeman -IA
なお、Brakemanの詳細については公式ドキュメントをご覧ください。 また、brakemanコマンドのオプションについては日本語訳もあります。
GitHubワークフロー(CI)でのBrakemanの実行
.github/workflows/ci.ymlに相当するファイルがなければ作成します。
この.ymlファイルを編集して、以下のようにjobsの下にscan_rubyジョブを追加します。
ymlファイルの設定に関する注意点
timeout-minutes:の設定は、実行時間に対して十分余裕を持たせて下さい。- 「
ruby-version: .ruby-version」という設定は、プロジェクトルートにある.ruby-versionという名前のファイルで指定されているrubyのバージョンという意味になります。この設定についての詳細はこちらのTopicをご覧下さい。
jobs:
scan_ruby:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: .ruby-version
bundler-cache: true
- name: Scan for common Rails security vulnerabilities using static analysis
run: bin/brakeman --no-pager
bundle binstubs brakemanを用いるやり方に更新しました。
基本的にはRailsガイドの手順に従えば良いと思います。
ガイドの手順にもありますが、ぜひ
bin/rails app:updateコマンドを活用しましょう。Rails 8.1への移行で対応が必要そうな個別の作業について、以下のコメントでそれぞれ取り上げますので、ご参考になれば幸いです。
Rails 8.1におけるconfig.action_dispatch.verbose_redirect_logs
config.action_dispatch.verbose_redirect_logsは、リダイレクトのソース位置を関連するログ行の下にログ出力するかどうかの設定です。rails newで生成されるconfig/environments/development.rbにおいて、 8.1からは以下のようにこの設定を有効にするコードが追加されました(この変更のPRとこの変更のCommit)。development環境でデバッグする際に使える情報が増える形になると思いますので、既存アプリでもこの設定を追加しても良いかもしれません。
Rails 8.1におけるRails.configuration.action_view.remove_hidden_field_autocomplete
config.action_view.remove_hidden_field_autocompleteは、hiddenフィールドからautocomplete="off"属性を除去するかどうかの設定です。 (この機能のPRとこの機能のCommit)bin/rails app:updateコマンドが生成する config/initializers/new_framework_defaults_8_1.rbには、 以下のようにこの設定を有効にするコードがあります。この機能が導入された背景
この機能は、HTML標準へ より準拠したHTMLをRailsが生成するように追加されました。
具体的には、この機能を有効にしない場合、Railsは以下のような
autocomplete="off"属性を付けたhiddenフィールドを生成していました。このautocomplete属性付きのhiddenフィールドはHTML標準に沿っておらず、 Nu Html Checkerは 以下のメッセージとともに「エラー」として指摘していました。
Rails 8.1におけるconfig.content_security_policy_nonce_auto
config.content_security_policy_nonce_autoは、 Rails 8.1で追加された「CSPのnonceを自動で追加する機能」を有効にするかどうかの設定です。rails newで生成されるconfig/initializers/content_security_policy.rbにおいて、 8.1からは以下のようにこの設定のコードが追加されました(この変更のPRとこの変更のCommit)。デフォルトではCSP設定自体がコメントアウトされており、この機能の設定もコメントアウトされています。
Railsガイドでは、CSPについての説明が「Content-Security-Policyヘッダー」の項目にあり、 このnonceを自動で追加する機能の説明が「nonceを追加する」の項目にあります。