mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
has_attribute?
should be aware of attribute aliases
Related to #39495. For now, `read_attribute`, `write_attribute`, `[]`, `[]=` are aware of attribute aliases, but `has_attribute?` is not. It will easily miss attribute alias resolution before using `has_attribute?`, it is very inconvenient. I think the inconvenience is not intended, so `has_attribute?` should be aware of attribute aliases like as others for consistency.
This commit is contained in:
parent
8504576bcf
commit
c53c418e6f
9 changed files with 59 additions and 20 deletions
|
@ -315,7 +315,7 @@ module ActiveRecord
|
|||
|
||||
# Returns true if record contains the foreign_key
|
||||
def foreign_key_for?(record)
|
||||
record.has_attribute?(reflection.foreign_key)
|
||||
record._has_attribute?(reflection.foreign_key.to_s)
|
||||
end
|
||||
|
||||
# This should be implemented to return the values of the relevant key(s) on the owner,
|
||||
|
|
|
@ -172,13 +172,21 @@ module ActiveRecord
|
|||
# Returns true if the given attribute exists, otherwise false.
|
||||
#
|
||||
# class Person < ActiveRecord::Base
|
||||
# alias_attribute :new_name, :name
|
||||
# end
|
||||
#
|
||||
# Person.has_attribute?('name') # => true
|
||||
# Person.has_attribute?(:age) # => true
|
||||
# Person.has_attribute?(:nothing) # => false
|
||||
# Person.has_attribute?('name') # => true
|
||||
# Person.has_attribute?('new_name') # => true
|
||||
# Person.has_attribute?(:age) # => true
|
||||
# Person.has_attribute?(:nothing) # => false
|
||||
def has_attribute?(attr_name)
|
||||
attribute_types.key?(attr_name.to_s)
|
||||
attr_name = attr_name.to_s
|
||||
attr_name = attribute_aliases[attr_name] || attr_name
|
||||
_has_attribute?(attr_name)
|
||||
end
|
||||
|
||||
def _has_attribute?(attr_name) # :nodoc:
|
||||
attribute_types.key?(attr_name)
|
||||
end
|
||||
|
||||
# Returns the column object for the named attribute.
|
||||
|
@ -227,7 +235,7 @@ module ActiveRecord
|
|||
# have been allocated but not yet initialized.
|
||||
if defined?(@attributes)
|
||||
if name = self.class.symbol_column_to_string(name.to_sym)
|
||||
return has_attribute?(name)
|
||||
return _has_attribute?(name)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -237,14 +245,22 @@ module ActiveRecord
|
|||
# Returns +true+ if the given attribute is in the attributes hash, otherwise +false+.
|
||||
#
|
||||
# class Person < ActiveRecord::Base
|
||||
# alias_attribute :new_name, :name
|
||||
# end
|
||||
#
|
||||
# person = Person.new
|
||||
# person.has_attribute?(:name) # => true
|
||||
# person.has_attribute?('age') # => true
|
||||
# person.has_attribute?(:nothing) # => false
|
||||
# person.has_attribute?(:name) # => true
|
||||
# person.has_attribute?(:new_name) # => true
|
||||
# person.has_attribute?('age') # => true
|
||||
# person.has_attribute?(:nothing) # => false
|
||||
def has_attribute?(attr_name)
|
||||
@attributes.key?(attr_name.to_s)
|
||||
attr_name = attr_name.to_s
|
||||
attr_name = self.class.attribute_aliases[attr_name] || attr_name
|
||||
_has_attribute?(attr_name)
|
||||
end
|
||||
|
||||
def _has_attribute?(attr_name) # :nodoc:
|
||||
@attributes.key?(attr_name)
|
||||
end
|
||||
|
||||
# Returns an array of names for the attributes available on this object.
|
||||
|
|
|
@ -470,7 +470,7 @@ module ActiveRecord
|
|||
def association_foreign_key_changed?(reflection, record, key)
|
||||
return false if reflection.through_reflection?
|
||||
|
||||
record.has_attribute?(reflection.foreign_key) && record[reflection.foreign_key] != key
|
||||
record._has_attribute?(reflection.foreign_key.to_s) && record._read_attribute(reflection.foreign_key) != key
|
||||
end
|
||||
|
||||
# Saves the associated record if it's new or <tt>:autosave</tt> is enabled.
|
||||
|
|
|
@ -533,7 +533,7 @@ module ActiveRecord
|
|||
# allocated but not initialized.
|
||||
inspection = if defined?(@attributes) && @attributes
|
||||
self.class.attribute_names.collect do |name|
|
||||
if has_attribute?(name)
|
||||
if _has_attribute?(name)
|
||||
attr = _read_attribute(name)
|
||||
value = if attr.nil?
|
||||
attr.inspect
|
||||
|
@ -557,7 +557,7 @@ module ActiveRecord
|
|||
return super if custom_inspect_method_defined?
|
||||
pp.object_address_group(self) do
|
||||
if defined?(@attributes) && @attributes
|
||||
attr_names = self.class.attribute_names.select { |name| has_attribute?(name) }
|
||||
attr_names = self.class.attribute_names.select { |name| _has_attribute?(name) }
|
||||
pp.seplist(attr_names, proc { pp.text "," }) do |attr_name|
|
||||
pp.breakable " "
|
||||
pp.group(1) do
|
||||
|
|
|
@ -52,7 +52,7 @@ module ActiveRecord
|
|||
raise NotImplementedError, "#{self} is an abstract class and cannot be instantiated."
|
||||
end
|
||||
|
||||
if has_attribute?(inheritance_column)
|
||||
if _has_attribute?(inheritance_column)
|
||||
subclass = subclass_from_attributes(attributes)
|
||||
|
||||
if subclass.nil? && scope_attributes = current_scope&.scope_for_create
|
||||
|
@ -245,7 +245,7 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
def using_single_table_inheritance?(record)
|
||||
record[inheritance_column].present? && has_attribute?(inheritance_column)
|
||||
record[inheritance_column].present? && _has_attribute?(inheritance_column)
|
||||
end
|
||||
|
||||
def find_sti_class(type_name)
|
||||
|
|
|
@ -97,16 +97,14 @@ module ActiveRecord
|
|||
def cache_version
|
||||
return unless cache_versioning
|
||||
|
||||
timestamp_column = self.class.attribute_aliases["updated_at"] || "updated_at"
|
||||
|
||||
if has_attribute?(timestamp_column)
|
||||
if has_attribute?("updated_at")
|
||||
timestamp = updated_at_before_type_cast
|
||||
if can_use_fast_cache_version?(timestamp)
|
||||
raw_timestamp_to_cache_version(timestamp)
|
||||
elsif timestamp = updated_at
|
||||
timestamp.utc.to_s(cache_timestamp_format)
|
||||
end
|
||||
elsif self.class.has_attribute?(timestamp_column)
|
||||
elsif self.class.has_attribute?("updated_at")
|
||||
raise ActiveModel::MissingAttributeError, "missing attribute: updated_at"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -11,7 +11,7 @@ module ActiveRecord #:nodoc:
|
|||
end
|
||||
|
||||
def serializable_hash(options = nil)
|
||||
if self.class.has_attribute?(self.class.inheritance_column)
|
||||
if self.class._has_attribute?(self.class.inheritance_column)
|
||||
options = options ? options.dup : {}
|
||||
|
||||
options[:except] = Array(options[:except]).map(&:to_s)
|
||||
|
|
|
@ -1277,14 +1277,38 @@ class BasicsTest < ActiveRecord::TestCase
|
|||
assert Company.has_attribute?("id")
|
||||
assert Company.has_attribute?("type")
|
||||
assert Company.has_attribute?("name")
|
||||
assert Company.has_attribute?("new_name")
|
||||
assert Company.has_attribute?("metadata")
|
||||
assert_not Company.has_attribute?("lastname")
|
||||
assert_not Company.has_attribute?("age")
|
||||
|
||||
company = Company.new
|
||||
assert company.has_attribute?("id")
|
||||
assert company.has_attribute?("type")
|
||||
assert company.has_attribute?("name")
|
||||
assert company.has_attribute?("new_name")
|
||||
assert company.has_attribute?("metadata")
|
||||
assert_not company.has_attribute?("lastname")
|
||||
assert_not company.has_attribute?("age")
|
||||
end
|
||||
|
||||
def test_has_attribute_with_symbol
|
||||
assert Company.has_attribute?(:id)
|
||||
assert Company.has_attribute?(:type)
|
||||
assert Company.has_attribute?(:name)
|
||||
assert Company.has_attribute?(:new_name)
|
||||
assert Company.has_attribute?(:metadata)
|
||||
assert_not Company.has_attribute?(:lastname)
|
||||
assert_not Company.has_attribute?(:age)
|
||||
|
||||
company = Company.new
|
||||
assert company.has_attribute?(:id)
|
||||
assert company.has_attribute?(:type)
|
||||
assert company.has_attribute?(:name)
|
||||
assert company.has_attribute?(:new_name)
|
||||
assert company.has_attribute?(:metadata)
|
||||
assert_not company.has_attribute?(:lastname)
|
||||
assert_not company.has_attribute?(:age)
|
||||
end
|
||||
|
||||
def test_attribute_names_on_table_not_exists
|
||||
|
|
|
@ -14,6 +14,7 @@ class Company < AbstractCompany
|
|||
has_many :contracts
|
||||
has_many :developers, through: :contracts
|
||||
|
||||
alias_attribute :new_name, :name
|
||||
attribute :metadata, :json
|
||||
|
||||
scope :of_first_firm, lambda {
|
||||
|
|
Loading…
Reference in a new issue