+ def self.permit_attribute_params raw_params
+ # strong_parameters does not provide security: permissions are
+ # implemented with before_save hooks.
+ #
+ # The following permit! is necessary even with
+ # "ActionController::Parameters.permit_all_parameters = true",
+ # because permit_all does not permit nested attributes.
+ raw_params ||= {}
+
+ if raw_params
+ raw_params = raw_params.to_hash
+ raw_params.delete_if { |k, _| self.protected_attributes.include? k }
+ serialized_attributes.each do |colname, coder|
+ param = raw_params[colname.to_sym]
+ if param.nil?
+ # ok
+ elsif !param.is_a?(coder.object_class)
+ raise ArgumentError.new("#{colname} parameter must be #{coder.object_class}, not #{param.class}")
+ elsif has_nonstring_keys?(param)
+ raise ArgumentError.new("#{colname} parameter cannot have non-string hash keys")
+ end
+ end
+ # Check JSONB columns that aren't listed on serialized_attributes
+ columns.select{|c| c.type == :jsonb}.collect{|j| j.name}.each do |colname|
+ if serialized_attributes.include? colname || raw_params[colname.to_sym].nil?
+ next
+ end
+ if has_nonstring_keys?(raw_params[colname.to_sym])
+ raise ArgumentError.new("#{colname} parameter cannot have non-string hash keys")
+ end
+ end
+ end
+ ActionController::Parameters.new(raw_params).permit!
+ end
+
+ def initialize raw_params={}, *args
+ super(self.class.permit_attribute_params(raw_params), *args)
+ end
+
+ # Reload "old attributes" for logging, too.
+ def reload(*args)
+ super
+ log_start_state
+ end
+
+ def self.create raw_params={}, *args
+ super(permit_attribute_params(raw_params), *args)
+ end
+
+ def update_attributes raw_params={}, *args
+ super(self.class.permit_attribute_params(raw_params), *args)
+ end
+