aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/attribute_methods
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib/active_record/attribute_methods')
-rw-r--r--activerecord/lib/active_record/attribute_methods/dirty.rb18
-rw-r--r--activerecord/lib/active_record/attribute_methods/read.rb3
-rw-r--r--activerecord/lib/active_record/attribute_methods/serialization.rb24
-rw-r--r--activerecord/lib/active_record/attribute_methods/write.rb3
4 files changed, 39 insertions, 9 deletions
diff --git a/activerecord/lib/active_record/attribute_methods/dirty.rb b/activerecord/lib/active_record/attribute_methods/dirty.rb
index 5efe051125..48d33e6744 100644
--- a/activerecord/lib/active_record/attribute_methods/dirty.rb
+++ b/activerecord/lib/active_record/attribute_methods/dirty.rb
@@ -5,7 +5,7 @@ require_relative "../attribute_mutation_tracker"
module ActiveRecord
module AttributeMethods
- module Dirty # :nodoc:
+ module Dirty
extend ActiveSupport::Concern
include ActiveModel::Dirty
@@ -47,7 +47,7 @@ module ActiveRecord
clear_mutation_trackers
end
- def changes_applied
+ def changes_applied # :nodoc:
@mutations_before_last_save = mutation_tracker
@mutations_from_database = AttributeMutationTracker.new(@attributes)
@changed_attributes = ActiveSupport::HashWithIndifferentAccess.new
@@ -55,27 +55,27 @@ module ActiveRecord
clear_mutation_trackers
end
- def clear_changes_information
+ def clear_changes_information # :nodoc:
@mutations_before_last_save = nil
@changed_attributes = ActiveSupport::HashWithIndifferentAccess.new
forget_attribute_assignments
clear_mutation_trackers
end
- def write_attribute_without_type_cast(attr_name, *)
+ def write_attribute_without_type_cast(attr_name, *) # :nodoc:
result = super
clear_attribute_change(attr_name)
result
end
- def clear_attribute_changes(attr_names)
+ def clear_attribute_changes(attr_names) # :nodoc:
super
attr_names.each do |attr_name|
clear_attribute_change(attr_name)
end
end
- def changed_attributes
+ def changed_attributes # :nodoc:
# This should only be set by methods which will call changed_attributes
# multiple times when it is known that the computed value cannot change.
if defined?(@cached_changed_attributes)
@@ -85,17 +85,17 @@ module ActiveRecord
end
end
- def changes
+ def changes # :nodoc:
cache_changed_attributes do
super
end
end
- def previous_changes
+ def previous_changes # :nodoc:
mutations_before_last_save.changes
end
- def attribute_changed_in_place?(attr_name)
+ def attribute_changed_in_place?(attr_name) # :nodoc:
mutation_tracker.changed_in_place?(attr_name)
end
diff --git a/activerecord/lib/active_record/attribute_methods/read.rb b/activerecord/lib/active_record/attribute_methods/read.rb
index 6adaeb0121..615b2fa701 100644
--- a/activerecord/lib/active_record/attribute_methods/read.rb
+++ b/activerecord/lib/active_record/attribute_methods/read.rb
@@ -31,9 +31,11 @@ module ActiveRecord
temp_method = "__temp__#{safe_name}"
ActiveRecord::AttributeMethods::AttrNames.set_name_cache safe_name, name
+ sync_with_transaction_state = "sync_with_transaction_state" if name == primary_key
generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
def #{temp_method}
+ #{sync_with_transaction_state}
name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{safe_name}
_read_attribute(name) { |n| missing_attribute(n, caller) }
end
@@ -57,6 +59,7 @@ module ActiveRecord
end
name = self.class.primary_key if name == "id".freeze && self.class.primary_key
+ sync_with_transaction_state if name == self.class.primary_key
_read_attribute(name, &block)
end
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/lib/active_record/attribute_methods/write.rb b/activerecord/lib/active_record/attribute_methods/write.rb
index fa01f832ac..62c5ce059b 100644
--- a/activerecord/lib/active_record/attribute_methods/write.rb
+++ b/activerecord/lib/active_record/attribute_methods/write.rb
@@ -15,10 +15,12 @@ module ActiveRecord
def define_method_attribute=(name)
safe_name = name.unpack("h*".freeze).first
ActiveRecord::AttributeMethods::AttrNames.set_name_cache safe_name, name
+ sync_with_transaction_state = "sync_with_transaction_state" if name == primary_key
generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
def __temp__#{safe_name}=(value)
name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{safe_name}
+ #{sync_with_transaction_state}
_write_attribute(name, value)
end
alias_method #{(name + '=').inspect}, :__temp__#{safe_name}=
@@ -38,6 +40,7 @@ module ActiveRecord
end
name = self.class.primary_key if name == "id".freeze && self.class.primary_key
+ sync_with_transaction_state if name == self.class.primary_key
_write_attribute(name, value)
end