diff options
Diffstat (limited to 'app/models/concerns/safely_change_column_default.rb')
-rw-r--r-- | app/models/concerns/safely_change_column_default.rb | 46 |
1 files changed, 46 insertions, 0 deletions
diff --git a/app/models/concerns/safely_change_column_default.rb b/app/models/concerns/safely_change_column_default.rb new file mode 100644 index 00000000000..567f690d950 --- /dev/null +++ b/app/models/concerns/safely_change_column_default.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +# == SafelyChangeColumnDefault concern. +# +# Contains functionality that allows safely changing a column default without downtime. +# Without this concern, Rails can mutate the old default value to the new default value if the old default is explicitly +# specified. +# +# Usage: +# +# class SomeModel < ApplicationRecord +# include SafelyChangeColumnDefault +# +# columns_changing_default :value +# end +# +# # Assume a default of 100 for value +# SomeModel.create!(value: 100) # INSERT INTO some_model (value) VALUES (100) +# change_column_default('some_model', 'value', from: 100, to: 101) +# SomeModel.create!(value: 100) # INSERT INTO some_model (value) VALUES (100) +# # Without this concern, would be INSERT INTO some_model (value) DEFAULT VALUES and would insert 101. +module SafelyChangeColumnDefault + extend ActiveSupport::Concern + + class_methods do + # Indicate that one or more columns will have their database default change. + # + # By indicating those columns here, this helper prevents a case where explicitly writing the old database default + # will be mutated to the new database default. + def columns_changing_default(*columns) + self.columns_with_changing_default = columns.map(&:to_s) + end + end + + included do + class_attribute :columns_with_changing_default, default: [] + + before_create do + columns_with_changing_default.to_a.each do |attr_name| + attr = @attributes[attr_name] + + attribute_will_change!(attr_name) if !attr.changed? && attr.came_from_user? + end + end + end +end |