From a0751d27c52185b86370baf6a565e757092646de Mon Sep 17 00:00:00 2001 From: Kir Shatrov Date: Wed, 21 Jun 2017 13:58:07 -0400 Subject: [PATCH] Do not let use `serialize` on native JSON/array column --- .../attribute_methods/serialization.rb | 24 +++++++++++++++++++ .../cases/adapters/postgresql/array_test.rb | 9 +++++++ .../cases/adapters/postgresql/json_test.rb | 9 +++++++ 3 files changed, 42 insertions(+) diff --git a/activerecord/lib/active_record/attribute_methods/serialization.rb b/activerecord/lib/active_record/attribute_methods/serialization.rb index 6ed45d8737..acd47629dd 100644 --- a/activerecord/lib/active_record/attribute_methods/serialization.rb +++ b/activerecord/lib/active_record/attribute_methods/serialization.rb @@ -5,6 +5,16 @@ module ActiveRecord module Serialization extend ActiveSupport::Concern + class ColumnNotSerializableError < StandardError + def initialize(name, type) + super <<-EOS.strip_heredoc + Column `#{name}` of type #{type.class} does not support `serialize` feature. + Usually it means that you are trying to use `serialize` + on a column that already implements serialization natively. + EOS + end + end + module ClassMethods # If you have an attribute that needs to be saved to the database as an # object, and retrieved as the same object, then specify the name of that @@ -60,9 +70,23 @@ module ActiveRecord end decorate_attribute_type(attr_name, :serialize) do |type| + if type_incompatible_with_serialize?(type) + raise ColumnNotSerializableError.new(attr_name, type) + end + Type::Serialized.new(type, coder) end end + + private + + def type_incompatible_with_serialize?(type) + type.is_a?(ActiveRecord::Type::Json) || + ( + defined?(ActiveRecord::ConnectionAdapters::PostgreSQL) && + type.is_a?(ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Array) + ) + end end end end diff --git a/activerecord/test/cases/adapters/postgresql/array_test.rb b/activerecord/test/cases/adapters/postgresql/array_test.rb index 8507dbb463..08b17f37e2 100644 --- a/activerecord/test/cases/adapters/postgresql/array_test.rb +++ b/activerecord/test/cases/adapters/postgresql/array_test.rb @@ -47,6 +47,15 @@ class PostgresqlArrayTest < ActiveRecord::PostgreSQLTestCase assert ratings_column.array? end + def test_not_compatible_with_serialize + new_klass = Class.new(PgArray) do + serialize :tags, Array + end + assert_raises(ActiveRecord::AttributeMethods::Serialization::ColumnNotSerializableError) do + new_klass.new + end + end + def test_default @connection.add_column "pg_arrays", "score", :integer, array: true, default: [4, 4, 2] PgArray.reset_column_information diff --git a/activerecord/test/cases/adapters/postgresql/json_test.rb b/activerecord/test/cases/adapters/postgresql/json_test.rb index aa5e03df41..79dcfe110c 100644 --- a/activerecord/test/cases/adapters/postgresql/json_test.rb +++ b/activerecord/test/cases/adapters/postgresql/json_test.rb @@ -33,6 +33,15 @@ module PostgresqlJSONSharedTestCases x.reload assert_equal ["foo" => "bar"], x.objects end + + def test_not_compatible_with_serialize_macro + new_klass = Class.new(klass) do + serialize :payload, JSON + end + assert_raises(ActiveRecord::AttributeMethods::Serialization::ColumnNotSerializableError) do + new_klass.new + end + end end class PostgresqlJSONTest < ActiveRecord::PostgreSQLTestCase