1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

Allow attribute aliases for timestamp magic columns

For now, timestamp magic columns are only allowed for real physical
columns, it is not a matter for newly created app, but it is harder to
get the usefulness for legacy databases.

The reason that doesn't work is some low-level API does not care about
attribute aliases. I think that uses low-level API without attribute
alias resolution for timestamp attributes is not intended (e.g.
`updated_at_before_type_cast` works, but `has_attribute?("updated_at")`
and `_read_attribute("updated_at")` doesn't work).

I've addressed all missing attribute alias resolution for timestamp
attributes to work that consistently.

Fixes #37554.
This commit is contained in:
Ryuta Kamizono 2020-06-01 04:09:24 +09:00
parent e803ab6e80
commit abae7fcd5f
10 changed files with 45 additions and 26 deletions

View file

@ -21,8 +21,7 @@ module ActiveRecord
end
def timestamp_column_names
@timestamp_column_names ||=
%w(created_at created_on updated_at updated_on) & @model_class.column_names
@model_class.all_timestamp_attributes_in_model
end
def inheritance_column_name

View file

@ -97,17 +97,17 @@ module ActiveRecord
def cache_version
return unless cache_versioning
if has_attribute?("updated_at")
timestamp_column = self.class.attribute_aliases["updated_at"] || "updated_at"
if has_attribute?(timestamp_column)
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
else
if self.class.has_attribute?("updated_at")
raise ActiveModel::MissingAttributeError, "missing attribute: updated_at"
end
elsif self.class.has_attribute?(timestamp_column)
raise ActiveModel::MissingAttributeError, "missing attribute: updated_at"
end
end

View file

@ -343,10 +343,13 @@ module ActiveRecord
end
def compute_cache_version(timestamp_column) # :nodoc:
timestamp_column = timestamp_column.to_s
timestamp_column = klass.attribute_aliases[timestamp_column] || timestamp_column
if loaded? || distinct_value
size = records.size
if size > 0
timestamp = max_by(&timestamp_column)._read_attribute(timestamp_column)
timestamp = records.map { |record| record._read_attribute(timestamp_column) }.max
end
else
collection = eager_loading? ? apply_join_dependency : self

View file

@ -80,11 +80,11 @@ module ActiveRecord
private
def timestamp_attributes_for_create
["created_at", "created_on"]
["created_at", "created_on"].map! { |name| attribute_aliases[name] || name }
end
def timestamp_attributes_for_update
["updated_at", "updated_on"]
["updated_at", "updated_on"].map! { |name| attribute_aliases[name] || name }
end
def reload_schema_from_cache

View file

@ -55,10 +55,10 @@ class PostgresqlTimestampFixtureTest < ActiveRecord::PostgreSQLTestCase
end
def test_load_infinity_and_beyond
d = Developer.find_by_sql("select 'infinity'::timestamp as updated_at")
d = Developer.find_by_sql("select 'infinity'::timestamp as legacy_updated_at")
assert d.first.updated_at.infinite?, "timestamp should be infinite"
d = Developer.find_by_sql("select '-infinity'::timestamp as updated_at")
d = Developer.find_by_sql("select '-infinity'::timestamp as legacy_updated_at")
time = d.first.updated_at
assert time.infinite?, "timestamp should be infinite"
assert_operator time, :<, 0

View file

@ -1106,8 +1106,8 @@ class BasicsTest < ActiveRecord::TestCase
end
def test_find_keeps_multiple_group_values
combined = Developer.all.merge!(group: "developers.name, developers.salary, developers.id, developers.created_at, developers.updated_at, developers.created_on, developers.updated_on").to_a
assert_equal combined, Developer.all.merge!(group: ["developers.name", "developers.salary", "developers.id", "developers.created_at", "developers.updated_at", "developers.created_on", "developers.updated_on"]).to_a
combined = Developer.merge(group: "developers.name, developers.salary, developers.id, developers.legacy_created_at, developers.legacy_updated_at, developers.legacy_created_on, developers.legacy_updated_on").to_a
assert_equal combined, Developer.merge(group: ["developers.name", "developers.salary", "developers.id", "developers.created_at", "developers.updated_at", "developers.created_on", "developers.updated_on"]).to_a
end
def test_find_symbol_ordered_last

View file

@ -166,10 +166,10 @@ class DefaultScopingTest < ActiveRecord::TestCase
end
def test_unscope_string_where_clauses_involved
dev_relation = Developer.order("salary DESC").where("created_at > ?", 1.year.ago)
dev_relation = Developer.order("salary DESC").where("legacy_created_at > ?", 1.year.ago)
expected = dev_relation.collect(&:name)
dev_ordered_relation = DeveloperOrderedBySalary.where(name: "Jamis").where("created_at > ?", 1.year.ago)
dev_ordered_relation = DeveloperOrderedBySalary.where(name: "Jamis").where("legacy_created_at > ?", 1.year.ago)
received = dev_ordered_relation.unscope(where: [:name]).collect(&:name)
assert_equal expected.sort, received.sort

View file

@ -44,7 +44,7 @@ class TimestampTest < ActiveRecord::TestCase
assert_predicate @developer, :changed?, "developer should be marked as changed"
assert_equal ["salary"], @developer.changed
assert_predicate @developer, :saved_changes?
assert_equal ["updated_at", "updated_on"], @developer.saved_changes.keys.sort
assert_equal ["legacy_updated_at", "legacy_updated_on"], @developer.saved_changes.keys.sort
@developer.reload
assert_equal previous_salary, @developer.salary
@ -57,7 +57,7 @@ class TimestampTest < ActiveRecord::TestCase
assert_not_equal @previously_updated_at, developer.updated_at
assert_not_predicate developer, :changed?
assert_predicate developer, :saved_changes?
assert_equal ["updated_at", "updated_on"], developer.saved_changes.keys.sort
assert_equal ["legacy_updated_at", "legacy_updated_on"], developer.saved_changes.keys.sort
developer.reload
assert_not_equal @previously_updated_at, developer.updated_at

View file

@ -3,6 +3,19 @@
require "ostruct"
class Developer < ActiveRecord::Base
module TimestampAliases
extend ActiveSupport::Concern
included do
alias_attribute :created_at, :legacy_created_at
alias_attribute :updated_at, :legacy_updated_at
alias_attribute :created_on, :legacy_created_on
alias_attribute :updated_on, :legacy_updated_on
end
end
include TimestampAliases
module ProjectsAssociationExtension2
def find_least_recent
order("id ASC").first
@ -193,6 +206,8 @@ class LazyBlockReferencingScopeDeveloperCalledDavid < ActiveRecord::Base
end
class DeveloperCalledJamis < ActiveRecord::Base
include Developer::TimestampAliases
self.table_name = "developers"
default_scope { where(name: "Jamis") }
@ -280,6 +295,8 @@ class ThreadsafeDeveloper < ActiveRecord::Base
end
class CachedDeveloper < ActiveRecord::Base
include Developer::TimestampAliases
self.table_name = "developers"
self.cache_timestamp_format = :number
end

View file

@ -295,15 +295,15 @@ ActiveRecord::Schema.define do
t.references :firm, index: false
t.integer :mentor_id
if supports_datetime_with_precision?
t.datetime :created_at, precision: 6
t.datetime :updated_at, precision: 6
t.datetime :created_on, precision: 6
t.datetime :updated_on, precision: 6
t.datetime :legacy_created_at, precision: 6
t.datetime :legacy_updated_at, precision: 6
t.datetime :legacy_created_on, precision: 6
t.datetime :legacy_updated_on, precision: 6
else
t.datetime :created_at
t.datetime :updated_at
t.datetime :created_on
t.datetime :updated_on
t.datetime :legacy_created_at
t.datetime :legacy_updated_at
t.datetime :legacy_created_on
t.datetime :legacy_updated_on
end
end