Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails関連

Rails 7.1.0 Active Record CHANGELOG(翻訳)

rc2以後の更新はありません。

🔗 Active Record CHANGELOG(v7.1.0.rc2)

🔗 rails db:drop now removes -shm and -wal sqlite files by codergeek121 · Pull Request #49436 · rails/rails

rails db:drop実行時にSQLite3のshmファイルや-walファイルも削除するようになった。

Niklas Häusele
同CHANGELOGより

🔗 Revert "Merge pull request #49056 from joshuay03/raise-on-duplicate-a… · rails/rails@0ba14eb

同一クラス内で、ある関連付けに対する#accepts_nested_attributes_forが2回以上宣言された場合にArgumentErrorを発生する変更(#49056)を取り消し。

ここで取り消された振る舞いは、#accepts_nested_attributes_for内で定義したconcernが、そのconcernをincludeしているクラス内でオーバーライドされる場合に壊れる。

Rafael Mendonça França
同CHANGELOGより

🔗 Active Record CHANGELOG(v7.1.0.rc1)

🔗 Rename back unique keys to unique constraints by kamipo · Pull Request #49383 · rails/rails

関連: Add support for unique constraints (PostgreSQL-only). by alpaca-tc · Pull Request #46192 · rails/rails

(PostgreSQL)unique制約のネーミングを改善。

*_unique_keyという命名だと、uniqueインデックスの短縮形と誤解される。
*_unique_constraintという命名なら誤解されない。

Rails 7.1.0.beta1以前:

add_unique_key :sections, [:position], deferrable: :deferred, name: "unique_section_position"
remove_unique_key :sections, name: "unique_section_position"

変更後:

add_unique_constraint :sections, [:position], deferrable: :deferred, name: "unique_section_position"
remove_unique_constraint :sections, name: "unique_section_position"

Ryuta Kamizono
同CHANGELOGより

🔗 Fix duplicate escaping of quotes for check constraint expressions in MySQL schema by Flixt · Pull Request #42429 · rails/rails

MySQLを利用中、スキーマダンプ内のCHECK制約に含まれる一重引用符のエスケープが重複する(\')問題を修正。

CHECK制約に渡す式に引用符が既に含まれていると、mysql2アダプタのスキーマダンプが無効になっていた。

修正: #42424

Felix Tscheulin
同CHANGELOGより

🔗 Performance tune the SQLite3 adapter connection configuration by fractaledmind · Pull Request #49349 · rails/rails

SQLite3アダプタのコネクション設定のパフォーマンスを高速化。

Write-Ahead-Log(WAL)synchronous=NORMALモードで利用するようにし、ジャーナルサイズの上限(journal_size_limit)、健全なサイズの共有メモリバッファ(mmap_size)、共有キャッシュ(cache_size)を設定したことで、Railsアプリケーションでのパフォーマンスが平均して2倍改善された。

Stephen Margheim
同CHANGELOGより

参考: Enhancing your Rails app with SQLite | Fractaled Mind
参考: 週刊Railsウォッチ20231004: SQLite3アダプタのコネクション設定のパフォーマンスチューニング

🔗 Allow SQLite3 busy_handler to be configured with simple max number of retries by fractaledmind · Pull Request #49352 · rails/rails

SQLite3のbusy_handlerで、retriesをシンプルに上限回数まで行える設定を追加。

パフォーマンスが重要なアプリケーションでは、ビジーなコネクションを遅延なしで再試行するのが望ましい。database.ymlretries値(integer)で指定した回数に達するまで、busy_handler関数でビジーコネクションへの再接続を(待ち時間を指数増加せずに)試行するようになった。

Stephen Margheim
同CHANGELOGより

🔗 Add SQLite3 support for supports_insert_returning? by fractaledmind · Pull Request #49290 · rails/rails

SQLite3アダプタでsupports_insert_returning?をサポート。

supports_insert_returning?AbstractAdapter)を完全に実装することで、SQLite3アダプタでも自動生成カラム(#48241)とカスタム主キーをサポートする。

Stephen Margheim
同CHANGELOGより

参考: Rails API ActiveRecord::ConnectionAdapters::AbstractAdapter
参考: 週刊Railsウォッチ20231004: SQLite3でsupports_insert_returning?をサポート

🔗 Ensure the SQLite3 adapter handles default functions with the || concatenation operator by fractaledmind · Pull Request #49287 · rails/rails

SQLite3アダプタで、結合演算子||を含むデフォルト関数を処理できるようになった。

従来は、デフォルト関数で"'Ruby ' || 'on ' || 'Rails'"のように静的な文字列が生成されていた。
改修によって、"Ruby on Rails"を適切にアダプタに渡して利用できるようになった。

change_column_default "test_models", "ruby_on_rails", -> { "('Ruby ' || 'on ' || 'Rails')" }

Stephen Margheim
同CHANGELOGより

参考: 週刊Railsウォッチ20231004: SQLite3の結合演算子||をアダプタのデフォルト関数に渡せるようになった

🔗 dump PostgreSQL schemas as part of the schema dump by lsylvester · Pull Request #49164 · rails/rails

PostgreSQL固有のスキーマをschema.rbにダンプできるようにするcreate_schemaを追加。

Lachlan Sylvester
同CHANGELOGより

🔗 Active Record CHANGELOG(v7.1.0.beta1)

🔗 Encryption: support support_unencrypted_data at a per-attribute level by ghiculescu · Pull Request #49072 · rails/rails

属性ごとに設定されるsupport_unencrypted_dataオプションを暗号化に追加。

特定の暗号化済み属性だけをsupport_unencrypted_dataで暗号化しないように設定可能になった。
このオプションは、ActiveRecord::Encryption.config.support_unencrypted_data == trueが設定済みの場合にのみ有効。

class User < ActiveRecord::Base
  encrypts :name, deterministic: true, support_unencrypted_data: false
  encrypts :email, deterministic: true
end

Alex Ghiculescu
同CHANGELOGより

参考: 週刊Railsウォッチ20230926: 暗号化属性の平文データサポートをsupport_unencrypted_dataオプションで属性ごとに無効化できるようになった

🔗 Instrument Active Record transactions by ipc103 · Pull Request #49192 · rails/rails

Active Recordのトランザクションでinstrumentation(計測)を利用可能になった。

トランザクションイベントをサブスクライブすることでトラッキングやinstrumentationで利用できるようになる。イベントペイロードには、コネクションの他にタイミングの詳細詳細情報も含まれる。

ActiveSupport::Notifications.subscribe("transaction.active_record") do |event|
  puts "Transaction event occurred!"
  connection = event.payload[:connection]
  puts "Connection: #{connection.inspect}"
end

Daniel Colson, Ian Candy
同CHANGELOGより

参考: 週刊Railsウォッチ20230926: Active Recordのトランザクションをinstrumentationで計測できるようになった
参考: Active Support の Instrumentation 機能 - Railsガイド

🔗 Support composite foreign keys via migration helpers by fatkodima · Pull Request #47637 · rails/rails

マイグレーションヘルパーで複合主キーをサポート。

# "carts"テーブルの主キーが"(shop_id, user_id)"だとする

add_foreign_key(:orders, :carts, primary_key: [:shop_id, :user_id])

remove_foreign_key(:orders, :carts, primary_key: [:shop_id, :user_id])
foreign_key_exists?(:orders, :carts, primary_key: [:shop_id, :user_id])

fatkodima
同CHANGELOGより

参考: 週刊Railsウォッチ20230926: マイグレーションヘルパーで複合主キーをサポート

🔗 Adds support for if_not_exists when adding a check constraint. by ccutrer · Pull Request #49178 · rails/rails

マイグレーションにCHECK制約を追加するときのif_not_existsオプションをサポート。

add_check_constraint :posts, "post_type IN ('blog', 'comment', 'share')", if_not_exists: true

Cody Cutrer
同CHANGELOGより

参考: 週刊Railsウォッチ20230926: マイグレーションのadd_check_constraintif_not_existsオプションを渡せるようになった

🔗 [Fix #49055] Raise an ArgumentError when #accepts_nested_attributes_for is redeclared for an association by joshuay03 · Pull Request #49056 · rails/rails

注: このプルリクはその後0f7fe4aで取り消されました。

同じクラス内の関連付けに対して#accepts_nested_attributes_forが複数回宣言されるとArgumentErrorを発生するようになった。
従来は、最後の宣言が以前の宣言をエラーなしでオーバーライドしていた。サブクラスでのオーバーライドは引き続き許可されている。

Joshua Young
同CHANGELOGより

Rails API: ActiveRecord::NestedAttributes(翻訳)

🔗 Deprecate passing rewhere to merge by HParker · Pull Request #45498 · rails/rails

#mergeメソッドのrewhereオプションを非推奨化。

#mergeメソッドのrewhereオプションは置き換えなしに非推奨化され、Rails 7.2で削除される予定。

Adam Hess
同CHANGELOGより

参考: 週刊Railsウォッチ20230913: mergerewhereオプションを渡すことが非推奨化された

🔗 Fix unscope not working when where by tripe dot range by ippachi · Pull Request #48095 · rails/rails

unscopeが特定のケース(トリプルドット...)で動作しない問題を修正。

修正前:

Post.where(id: 1...3).unscope(where: :id).to_sql # "SELECT `posts`.* FROM `posts` WHERE `posts`.`id` >= 1 AND `posts`.`id` < 3"

修正後:

Post.where(id: 1...3).unscope(where: :id).to_sql # "SELECT `posts`.* FROM `posts`"

修正:#48094

Kazuya Hatanaka
同CHANGELOGより

参考: 週刊Railsウォッチ20230913: whereでトリプルドット... rangeを使うとunscopeが効かない問題を修正

🔗 Change has_secure_token default to on: :initialize by seanpdoyle · Pull Request #48912 · rails/rails

has_secure_tokenのデフォルトをon: :initializeに変更。

新しいデフォルト値をon: :createからon: :initializeに変更した。
この設定はconfig.active_record.generate_secure_token_onコンフィグで変更可能。

config.active_record.generate_secure_token_on = :create

Sean Doyle
同CHANGELOGより

参考: config.active_record.generate_secure_token_on -- Rails アプリケーションを設定する - Railsガイド

🔗 Fix change_column not setting precision for sqlite by skipkayhil · Pull Request #49090 · rails/rails

7.0以降のマイグレーションとSQLiteを利用するとchange_columndatetimeカラムがprecision: 6に設定されない問題を修正。

Hartley McGuire
同CHANGELOGより

🔗 Support composite identifiers in to_key by nvasilevski · Pull Request #48998 · rails/rails

to_keyで複合idをサポート。

#idが既に配列の場合はto_key#id値をArrayでラップしないようになる。

Nikita Vasilevsky
同CHANGELOGより

参考: Rails API to_key -- ActiveModel::Conversion
参考: Rails API to_key -- ActiveRecord::AttributeMethods::PrimaryKey

🔗 Make enums validatable without raising error by mechnicov · Pull Request #49100 · rails/rails

enumvalidateオプションを追加。

class Contract < ApplicationRecord
  enum :status, %w[in_progress completed], validate: true
end
Contract.new(status: "unknown").valid?   # => false
Contract.new(status: nil).valid?         # => false
Contract.new(status: "completed").valid? # => true

class Contract < ApplicationRecord
  enum :status, %w[in_progress completed], validate: { allow_nil: true }
end
Contract.new(status: "unknown").valid?   # => false
Contract.new(status: nil).valid?         # => true
Contract.new(status: "completed").valid? # => true

Edem Topuzov, Ryuta Kamizono
同CHANGELOGより

参考: 週刊Railsウォッチ20230913: Active Recordのenumにエラーをraiseしないvalidateオプションが追加された

🔗 Use already loaded relation when batching if possible by HParker · Pull Request #48876 · rails/rails

in_batchesメソッドが、可能な場合に読み込み済みのリレーションを利用するようになった。

既に読み込まれているリレーションでバッチメソッドを呼び出すと、データベースからレコードを再度取得せずに読み込み済みのレコードを使うようになる。

Adam Hess
同CHANGELOGより

参考: Rails API in_batches -- ActiveRecord::Batches

🔗 Deprecate read_attribute(:id) returning the primary key by adrianna-chang-shopify · Pull Request #49019 · rails/rails

主キーが:idでない場合にread_attribute(:id)が主キーを返す振る舞いを非推奨化。

Rails 7.2以後は、read_attribute(:id)はモデルの主キーに関係なくidカラムの値を返すようになる。主キーの値を取得するには、代わりに#idを使うこと。複合主キーモデルでは、read_attribute(:id)は今後もidカラムの値を返すようになる。

Adrianna Chang
同CHANGELOGより

参考: 週刊Railsウォッチ20230906: 主キーが:idでない場合に主キーを返すread_attribute(:id)を非推奨化

🔗 Fix 6.1 change_table setting datetime precision by skipkayhil · Pull Request #48974 · rails/rails

マイグレーション6.1のchange_tableが設定するdatetimeカラムの精度を修正。

Hartley McGuire
同CHANGELOGより

🔗 Fix 6.1 change_column setting datetime precision by skipkayhil · Pull Request #48969 · rails/rails

マイグレーション6.1のchange_columnが設定するdatetimeカラムの精度を修正。

Hartley McGuire
同CHANGELOGより

🔗 Use alias_attribute to provide id_value alias for id attribute by adrianna-chang-shopify · Pull Request #48930 · rails/rails

レコードのidカラムの生の値にアクセスするActiveRecord::Base#id_valueを追加。

このエイリアスは、:idカラムを宣言しているモデルに対してのみ提供される。

Adrianna Chang
同CHANGELOGより

参考: 週刊Railsウォッチ20230906: モデルのid属性をid_valueで取得できるようになった

🔗 Fix tracking previous changes for ActiveRecord::Store accessors with underlying JSON data column by rdimartino · Pull Request #43386 · rails/rails

JSONで構造化されたデータ型を使うカラムにおけるActiveRecord::Storeの直前の変更トラッキングを修正。

従来は、JSONで構造化されたデータベース型を持っているカラムに対してストアがstore_accessorとして定義されていると、最後に保存したときの変更にアクセスする以下のメソッドが動作しなかった。

  • #saved_change_to_key?
  • #saved_change_to_key
  • #key_before_last_save

Robert DiMartino
同CHANGELOGより

🔗 Support NULLS NOT DISTINCT in Postgres 15+ by grjones · Pull Request #48608 · rails/rails

PostgreSQL 15以降のインデックスでNULLS [NOT] DISTINCTを完全サポート。

前回の作業でマイグレーションでのインデックス作成は行えるようになっていたが、schema.rbではサポートされていなかった。また、NULLS [NOT] DISTINCTのマッチ順序が正しくなかったため、スキーマ検出が一定していなかった。

Gregory Jones
同CHANGELOGより

参考: 週刊Railsウォッチ20230823: PostgreSQL 15のNULLS NOT DISTINCTをサポート
参考: PostgreSQL 15.4ドキュメント CREATE INDEX

🔗 Allow escaping of literal colons in ActionRecord::Sanitization#replace_named_bind_variables by f3ndot · Pull Request #48852 · rails/rails

名前付きバインド変数が使われている場合にsanitize_sql_*メソッドでコロン:をエスケープするようになった。

Justin Bull
同CHANGELOGより

参考: 週刊Railsウォッチ20230823: ActionRecord::Sanitization#replace_named_bind_variablesでコロン:をエスケープできるよう修正

🔗 Fix #previously_new_record? on destroyed records by adrianna-chang-shopify · Pull Request #48796 · rails/rails

#previously_new_record?が削除済みレコードでtrueを返す問題を修正。

従来は、レコードを作成してから削除すると#previously_new_record?trueを返すことがあった。
修正後は、レコードに対するどのUPDATEおよびDELETEでも変更を考慮することで#previously_new_record?falseを返すようになった。

Adrianna Chang
同CHANGELOGより

参考: この修正はRails 7.0.7でリリース済みです

🔗 Specify when to generate has_secure_token by seanpdoyle · Pull Request #47420 · rails/rails

has_secure_tokenon:でコールバックを指定できるようになった。

class User < ApplicationRecord
  has_secure_token on: :initialize
end

User.new.token # => "abc123...."

Sean Doyle
同CHANGELOGより

訳注: デフォルトはon: createです。

参考: 週刊Railsウォッチ20230809: has_secure_tokenを生成するタイミングを指定可能になった

🔗 Fix counter_cache create/concat with overlapping counter_cache_column by casperisfine · Pull Request #48665 · rails/rails

関連付けのカラムがオーバーラップしている場合のインメモリカウンタキャッシュの増加を修正。

2つの関連付けに名前の似ているカウンタキャッシュカラムがある場合、Active Recordが間違った方をインクリメントする可能性があった。

Jacopo Beschi, Jean Boussier
同CHANGELOGより

🔗 Don't show secrets for Active Record's Cipher::Aes256Gcm#inspect. by p8 · Pull Request #48679 · rails/rails

Active RecordのCipher::Aes256Gcm#inspectでsecretsを表示しないようになった。

修正前:

ActiveRecord::Encryption::Cipher::Aes256Gcm.new(secret).inspect
"#<ActiveRecord::Encryption::Cipher::Aes256Gcm:0x0000000104888038 ... @secret="\xAF\bFh]LV}q\nl\xB2U\xB3 ... >"

修正後:

ActiveRecord::Encryption::Cipher::Aes256Gcm(secret).inspect
"#<ActiveRecord::Encryption::Cipher::Aes256Gcm:0x0000000104888038>"

Petrik de Heus
同CHANGELOGより

参考: Rails API ActiveRecord::Encryption::Cipher::Aes256Gcm

🔗 Active Record commit transaction on return, break and throw by casperisfine · Pull Request #48600 · rails/rails

トランザクションの振る舞いを非ローカルなreturnbreakthrowでコミットする以前の振る舞いに戻した。

Model.transaction do
  model.save
  return
  other_model.save # 実行されなくなった
end

かつて、エラーが発生した場合にのみロールバックがトリガーされていた時代があったが、Ruby 2.3のtimeoutライブラリが実行を中断するためにthrowを使うようになり、オープン中のトランザクションがコミットされるという逆効果が生じていた。

これを解決するために、Active Record 6.1ではトランザクションを(コミットではなく)ロールバックするように動作を変更していた(不完全なトランザクションをコミットする可能性よりも安全性が高いため)。
Rails 6.1以降は、transactionブロック内でのreturnbreakthrowの利用は根本的に非推奨となっていた。

しかし、timeout 0.4.0のリリースにより、Timeout.timeoutで(throwではなく)再びエラーをraiseするようになった。これによって、Active Recordの振る舞いを当初の(驚きの少ない)動作に戻すことが可能になった。

Rails 6.1より前の「歴史的な」振る舞いは、以下の設定で有効にできる。

Rails.application.config.active_record.commit_transaction_on_non_local_return = true

また、Rails 7.1で作成する新規アプリケーションはデフォルトでこの振る舞いになる。

Jean Boussier
同CHANGELOGより

参考: Deprecate committing a transaction exited with return or throw by dylanahsmith · Pull Request #29333 · rails/rails
参考: config.active_record.commit_transaction_on_non_local_return -- Rails アプリケーションを設定する - Railsガイド
参考: 週刊Railsウォッチ20230809: トランザクションがreturnbreakthrowでコミットするようになった

以下はRails 6.1で振る舞いが変更されたときの記事です。

Rails 6.1でreturnやbreakやthrowによるトランザクション終了が非推奨化(翻訳)

🔗 Deprecate name argument in remove_connection by eileencodes · Pull Request #48681 · rails/rails

#remove_connectionname引数を非推奨化。

#remove_connectionname引数は置き換えなしで非推奨化された。
#remove_connectionは、コネクションを確立するクラスで直接呼び出されるべき。

Eileen M. Uchitelle
同CHANGELOGより

参考: Rails API remove_connection -- ActiveRecord::ConnectionHandling

🔗 Fix has_one through singular building with inverse. by gmcgibbon · Pull Request #48674 · rails/rails

単数形の逆関連付けを経由するhas_one through:関連付けでレコードをビルド可能になった。

belongs_to through:関連付けでは、外部キーを主キーのモデルにリンクする必要はない。
has_oneの場合はこの関連付けがミュータブルではないため、レコードをビルドできなかった。

Gannon McGibbon
同CHANGELOGより

参考: 週刊Railsウォッチ20230802: belongs_to :inverse_ofを介したhas_one :through関連付けで、レコードをビルドできるように修正

🔗 Disable database prepared statements when query logs are enabled by zzak · Pull Request #48631 · rails/rails

データベースでクエリログが有効な場合はプリペアドステートメントを無効にするようになった。

クエリログは毎回一意のクエリを作成するため、プリペアドステートメントとクエリログは互換性がない。

zzak, Jean Boussier
同CHANGELOGより

参考: 週刊Railsウォッチ20230802: クエリログが有効な場合はプリペアドステートメントを無効にするよう修正

🔗 Add a encryption option to support previous data encrypted non-deterministically with a SHA1 hash digest by jorgemanrubia · Pull Request #48530 · rails/rails

SHA1ハッシュダイジェストで非決定論的に暗号化された既存データの復号をサポート

これにより、Active Recordの新しい暗号化オプションが追加され、SHA1ハッシュダイジェストで非決定論的に暗号化されたデータを復号できるようになる。

Rails.application.config.active_record.encryption.support_sha1_for_non_deterministic_encryption = true

新しいオプションは、7.0から7.1にアップグレードする際の問題に対処する。Active Record暗号化の初期化方法にバグがあったため、非決定論的暗号化に用いるキープロバイダは、RailsがRails.application.config.active_support.key_generator_hash_digest_classでグローバルに設定したものではなく、SHA-1をダイジェストクラスとして利用していた。

Cadu Ribeiro and Jorge Manrubia
同CHANGELOGより

参考: config.active_record.encryption.support_sha1_for_non_deterministic_encryption -- Rails アプリケーションを設定する - Railsガイド
参考: 週刊Railsウォッチ20230725: SHA1ハッシュダイジェストで非決定論的に暗号化された従来のデータの復号をサポート

🔗 Adding PG enum drop, rename, add value, rename value by rayfaddis · Pull Request #44898 · rails/rails

PostgreSQLのマイグレーションでenumのリネーム、値の追加、値のリネームが可能になった。

rename_enumおよびrename_enum_valueはリバース可能である。add_enum_valueはPostgreSQLの制約(enum値を削除できない)によりリバースできない。代替手段として、enum全体を削除してから再作成すること。

rename_enum :article_status, to: :article_state
add_enum_value :article_state, "archived" # will be at the end of existing values
add_enum_value :article_state, "in review", before: "published"
add_enum_value :article_state, "approved", after: "in review"
rename_enum_value :article_state, from: "archived", to: "deleted"

Ray Faddis
同CHANGELOGより

Rails: PostgreSQLアダプタでenumのリネーム、enum値の追加とリネームが可能になった(翻訳)

🔗 Allow composite primary key to be derived from schema by nvasilevski · Pull Request #47633 · rails/rails

スキーマから複合主キーを導出可能になった。

複合主キーを含むスキーマを持つアプリケーションを起動したときにwarningが発生しないようになり、ActiveRecord::Base#primary_keyの値がnilになることもなくなる。
以下のようなtravel_routesテーブル定義とTravelRouteモデルがあるとする。

create_table :travel_routes, primary_key: [:origin, :destination], force: true do |t|
  t.string :origin
  t.string :destination
end

class TravelRoute < ActiveRecord::Base; end

このTravelRoute.primary_keyの値は自動的に["origin", "destination"]に導出される。

Nikita Vasilevsky
同CHANGELOGより

参考: 週刊Railsウォッチ20230628: スキーマから複合主キーを導出可能になった

🔗 Store connection_pool in database-related exceptions by luanzeba · Pull Request #48295 · rails/rails

connection_poolにコネクションアダプタからraiseされる例外を保存するようになった。

例外を引き起こしたコネクションや、ロール、シャードなどのコンテキストがconnection_poolに追加される。

Luan Vieira
同CHANGELOGより

参考: 週刊Railsウォッチ20230628: データベース関連の例外をconnection_poolに保存するようになった

🔗 Support batching using composite primary keys and multiple column ordering by TakuyaKurimoto · Pull Request #48268 · rails/rails

複合主キーを持つテーブルのfind_eachfind_in_batchesin_batches:asc:descを指定可能になった。

複合主キーがあるテーブルでfind_eachfind_in_batchesin_batchesを実行するときに、キーごとに:asc:descを指定可能になる。

Person.find_each(order: [:desc, :asc]) do |person|
  person.party_all_night!
end

Takuya Kurimoto
同CHANGELOGより

参考: 週刊Railsウォッチ20230628: 複合主キーを持つテーブルのfind_eachfind_in_batchesin_batches:asc:descを指定可能になった

🔗 Fix polymorphic association subquery by lazaronixon · Pull Request #48362 · rails/rails

has_one/has_manyポリモーフィックリレーションの関連付けにおけるwhereの振る舞いを修正。

修正前:

Treasure.where(price_estimates: PriceEstimate.all)
#=> SELECT (...) WHERE "treasures"."id" IN (SELECT "price_estimates"."estimate_of_id" FROM "price_estimates")

修正後:

Treasure.where(price_estimates: PriceEstimate.all)
#=> SELECT (...) WHERE "treasures"."id" IN (SELECT "price_estimates"."estimate_of_id" FROM "price_estimates" WHERE "price_estimates"."estimate_of_type" = 'Treasure')

Lázaro Nixon
同CHANGELOGより

この修正は、Rails 7.0.6でリリース済みです

🔗 Assign auto populated columns on Active Record object creation by nvasilevski · Pull Request #48241 · rails/rails

Active Recordでのレコード作成時に自動生成カラムを割り当てられるようになる。

レコード作成ロジックを変更することで、auto_incrementカラムをレコード作成時に割り当て可能にする。これによって、モデルの主キーへのリレーションにかかわらず、レコード作成直後にauto_incrementカラムを割り当てられるようになる。

この変更によるメリットが最も大きいのはPostgreSQLアダプタで、RETURNINGステートメントを利用するレコードのINSERT後に、任意の個数の自動生成カラムをオブジェクトに割り当てられるようになる。

Nikita Vasilevsky
同CHANGELOGより

参考: その後#49290でSQLite3でも自動生成カラムをサポートしました。
参考: 週刊Railsウォッチ20230621: オブジェクト作成時にデータベース側の自動入力属性を割り当て可能にする

🔗 Set default_shard from connects_to hash by eileencodes · Pull Request #48353 · rails/rails

connected_toshardsハッシュの最初のキーをdefault_shardで使うようになった。

アプリケーションによっては、コネクションモデルのシャード名に:defaultを使いたくない場合がある。残念なことに、Active Recordはプールマネージャから正しいコネクションを得るために何らかのシャードを仮定しなければならないため、:defaultシャードの存在を期待する。

アプリケーションで強制的に手動設定する代わりに、connects_toがシャードのハッシュからデフォルトシャード名を推測して、最初のシャードをデフォルトであると仮定するようになる。

たとえば以下のようなモデルがあるとする。

class ShardRecord < ApplicationRecord
self.abstract_class = true

connects_to shards: {
  shard_one: { writing: :shard_one },
  shard_two: { writing: :shard_two }
}

これで、このクラスのdefault_shardshard_oneに設定されるようになる。

修正: #45390

Eileen M. Uchitelle
同CHANGELOGより

参考: 週刊Railsウォッチ20230621: connected_toshardsハッシュの最初のキーをdefault_shardで使うようになった

🔗 Fix change_in_place? for binary serialized columns by casperisfine · Pull Request #48274 · rails/rails

背後のカラムがバイナリエンコードになっているシリアライズ属性での変更検出を修正。

Jean Boussier
同CHANGELOGより

この修正は、Rails 7.0.5でリリース済みです
参考: 週刊Railsウォッチ20230613: change_in_place?の挙動を修正

🔗 Implement ActiveRecord.disconnect_all! to close all connections by casperisfine · Pull Request #47856 · rails/rails

全コネクションプールにある全コネクションを即座にクローズするActiveRecord.disconnect_all!を追加。

Jean Boussier
同CHANGELOGより

参考: 週刊Railsウォッチ20230613: マルチプルDBで使えるActiveRecord.disconnect_all!が追加

🔗 Discard connections which may be left in a transaction by nicholasdower · Pull Request #48200 · rails/rails

トランザクションに残っているコネクションを破棄する機能を改善。

エラーが原因で、within_new_transactionのコネクションがオープン中のトランザクションに予期せず残ってしまうことがある。そうなるとコネクションが再利用されてしまい、以下のようなエラーが発生する可能性がある。

  • 書き込みが実際は成功しているにもかかわらず失敗したように見える
  • 書き込みが実際は失敗しているにもかかわらず成功したように見える
  • 読み込んだデータが古い、またはコミットされていない

従来は以下のケースについては検出されていた。

  • トランザクション内でエラーが発生し、ロールバックを試行中に別のエラーが発生した場合

改修によって、以下のケースも検出されるようになった。

  • トランザクションの開始直後にエラーが発生した場合
  • トランザクションのコミット中にエラーが発生し、ロールバックを試行中に別のエラーが発生した場合
  • トランザクションのロールバック中にエラーが発生した場合

Nick Dower
同CHANGELOGより

🔗 Make Active Record's query cache an LRU by casperisfine · Pull Request #48110 · rails/rails

Active Recordのクエリキャッシュが直近で最も利用頻度の低い(LRU: least recently used)エントリを削除するようになった。

デフォルトでは、最も直近で利用された100件のクエリのみを維持する。
このキャッシュサイズはdatabase.ymlで変更可能。

development:
adapter: mysql2
query_cache: 200

クエリキャッシュそのものを無効にすることも可能。

development:
adapter: mysql2
query_cache: false

Jean Boussier
同CHANGELOGより

参考: 週刊Railsウォッチ20230607: 保持するクエリキャッシュを直近50件までに変更した

🔗 Deprecate check_pending! in favor of check_all_pending! by eileencodes · Pull Request #48134 · rails/rails

check_pending!を非推奨化。今後はcheck_all_pending!を使うこと。

check_pending!が保留中のマイグレーションをチェックする対象は、現在の(または渡された)データベースコネクションのみでマルチプルデータベースに対応していない。このcheck_pending!は非推奨化される。今後は、指定の環境にある複数のデータベース設定ですべての保留中マイグレーションをチェックするcheck_all_pending!を使うこと。

Eileen M. Uchitelle
同CHANGELOGより

参考: Rails API check_all_pending! -- ActiveRecord::Migration

🔗 Make increment_counter/decrement_counter accept an amount argument by fatkodima · Pull Request #48128 · rails/rails

increment_counterdecrement_counterby:オプションで増分値を渡せるようになった。

Post.increment_counter(:comments_count, 5, by: 3)

fatkodima
同CHANGELOGより

参考: 週刊Railsウォッチ20230607: Active Recordのカウンタインクリメントやデクリメントに1以外の増分量を効果的に指定できるようになった

🔗 Add intersects? to Relation by john-h-k · Pull Request #47670 · rails/rails

Array#intersect?ActiveRecord::Relationに追加。

Array#intersect?はRuby 3.1以降でのみ利用可能。

これにより、ActiveRecord::RelationでRuboCopのStyle/ArrayIntersect copを利用できるようになる。

John Harry Kelly
同CHANGELOGより

参考: 週刊Railsウォッチ20230524: ActiveRecord::Relationintersects?が追加された

🔗 deferrable foreign key can be passed to references by alpaca-tc · Pull Request #47671 · rails/rails

マイグレーションでdeferrableオプション(先延ばし可能)を有効にした外部キーをt.referencesに渡せるよう修正。

Hiroyuki Ishii
同CHANGELOGより

参考: 週刊Railsウォッチ20230524: マイグレーションでreferencesdeferrableオプション付きの外部キーを渡しても無視される問題を修正

🔗 Deprecate deferrable: true option of add_foreign_key by alpaca-tc · Pull Request #47659 · rails/rails

関連: #46192

add_foreign_keydeferrable: trueオプションを非推奨化。

非推奨化されたdeferrable: trueオプションはRails 7.2で削除される予定。今後はdeferrable: :immediateが推奨される。

非推奨化の理由は、deferrable: truedeferrable: :deferredの意味がわかりにくいため(どちらの値もtruthyに見えてしまう)。

推奨されるdeferrable: :immediateの振る舞いは、#46192で追加されたadd_unique_keydeferrableオプションと同じ。

Hiroyuki Ishii
同CHANGELOGより

参考: 週刊Railsウォッチ20230524: add_foreign_keyのdeferrable: trueオプションを非推奨化する

🔗 Make Adapter#exec_query clear the query cache by casperisfine · Pull Request #48069 · rails/rails

関連: Active Record: clear query cache automatically when calling #execute by casperisfine · Pull Request #48061 · rails/rails

AbstractAdapter#execute#exec_queryでクエリキャッシュをクリアするよう修正。

リードオンリーのSQLクエリをクエリキャッシュをクリアせずに実行する必要がある場合は、AbstractAdapter#select_allを使うこと。

Jean Boussier
同CHANGELOGより

参考: Rails API select_all -- ActiveRecord::ConnectionAdapters::DatabaseStatements

🔗 Add CTE support for joins by palkan · Pull Request #46843 · rails/rails

関連: Common Table Expression support added "out-of-the-box" by vlado · Pull Request #37944 · rails/rails

.joins.left_outer_joinsもCTE(Common Table Expression)で使えるようになった。

例:

Post
  .with(commented_posts: Comment.select(:post_id).distinct)
  .joins(:commented_posts)
#=> WITH (...) SELECT ... INNER JOIN commented_posts on posts.id = commented_posts.post_id

Vladimir Dementyev
同CHANGELOGより

参考: §Common Table Expression -- Hierarchical and recursive queries in SQL - Wikipedia
参考: 週刊Railsウォッチ20230524: joinsでCTEをサポート

🔗 Add load hook for ActiveRecord::ConnectionAdapters::Mysql2Adapter by fatkodima · Pull Request #48012 · rails/rails

ActiveRecord::ConnectionAdapters::Mysql2Adapter(名前はactive_record_mysql2adapter)にloadフックを追加。これにより、ActiveRecord::ConnectionAdapters::Mysql2Adapterの一部をオーバーライド可能になり、既にloadフックを備えているPostgreSQLAdapterSQLite3Adapterと統一される。

fatkodima
同CHANGELOGより

参考: 週刊Railsウォッチ20230524: Mysql2Adapterにloadフックを追加

🔗 Introduce adapter for Trilogy, a MySQL-compatible DB client by adrianna-chang-shopify · Pull Request #47880 · rails/rails

Trilogyデータベースクライアント用のアダプタを導入。

TrilogyはMySQL互換のデータベースクライアント。Railsアプリケーションのconfig/database.ymlファイルを以下のように設定することでTrilogyを利用できる。

development:
adapter: trilogy
database: blog_development
pool: 5

または、以下のようにDATABASE_URL環境変数による設定でも利用できる。

ENV['DATABASE_URL'] # => "trilogy://localhost/blog_development?pool=5"

Adrianna Chang
同CHANGELOGより

trilogy-libraries/trilogy - GitHub
trilogy-libraries/activerecord-trilogy-adapter - GitHub

参考: 週刊Railsウォッチ20230502: Trilogyデータベースクライアント用のアダプタが導入される

🔗 Run after_commit callbacks defined on models in the correct order by ghiculescu · Pull Request #46992 · rails/rails

モデルで定義されているafter_commitコールバックが、定義順に正しく実行されるようになった。

class User < ActiveRecord::Base
  after_commit { puts("これが最初に呼ばれる") }
  after_commit { puts("これが次に呼ばれる") }
end

従来のコールバックは逆順で実行されていた。以下のコンフィグで新しい振る舞いを有効にできる。

config.active_record.run_after_transaction_callbacks_in_order_defined = true

新規アプリでは、これがデフォルトの振る舞いになる。

Alex Ghiculescu
同CHANGELOGより

参考: config.active_record.run_after_transaction_callbacks_in_order_defined -- Rails アプリケーションを設定する - Railsガイド

🔗 Infer foreign_key when inverse_of is present by Tiedye · Pull Request #47797 · rails/rails

has_one関連付けやhas_many関連付けでinverse_ofが指定されている場合にforeign_keyを推論するようになった。

has_many :citations, foreign_key: "book1_id", inverse_of: :book

上を以下のようにシンプルに書けるようになる。

has_many :citations, inverse_of: :book

foreign_keyは、対応するbelongs_to関連付けから読み取られる。

Daniel Whitney
同CHANGELOGより

参考: 週刊Railsウォッチ20230412: has_onehas_manyinverse_ofが存在する場合にforeign_keyを推論するようになった

🔗 Fix Rails generated index name being too long by mscoutermarsh · Pull Request #47753 · rails/rails

自動生成されるインデックス名の長さに上限が設定されるようになった。

自動生成されるインデックス名は最大62バイトになった。この長さは、MySQL、PostgreSQL、SQLite3のインデックス名のデフォルトの最大長さに収まる。

インデックス名がこの上限を超えた場合は自動的に短縮される。

変更前(長すぎる):

index_testings_on_foo_and_bar_and_first_name_and_last_name_and_administrator

変更後(短縮形):

idx_on_foo_bar_first_name_last_name_administrator_5939248142

短縮形には、インデックス名がデータベースで一意になるようハッシュが追加される。

Mike Coutermarsh
同CHANGELOGより

参考: 週刊Railsウォッチ20230425: 自動生成されるインデックス名を上限で切り詰めるようになった

🔗 Implement marshal_dump and marshal_load on ActiveRecord::Base by casperisfine · Pull Request #47747 · rails/rails

安定性が高く最適化されたMarshalシリアライザがActive Recordモデルに導入された。

以下のコンフィグで有効になる。

config.active_record.marshalling_format_version = 7.1

Jean Boussier
同CHANGELOGより

参考: config.active_record.marshalling_format_version -- Rails アプリケーションを設定する - Railsガイド
参考: 週刊Railsウォッチ20230412: ActiveRecord::Basemarshal_dumpmarshal_loadを実装

🔗 Introduce query-by-tuple syntax by paarthmadan · Pull Request #47729 · rails/rails

複合主キー向けの"タプル"構文をwhereに導入。

whereを用いるクエリで新しいタプル(tuple)構文が使えるようになった。これは、キーに「カラムの配列」、値に「それに対応するタプルの配列」をそれぞれ渡せる。
このキーではカラムのリストを指定するが、値はそのカラムリストに適合する順序付きタプルの配列となる。
例:

# Cpk::Book => Cpk::Book(author_id: integer, number: integer, title: string, revision: integer)
# Cpk::Book.primary_key => ["author_id", "number"]
book = Cpk::Book.create!(author_id: 1, number: 1)
Cpk::Book.where(Cpk::Book.primary_key => [[1, 2]]) # => [book]
# Topic => Topic(id: integer, title: string, author_name: string...)
Topic.where([:title, :author_name] => [["The Alchemist", "Paul Coelho"], ["Harry Potter", "J.K Rowling"]])

Paarth Madan
同CHANGELOGより

参考: 週刊Railsウォッチ20230412: 複合主キー向けの"タプル"構文をwhereに導入

🔗 Allow SQL Warnings to be ignored using error codes by nickborromeo · Pull Request #47650 · rails/rails

SQL警告メッセージをエラーコードでフィルタできるようになった。

以下のActive Recordコンフィグで特定の警告コードを無視できる。

# 常に無視すべき警告の許可リストを設定する
config.active_record.db_warnings_ignore = [
  "1062", # MySQL Error 1062: Duplicate entry
]

この機能はMySQLアダプタとPostgreSQLアダプタでサポートされる。
Nick Borromeo
同CHANGELOGより

参考: config.active_record.db_warnings_ignore -- Rails アプリケーションの設定項目 - Railsガイド
参考: 週刊Railsウォッチ20230412: SQL警告メッセージをエラーコードでフィルタできるようになった

🔗 Run a load hook when TestFixtures is included by andrewn617 · Pull Request #47690 · rails/rails

:active_record_fixtures遅延読み込みフックを導入。

この名前で定義したフックは、クラスにTestFixturesincludeされると常に実行されるようになる。

ActiveSupport.on_load(:active_record_fixtures) do
  self.fixture_paths << "test/fixtures"
end

klass = Class.new
klass.include(ActiveRecord::TestFixtures)

klass.fixture_paths # => ["test/fixtures"]

Andrew Novoselac
同CHANGELOGより

参考: 週刊Railsウォッチ20230412: TestFixturesincludeするたびにloadフックを実行するように修正

🔗 Introduce TestFixtures#fixture_paths by andrewn617 · Pull Request #47675 · rails/rails

TestFixtures#fixture_paths(複数形)を追加。

#fixture_pathsアクセサを使うことで、複数のフィクスチャパスを指定できるようになった。
アプリは引き続きデフォルトでtest/fixturesを単一のフィクスチャパスとして持つが、追加のフィクスチャパスも指定できるようになる。

ActiveSupport::TestCase.fixture_paths << "component1/test/fixtures"
ActiveSupport::TestCase.fixture_paths << "component2/test/fixtures"

TestFixtures#fixture_path(単数形)は非推奨化された。

Andrew Novoselac
同CHANGELOGより

参考: 週刊Railsウォッチ20230405: fixtureパスをRailsエンジン単位で指定可能になった

🔗 Adds support for deferrable exclude constraints in PostgreSQL. by alpaca-tc · Pull Request #47655 · rails/rails

PostgreSQLのEXCLUDE制約でDEFERRABLEをサポート。

デフォルトでは、PostgreSQLのEXCLUDE制約は個別のステートメントの後でチェックされる。
ほとんどのユースケースではこれで上手くいくが、以下のように範囲がオーバーラップする複数のステートメントを用いてレコードを置き換える場合は大きな制限となる。

exclusion_constraint :users, "daterange(valid_from, valid_to) WITH &&", deferrable: :immediate

deferrable: :immediateを指定すると、制約が個別のステートメントの後でチェックされる。
しかしトランザクション内でSET CONSTRAINTS ALL DEFERREDを用いてチェックを手動で延期すれば、トランザクション終了後にEXCLUDE制約をチェックするようになる。

これと同じことを、以下のようにデフォルトの振る舞いをimmediateチェック(=個別のステートメントの後でチェックする)からdeferredチェック(=トランザクション終了後にチェックする)に変更することでも可能になった。

exclusion_constraint :users, "daterange(valid_from, valid_to) WITH &&", deferrable: :deferred

Hiroyuki Ishii
同CHANGELOGより

参考: PostgreSQL 15ドキュメント SET CONSTRAINTS

🔗 Delegated Type supports customizeable foreign_type column by jasonkarns · Pull Request #45041 · rails/rails

delegated_typeforeign_typeオプションを渡すことで{role}_classメソッドに反映されるようになった。

foreign_type オプションを渡すことで、delegated_typeで標準でない{role}_typeカラム名を利用できるようになった。
このオプションは、delegated_type がラップしている背後のbelongs_to関連付けへforwardされるforeign_type と同じ。

Jason Karns
同CHANGELOGより

参考: 週刊Railsウォッチ20230405: Delegated Typesでカスタムカラム名を指定するforeign_typeオプションが追加された

Rails: ActiveRecord::DelegatedType APIドキュメント(翻訳)

🔗 Add support for unique constraints (PostgreSQL-only). by alpaca-tc · Pull Request #46192 · rails/rails

関連: Adds support USING INDEX for unique constraints in PostgreSQL. by alpaca-tc · Pull Request #47971 · rails/rails
関連: Deprecate deferrable: true option of add_foreign_key by alpaca-tc · Pull Request #47659 · rails/rails

UNIQUE制約のサポートを追加(PostgreSQLのみ)。

add_unique_key :sections, [:position], deferrable: :deferred, name: "unique_section_position"
remove_unique_key :sections, name: "unique_section_position"

UNIQUE制約に関しては、PostgreSQLのUNIQUE制約のドキュメントを参照。

デフォルトでは、PostgreSQLでのユニーク制約は個別のステートメントの後にチェックされる。
ほとんどの場合はこれで問題ないが、複数のステートメントを利用してレコードのuniqueカラムを置換する場合には大きな成約となる。

以下の例では、レコード間でuniqueカラムを交換している。

# このpositionはuniqueカラム
old_item = Item.create!(position: 1)
new_item = Item.create!(position: 2)

Item.transaction do
  old_item.update!(position: 2)
  new_item.update!(position: 1)
end

デフォルトの振る舞いでは、最初のUPDATEステートメントを実行する時点で失敗する。

マイグレーションで以下のようにadd_unique_key:deferrableオプションを渡すことで、このチェックを先延ばしできるようになった。

add_unique_key :items, [:position], deferrable: :immediate

deferrable: :immediate を指定しても、振る舞いは最初の例と変わらないが、トランザクション内で SET CONSTRAINTS ALL DEFERREDを使ってこのチェックを手動で先延ばしできるようになる。
これにより、UNIQUE制約がトランザクションの完了後にチェックされるようになる。

また、以下のようにdeferrable: :deferredを指定することで、個別のステートメント後にチェックするデフォルトの振る舞いを、トランザクション完了後にチェックする先延ばしチェックに変更することも可能。

add_unique_key :items, [:position], deferrable: :deferred

既存のuniqueインデックスを先延ばし可能にしたい場合は、以下のように:using_indexオプションを渡すことで先延ばし可能なUNIQUE制約を作成できる。

add_unique_key :items, deferrable: :deferred, using_index: "index_items_on_position"

Hiroyuki Ishii
同CHANGELOGより

参考: 週刊Railsウォッチ20230502: PostgreSQL: UNIQUE制約でUSING INDEXをサポート

🔗 Remove deprecated Tasks::DatabaseTasks.schema_file_type · rails/rails@049dfd4

関連: Remove deprecated methods in ``Tasks::DatabaseTasks` · rails/rails@71f61b1

非推奨化されていたTasks::DatabaseTasks.schema_file_typeを削除。

Rafael Mendonça França
同CHANGELOGより

🔗 Remove deprecated config.active_record.partial_writes · rails/rails@96b9fd6

関連: Deprecate partial_writes in favor of partial_inserts and partial_updates by casperisfine · Pull Request #42355 · rails/rails

非推奨化されていたconfig.active_record.partial_writesを削除。

Rafael Mendonça França
同CHANGELOGより

🔗 Remove deprecated ActiveRecord::Base config accessors · rails/rails@96c9db1

関連: Define deprecated delegators for the cattr that were moved out of AR::Base by casperisfine · Pull Request #42489 · rails/rails

非推奨化されていたActiveRecord::Baseコンフィグのアクセサメソッドを削除。

Rafael Mendonça França
同CHANGELOGより

🔗 Allow configs_for to accept a custom hash key by eileencodes · Pull Request #47536 · rails/rails

configs_forから:include_replicas引数を削除。今後は:include_hiddenを使うこと。

Eileen M. Uchitelle
同CHANGELOGより

アプリケーションのコンフィグをカスタムのハッシュキーで検索できるようになった。

カスタム設定を登録した場合や、ハッシュが特定のキーとマッチする設定を見つけたい場合は、configs_forconfig_keyオプションを渡せるようになった。
たとえば、キーがvitessdb_configがある場合、そのキーにマッチするデータベース設定のハッシュを以下のように検索できる。

ActiveRecord::Base.configurations.configs_for(env_name: "development", name: "primary", config_key: :vitess)
ActiveRecord::Base.configurations.configs_for(env_name: "development", config_key: :vitess)

Eileen M. Uchitelle
同CHANGELOGより

参考: 週刊Railsウォッチ20230322: データベース設定のカスタムハンドラを登録可能になった

🔗 Allow applications to register custom database configurations by eileencodes · Pull Request #47522 · rails/rails

アプリケーションでカスタムのデータベース設定ハンドラを登録できるようになった。

データベース設定がカスタムメソッドに応答するようにカスタマイズしたい場合に、カスタムハンドラを登録する仕組みを追加する。これは、Rails以外のデータベースアダプタやVitessなどのツールで、標準のHashConfigUrlConfigとは異なる方法で設定したい場合に有用。

以下のデータベースYAMLで、primary データベースを UrlConfig にしたまま、animals dbでCustomConfigオブジェクトを作成したいとする。

development:
  primary:
    url: postgres://localhost/primary
  animals:
    url: postgres://localhost/animals
    custom_config:
      sharded: 1

カスタムハンドラを登録するには、最初にカスタムメソッドを持つクラスを作成する。

class CustomConfig < ActiveRecord::DatabaseConfigurations::UrlConfig
  def sharded?
    custom_config.fetch("sharded", false)
  end

  private
    def custom_config
      configuration_hash.fetch(:custom_config)
    end
end

次にこのコンフィグをイニシャライザで登録する。

ActiveRecord::DatabaseConfigurations.register_db_config_handler do |env_name, name, url, config|
  next unless config.key?(:custom_config)
  CustomConfig.new(env_name, name, url, config)
end

これで、アプリケーションが起動すると、:custom_configキーの設定ハッシュがCustomConfigオブジェクトになり、sharded?に応答するようになる。アプリケーションは、Active Recordでこのカスタムハンドラが利用されるように条件を処理する必要がある。

Eileen M. Uchitelle and John Crepezzi
同CHANGELOGより

参考: 週刊Railsウォッチ20230322: データベース設定のカスタムハンドラを登録可能になった

🔗 Stop serializing columns as YAML by default by casperisfine · Pull Request #47422 · rails/rails

ActiveRecord::Base.serializeのデフォルトがYAMLでなくなった。

YAMLのパフォーマンスは高くないうえに、注意しないとセキュリティ上の問題を引き起こす可能性がある。

残念ながら、Rubyの標準ライブラリには置き換えに適したシリアライザがない。

明らかな選択肢としてはJSONがあり、このユースケースには適したフォーマットだが、Ruby標準ライブラリのJSONシリアライザは厳密性が十分ではない(未知の型を文字列にキャストする形でフォールバックするため、データが破損する可能性がある)。

OjなどのサードパーティJSONライブラリは、これに適したstrictモードを備えている。

ユーザーは、自分たちの制約に基づいてシリアライザを選択することが望ましい。

従来のデフォルト設定に戻したい場合は、以下を設定する。

config.active_record.default_column_serializer = YAML

Jean Boussier
同CHANGELOGより

参考: config.active_record.default_column_serializer -- Rails アプリケーションを設定する - Railsガイド

ohler55/oj - GitHub

🔗 Allow to define the default column serializer by casperisfine · Pull Request #47463 · rails/rails

ActiveRecord::Base.serializeのシグネチャが変更された。

serializeに、2個の可能な値を渡せる単一の第2位置引数ではなく、2種類のキーワード引数(codertype)を渡せるように変更された。

変更前:

serialize :content, JSON
serialize :backtrace, Array

変更後:

serialize :content, coder: JSON
serialize :backtrace, type: Array

Jean Boussier
同CHANGELOGより

参考: 週刊Railsウォッチ20230314: デフォルトのカラムシリアライザを定義可能になった

🔗 YAMLColumn: use YAML.safe_dump if available by casperisfine · Pull Request #47103 · rails/rails

YAMLカラムで可能な場合はYAML.safe_dumpを使うようになった。

psych 4.0.1以降、YAML.safe_loadと同様の型制約を利用可能で、使いやすいYAML.safe_dumpを適用可能になった。

最初にシリアライズするときは、許可された型だけをペイロードで使うことが望ましい。そうしないと、データベース内に無効なレコードが残る可能性がある。

Jean Boussier
同CHANGELOGより

ruby/psych - GitHub

🔗 ActiveRecord::QueryLogs: handle invalid encoding by casperisfine · Pull Request #47214 · rails/rails

ActiveRecord::QueryLogsにおける壊れたエンコーディングの処理を改善。

BLOBフィールドを含むクエリをビルドするときにバイナリデータが含まれることがよくある。文字列がASCII-8BITで慎重にエンコードされない限り、通常はUTF-8でエンコードされてQueryLogsが失敗する可能性があった。

この修正により、ActiveRecord::QueryLogsはクエリの正しいエンコードに依存しなくなった。

Jean Boussier
同CHANGELOGより

参考: 週刊Railsウォッチ20230221: クエリのエンコードが壊れている場合のActiveRecord::QueryLogsの処理を改善

🔗 Model Generator Source Paths Should Allow for Customization by spencerneste · Pull Request #47181 · rails/rails

create_table_migrationテンプレートのオーバーライドがActiveRecord::Generators::ModelGeneratorで反映されないバグを修正。

rails g model create_books title:string content:text

修正により、上のジェネレータが以下の場所からこの順序でcreate_table_migration.rb.ttテンプレートを読み込むようになった。

lib/templates/active_record/model/create_table_migration.rb
lib/templates/active_record/migration/create_table_migration.rb

Spencer Neste
同CHANGELOGより

参考: 週刊Railsウォッチ20230613: モデルのジェネレータにcreate_table_migrationテンプレートのオーバーライドが正しく反映されない問題を修正

🔗 ActiveRecord::Relation#explain accepts options by reid-rigo · Pull Request #47043 · rails/rails

ActiveRecord::Relation#explainにオプションを渡せるようになった。

explain:analyzeオプションやverboseオプションを渡すことで、クエリプラン分析を詳細に行えるようになった。現在サポートされているデータベースおよびアダプタはPostgreSQLとMySQL。

Customer.where(id: 1).joins(:orders).explain(:analyze, :verbose)

Reid Lynch
同CHANGELOGより

Rails 7.1: ActiveRecord::Relation#explainに:verboseや:analyzeオプションが追加(翻訳)

🔗 Add Arel functionality for "stitching together" SQL by olefriis · Pull Request #46948 · rails/rails

複数のArel::Nodes::SqlLiteralノードを互いに追加してArel::Nodes::Fragmentsノードを形成できるようになった。これにより、多くのSQLスニペットを結合できるようになる。

Matthew Draper, Ole Friis
同CHANGELOGより

🔗 ActiveRecord::Base#signed_id: raise if called on a new record by ghiculescu · Pull Request #47027 · rails/rails

新規レコードでActiveRecord::Base#signed_idが呼び出されたらエラーを発生するようになった。

従来はid = nilを元にしていたため、利用できないIDを返すことがあった。

Alex Ghiculescu
同CHANGELOGより

🔗 Allow SQL warnings to be reported. by adrianna-chang-shopify · Pull Request #46690 · rails/rails

SQLのwarningを通知できるようにした。

以下のActive Recordコンフィグを設定することで、SQL warningの通知が有効になる。

# SQLクエリでwarningが発生した場合の操作を設定する
config.active_record.db_warnings_action = :raise

# warningの許可リストを設定する(設定したwarningは常に無視される)
config.active_record.db_warnings_ignore = [
  /Invalid utf8mb4 character string/,
  "An exact warning message",
]

この機能はMySQLアダプタとPostgreSQLアダプタでサポートされる。

Adrianna Chang, Paarth Madan
同CHANGELOGより

参考: config.active_record.db_warnings_action -- Rails アプリケーションを設定する - Railsガイド
参考: config.active_record.db_warnings_ignore -- Rails アプリケーションを設定する - Railsガイド

🔗 Add regroup method to ActiveRecord by dvisockas · Pull Request #47010 · rails/rails

#regroupクエリメソッドを追加。これは.unscope(:group).group(fields)のショートハンド。

例:

Post.group(:title).regroup(:author)
# SELECT `posts`.`*` FROM `posts` GROUP BY `posts`.`author`

Danielius Visockas
同CHANGELOGより

参考: 週刊Railsウォッチ20230207: Active Recordにregroupメソッドとregroup!メソッドが追加された

🔗 Adds schema parameter into enable_extension by lomefin · Pull Request #46894 · rails/rails

PostgreSQLアダプタのenable_extensionメソッドで、他のスキーマによってインストールされていなければならないPostgreSQL拡張をスキーマ名.拡張名形式で指定できるようになった。

例: enable_extension('heroku_ext.hstore')

Leonardo Luarte
同CHANGELOGより

🔗 Add support for :include index option by steve-abrams · Pull Request #44803 · rails/rails

マイグレーションのadd_index:includeオプションを追加(PostgreSQLのみ)。

PostgreSQLのINCLUDE(キーでないカラムをインデックスに含める)のサポートを追加。

add_index(:users, :email, include: [:id, :created_at])

上は以下を生成する。

CREATE INDEX index_users_on_email USING btree (email) INCLUDE (id, created_at)

Steve Abrams
同CHANGELOGより

参考: 週刊Railsウォッチ20230328: PostgreSQLのadd_indexincludewhereを両方使えるようにする

🔗 ActiveRecord::Relation#none?/#any?/#one?: support pattern arg by georgeclaghorn · Pull Request #46728 · rails/rails

ActiveRecord::Relation’の以下のメソッドに、同等のEnumerableにより近い形でマッチするパターン引数をオプションで渡せるようになった。

  • #any?
  • #none?
  • #one?

George Claghorn
同CHANGELOGより

# 以下の2つは同等
products.any?(MediaBlock)
products.any? { |product| MediaBlock === product }

参考: 週刊Railsウォッチ20230125: ActiveRecord::Relation#none?#any?#one?Enumerableと同様のパターン引数を渡せるようになった

🔗 Add ActiveRecord::Base::normalizes by jonathanhefner · Pull Request #43945 · rails/rails

属性の正規化を宣言するActiveRecord::Base.normalizesを追加。

属性の正規化は、属性の代入または更新時に適用され、正規化された値はデータベースに永続化される。また、この正規化はクエリメソッドの対応するキーワード引数にも適用されるので、正規化されていない値でレコードをクエリできるようになる。

例:

class User < ActiveRecord::Base
  normalizes :email, with: -> email { email.strip.downcase }
  normalizes :phone, with: -> phone { phone.delete("^0-9").delete_prefix("1") }
end

user = User.create(email: " CRUISE-CONTROL@EXAMPLE.COMn")
user.email                  # => "cruise-control@example.com"

user = User.find_by(email: "tCRUISE-CONTROL@EXAMPLE.COM ")
user.email                  # => "cruise-control@example.com"
user.email_before_type_cast # => "cruise-control@example.com"

User.where(email: "tCRUISE-CONTROL@EXAMPLE.COM ").count         # => 1
User.where(["email = ?", "tCRUISE-CONTROL@EXAMPLE.COM "]).count # => 0

User.exists?(email: "tCRUISE-CONTROL@EXAMPLE.COM ")         # => true
User.exists?(["email = ?", "tCRUISE-CONTROL@EXAMPLE.COM "]) # => false

User.normalize_value_for(:phone, "+1 (555) 867-5309") # => "5558675309"

Jonathan Hefner
同CHANGELOGより

Rails 7.1: ActiveRecord::Baseにnormalizesが追加された(翻訳)

🔗 Hide changes to before_committed! behaviour behind config by adrianna-chang-shopify · Pull Request #46739 · rails/rails

before_committed!コールバックの振る舞いの変更をコンフィグで戻せるようになった。

#46525before_committed!コールバックの動作が変更され、トランザクションに登録されたすべてのレコードでコールバックが実行されるようになった。この振る舞いを、config.active_record.before_committed_on_all_recordsという設定オプションで制御できるようになった(Rails 7.1ではデフォルトで有効)。

Adrianna Chang
同CHANGELOGより

参考: 週刊Railsウォッチ20221206: before_committed!コールバックをレコードの直近のコピーに対して実行するよう修正
参考: config.active_record.before_committed_on_all_records -- Rails アプリケーションを設定する - Railsガイド

🔗 Query Logs: namespaced_controller tag should match controller format by ghiculescu · Pull Request #46641 · rails/rails

クエリログのnamespaced_controllerタグがコントローラの名前空間フォーマットとマッチするよう修正。

たとえばNameSpaced::UsersControllerで処理されたリクエストは以下のようにログ出力されるようになった。

:controller # "users"
:namespaced_controller # "name_spaced/users"

Alex Ghiculescu
同CHANGELOGより

参考: Rails API ActiveRecord::QueryLogs
参考: config.active_record.query_log_tags -- Rails アプリケーションを設定する - Railsガイド

🔗 Fix: ActiveRecord::Calculations#ids returns duplicate ids by joshuay03 · Pull Request #46503 · rails/rails

ActiveRecord::Calculations#idsが一意のidリストだけを返すよう修正。

ActiveRecord::Calculations#idsが更新され、eager_loadpreloadincludesでベースモデルの一意のidリストだけを返すようになった。

Post.find_by(id: 1).comments.count
# => 5
Post.includes(:comments).where(id: 1).pluck(:id)
# => [1, 1, 1, 1, 1]
Post.includes(:comments).where(id: 1).ids
# => [1]

Joshua Young
同CHANGELOGより

参考: 週刊Railsウォッチ20221220: ActiveRecord::Calculations#idsが返すidが重複する問題を修正

🔗 Don't use lower() for citext columns by pirj · Pull Request #46592 · rails/rails

PostgreSQLのcitextカラムでは大文字小文字を区別しないクエリにlower()を追加しないよう修正。

従来はuniquenessバリデーションなどでcase_sensitive: falseを指定したときのクエリにlower()が追加されていた。
しかし、lower()が定義されていないインデックスに対してこれを行うとインデックスが効かなくなることが見落とされていた。

Phil Pirozhkov
同CHANGELOGより

参考: PostgreSQL 15ドキュメント 9.4. 文字列関数と演算子
参考: PostgreSQL 15ドキュメント F.10. citext
参考: 週刊Railsウォッチ20221213: PostgreSQLのcitext型カラムの検索時にlower()を使わないようにした

🔗 Extract #sync_timezone_changes method in AbstractMysqlAdapter by adrianna-chang-shopify · Pull Request #46604 · rails/rails

AbstractMysqlAdapter#sync_timezone_changesメソッドをMySQL::DatabaseStatementsに移動した。
これにより、サブクラスで#raw_executeをオーバーライドせずにデータベースのタイムゾーン変更を同期できるようになった。

Adrianna Chang, Paarth Madan
同CHANGELOGより

🔗 Do not write additional new lines when dumping sql migration versions by mishaschwartz · Pull Request #46454 · rails/rails

マイグレーションのSQLダンプでバージョン番号に余分な行が追加されていたのを修正。

この変更によってinsert_versions_sql関数が修正され、現在のマイグレーションバージョン番号を含む挿入文字列の末尾に余分な改行文字が2つ含まれないようになる。

Misha Schwartz
同CHANGELOGより

🔗 Fix composed_of freezing by gregnavis · Pull Request #46377 · rails/rails

composed_ofの値のfreezedupを修正。

従来のコンポジション値は、混乱を招く2通りの振る舞いを示していた。

  1. コンポジション値を読み取るときは値がfreezeされない。このため、背後のデータベースカラムとの同期がずれてしまう。
  2. コンポジション値を書き込むときは引数がfreezeされる。これによって呼び出し側が混乱する可能性がある。

修正後、データベースカラムに基づいてインスタンス化されたコンポジション値はfreezeされるようになった(問題1の修正)。代入したコンポジション値はdupされるようになり、dupしたものをfreezeするようになった(問題2の修正)。

Greg Navis
同CHANGELOGより

参考: Rails API ActiveRecord::Aggregations::ClassMethods

🔗 Fix incorrect caching of case-insensitivity · rails/rails@b39050e

大文字小文字を区別しないカラムでキャッシュが効かなかったのを修正。

カラムが大文字小文字を区別しない比較が可能かどうかをチェックすることで余分なクエリを発行しないよう修正した。

Phil Pirozhkov
同CHANGELOGより

🔗 option to disable all methods that ActiveRecord.enum generates by alfie-max · Pull Request #46490 · rails/rails

ActiveRecord.enumによるメソッド生成をinstance_methods: falseオプションで無効にできるようになった。

Alfred Dominic
同CHANGELOGより

参考: Rails 7.1に入る主要な機能まとめ(3) -- ActiveRecord#enumのメソッド生成を無効にするオプションが追加された

🔗 Avoid validating belongs_to association if it has not changed by fatkodima · Pull Request #46522 · rails/rails

belongs_to関連付けで変更が生じていない場合のバリデーションを回避するようになった。

従来のActive Recordは、レコード更新時にbelongs_to関連付けで(存在が必須と設定されている場合に)存在チェックの追加クエリを実行していたが、属性が変更されていない場合でも実行していた。

修正後は、belongs_toに関連するカラムだけが存在チェックされるようになった。ただしこの方法では孤立レコードができてしまう可能性があるので、この問題を回避するには外部キーを使う必要がある。

この振る舞いは、以下のコンフィグで制御できる。

config.active_record.belongs_to_required_validates_foreign_key = false

この設定はconfig.load_defaults 7.1ではデフォルトでfalseに設定される。

fatkodima
同CHANGELOGより

参考: config.active_record.belongs_to_required_validates_foreign_key -- Rails アプリケーションを設定する - Railsガイド

参考: Rails 7.1に入る主要な機能まとめ(3) -- 属性が変更されていないbelongs_to関連付けのバリデーションを回避するようになった

🔗 Allow resetting singular associations by georgeclaghorn · Pull Request #46165 · rails/rails

has_one関連付けとbelongs_to関連付けで、オーナーモデルにreset_関連付け名が定義されるようになった。

このメソッドは、キャッシュされた関連付けレコードがあればアンロードし、次回のアクセスでデータベースから読み込むようになる。

George Claghorn
同CHANGELOGより

参考: Rails 7.1に入る主要な機能まとめ(3) -- 単数形の関連付けをリセットできるようになった

🔗 Permit YAML classes and unsafe load per attribute · rails/rails@e313fc5

serializeyamlオプションが追加され、permitted_classessafe_load用)やunsafe_loadを属性単位で設定できるようになった。

Carlos Palhares
同CHANGELOGより

# 同APIドキュメントより
class User < ActiveRecord::Base
  serialize :preferences, yaml: { permitted_classes: [Symbol, Time] }
 end

🔗 Add a build persistence method by seand7565 · Pull Request #45696 · rails/rails

永続化を行うbuildメソッドを追加。

newのラッパーを提供し、関連付けのbuildメソッドと同じ記法で、createと同様にハッシュの配列から複数のレコードを作成する機能を提供する。

Sean Denny
同CHANGELOGより

# 同APIドキュメントより
# 単一のオブジェクトをビルド
User.build(first_name: 'Jamie')

# 新規オブジェクトの配列を渡してビルド
User.build([{ first_name: 'Jamie' }, { first_name: 'Jeremy' }])

# 単一のオブジェクトをビルドし、ブロックで他の属性を設定
User.build(first_name: 'Jamie') do |u|
  u.is_admin = false
end

# 新規オブジェクトの配列を渡してビルド:(ブロックはオブジェクトごとに設定される)
User.build([{ first_name: 'Jamie' }, { first_name: 'Jeremy' }]) do |u|
  u.is_admin = false
end

参考: 週刊Railsウォッチ20221206: buildメソッドにハッシュの配列を渡すことで複数のオブジェクトをまとめてbuildできるようになった

🔗 Raise on assignment to readonly attributes by hmcguire-shopify · Pull Request #46105 · rails/rails

attr_readonlyで生成した属性に代入するとエラーを発生するようになった。

class Post < ActiveRecord::Base
  attr_readonly :content
end
Post.create!(content: "cannot be updated")
post.content # "cannot be updated"
post.content = "something else" # => ActiveRecord::ReadonlyAttributeError

従来はデータベースに書き込まずに代入に成功してしまい、エラーを発生しなかった。

この振る舞いは以下のコンフィグで制御できる。

config.active_record.raise_on_assign_to_attr_readonly = true

config.load_defaults 7.1ではこの振る舞いがデフォルトで有効になる。

Alex Ghiculescu, Hartley McGuire
同CHANGELOGより

参考: config.active_record.raise_on_assign_to_attr_readonly -- Rails アプリケーションを設定する - Railsガイド
参考: 週刊Railsウォッチ20221129: readonlyの属性に代入すると`ActiveRecord::ReadonlyAttributeError`を発生するようになった

🔗 Allow unscoping of preload and eager_load associations by dmorehouse · Pull Request #45147 · rails/rails

unscopeで関連付けのpreloadeager_loadも指定できるようになった。

includesjoinsなどと同様に、unscopeで関連付けのpreloadeager_loadを行う機能が追加された。サポートされているunscope可能なスコープの完全なリストについては、ActiveRecord::QueryMethods::VALID_UNSCOPING_VALUESを参照。

query.unscope(:eager_load, :preload).group(:id).select(:id)

David Morehouse
同CHANGELOGより

参考: 週刊Railsウォッチ20221129: 関連付けのpreloadeager_loadunscopeできるようになった

🔗 Add filtering of encrypted attributes in #inspect by skipkayhil · Pull Request #46453 · rails/rails

inspectで暗号化済み属性をフィルタで自動的に除外するようになった。

この機能はデフォルトで有効になるが、以下のコンフィグを設定することで無効にできる。

config.active_record.encryption.add_to_filter_parameters = false

Hartley McGuire
同CHANGELOGより

この機能により、暗号化済み属性は自動的にログからも除外されるようになります。

参考: config.active_record.add_to_filter_parameters -- Rails アプリケーションを設定する - Railsガイド
参考: 週刊Railsウォッチ20221129: #inspect実行時に暗号化済み属性を自動的にフィルタで除外する機能が追加された

🔗 Clear locking column on #dup by shouichi · Pull Request #46243 · rails/rails

レコードを#dupしたときにlocking_columnを解除するようになった。

この変更により、idやタイムスタンプなどのlocking_columnが複製されない問題が修正される。

car = Car.create!
car.touch
car.lock_version #=> 1
car.dup.lock_version #=> 0

Shouichi Kamiya, Seonggi Yang, Ryohei UEDA
同CHANGELOGより

参考: Rails API locking_column -- ActiveRecord::Locking::Optimistic::ClassMethods

🔗 Invalidate transaction as early as possible by nvasilevski · Pull Request #46367 · rails/rails

トランザクションを可能な限り早期に無効化するようになった。

TransactionRollbackError例外をrescueした後、トランザクションをフローの早い段階で無効化し、フレームワークがROLLBACKステートメントの発行をスキップするケースを増やす。
これが影響するのは、savepoint_errors_invalidate_transactions?trueを設定しているアダプタのみであり、現時点ではmysql2アダプタにのみ影響する。

Nikita Vasilevsky
同CHANGELOGより

参考: Rails API savepoint_errors_invalidate_transactions? -- ActiveRecord::ConnectionAdapters::Mysql2Adapter

🔗 Allow specifying columns to use in ActiveRecord::Base object queries by nvasilevski · Pull Request #46331 · rails/rails

ActiveRecord::Baseオブジェクトが発行するSQLクエリで複数カラムのリストを設定できるようになった。

複数カラムをリストとして設定可能になったことで、ActiveRecord::Baseオブジェクトを更新/削除/リロードしたときのSQLクエリ句のビルドで使われるようになった。

class Developer < ActiveRecord::Base
  query_constraints :company_id, :id
end

developer = Developer.first.update(name: "Bob")
# => UPDATE "developers" SET "name" = 'Bob' WHERE "developers"."company_id" = 1 AND "developers"."id" = 1

Nikita Vasilevsky
同CHANGELOGより

参考: 週刊Railsウォッチ20221115: ActiveRecord::Baseオブジェクトのクエリでカラムのリストを指定可能になった

🔗 Adds validate to foreign keys and check constraints in schema.rb by TAGraves · Pull Request #46339 · rails/rails

(PostgreSQL)schema.rbの外部キーやCHECK制約にvalidateを追加するようになった。

従来は、外部キーやCHECK制約の追加にvalidate: falseを指定したかどうかがschema.rbに記録されていなかったため、データベースをスキーマからリストアすると外部キーやCHECK制約が誤ってバリデーションされる可能性があった。

Tommy Graves
同CHANGELOGより

参考: 週刊Railsウォッチ20221115: schema.rbの外部キーやCHECK制約にvalidate: falseを追加するようになった(PostgreSQL)

🔗 Allow adapter #execute methods to take allow_retry option by adrianna-chang-shopify · Pull Request #46273 · rails/rails

データベースアダプタの#executeメソッドにallow_retryオプションを渡せるようになった。

このオプションをtrueに設定するとSQLステートメントがリトライされるようになる。リトライは、リトライ回数がデータベース設定のconnection_retries値に達するまで、またはコネクション関連のエラーが発生するまで行われる。

Adrianna Chang
同CHANGELOGより

参考: 週刊Railsウォッチ20221115: DBアダプタの#executeメソッドにallow_retryオプションを渡せるようになった

🔗 Don't trigger after_commit :destroy callback again on destroy if record previously was destroyed by bensheldon · Pull Request #46197 · rails/rails

データベース行が削除済みの場合にのみafter_commit :destroyがトリガーされるようになった。

これにより、同じレコードに対してdestroyが複数回呼び出された場合にafter_commit :destroyコールバックが複数回トリガーされるのを防止する。

Ben Sheldon
同CHANGELOGより

参考: 週刊Railsウォッチ20221101: 同一レコードでafter_commit :destroyの重複トリガーを解消

🔗 Fix ciphertext_for for yet-to-be-encrypted values by jonathanhefner · Pull Request #46284 · rails/rails

ciphertext_forが返す値が暗号化されないバグを修正。

従来のciphertext_forは、永続化されていないレコードなどから暗号化されていない平文を返していた。

Post.encrypts :body

post = Post.create!(body: "Hello")
post.ciphertext_for(:body)
# => "{"p":"abc..."

post.body = "World"
post.ciphertext_for(:body)
# => "World"

修正後のciphertext_forは、暗号化属性から常に暗号化済みテキストを返すようになった。

Post.encrypts :body

post = Post.create!(body: "Hello")
post.ciphertext_for(:body)
# => "{"p":"abc..."

post.body = "World"
post.ciphertext_for(:body)
# => "{"p":"xyz..."

Jonathan Hefner
同CHANGELOGより

参考: 週刊Railsウォッチ20221101: ciphertext_forが暗号化前の値を返す問題を修正

🔗 Fix a bug where using groups and counts with long table names would return incorrect results. by Dooor · Pull Request #46287 · rails/rails

テーブル名が長い場合にgroupcountが誤った値を返すバグを修正。

Shota Toguchi, Yusaku Ono
同CHANGELOGより

この修正は、Rails 7.0.5でリリース済みです

🔗 Fix encryption of column default values by jonathanhefner · Pull Request #46281 · rails/rails

カラムのデフォルト値の暗号化を修正。

従来は、カラムのデフォルト値が設定されている暗号化属性がレコード作成時に暗号化されているように見えていたが、実際には暗号化されていなかった。

Book.encrypts :name

book = Book.create!
book.name
# => "<untitled>"
book.name_before_type_cast
# => "{"p":"abc..."
book.reload.name_before_type_cast
# => "<untitled>"

修正後は、カラムのデフォルト値を設定した暗号化属性が暗号化されるようになった。

Book.encrypts :name

book = Book.create!
book.name
# => "<untitled>"
book.name_before_type_cast
# => "{"p":"abc..."
book.reload.name_before_type_cast
# => "{"p":"abc..."

Jonathan Hefner
同CHANGELOGより

🔗 Deprecate delegation to connection handler from Base by eileencodes · Pull Request #46274 · rails/rails

Baseからconnection_handlerへの委譲を非推奨化。

以下の呼び出しが非推奨化された。

  • Base.clear_all_connections!
  • Base.clear_active_connections!
  • Base.clear_reloadable_connections!
  • Base.flush_idle_connections!

今後これらのメソッドはコネクションハンドラで直接呼び出すこと。
Baseからconnection_handlerへの委譲は今後のRailsで削除される予定。

Eileen M. Uchitelle
同CHANGELOGより

参考: Rails API ActiveRecord::ConnectionAdapters::ConnectionHandler

🔗 Allow ActiveRecord::QueryMethods#reselect to accept a hash by sampatbadhe · Pull Request #46253 · rails/rails

ActiveRecord::QueryMethods#reselectにもActiveRecord::QueryMethods#selectと同様にハッシュ値を渡せるようになった。

Sampat Badhe
同CHANGELOGより

# 同PRより(以下の2つは同じ)
 Post.select(:title, posts: { title: :post_title })
 Post.select(:title, :body).reselect(:title, posts: { title: :post_title })

参考: 週刊Railsウォッチ20221101: ActiveRecord::QueryMethods#reselectにもカラムやエイリアスを含むハッシュを渡せるようになった

🔗 Validate options when managing columns and tables in migration by tgxworld · Pull Request #46178 · rails/rails

マイグレーションのカラム/テーブル管理メソッドに渡されるオプションが有効かどうかをバリデーションするようになった。

create_tableadd_columnなどのマイグレーション用メソッドに無効なオプションを渡すと、従来は単に無視されていたが、修正後はエラーを発生するようになった。
オプションのバリデーションは、新規作成されたマイグレーションに対してのみ適用される。

Guo Xiang Tan, George Wambold
同CHANGELOGより

参考: 週刊Railsウォッチ20221025: 新規マイグレーションのカラム/テーブル管理で無効なオプションが指定されるとraiseするようになった

🔗 Add ability to set the tags_format for QueryLogs by iheanyi · Pull Request #45081 · rails/rails

QueryLogsのタグフォーマットにデフォルトでSQLCommenter形式が使われるようになった。
詳しくは#46179を参照。

QueryLogsでSQLCommenter形式のタグを無効にするには、config.active_record.query_log_tags_format = :legacyを設定する。
デフォルトでは:sqlcommenterが設定される。

Modulitos and Iheanyi
同CHANGELOGより

参考: Rails API ActiveRecord::QueryLogs
参考: config.active_record.query_log_tags_format -- Rails アプリケーションを設定する - Railsガイド
参考: 週刊Railsウォッチ20221018: QueryLogsでtags_formatオプションを指定可能になった

🔗 Facilitate use of any regular ERB in database.yml by eikes · Pull Request #46134 · rails/rails

rakeタスク作成時にdatabase.ymlで任意のERBを書けるようになった。

環境設定にアクセスする場合でもdatabase.ymlに任意のERBを書けるようになった。

config.active_record.suppress_multiple_database_warning設定は非推奨化された。

Eike Send
同CHANGELOGより

参考: RailsガイドのRails アプリケーションを設定するには、このconfig.active_record.suppress_multiple_database_warningはこれまで記載されていません。
参考: 週刊Railsウォッチ20221018: database.ymlのYAMLキーに任意のERBを書けるようになった

🔗 Add table name to error for duplicate column definitions by p8 · Pull Request #46117 · rails/rails

カラム定義が重複していた場合のエラーメッセージにテーブル名も出力されるようになった。

マイグレーションでテーブルのカラム定義が重複している場合に、問題が生じたテーブル名を含むエラーメッセージが表示される。

Petrik de Heus
同CHANGELOGより

# 同PRより: テーブル名'testings'が出力される
you can't define an already defined column 'testing_column' on 'testings'.

🔗 Fix erroneous nil default precision on virtual datetime columns by sambostock · Pull Request #46110 · rails/rails

仮想のdatetimeカラムがデフォルトでprecision: nilになっていたのを修正。

この修正の前は、仮想のdatetimeカラムのデフォルト精度が通常のdatetimeカラムのデフォルト精度と同じになっておらず、以下の2つが誤って同等になっていた。

t.virtual :name, type: datetime,                 as: "expression"
t.virtual :name, type: datetime, precision: nil, as: "expression"

この変更では、デフォルト精度の探索が修正され、datetimeのデフォルト精度が仮想カラムでも通常カラムでも一致するようになった。

Sam Bostock
同CHANGELOGより

この修正は、Rails 7.0.5でリリース済みです

🔗 Use #with_raw_connection in #quote_string to retry connection errors by adrianna-chang-shopify · Pull Request #46108 · rails/rails

#quote_string#with_raw_connectionのコネクションを使うようになった。

これにより、#with_raw_connectionが提供する再接続・リトライロジックによって#quote_stringがラップされるようになる。

Adrianna Chang
同CHANGELOGより

参考: Rails API quote_string -- ActiveRecord::ConnectionAdapters::Quoting

🔗 Add expires_at option to signed_id · rails/rails@364939c

失効日時を指定するexpires_atオプションをsigned_idに追加。

Shouichi Kamiya
同CHANGELOGより

# 同PRより
Account.find_signed(@account.signed_id(expires_at: 1.minute.from_now)

参考: Rails API signed_id -- ActiveRecord::SignedId

🔗 Take into account timeout limit when retrying queries by adrianna-chang-shopify · Pull Request #46046 · rails/rails

クエリのリトライ期間に上限を設定できるようになった。

#44576#44591で行われた作業を基に、データベースコネクションの自動再接続ロジックを拡張してタイムアウト制限を考慮するようになった。クエリが最初に試行されてから一定時間経過すると、クエリは再試行されなくなる。この値にはデフォルトではnilが設定されている(経過時間に関係なくすべての再試行可能なクエリが再試行される)。これはデータベース設定のretry_deadlineオプションで変更可能。

Adrianna Chang
同CHANGELOGより

# 同PRより
development:
  adapter: mysql2
  retry_deadline: 5 # 5秒経過したらリトライを停止

🔗 Dup and freeze complex types when making query attributes by tenderlove · Pull Request #46048 · rails/rails

クエリキャッシュが誤った値を返すことがあるバグを修正。

#46044を参照。

Aaron Patterson
同CHANGELOGより

🔗 Add ssl-mode option to dbconsole command and MySQLDatabaseTasks by p8 · Pull Request #46008 · rails/rails

MySQLDatabaseTasksでMySQLのSSLモードオプションをサポート。

データベースサーバーの識別情報を検証するには、--ssl-modeオプションにVERIFY_CAまたはVERIFY_IDENTITYを設定する必要がある。従来、データベース作成やstructureダンプなどのMySQLデータベースタスクでこのオプションが無視されていた。

Petrik de Heus
同CHANGELOGより

参考: 週刊Railsウォッチ20221011: dbconsoleコマンドとMySQLDatabaseTasksに--ssl-modeオプションを追加

🔗 Move InternalMetadata to an independent object by eileencodes · Pull Request #45982 · rails/rails

ActiveRecord::InternalMetadataを別オブジェクトに移動。

ActiveRecord::InternalMetadataActiveRecord::Baseを継承しなくなり、connectionを渡して初期化する独立したオブジェクトになった。
このクラスはprivateであり、アプリケーションから直接使うべきではない。スキーママイグレーションのテーブルとやりとりする必要がある場合は、ActiveRecord::Base.connection.schema_migrationのように直接コネクションにアクセスすること。

Eileen M. Uchitelle
同CHANGELOGより

参考: Rails API ActiveRecord::InternalMetadata

🔗 Deprecate quoting ActiveSupport::Duration as an integer (#44341) by aramgre · Pull Request #44438 · rails/rails

ActiveSupport::Durationを整数値として式展開することを非推奨化。

SQL文字列テンプレート内でActiveSupport::Durationを式展開にバインドするパラメータとして利用することが非推奨化された。
この非推奨警告表示を回避するには、明示的にDurationをより具体的なデータベース型に変換すること。
たとえば、Durationを秒単位の整数値として利用したい場合は、以下のように書くこと。

Record.where("duration = ?", 1.hour.to_i)

DurationISO 8601形式の文字列として使いたい場合は、以下のように書くこと。

Record.where("duration = ?", 1.hour.iso8601)

Aram Greenman
同CHANGELOGより

参考: この変更には後方互換用のコンフィグはありません(#44438コメント)。

🔗 improve "in_order_of" to allow string column name by igorkasyanchuk · Pull Request #45971 · rails/rails

QueryMethods#in_order_ofによる並べ替えが、カラム名が文字列の場合にも使えるようになった。

Post.in_order_of("id", [4,2,3,1]).to_a
Post.joins(:author).in_order_of("authors.name", ["Bob", "Anna", "John"]).to_a

Igor Kasyanchuk
同CHANGELOGより

参考: Rails API in_order_of -- ActiveRecord::QueryMethods

🔗 Move SchemaMigration to an independent object by eileencodes · Pull Request #45908 · rails/rails

ActiveRecord::SchemaMigrationを別オブジェクトに移動。

ActiveRecord::SchemaMigrationActiveRecord::Baseを継承しなくなり、connectionを渡して初期化する独立したオブジェクトになった。
このクラスはprivateであり、アプリケーションから直接使うべきではない。スキーママイグレーションのテーブルとやりとりする必要がある場合は、ActiveRecord::Base.connection.schema_migrationのように直接コネクションにアクセスすること。

Eileen M. Uchitelle
同CHANGELOGより

参考: Rails API ActiveRecord::SchemaMigration

🔗 Make connection_pool_list take an explicit argument by eileencodes · Pull Request #45961 · rails/rails

all_connection_poolsを非推奨化し、connection_pool_listにオプションを明示的に渡すようにした。

#45924に続いて、all_connection_poolsを非推奨化した。
connection_pool_listには明示的にrole引数を渡すようになった。この引数に:allを渡すことで、アプリケーションで新しい振る舞いを選択できるようになる。

Eileen M. Uchitelle
同CHANGELOGより

参考: Rails API connection_pool_list -- ActiveRecord::ConnectionAdapters::ConnectionHandler

🔗 Fix bug in connection handler methods using all pools by eileencodes · Pull Request #45924 · rails/rails

コネクションハンドラのメソッドがすべてのコネクションプールで動作するよう修正。

以下のメソッドが、デフォルトですべてのコネクションプールで動作するようになった。

  • active_connections?
  • clear_active_connections!
  • clear_reloadable_connections!
  • clear_all_connections!
  • flush_idle_connections!

従来は、ロールを指定しない場合にデフォルトでcurrent_roleロールまたは:writingロールが使われていた。

Eileen M. Uchitelle
同CHANGELOGより

参考: 週刊Railsウォッチ20221003: コネクションハンドラメソッドのバグを修正

🔗 Allow ActiveRecord::QueryMethods#select to accept a hash by alextrueman · Pull Request #45612 · rails/rails

関連: Allow ActiveRecord::QueryMethods#reselect to accept a hash by sampatbadhe · Pull Request #46253 · rails/rails

ActiveRecord::QueryMethods#selectにハッシュ値を渡せるようになった。

従来は、selectでカラム定義やselectのエイリアス定義を行うには生SQLまたはシンボルを渡すしかなかった。

この変更によって、以下のようにhashを引数として渡せるようになる。

Post.joins(:comments).select(posts: [:id, :title, :created_at], comments: [:id, :body, :author_id])
#=> "SELECT "posts"."id", "posts"."title", "posts"."created_at", "comments"."id", "comments"."body", "comments"."author_id"
#   FROM "posts" INNER JOIN "comments" ON "comments"."post_id" = "posts"."id""

Post.joins(:comments).select(posts: { id: :post_id, title: :post_title }, comments: { id: :comment_id, body: :comment_body })
#=> "SELECT posts.id as post_id, posts.title as post_title, comments.id as comment_id, comments.body as comment_body
#    FROM "posts" INNER JOIN "comments" ON "comments"."post_id" = "posts"."id""

Oleksandr Holubenko, Josef Šimánek, Jean Boussier
同CHANGELOGより

参考: 週刊Railsウォッチ20220926: ActiveRecord::QueryMethods#selectにハッシュを渡せるようになった

🔗 Normalize virtual attributes on ActiveRecord::Persistence#becomes by intrip · Pull Request #42650 · rails/rails

ActiveRecord::Persistence#becomesで仮想属性を使えるようになった。

ソースクラスとターゲットクラスの属性セットが異なる場合は、ターゲットクラスの属性を追加する形で属性を適応させるようになる。

class Person < ApplicationRecord
end

class WebUser < Person
  attribute :is_admin, :boolean
  after_initialize :set_admin

  def set_admin
    write_attribute(:is_admin, email =~ /@ourcompany.com$/)
  end
end

person = Person.find_by(email: "email@ourcompany.com")
person.respond_to? :is_admin
# => false
person.becomes(WebUser).is_admin?
# => true

Jacopo Beschi, Sampson Crowley
同CHANGELOGより

参考: Rails API becomes -- ActiveRecord::Persistence
参考: 週刊Railsウォッチ20220920: ActiveRecord::Persistence#becomesをvirtual attributeに適応させる

🔗 Fix ActiveRecord::QueryMethods#in_order_of to work with nils by fatkodima · Pull Request #45670 · rails/rails

Enumerable#in_order_ofの振る舞いに合わせるため、ActiveRecord::QueryMethods#in_order_ofnilを扱えるよう修正。

たとえば、Post.in_order_of(:title, [nil, "foo"])でタイトルがnilのpostsも含まれるようになる。これはPost.all.to_a.in_order_of(:title, [nil, "foo"])の振る舞いと同じ。

fatkodima
同CHANGELOGより

参考: Rails API in_order_of -- ActiveRecord::QueryMethods
参考: Rails API in_order_of -- Enumerable
参考: 週刊Railsウォッチ20220905: ActiveRecord::QueryMethods#in_order_ofのソート対象の値がnilでも動作するよう修正

🔗 Optimize add_timestamps to use a single SQL statement when supported by ilianah · Pull Request #45723 · rails/rails

add_timestampsが発行するSQLステートメントが1つだけになるよう最適化。

add_timestamps :my_table

上によって以下のSQLが生成されるようになる。

ALTER TABLE "my_table" ADD COLUMN "created_at" datetime(6) NOT NULL, ADD COLUMN "updated_at" datetime(6) NOT NULL

Iliana Hadzhiatanasova
同CHANGELOGより

参考: §3.9 changeメソッドを使う -- Active Record マイグレーション - Railsガイド

🔗 Add drop_enum command for Postgres by ghiculescu · Pull Request #45735 · rails/rails

マイグレーションコマンドにdrop_enumを追加(PostgreSQLのみ)。

これはcreate_enumと逆の動作。enumを削除する前には、そのenumに依存しているカラムを必ず削除しておくこと。

Alex Ghiculescu
同CHANGELOGより

Rails 7: PostgreSQLのカスタムenum型が使いやすくなった(翻訳)

🔗 Add support for if_exists option when removing a check constraint · rails/rails@25f97a6

CHECK制約の削除でif_existsをサポート。

remove_check_constraintメソッドにif_existsオプションを渡せるようになった。if_exists: trueを設定すると、そのCHECK制約が存在しない場合にエラーを発生しなくなる。

Margaret Parsa and Aditya Bhutani
同CHANGELOGより

参考: §3.9 changeメソッドを使う -- Active Record マイグレーション - Railsガイド
参考: 週刊Railsウォッチ20220822: CHECK制約の削除でif_existsオプションが利用可能になる

🔗 find_or_create_by: handle race condition by finding again by casperisfine · Pull Request #45720 · rails/rails

find_or_create_byRecordNotUniqueが発生した場合はfindをリトライするようになった。

find_or_create_byは本質的に競合がつきものなので、適切なunique制約が設定されているかどうかで「重複レコードを作成する」か「ActiveRecord::RecordNotUniqueで失敗する」かが決まる。

このユースケース向けにcreate_or_find_byが導入されたが、レコードが既に存在する可能性が非常に高い場合は、INSERTはSELECTよりも多くのデータ送信が必要になり、データベース側の作業も増加するため、効率がかなり悪くなる。また、データベースによっては、主キーのインクリメント(望ましくない)が消費される可能性もある。

そのため、レコードが既に存在する可能性が非常に高いユースケースでは、createActiveRecord::RecordNotUniqueで失敗した場合はfindを再試行することで競合が発生しないようにできる。これはテーブルで適切なunique制約が設定されていることが前提となる。さもないと、find_or_create_byで引き続き重複レコードが発生する。

Jean Boussier, Alex Kitchens
同CHANGELOGより

参考: §19.1 find_or_create_by -- Active Record クエリインターフェイス - Railsガイド
参考: Rails API create_or_find_by -- ActiveRecord::Relation
参考: 週刊Railsウォッチ20220822: find_or_create_byRecordNotUniqueエラーの場合にfindをリトライするようになった

🔗 Simplify adapter construction; defer connect until first use by matthewd · Pull Request #44591 · rails/rails

Active Recordのデータベースアダプタに、よりシンプルなコンストラクタAPIが導入された。

従来は、データベースアダプタが再接続をサポートするために新しいraw_connectionのビルド方法を知っている必要があったが、最初の確立済みコネクションを渡されることも期待されていた。

改修後は、アダプタのインスタンスを手動で作成する場合は、コンフィグ用ハッシュを1個渡すだけで、必要に応じて実際の接続が確立するようになった。

Matthew Draper
同CHANGELOGより

可能な場合は、DBプールのチェックアウト中にSELECT 1による余分なコネクションバリデーション用クエリを避けるようになった。

リクエスト中の最初のクエリが冪等であることがわかっている場合は、これを用いてコネクションを直接バリデーションできるので、ネットワークのやりとりが削減される。

Matthew Draper
同CHANGELOGより

🔗 Defer verification of database connections by matthewd · Pull Request #44576 · rails/rails

コネクションが切断された場合、安全であればリクエストの途中でもデータベースコネクションを自動で再接続するようになった。

冪等であることがわかっているクエリを実行しようとしてエラーが発生した場合、かつトランザクション内でない場合は、直ちにデータベースサーバーに再接続しても安全である。今後はこれがデフォルトの振る舞いになる。

新しいデフォルトの振る舞いは常に安全でなければならない。この振る舞いをサポートするため、どのクエリを冪等と認識するかについては保守的なアプローチを取っている。ただし、この振る舞いは、データベースコネクションのconnection_retriesオプションに0を設定することで無効にできる。

Matthew Draper
同CHANGELOGより

参考: Rails API connection_retries -- ActiveRecord::ConnectionAdapters::AbstractAdapter

🔗 Avoid removing a PostgreSQL extension when there are dependent objects by fatkodima · Pull Request #45474 · rails/rails

PostgreSQL拡張に依存しているオブジェクトが存在する場合は、その拡張の削除を回避するようになった。

従来は、拡張を削除すると依存オブジェクトも暗黙で削除されていた。
改修後は、このような削除を行うとエラーを発生するようになった。

マイグレーションで以下のように拡張を強制削除することも可能。

disable_extension :citext, force: :cascade

修正: #29091

fatkodima
同CHANGELOGより

参考: 週刊Railsウォッチ20220801: PostgreSQL拡張機能に依存するオブジェクトがある場合は削除しないようになった

🔗 Accept nested functions in Dangerous Query Methods by siegfault · Pull Request #44010 · rails/rails

ネストしたSQL関数を安全なSQL文字列として扱えるようになった。

Michael Siegfried
同CHANGELOGより

# 同PRより
Post.pluck(Arel.sql("length(trim(title))"))

参考: 週刊Railsウォッチ20220725: ネストしたSQL関数を安全なSQL文字として許容する

🔗Defer constant loading of ActiveRecord::DestroyAssociationAsyncJob via a String instead of a class constant by bensheldon · Pull Request #45476 · rails/rails

destroy_association_async_job=に設定するクラス(定数)を、クラス名の文字列でも設定できるようになった。

ActiveRecord::BaseActiveJob::Baseの間のオートローディングを先延ばしするようにし、ActiveRecord::DestroyAssociationAsyncJobの設定をActive JobからActive Recordに移動した。

ActiveRecord::ActiveJobRequiredErrorは非推奨化された。
これにより、ジョブクラスが読み込み不能な場合はNameErrorが発生するようになった。

また、関連付けでdependent: :destroy_asyncが宣言され、かつジョブクラスが未設定の場合はActiveRecord::ConfigurationErrorが発生するようになった。

Ben Sheldon
同CHANGELOGより

参考: Rails API destroy_association_async_job -- ActiveRecord::Core
参考: config.active_record.destroy_association_async_job -- Rails アプリケーションを設定する - Railsガイド

🔗 ActiveRecord::Store encode store as a regular Hash by casperisfine · Pull Request #45591 · rails/rails

ActiveRecord::Storeにおけるハッシュのシリアライズを通常のハッシュとして行うよう修正。

従来はActiveSupport::HashWithIndifferentAccessとしてシリアライズしていたが、これは無駄が多く、YAML safe_loadで問題を生じる。

Jean Boussier
同CHANGELOGより

この修正は、Rails 7.0.4と6.1.7でリリース済みです

🔗 Add timestamptz as a time zone aware type for PostgreSQL by ghiculescu · Pull Request #44601 · rails/rails

PostgreSQLのタイムゾーン対応型timestamptzを追加。

データベース内でtimestamp with time zone値を正しく解析するにはこの型が必要。

これを使いたくない場合は、イニシャライザに以下を追加することで無効にできる。

ActiveRecord::Base.time_zone_aware_types -= [:timestamptz]

Alex Ghiculescu
同CHANGELOGより

この修正は、Rails 7.0.4でリリース済みです

🔗 Add ActiveRecord::Base::generates_token_for by jonathanhefner · Pull Request #44189 · rails/rails

ActiveRecord::Base.generates_token_for APIを新たに追加。

現在のsigned_idは、パスワードのリセットなどでトークンを生成する役割を担当している。しかしsigned_idにはレコードのステートを反映できないので、トークンを1回だけ使う場合は、少なくとも期限切れまでデータベースでトラッキングしなければならなくなる。

generates_token_forを使うことで、トークンにレコードのデータを埋め込めるようになる。このトークンを用いてレコードを取得すると、トークンのデータと現在のレコードのデータが比較される。両者が一致しない場合、トークンは無効と見なされるため、期限切れした場合と同じ扱いになる。

例:

class User < ActiveRecord::Base
  has_secure_password

  generates_token_for :password_reset, expires_in: 15.minutes do
    # `password_salt`(`has_secure_password`で定義される)は、
    # そのパスワードのsaltを返す。パスワードが変更されるとsaltも変更されるので、
    # パスワードが変更されるとこのトークンは無効になる。
    BCrypt::Password.new(password_digest).salt[-10..]
  end
end

user = User.first
token = user.generate_token_for(:password_reset)

User.find_by_token_for(:password_reset, token) # => user

user.update!(password: "new password")
User.find_by_token_for(:password_reset, token) # => nil

Jonathan Hefner
同CHANGELOGより

参考: Rails API generates_token_for -- `ActiveRecord::TokenFor::ClassMethods

🔗 Optimize Active Record batching for whole table iterations by fatkodima · Pull Request #45414 · rails/rails

Active Recordのテーブル全体をイテレーションするバッチを最適化。

従来のin_batchesでは、全idを取得したうえでバッチごとにINベースのクエリを構築していた。テーブル全体をイテレーションする場合、この方法では不要なidまで読み込まれてしまい、INクエリの項目数が増えて遅くなる。

改修後は、テーブル全体のイテレーションでは範囲指定(id >= x AND id <= y)をデフォルトで使うようになり、イテレーションが数倍高速になる。たとえば、PostgreSQLで1000万件のレコードを持つテーブルでテストした場合のクエリ時間は253s-> 30s、更新は288s->124s、削除は268s-> 83sとなった。

このイテレーションをデフォルトで使うのはテーブル全体をイテレーションする場合に限られる。この振る舞いは、use_ranges: falseオプションを渡すことで無効化できる。

テーブル全体のイテレーションで、 archived_at: nil のような条件だけを指定する場合(かつアーカイブ済みのレコードがごく一部しかない場合)、このアプローチを採用する意義がある。

Project.where(archived_at: nil).in_batches(use_ranges: true) do |relation|
  # 何かする
end

詳しくは#45414を参照。

fatkodima
同CHANGELOGより

参考: 週刊Railsウォッチ20220711: Active Recordのin_batchesuse_ranges: trueを指定可能になった

🔗 Common Table Expression support added "out-of-the-box" by vlado · Pull Request #37944 · rails/rails

.withクエリメソッドを追加。
CTE(Common Table Expression)を手軽に構築してActiveRecord::Relationを得られるようになった。

Post.with(posts_with_comments: Post.where("comments_count > ?", 0))
# => ActiveRecord::Relation
# WITH posts_with_comments AS (SELECT * FROM posts WHERE (comments_count > 0)) SELECT * FROM posts

Vlado Cingel
同CHANGELOGより

参考: PostgreSQL 15ドキュメント 7.8. WITH問い合わせ(共通テーブル式)
参考: 週刊Railsウォッチ20220711: ActiveRecord::RelationにCTEを利用できるwithメソッドが追加

🔗 Only remove connection for an existing pool if the config is different by eileencodes · Pull Request #45450 · rails/rails

同一のコネクションプールが既に存在する場合は新しいコネクションを確立しないようになった。

従来は、既に確立済みのコネクションを持つクラスで establish_connection が呼び出されると、設定が同じかどうかに関係なく既存のコネクションが削除された。
改修後は、新しいコネクションと同じ値を持つコネクションプールが見つかった場合、新しいコネクションではなく既存のコネクションを返すようになった。

アプリケーションのコードが、新しいコネクションが既存のコネクションと同一かどうかにかかわらず確立される振る舞いに依存している場合、振る舞いがわずかに変更されることになる。
古い振る舞いに戻したい場合は、新しいコネクションを確立する前にActiveRecord::Base#remove_connectionを呼び出す必要がある。設定を変えてestablish_connectionを呼び出す場合の振る舞いは、従来と同様になる。

Eileen M. Uchitelle
同CHANGELOGより

参考: Rails API establish_connection -- ActiveRecord::ConnectionHandling

🔗 Allow db:prepare to load schema if database already exists but is empty; also dumps schema after migrations by bensheldon · Pull Request #45464 · rails/rails

db:prepareタスクを更新。

初期化されていないデータベースが存在する場合はスキーマを読み込み、残りのマイグレーションがあれば実行して、その後スキーマをダンプするように変更した。

Ben Sheldon
同CHANGELOGより

参考: 週刊Railsウォッチ20220719: 空のデータベースが存在していてもdb:prepareでスキーマを読み込み可能にした

🔗 Fix supporting timezone awareness for tsrange array columns. by morgoth · Pull Request #45348 · rails/rails

(PostgreSQLのみ)範囲型のtsrangeカラムとtstzrangeカラムでタイムゾーンを認識するサポートを修正。

# データベースのマイグレーション内
add_column :shops, :open_hours, :tsrange, array: true

# アプリのコンフィグ内
ActiveRecord::Base.time_zone_aware_types += [:tsrange]

# このコードのtimeがアプリのタイムゾーンに正しく変換されるようになる
Shop.create!(open_hours: [Time.current..8.hour.from_now])

Wojciech Wnętrzak
同CHANGELOGより

この修正は、Rails 7.0.4でリリース済みです

参考: Rails API ActiveRecord::ConnectionAdapters::PostgreSQL::ColumnMethods
参考: PostgreSQL 15ドキュメント 8.17. 範囲型

🔗 Introduce "Execution Strategy" object for Migrations by adrianna-chang-shopify · Pull Request #45324 · rails/rails

マイグレーションの実行にStrategyパターンを導入。

デフォルトでは、メソッドをコネクションアダプタに委譲するstrategyオブジェクトをマイグレーションで使う。利用側(consumer)は、カスタムのstrategyオブジェクトを実行することでマイグレーションの実行方法を変更できるようになる。

Adrianna Chang
同CHANGELOGより

参考: config.active_record.migration_strategy -- Rails アプリケーションを設定する - Railsガイド
参考: Strategy パターン - Wikipedia
参考: 週刊Railsウォッチ20220704: マイグレーションをExecutionStrategyでカスタマイズ可能にする

🔗 Add adapter option disallowing foreign keys by promulo · Pull Request #45301 · rails/rails

アダプタ設定ファイルのオプションで外部キーを禁止できるようになった。

新しいforeign_keysオプションはdatabase.ymlに追加可能。これにより、背後のデータベースで外部キー制約がサポートされていても外部キー制約をスキップできるようになる。

利用法:

development:
    <<: *default
    database: storage/development.sqlite3
    foreign_keys: false

Paulo Barros
同CHANGELOGより

参考: 週刊Railsウォッチ20220704: database.ymlでforeign_keys: falseを指定可能になった

🔗 Add configurable deprecation warning for singular associations by HParker · Pull Request #45344 · rails/rails

単数形の関連付けに対する非推奨化警告をコンフィグで設定できるようになった。

このコンフィグは、単数形の関連付け名をwhere内で複数形で参照する非推奨の書き方(遅くなる)に警告を表示するかどうかを制御する。

以下の設定にするとパフォーマンスが改善する。

config.active_record.allow_deprecated_singular_associations_name = false

Adam Hess
同CHANGELOGより

このコンフィグは、Rails 7.1ではデフォルトでfalseになります。
参考: config.active_record.allow_deprecated_singular_associations_name -- Rails アプリケーションを設定する - Railsガイド
参考: 週刊Railsウォッチ20220704: 関連付け先の単数形の名前をwhere内から複数形で参照すると警告を出す

🔗 Run transactional callbacks on instances most likely to match DB state by cbothner · Pull Request #45280 · rails/rails

トランザクション内でレコードをsaveすると、最も新しいインスタンスでトランザクションコールバックを実行するようになった。

1つのトランザクション内で複数のActive Recordインスタンスが同じレコードを変更する場合、そのうちの1つだけがafter_commitafter_rollbackを実行する。
Railsでどのインスタンスがコールバックを受け取るかを指定できるよう、config.active_record.run_commit_callbacks_on_first_saved_instances_in_transactionコンフィグが追加された。フレームワークはデフォルトで新しいロジックを使うよう変更された。

config.active_record.run_commit_callbacks_on_first_saved_instances_in_transactiontrueの場合は、インスタンスのステートがstaleしていても(=古くなっても)、最初に保存したインスタンスでトランザクションコールバックが実行される。
これがfalseの場合は7.1からフレームワークのデフォルトになるが、トランザクションコールバックはステートが最新のインスタンスで実行される。インスタンスは以下のように選択される。

  • 一般に、トランザクションコールバックは最新のインスタンスで実行され、トランザクション内で指定のレコードを保存する。
  • ただし例外が2つある。
    • トランザクション内でレコードを作成して別のインスタンスで更新すると、after_create_commitは2番目のインスタンスで実行される。これは、インスタンスのステートに基づいてナイーブに実行されるafter_update_commitコールバックの代わりとなる。
    • レコードがトランザクション内で削除されると、after_destroy_commitコールバックは最後に削除されたインスタンスで実行される。これは、たとえstaleしたインスタンスがその後更新を行ったとしても同様で、この更新はどの行にも影響しない。

Cameron Bothner and Mitch Vollebregt
同CHANGELOGより

参考: 週刊Railsウォッチ20220620: トランザクション内に同一モデルのインスタンスが複数ある場合にどのインスタンスからコールバックを呼び出すかを変更

🔗 Add :strict option to default SQLite database.yml template by fatkodima · Pull Request #45346 · rails/rails

SQLite3Adapterで"strict strings"モードを有効にした。

SQLite3で"strict strings"モードを設定することで、二重引用符("")で囲まれた文字列リテラルが無効になった。

SQLite3では、二重引用符で囲んだ文字列リテラルにいくつかの癖がある。
最初は二重引用符で囲んだ文字列を識別子名とみなそうとするが、存在しない場合は文字列リテラルとみなす。この振る舞いのせいで、タイポがあっても通知されない。たとえば、存在しないカラムに対してインデックスを作成できてしまう。
詳しくは以下のSQLite3ドキュメントを参照。

この振る舞いを無効にしたい場合は、以下の設定で行える。

# config/application.rb
config.active_record.sqlite3_adapter_strict_strings_by_default = false

修正: #27782

fatkodima, Jean Boussier
同CHANGELOGより

参考: config.active_record.sqlite3_adapter_strict_strings_by_default -- Rails アプリケーションを設定する - Railsガイド
参考: 週刊Railsウォッチ20220620: SQLiteのdatabase.ymlにデフォルトで:strictオプションを追加

🔗 Update AR Relation method to reset cache_version by austenmadden · Pull Request #45342 · rails/rails

リレーションのcache_versionがstaleする(古くなる)可能性がある問題を修正。

従来は、リレーションオブジェクトでresetを呼び出しても@cache_versionsがリセットされていなかったため、最新の正しいデータがあるにもかかわらず、古いcache_versionの値が返されて混乱することがあった。

利用法:

developers = Developer.all
developers.cache_version

Developer.update_all(updated_at: Time.now.utc + 1.second)

developers.cache_version # cache_versionがstaleする
developers.reset
developers.cache_version # 最新の正しいcache_versionを返す

修正: #45341

Austen Madden
同CHANGELOGより

参考: 週刊Railsウォッチ20220620: Active Recordリレーションのresetcache_versionがリセットするよう修正

🔗 Add support for exclusion constraints (PostgreSQL-only) by agrobbin · Pull Request #40224 · rails/rails

(PostgreSQLのみ)EXCLUDE制約をサポート。

  • add_exclusion_constraint
  • remove_exclusion_constraint
add_exclusion_constraint :invoices, "daterange(start_date, end_date) WITH &&", using: :gist, name: "invoices_date_overlap"
remove_exclusion_constraint :invoices, name: "invoices_date_overlap"

EXCLUDE制約について詳しくはPostgreSQLドキュメントのCREATE TABLE ... EXCLUDE ...を参照。

Alex Robbin
同CHANGELOGより

参考: 週刊Railsウォッチ20220620: (PostgreSQLのみ)EXCLUDE制約のサポートが追加

🔗 change_column_null should raise if a non-boolean 3rd argument is provided by ghiculescu · Pull Request #45229 · rails/rails

change_column_nullの第3引数がブーリアンでない場合はエラーを発生するよう修正。

従来は、change_column_nullの第3引数がブーリアンでない場合にtruthyとして扱われ、カラムがnull許容になった。この振る舞いは予想に反するので、trueまたはfalseのみを渡せるよう変更された。

change_column_null :table, :column, true # good
change_column_null :table, :column, false # good
change_column_null :table, :column, from: true, to: false # raiseする(従来はnullableカラムになった)

Alex Ghiculescu
同CHANGELOGより

参考: 週刊Railsウォッチ20220620: change_column_nullにブーリアン以外の値を渡すとエラーになるように修正

🔗 Enforce limit on table names length by fatkodima · Pull Request #45136 · rails/rails

テーブル名の長さに上限を設けた。

修正: #45130

fatkodima
同CHANGELOGより

参考: 週刊Railsウォッチ20220620: テーブル名の長さに上限を設定

🔗 Correct minimum MariaDB version for CHECK_CONSTRAINTS by elebow · Pull Request #45326 · rails/rails

CHECK制約サポートのため、MariaDBの最小バージョン指定を10.2.22に修正。

Eddie Lebow
同CHANGELOGより

🔗 Fix Hstore deserialize regression by edsharp · Pull Request #45222 · rails/rails

PostgreSQLのHstoreデータ型のデシリアイズで再発した不具合を修正。

edsharp
同CHANGELOGより

参考: PostgreSQL 15ドキュメント F.18. hstore

🔗 Add validity for PostgreSQL indexes by fatkodima · Pull Request #45160 · rails/rails

PostgreSQLインデックスの有効性をチェックするvalid: trueオプションを追加。

connection.index_exists?(:users, :email, valid: true)
connection.indexes(:users).select(&:valid?)

fatkodima
同CHANGELOGより

参考: 週刊Railsウォッチ20220606: PostgreSQL用のindex_exists?valid:キーワード引数が追加

🔗 Fix eager loading models without primary keys by mattalat · Pull Request #43402 · rails/rails

主キーがないモデルのeager_loadが正しく行われない問題を修正。

Anmol Chopra, Matt Lawrence, and Jonathan Hefner
同CHANGELOGより

プルリクメッセージには、通常のActive Recordでも、データベースVIEWを使っている場合や、以下のようなメソッドチェインでこの問題が起きていたと書かれています。

my_instance.includes(:model_without_primary_key).order('model_without_primary_key.name')

参考: 週刊Railsウォッチ20220606: 主キーのないモデルのeager_loadを修正

🔗 Avoid validating a unique field if it has not changed and is backed by a unique index by fatkodima · Pull Request #45149 · rails/rails

uniqueインデックスのあるフィールドで変更が生じなかった場合は、フィールドのuniquenessバリデーションを回避するようになった。

従来は、レコードをsaveしたときにuniquenessバリデーションが設定されている属性でuniquenessチェックのためのクエリが余分に送信されていた。これは属性に変更がなかった場合でも発生していた。

これに対応するuniqueインデックスがデータベース側にあれば、永続化でこのバリデーションが失敗することはありえないので、このバリデーションを安全にスキップできる。

fatkodima
同CHANGELOGより

参考: §2.12 uniqueness -- Active Record バリデーション - Railsガイド
参考: 週刊Railsウォッチ20220531: uniqueness指定のフィールドが変更されていない場合のバリデーションを回避

🔗 no longer set sql_auto_is_null by HParker · Pull Request #45134 · rails/rails

MySQLアダプタでvariables["sql_auto_is_null"] = 0を設定しないようになった。

この設定はMySQL 5.5以降デフォルトでオフになったので、手動でわざわざオフにする必要はない。

Adam Hess
同CHANGELOGより

参考: PDF MySQL 5.5 Release Notes

🔗 Fix touch to raise an error for readonly columns by fatkodima · Pull Request #45125 · rails/rails

attr_readonlyカラムにtouchしたらActiveRecord::ActiveRecordErrorを発生するよう修正。

fatkodima
同CHANGELOGより

同プルリクメッセージでは以下のように書かれています。

  • update_attributeおよびupdate_attributesではエラーになる
  • 属性への代入やupdateは、readonly属性についてはsave時に単に無視される(以下のAPIドキュメントに書かれている通り)。

参考: Rails API attr_readonly -- ActiveRecord::ReadonlyAttributes::ClassMethods

🔗 Add ability to ignore tables by regexp for SQL schema dumps by fatkodima · Pull Request #45091 · rails/rails

SQLスキーマダンプで除外したいテーブルを正規表現で指定できるようになった。

ActiveRecord::SchemaDumper.ignore_tables = [/^_/]

fatkodima
同CHANGELOGより

参考: ActiveRecord::SchemaDumper.ignore_tables -- Rails アプリケーションを設定する - Railsガイド

🔗 Avoid query from calculations on contradictory relation by luanzeba · Pull Request #45030 · rails/rails

矛盾のあるリレーションで計算メソッドを実行するときにクエリ送信を回避するよう修正。

従来は、User.where(id: []).countのように矛盾のあるリレーションを渡すと計算時にクエリが送信されていた。修正により、このようなシナリオでクエリを送信しなくなった。

該当する計算メソッドは以下のとおり。

  • count
  • sum
  • average
  • minimum
  • maximum

Luan Vieira, John Hawthorn and Daniel Colson
同CHANGELOGより

参考: §22 計算 -- Active Record クエリインターフェイス - Railsガイド

Rails 7: リレーションの結果が空になる計算でクエリ送信を回避する(翻訳)

🔗 Allow using aliased attributes with insert_all/upsert_all by fatkodima · Pull Request #45036 · rails/rails

insert_allupsert_allでエイリアス属性も指定できるようになった。

class Book < ApplicationRecord
  alias_attribute :title, :name
end

Book.insert_all [{ title: "Remote", author_id: 1 }], returning: :title

fatkodima
同CHANGELOGより

参考: Rails API insert_all -- ActiveRecord::Persistence::ClassMethods
参考: Rails API upsert_all -- ActiveRecord::Persistence::ClassMethods

Rails 7: insert_allとupsert_allで属性のエイリアスを指定可能になる(翻訳)

🔗 Support encrypted attributes on columns with default values by jorgemanrubia · Pull Request #45033 · rails/rails

カラムの暗号化属性でデータベースのデフォルト値をサポート。

これにより、カラムに定義された暗号化属性にデフォルト値を設定できるようになる。値は作成時に暗号化される。
改修前は、config.active_record.encryption.support_unencrypted_datatrueを設定しないとエラーになっていた。

Jorge Manrubia and Dima Fatko
同CHANGELOGより

参考: §6.1.1 config.active_record.encryption.support_unencrypted_data -- Active Record と暗号化 - Railsガイド
参考: 週刊Railsウォッチ20220516: デフォルト値付きのカラムで暗号化属性をサポート

🔗 Multi database: define reading_request? in resolver by ghiculescu · Pull Request #44944 · rails/rails

DatabaseSelector::Resolverミドルウェアのreading_request?がオーバーライド可能になった。

デフォルトの実装ではリクエストがget?またはhead?かどうかをチェックしているが、この振る舞いを自由に変更できるようになった。
このメソッドがtrueを返すとResolver#readが呼び出されるようになり、リクエストがreplicaデータベースによっても配信されるようになる。

Alex Ghiculescu
同CHANGELOGより

参考: Rails API ActiveRecord::Middleware::DatabaseSelector

Rails 7: マルチプルDBのreading_request?がカスタマイズ可能になった(翻訳)

🔗 Remove legacy_connection_handling by eileencodes · Pull Request #44827 · rails/rails

Rails 6.1から非推奨化されていたActiveRecord.legacy_connection_handlingを削除。

Eileen M. Uchitelle
同CHANGELOGより

参考: §2.1 データベース単位のコネクション切り替え -- Ruby on Rails 6.1 リリースノート - Railsガイド

参考: 週刊Railsウォッチ20220411: legacy_connection_handlingを削除

🔗 rails db:schema:{dump,load} now checks ENV["SCHEMA_FORMAT"] before config by ghiculescu · Pull Request #44834 · rails/rails

rails db:schema:{dump,load}でコンフィグ前にENV["SCHEMA_FORMAT"]をチェックするようになった。

rails db:structure:{dump,load}は既に非推奨化されているため、スキーマをSQL形式とRuby形式のどちらでも手軽に(=コンフィグを変更せずに)ダンプできる方法がなかった。
この改修により、以下のように環境変数を設定することでこれを行えるようになった。

SCHEMA_FORMAT=sql rake db:schema:dump

Alex Ghiculescu
同CHANGELOGより

この変更は、Rails 7.0.4でリリース済みです

なおconfig.active_record.schema_formatのデフォルトは:rubyです。

参考: §3.8.10 config.active_record.schema_format -- Rails アプリケーションを設定する - Railsガイド

Rails 7: rails db:schema:dumpやloadのスキーマ形式を環境変数で指定可能になった(翻訳)

Rails: db:structure:loadとdb:structure:dumpタスクが非推奨化(翻訳)

🔗 Fixed MariaDB default function by kaspernj · Pull Request #44654 · rails/rails

MariaDBでのデフォルトSQL関数サポートを修正。

db/schema.rbへのダンプでデフォルト関数名が正しく書き込まれていなかったため、db:schema:loadを実行しても正しく動作しなかった。今後、より多くの関数が新規レコードの保存で文字列コンテンツとして追加されるようになるだろう。

kaspernj
同CHANGELOGより

参考: 週刊Railsウォッチ20220328: MariaDBのデフォルト関数サポートを修正

🔗 Add active_record.destroy_association_async_batch_size configuration by nholden · Pull Request #44617 · rails/rails

非同期バッチサイズを指定するactive_record.destroy_association_async_batch_sizeコンフィグを追加。

これにより、アプリケーションでの関連付けにdependent: :destroy_asyncオプションを指定して、単一のバックグラウンドジョブで削除する最大レコード数を指定できるようになる。デフォルトでは、現在の振る舞いを変えない(親レコードを削除すると、すべての依存レコードが単一のバックグラウンドジョブで削除される)。依存レコード数がこの設定を超えると、レコードの削除が複数のバックグラウンドジョブに分割されるようになる。

Nick Holden
同CHANGELOGより

参考: config.active_record.destroy_association_async_batch_size -- Rails アプリケーションを設定する - Railsガイド

Rails 7: バックグラウンドジョブで削除する最大レコード数を指定可能になった(翻訳)

🔗 Fix remove_foreign_key with :if_exists option when foreign key actually exists by fatkodima · Pull Request #44637 · rails/rails

remove_foreign_key:if_existsオプションを指定すると、外部キーが実際に存在している場合にエラーになっていたのを修正。

fatkodima
同CHANGELOGより

参考: §3.7 外部キー -- Active Record マイグレーション - Railsガイド

🔗 Remove --no-comments from Postgres structure dump command by ghiculescu · Pull Request #44633 · rails/rails

PostgreSQLのstructure dumpの--no-commentsフラグを廃止。

これにより、スキーマでカスタムコメントを使っている一部のアプリが動かなくなる。
structureダンプにコメントを含めたくない場合は、以下を設定できる。

ActiveRecord::Tasks::DatabaseTasks.structure_dump_flags = ['--no-comments']

Alex Ghiculescu
同CHANGELOGより

参考: §6 Structure Dumpについて -- Active Record と PostgreSQL - Railsガイド

🔗 Reduce the memory footprint of fixtures accessors by casperisfine · Pull Request #44528 · rails/rails

フィクスチャのアクセサでメモリフットプリントを削減。

従来は、フィクスチャのアクセサをdefine_methodでeagerに定義していたため、フィクスチャやテストスイートの量が増えるとメモリ使用量に直接影響していた。

改修後は、フィクスチャのアクセサをmethod_missingで実装したことで、メモリやCPUのオーバーヘッドが大きく軽減される。

Jean Boussier
同CHANGELOGより

参考: 週刊Railsウォッチ20220308: フィクスチャのメモリフットプリントを削減
参考: Module#define_method (Ruby 3.2 リファレンスマニュアル)
参考: BasicObject#method_missing (Ruby 3.2 リファレンスマニュアル)

🔗 Fix config.active_record.destroy_association_async_job configuration by nholden · Pull Request #44309 · rails/rails

config.active_record.destroy_association_async_jobコンフィグの不具合を修正。

config.active_record.destroy_association_async_jobは、dependent: :destroy_asyncオプションを指定したhas_many関連付けに対して行う削除をバックグラウンドで実行できるようにすべき。
従来はこのdependent: :destroy_asyncオプションが無視されていたため、ActiveRecord::DestroyAssociationAsyncJobによる削除が常にバックグラウンドで実行されていた。

Nick Holden
同CHANGELOGより

参考: config.active_record.destroy_association_async_job -- Rails アプリケーションを設定する - Railsガイド
参考: 週刊Railsウォッチ20220221: Active Recordのdestroy_association_async_jobコンフィグが効くように修正

🔗 Fix change_column_comment to preserve column's AUTO_INCREMENT in the MySQL adapter by fatkodima · Pull Request #44480 · rails/rails

MySQLアダプタ: change_column_commentでカラムのAUTO_INCREMENTが失われないよう修正。

fatkodima
同CHANGELOGより

参考: Rails API change_column_comment -- ActiveRecord::ConnectionAdapters::SchemaStatements

🔗 Handle quoting of Rational numbers for MySQL by kmcphillips · Pull Request #44404 · rails/rails

MySQLアダプタで、数値がActiveSupport::DurationRationalの場合の引用符処理を修正。

Kevin McPhillips
同CHANGELOGより

参考: 週刊Railsウォッチ20220221: mysql2アダプタでActiveSupport::Durationを適切に扱うよう修正

🔗 Allow column name with COLLATE as safe SQL string by shugo · Pull Request #44384 · rails/rails

orderでカラムにCOLLATEを指定した場合(例: title COLLATE "C")にも、SQL関数を指定したときと同様に安全なSQL文字として扱えるようになった。

Shugo Maeda
同CHANGELOGより

参考: Allow column name with function (e.g. length(title)) as safe SQL string by kamipo · Pull Request #36448 · rails/rails
参考: 週刊Railsウォッチ20220221: orderCOLLATEを安全なSQL文字列として使えるようになった

参考: PostgreSQL 15ドキュメント 24.2. 照合順序サポート
参考: MySQL :: MySQL 8.0 リファレンスマニュアル :: 10.8.1 SQL ステートメントでの COLLATE の使用

🔗 Accept _ integer notation in VERSION arg to database tasks · rails/rails@ef4bf94

データベース用rakeタスクのVERSION引数でアンダースコア(_)も使えるようになった。

Eddie Lebow
同CHANGELOGより

参考: 週刊Railsウォッチ20220221: DBのrakeタスクでVERSION envの数値に_が使えるようになった

🔗 Reverse the order of INSERT statements in structure.sql dumps by ghiculescu · Pull Request #44363 · rails/rails

structure.sqlダンプのINSERTステートメントで値の並び順を逆にした。

これにより、マージで競合する可能性が減るはず。
新しいマイグレーションでは、新しい値がリストのトップに追加されるようになる。

ただし、既存のアプリでは次回structure.sqlを生成したときに大量の差分が発生することになる。

Alex Ghiculescu, Matt Larraz
同CHANGELOGより

🔗 Postgres adapter passes keywords in a deprecated way (Ruby 2.7, ActiveRecord 6.1.4) · Issue #44307 · rails/rails

Ruby 2.7とActive Record 6.1.4でPG.connectにキーワード引数を渡すと非推奨警告が表示されていたのを修正。

修正: #44307

Nikita Vasilevsky
同CHANGELOGより

この修正は、Rails 7.0.2でリリース済みです

🔗 Fix rollbacks following serialization failures or deadlocks by zarqman · Pull Request #44127 · rails/rails

関連: [Tests only] Flunk if test is not using SavepointTransaction by nvasilevski · Pull Request #44686 · rails/rails

シリアライズが失敗してデッドロックした後でデータベースコネクションが切断されるバグを修正。

6.1.4より前は、シリアライズが失敗してデッドロックすると、実際のトランザクションとsavepointの両方でロールバックが発行されていた。MySQLはデッドロック後にsavepointのロールバックを許さないため、これによって壊れてしまう。

6.1.4では実際のトランザクションとsavepointの両方でロールバックが削除されたが、これによってデータベースコネクションのステートがunknownになり、切断されてしまう。

この修正によって、MySQLのsavepointを除いてロールバックが復元されるようになった。

Thomas Morgan
同CHANGELOGより

参考: MySQL :: MySQL 8.0 リファレンスマニュアル :: 13.3.4 SAVEPOINT、ROLLBACK TO SAVEPOINT および RELEASE SAVEPOINT ステートメント

🔗 Fiber-safe ConnectionPool by machty · Pull Request #44219 · rails/rails

ActiveRecord::ConnectionPoolがFiberセーフになった。

ActiveSupport::IsolatedExecutionState.isolation_level:fiberを指定すると、コネクションプールからコネクションをチェックアウトする同一のThreadからの複数のFiberをサポートするようになる。

Alex Matchneer
同CHANGELOGより

Rails 7: Active RecordのConnectionPoolsがFiberセーフになった(翻訳)

🔗 Add ActiveRecord::Persistence#update_attribute! by drewtempelmeyer · Pull Request #44141 · rails/rails

ActiveRecord::Persistenceupdate_attribute!を追加。

update_attributeと同様だが、before_*コールバックで:abortがスローされた場合はActiveRecord::RecordNotSavedをraiseする点が異なる。

class Topic < ActiveRecord::Base
  before_save :check_title

  def check_title
    throw(:abort) if title == "abort"
  end
end

topic = Topic.create(title: "Test Title")
# #=> #<Topic title: "Test Title">
topic.update_attribute!(:title, "Another Title")
# #=> #<Topic title: "Another Title">
topic.update_attribute!(:title, "abort")
# raises ActiveRecord::RecordNotSaved

Drew Tempelmeyer
同CHANGELOGより

参考: Rails API update_attribute -- ActiveRecord::Persistence
参考: 週刊Railsウォッチ20220117: update_attribute!が追加

🔗 Avoid eager loading in Relation#pretty_print by BuonOmo · Pull Request #43302 · rails/rails

ActiveRecord::Relation#pretty_printで全レコードの読み込みを回避するようになった。

# 修正前
pp Foo.all # 全テーブルを読み込む

# 修正後
pp Foo.all # 10件まで表示し、残りを"..."で表示

Ulysse Buonomo
同CHANGELOGより

参考: 週刊Railsウォッチ20220117: Relation#pretty_printのeager loadingを回避

🔗 QueryMethods#in_order_of drop records not listed by kddnewton · Pull Request #44097 · rails/rails

QueryMethods#in_order_ofメソッドで、指定の値にないレコードをWHEREで除外するように変更。

Enumerablein_order_ofと振る舞いを合わせるため、QueryMethodsin_order_ofに渡した値のリストで絞り込んだものを並べ替えるようにした。

Kevin Newton
同CHANGELOGより

# 同PRのAPIドキュメントより
 User.in_order_of(:id, [1, 5, 3])
 # SELECT "users".* FROM "users"
 #   ORDER BY FIELD("users"."id", 1, 5, 3)
 #   WHERE "users"."id" IN (1, 5, 3)

注: このプルリクによって、#43916の「指定外のレコードのソート順も維持する振る舞い」が打ち消されました。

参考: この改修はRails 7.0.1でリリース済みです

Rails 7: クエリ結果を任意の順序にできるActiveRecord::QueryMethods#in_order_of

🔗 Allow named expression indexes to be revertible. by oliverguenther · Pull Request #43333 · rails/rails

名前付きの式インデックスをロールバックできるよう修正。

従来は、リバーシブル(取り消し可能)なマイグレーションのロールバックで以下のコードがエラーになった(インデックスの削除でこのインデックス名が使われていなかったため)。

add_index(:settings, "(data->'property')", using: :gin, name: :index_settings_data_property)

修正: #43331

Oliver Günther
同CHANGELOGより

🔗 Updating the --no-comment argument to the correct --no-comments argument by aldent95 · Pull Request #44028 · rails/rails

PostgreSQLのstructure dumpタスクのヘルプ説明文にある引数名のスペルを修正。

Rails 7で追加された--no-comment引数を正しい--no-comments(複数形)に更新した。

Alex Dent
同CHANGELOGより

🔗 Fix migration compatibility to create SQLite references/belongs_to column as a integer when migration version is 6.0 by marcelolx · Pull Request #43295 · rails/rails

マイグレーションのバージョンが6.0の場合に、SQLite3のreferencesカラムとbelongs_toカラムがinteger型として作成されるよう修正。

バージョン6.0のreferencesカラムとbelongs_toカラムが、SQLite3のアダプタでinteger型ではなくbigint型で作成されていた。

Marcelo Lauxen
同CHANGELOGより

この修正は、Rails 6.1.5でリリース済みです

🔗 Fix QueryMethods#in_order_of to handle empty order list by casperisfine · Pull Request #43916 · rails/rails

QueryMethods#in_order_ofに空のリストを渡せるよう修正。

Post.in_order_of(:id, []).to_a

また、このカラムを明示的に第2ソート順として設定することで、指定以外の値のソート順を維持するようになった。

Jean Boussier

同CHANGELOGより

注: 指定以外の値のソート順を維持する振る舞いは、その後#44097でなくなりました。

この改修は、Rails 7.0.1でリリース済みです

Rails 7: クエリ結果を任意の順序にできるActiveRecord::QueryMethods#in_order_of

🔗 Properly quote autogenerated column aliases by casperisfine · Pull Request #43911 · rails/rails

計算系メソッドによって生成されるカラムエイリアスを正しく引用符で囲むよう修正。

このエイリアスはテーブル名から導出されるので、有効な識別子を得られるという前提が成り立つとは限らない(テーブル名が数字始まりの場合など)。

class Test < ActiveRecord::Base
  self.table_name = '1abc'
end

Test.group(:id).count
# syntax error at or near "1" (ActiveRecord::StatementInvalid)
# LINE 1: SELECT COUNT(*) AS count_all, "1abc"."id" AS 1abc_id FROM "1...

Jean Boussier
同CHANGELOGより

🔗 Add authenticate_by when using has_secure_password by jonathanhefner · Pull Request #43765 · rails/rails

has_secure_passwordauthenticate_byメソッドを追加。

このauthenticate_byメソッドは、以下のようなコードを置き換えるのが目的。このコードは、メールアドレスが一致するユーザーが見つからない場合に、見つかった場合よりも早期に処理が終了してしまう。

User.find_by(email: "...")&.authenticate("...")

このようなコードは、タイミングベースの列挙攻撃に対して脆弱である。攻撃者はこの脆弱性を使って、特定のメールアドレスを持つユーザーアカウントが存在するかどうかを検索時間の違いで判定可能になる。アカウントが存在することを攻撃者が確認できたら、他のデータベースから漏洩したパスワードの中から、そのメールアドレスに関連付けられたパスワードを試せるようになる。これは、ユーザーが同じパスワードを複数のサイトで使い回すというありがちな運用で起こる可能性がある。さらに、攻撃者がアカウントのメールアドレスを手に入れれば標的型フィッシング("spear phishing")攻撃を試みることも可能になる。

authenticate_byは、メールアドレスが一致するユーザーが見つかった場合にかかる時間と、一致するユーザーが見つからない場合にかかる時間を同じにすることで、この脆弱性を修正する。

User.authenticate_by(email: "...", password: "...")

Jonathan Hefner
同CHANGELOGより

注: その後、パスワードが空の場合の修正も7.1.0.beta1に含まれました↓。

参考: Short circuit authenticate_by on empty password by jonathanhefner · Pull Request #43958 · rails/rails

Rails 7: has_secure_password利用時のauthenticate_byメソッド(翻訳)


以前の変更については7-0-stableのCHANGELOGを参照。

関連記事

Rails 7.1に入る主要な機能まとめ(1)update_attribute!、CTEサポートほか(翻訳)

Rails 7.1に入る主要な機能まとめ(2)error_highlight対応、routes --grepほか(翻訳)

Rails 7.1に入る主要な機能まとめ(3)Docker関連ファイル導入ほか(翻訳)


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。