From abb6b207c0bf37a9dcd6a4f560444e01f26f72e5 Mon Sep 17 00:00:00 2001 From: Gannon McGibbon Date: Thu, 22 Jun 2023 23:24:47 -0500 Subject: [PATCH] Use _read_attribute to properly query id? Support primary key dirty tracking when ID is a composite primary key with an id column. Introduces `ActiveRecord::AttributeMethods::Query#_query_attribute` to bypass id reader. --- .../attribute_methods/primary_key.rb | 4 +- .../active_record/attribute_methods/query.rb | 45 ++++++++++++------- 2 files changed, 30 insertions(+), 19 deletions(-) diff --git a/activerecord/lib/active_record/attribute_methods/primary_key.rb b/activerecord/lib/active_record/attribute_methods/primary_key.rb index 8af0442a79c..b58c536c514 100644 --- a/activerecord/lib/active_record/attribute_methods/primary_key.rb +++ b/activerecord/lib/active_record/attribute_methods/primary_key.rb @@ -41,9 +41,9 @@ module ActiveRecord # Queries the primary key column's value. def id? if self.class.composite_primary_key? - @primary_key.all? { |col| query_attribute(col) } + @primary_key.all? { |col| _query_attribute(col) } else - query_attribute(@primary_key) + _query_attribute(@primary_key) end end diff --git a/activerecord/lib/active_record/attribute_methods/query.rb b/activerecord/lib/active_record/attribute_methods/query.rb index f24ffa1c6ff..aa371ab4b22 100644 --- a/activerecord/lib/active_record/attribute_methods/query.rb +++ b/activerecord/lib/active_record/attribute_methods/query.rb @@ -13,27 +13,38 @@ module ActiveRecord def query_attribute(attr_name) value = self.public_send(attr_name) - case value - when true then true - when false, nil then false - else - if !type_for_attribute(attr_name) { false } - if Numeric === value || !value.match?(/[^0-9]/) - !value.to_i.zero? - else - return false if ActiveModel::Type::Boolean::FALSE_VALUES.include?(value) - !value.blank? - end - elsif value.respond_to?(:zero?) - !value.zero? - else - !value.blank? - end - end + query_cast_attribute(attr_name, value) + end + + def _query_attribute(attr_name) # :nodoc: + value = self._read_attribute(attr_name.to_s) + + query_cast_attribute(attr_name, value) end alias :attribute? :query_attribute private :attribute? + + private + def query_cast_attribute(attr_name, value) + case value + when true then true + when false, nil then false + else + if !type_for_attribute(attr_name) { false } + if Numeric === value || !value.match?(/[^0-9]/) + !value.to_i.zero? + else + return false if ActiveModel::Type::Boolean::FALSE_VALUES.include?(value) + !value.blank? + end + elsif value.respond_to?(:zero?) + !value.zero? + else + !value.blank? + end + end + end end end end