Libraries

Latest comments

Bootstrap 5に由来するDart Sassの非推奨警告を抑制する:Railsアプリ(dartsass-rails)を例に

wakairo @wakairo

問題:Bootstrap 5に由来する大量の非推奨警告

Bootstrapは、 Sassの仕様変更に対して長期的に取り組みを進めている 最中であるため、Dart Sassでコンパイルすると以下のような非推奨(Deprecation)警告が大量に出力されます。

Deprecation Warning [import]: Sass @import rules are deprecated and will be removed in Dart Sass 3.0.0.

More info and automated migrator: https://sass-lang.com/d/import

  ╷
1 │ @import "bootstrap/mixins/banner";
  │         ^^^^^^^^^^^^^^^^^^^^^^^^^
  ╵
    _bootstrap.scss 1:9                          @use
    app/assets/stylesheets/application.scss 1:1  root stylesheet

この大量の警告にまぎれてしまい、自分で書いたコードに対する警告を見つけにくくなるという問題があります。 その結果、自分のコードに警告が出ていないことを確認するだけでも、手間と時間がかかってしまいます。

なお、2024年にリリースされたDart Sass 1.80.0以降、@importなどの非推奨警告がより積極的に出力されるようになり、この問題がさらに目立つようになりました。

解決策:--quiet-depsで依存ライブラリへの非推奨警告を抑制する

この問題の解決策は、sassコマンドを実行する際に--quiet-depsオプションを付与することです。 これにより、Bootstrapを含む依存ライブラリ(厳密には、ロードパス経由でインポートされたコード)に対する非推奨警告が出力されなくなります。 なお、ロードパス経由でない自分のコードに対する警告は引き続き出力されます。

Railsアプリ(dartsass-rails)で--quiet-depsを付与する方法

dartsass-rails gemを利用するRailsアプリでは、 config/initializers/dartsass.rbを開いて(なければ新規作成して)、 以下のコードを記述します。

Rails.application.config.dartsass.build_options << "--quiet-deps"

参考情報

0
Raw
https://www.techtips.page/en/comments/1116

Railsモデルのクラスメソッドはメソッドチェーンでも呼べる ― 注意点とscopeへ整理する方針

wakairo @wakairo

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つのアイデアです。 例えば、「findcreateなど、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_sprice: 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返却エラーを防いだり、コードの読み書きを容易にしたり出来ます。
0
Raw
https://www.techtips.page/en/comments/1115

Active Recordのallが持つ条件を引き継ぐ性質と動的クエリ構築への応用

wakairo @wakairo

RailsのActive Recordのallメソッドは、 その名前から「テーブルに存在する全レコードを取得するメソッド」と理解されがちです。 しかし実際には「その時点までに積み上げられた条件に該当するものを全て取得する」という実務上とても重要な性質を持っています。

本記事では、このallの性質の基本と、動的にデータベースクエリを組み立てる際の実践的な応用テクニックについて解説します。

allは「構築済みの条件」を引き継ぐ

データベースにPenモデルのレコードが3件保存されているとします。 以下のようにPenクラスに対して直接allを呼び出すと、テーブルに存在する全てのレコードが取得されます。

test(dev):000> Pen.all
  Pen Load (1.2ms)  SELECT "pens".* FROM "pens" /* loading for pp */ LIMIT 11 
=>
[#<Pen:0x00007cdf641ba6f0
  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:0x00007cdf66b23208
  id: 2,
  color: "blue",
  price: 80,
  created_at: "2026-02-20 05:53:03.457025000 +0000",
  updated_at: "2026-02-20 05:53:03.457025000 +0000">,
 #<Pen:0x00007cdf66b230c8
  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">]

では、別の条件に続けてallを呼び出すとどうなるでしょうか。 以下の実行例を見ると分かるように、allは「全て」ではなく、「それまでの条件を満たすもの全て」を返します。 ここではcolor: "red"を満たすレコードだけが取得されています。

test(dev):000> Pen.where(color: "red").all
  Pen Load (0.1ms)  SELECT "pens".* FROM "pens" WHERE "pens"."color" = 'red' /* loading for pp */ LIMIT 11 
=>
[#<Pen:0x00007cdf66b20648
  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:0x00007cdf66b20508
  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">]

このように、allは単に全件取得する機能だけでなく、 手前に条件がある場合には「そこまでに構築されたクエリ条件(Relationオブジェクト)を引き継ぎ、そのまま返す」という機能を担う側面があります。

応用例:動的クエリ構築においてallを起点にしてメソッドチェーン可能にする

この「そこまでに構築された条件を引き継ぐ」というallの性質は、 「URLのクエリパラメータが存在する場合だけ条件を追加する」といった動的なデータベースクエリ構築において役立ちます。

例えば、クエリパラメータを処理するfilter_byというスコープを定義することを考えてみましょう。 このスコープは、以下のように「1年以内に作成されたレコード」という前提条件(where)のあとにメソッドチェーンで呼び出される想定です。

test(dev):000> Pen.where(created_at: 1.year.ago..).filter_by(color: "red", max_price: 100)
  Pen Load (0.4ms)  SELECT "pens".* FROM "pens" WHERE "pens"."created_at" >= '2025-02-21 05:50:22.982591' AND "pens"."color" = 'red' AND "pens"."price" <= 100 /* loading for pp */ LIMIT 11 
=>
[#<Pen:0x000072d2c03393d8
  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">]

この挙動を実現するfilter_byスコープの実装は以下のようになります。 最初にallを呼び出して現在のRelation(既に構築済みのwhere条件など)を変数pensに格納し、 そのpensに対してパラメータの有無をチェックしながら条件を継ぎ足しています。

class Pen < ApplicationRecord
  scope :filter_by, ->(params) do
    pens = all
    pens = pens.where(color: params[:color]) if params[:color].present?
    pens = pens.where(price: ..params[:max_price]) if params[:max_price].present?
    pens
  end
end

ここで重要になるのが、filter_byの中で最初にallを呼んで変数に入れている点です。
allは「何も条件がない状態」ではなく、 「このスコープが呼ばれた時点のRelationをそのまま受け取る」役割を果たしています。 そのため、Pen.where(created_at: …)のように手前で条件を付けていても、 それをリセットすることなくさらに条件を積み上げられます。 つまり、allを変数に入れることで、 手前の条件を維持した上でさらに条件を積み上げていくための「起点」を作ることができるのです。 なお、クエリパラメータが全て空で条件追加がない場合でも、allが返した元のRelationがpensに入っているため、 メソッドチェーンをそのまま継続できます。

(参考)個別スコープへの切り出しとそのタイミング

先ほどの例ではfilter_byの内部で変数に再代入しながら条件を追加していましたが、 もしcolorpriceによる絞り込みをアプリケーション内の他の場所で単独で利用したい場合は、 それぞれを個別のスコープとして切り出すことでコードをよりキレイに書くことができます。

Active Recordのスコープは、ブロックの評価結果がnilまたはfalseになった場合、 自動的に元のRelationをそのまま返す(条件を追加せずにスルーする)という便利な仕様になっています。 そのため、以下のようにメソッドチェーンでそのままつなぐだけで、条件分岐をスコープの中に自然にカプセル化できます。

class Pen < ApplicationRecord
  scope :with_color, ->(color) { where(color: color) if color.present? }
  scope :with_max_price, ->(max_price) { where(price: ..max_price) if max_price.present? }
  scope :filter_by, ->(params) do
    with_color(params[:color]).with_max_price(params[:max_price])
  end
end

なお、YAGNI原則の観点からは、 これらの個別スコープ(with_colorwith_max_price)が他の場所で「本当に必要になるまで」は、 無理に切り出さない方が良いでしょう。 つまり、最初から再利用性を過剰に意識してスコープを量産するよりは、 まずは前述のpens = allを使うアプローチで1箇所にまとめておき、 個別スコープが本当に必要になったタイミングで切り出す方が、 結果的には開発コストを減らせるはずです。

0
Raw
https://www.techtips.page/en/comments/1114

RailsのAction MailerとAction Mailboxはmail gemを利用している

wakairo @wakairo

RailsでMailクラスに遭遇したら、それはmail gemのものです。ですので、そのAPI情報などは、Railsの情報源ではなく、以下のようなmail gemの情報源を見ると得られます。

0
Raw
https://www.techtips.page/en/comments/1109

Active Recordマイグレーション:t.referencesとadd_referenceで外部キー制約を詳細設定する方法

wakairo @wakairo

要点

Active Recordのマイグレーションファイルにてt.referencesまたはadd_referenceを使う際、 foreign_key:オプションに対してtrueの代わりに ハッシュを渡すことで外部キー制約の詳細を設定 できます。

このハッシュで指定できるオプションはadd_foreign_keyのAPIドキュメントに記載されているオプションです。

具体例

t.references を使う場合(新規テーブル作成時)

create_table :books do |t|
  t.references :author, foreign_key: { on_delete: :cascade }
end

add_reference を使う場合(既存テーブルへの追加)

add_reference :books, :author, foreign_key: { on_delete: :cascade }

説明

Active Recordのマイグレーションファイルにてテーブル間に関連付けをする際には、 t.referencesまたはadd_referenceを使うと便利です。

この2つのメソッドは、呼び出すときにforeign_key:オプションを付けると、 外部キー制約も同時に付与することが出来ます。例えば、foreign_key: trueという記述をよく見かけます。

foreign_key: trueは外部キー制約をかけることだけの指定となりますが、 ここでのtrueの代わりにハッシュを渡すことで、外部キー制約の詳細が設定可能になります。

このハッシュの中に入れられるオプション、つまり外部キー制約の詳細設定項目は、add_foreign_keyに渡せるオプションと同じです。 このadd_foreign_keyに渡せるオプションの具体的な種類とそれぞれの意味については、 add_foreign_keyのAPIドキュメントをご覧ください。

0
Raw
https://www.techtips.page/en/comments/1108

Active Recordマイグレーションでのadd_foreign_keyとadd_referenceの違い

wakairo @wakairo

Railsガイドを一読しただけでは、 add_foreign_keyとadd_referenceが、それぞれどのようなもので、どう違うのかがいまいちよく分かりませんでした。 そこで、add_foreign_keyとadd_referenceによってdb/schema.rbがどのように変化するかを基に、 それぞれの機能について確認してみました。

add_foreign_key

add_foreign_keyは外部キー制約の追加だけを行います。よって、カラムやインデックスの追加は行いません。

外部キー制約とは、子テーブルが参照している親のIDが親テーブルに存在することをデータベースレベルで保証する制約です。

例えば、以下の記述をマイグレーションファイルに行ったとします。

add_foreign_key :products, :users

すると以下の記述がdb/schema.rbに追加されます。

add_foreign_key "products", "users"

この記述は、products(子テーブル)のuser_idカラムに登場するIDが、users(親テーブル)のidカラムに必ず存在するように制約をかけています。

add_reference

add_referenceは、テーブル間の関連付けに関する複数の設定を一度に行える便利メソッドです。

add_referenceの基本機能は、カラムとインデックスの追加です。 例えば、以下の記述をマイグレーションファイルに行ったとします。

add_reference :products, :user

すると以下のように、users(親テーブル)に関連付ける2つの記述、具体的にはuser_idのカラムとインデックスをproducts(子テーブル)に追加する2つの記述が、db/schema.rbに追加されます。

t.integer "user_id"
t.index ["user_id"], name: "index_products_on_user_id"

またadd_referenceは、foreign_key:trueオプションを追加することで、前述の2つに加えて外部キー制約も同時に設定できます。 例えば、以下の記述をマイグレーションファイルに行ったとします。

add_reference :products, :user, foreign_key:true

すると以下のように、前述の2つの記述に加えて、add_foreign_keyの記述がdb/schema.rbに追加されます。

t.integer "user_id"
t.index ["user_id"], name: "index_products_on_user_id"
add_foreign_key "products", "users"

(参考)add_referenceとt.referencesの機能は基本的に同じ

add_referenceとcreate_tableのブロックの中で呼び出すt.referencesは基本的に同じ機能を提供します(オプション以外の引数の部分で違いはありますが。) 実際にt.referencesへ渡せるオプションはadd_referenceと同じです。 また、t.referencesの実装add_referenceの実装のどちらもReferenceDefinition.newを内部で呼ぶ形になっています(v8.1.2で確認)。

0
Raw
https://www.techtips.page/en/comments/1107

ActiveRecord::Rollbackで例外を伝播させずにロールバック後の処理を継続する

wakairo @wakairo

Active RecordのTransactionブロック内で例外が投げられた場合、ActiveRecord::Rollback以外の例外はロールバックの後に再度投げられます。

したがって、処理失敗で例外を投げるメソッド(save!等)を使えば、基本的にその例外はロールバックの後Transactionブロックの外へ伝播します。 これをキャッチしなければ、RailsはHTTPのエラーレスポンスをブラウザ等のクライアントに返します。 つまり、「処理に失敗したら、ロールバックして、あとの処理は切り上げて、エラーレスポンスを返す」という挙動は簡単に実装できます。

一方で、「ロールバック後に例外を出さずに処理を継続する」場合には、ActiveRecord::Rollbackを活用できます。 以下は、Transactionの成否でリダイレクト先を変えるコード例です。

completed = false

ActiveRecord::Base.transaction do
  unless obj1.save && obj2.save
    raise ActiveRecord::Rollback
  end
  completed = true
end

if completed
  redirect_to completed_path
else
  redirect_to uncompleted_path
end
0
Raw
https://www.techtips.page/en/comments/1106

minitest6にアップデートするとrails testがエラーになる

takuma_tech Takuma @takuma_tech

1/9にリリースされたRails v8.1.2は、minitest6で問題なさそうです。簡単にテストした分には、正常にテストが走りました。

ちなみにv8.1.1は、minitest6にすると、エラーにはならないものの、テストが全く拾われず0 runs, 0 assertions, 0 failures, 0 errors, 0 skipsになっていました。

0
Raw
https://www.techtips.page/en/comments/1085
😄2
🔄1
❤️1

minitest6にアップデートするとrails testがエラーになる

wakairo @wakairo

現象

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"
0
Raw
https://www.techtips.page/en/comments/1082
😿3
🔄1
🔧1

SprocketsからPropshaftへの移行に関する注意点

wakairo @wakairo

Propshaftへ移行したらSprockets用の設定は削除可能

config/environments/production.rb にある以下の設定はSprocketsの設定なので、 Propshaftへ移行したら、削除可能です。

config.assets.compile = false

参考情報

https://www.techtips.page/ja/comments/658

0
Raw
https://www.techtips.page/en/comments/1081