diff --git a/activemodel/CHANGELOG.md b/activemodel/CHANGELOG.md index af13b78a8e..abc036b2d9 100644 --- a/activemodel/CHANGELOG.md +++ b/activemodel/CHANGELOG.md @@ -1,3 +1,9 @@ +* Add `ActiveModel::AttributeSet#values_for_database` + + Returns attributes with values for assignment to the database. + + *Chris Salzberg* + * Fix delegation in ActiveModel::Type::Registry#lookup and ActiveModel::Type.lookup Passing a last positional argument `{}` would be incorrectly considered as keyword argument. diff --git a/activemodel/lib/active_model/attribute_set.rb b/activemodel/lib/active_model/attribute_set.rb index a6a741af78..5a4d455223 100644 --- a/activemodel/lib/active_model/attribute_set.rb +++ b/activemodel/lib/active_model/attribute_set.rb @@ -25,6 +25,10 @@ module ActiveModel attributes.transform_values(&:value_before_type_cast) end + def values_for_database + attributes.transform_values(&:value_for_database) + end + def to_hash keys.index_with { |name| self[name].value } end diff --git a/activemodel/test/cases/attribute_set_test.rb b/activemodel/test/cases/attribute_set_test.rb index 3ddfe6f8ea..d80d5c0777 100644 --- a/activemodel/test/cases/attribute_set_test.rb +++ b/activemodel/test/cases/attribute_set_test.rb @@ -209,6 +209,21 @@ module ActiveModel assert_equal "value from user", attributes.fetch_value(:foo) end + class MySerializedType < ::ActiveModel::Type::Value + def serialize(value) + value + " serialized" + end + end + + test "values_for_database" do + builder = AttributeSet::Builder.new(foo: MySerializedType.new) + attributes = builder.build_from_database + + attributes.write_from_user(:foo, "value") + + assert_equal({ foo: "value serialized" }, attributes.values_for_database) + end + test "freezing doesn't prevent the set from materializing" do builder = AttributeSet::Builder.new(foo: Type::String.new) attributes = builder.build_from_database(foo: "1") diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 637449a537..4121bbe651 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,9 @@ +* Add `ActiveRecord::Base#attributes_for_database` + + Returns attributes with values for assignment to the database. + + *Chris Salzberg* + * Use an empty query to check if the PostgreSQL connection is still active An empty query is faster than `SELECT 1`. diff --git a/activerecord/lib/active_record/attribute_methods/before_type_cast.rb b/activerecord/lib/active_record/attribute_methods/before_type_cast.rb index e4b3ce32cc..b34c0f3249 100644 --- a/activerecord/lib/active_record/attribute_methods/before_type_cast.rb +++ b/activerecord/lib/active_record/attribute_methods/before_type_cast.rb @@ -66,6 +66,11 @@ module ActiveRecord @attributes.values_before_type_cast end + # Returns a hash of attributes for assignment to the database. + def attributes_for_database + @attributes.values_for_database + end + private # Dispatch target for *_before_type_cast attribute methods. def attribute_before_type_cast(attr_name) diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb index dafeec65b6..6ac3d90621 100644 --- a/activerecord/test/cases/attribute_methods_test.rb +++ b/activerecord/test/cases/attribute_methods_test.rb @@ -216,6 +216,17 @@ class AttributeMethodsTest < ActiveRecord::TestCase end end + test "read attributes_for_database" do + topic = Topic.new + topic.content = { one: 1, two: 2 } + + db_attributes = Topic.instantiate(topic.attributes_for_database).attributes + before_type_cast_attributes = Topic.instantiate(topic.attributes_before_type_cast).attributes + + assert_equal topic.attributes, db_attributes + assert_not_equal topic.attributes, before_type_cast_attributes + end + test "read attributes_after_type_cast on a date" do tz = "Pacific Time (US & Canada)" diff --git a/activerecord/test/cases/enum_test.rb b/activerecord/test/cases/enum_test.rb index 0ae2c184a1..3ddb349e91 100644 --- a/activerecord/test/cases/enum_test.rb +++ b/activerecord/test/cases/enum_test.rb @@ -378,6 +378,14 @@ class EnumTest < ActiveRecord::TestCase assert_equal "published", @book.status end + test "attributes_for_database" do + assert_equal 2, @book.attributes_for_database["status"] + + @book.status = "published" + + assert_equal 2, @book.attributes_for_database["status"] + end + test "invalid definition values raise an ArgumentError" do e = assert_raises(ArgumentError) do Class.new(ActiveRecord::Base) do