Refactor `ActiveRecord::SignedId` to not rely on relation delegation

Ref: https://github.com/rails/rails/pull/50396
Ref: https://github.com/rails/rails/pull/51776

`ActiveRecord::Relation` automatically delegates missing methods
to the model class wrapped in a `scoping { }` block.

This is to support scoping in user defined class methods. The problem
however is that it's very error prone for the framework, because we
can mistakenly call model methods from inside `Relation` and not
realized we're applying a global scope.

In the best case scenario it's just a waste of performance, but
it can also lead to bugs like https://github.com/rails/rails/issues/51775

I'm planning to restrict this automatic delegation to methods defined
in childs of `ActiveRecord::Base` only: https://github.com/rails/rails/pull/50396
but for this to work we must first refactor any Rails code that rely on it.
This commit is contained in:
Jean Boussier 2024-05-13 10:27:19 +09:00
parent 007a609ef2
commit c6d0ef5974
2 changed files with 11 additions and 0 deletions

View File

@ -66,6 +66,7 @@ module ActiveRecord
include Enumerable
include FinderMethods, Calculations, SpawnMethods, QueryMethods, Batches, Explain, Delegation
include SignedId::RelationMethods
attr_reader :table, :klass, :loaded, :predicate_builder
attr_accessor :skip_preloading_value

View File

@ -13,6 +13,16 @@ module ActiveRecord
class_attribute :signed_id_verifier_secret, instance_writer: false
end
module RelationMethods # :nodoc:
def find_signed(...)
scoping { model.find_signed(...) }
end
def find_signed!(...)
scoping { model.find_signed!(...) }
end
end
module ClassMethods
# Lets you find a record based on a signed id that's safe to put into the world without risk of tampering.
# This is particularly useful for things like password reset or email verification, where you want