FIX: Upsert a custom field if a unique constraint fails

This commit is contained in:
Robin Ward 2019-07-04 13:25:09 -04:00
parent c78634284c
commit 72bac61c90
2 changed files with 36 additions and 10 deletions

View File

@ -240,10 +240,7 @@ module HasCustomFields
if v.is_a?(Array) && field_type != :json
v.each { |subv| _custom_fields.create!(name: k, value: subv) }
else
_custom_fields.create!(
name: k,
value: v.is_a?(Hash) || field_type == :json ? v.to_json : v
)
create_singular(k, v, field_type)
end
end
end
@ -252,7 +249,16 @@ module HasCustomFields
end
end
protected
def create_singular(name, value, field_type = nil)
write_value = value.is_a?(Hash) || field_type == :json ? value.to_json : value
_custom_fields.create!(name: name, value: write_value)
rescue ActiveRecord::RecordNotUnique
# We support unique indexes on certain fields. In the event two concurrenct processes attempt to
# update the same custom field we should catch the error and perform an update instead.
_custom_fields.where(name: name).update_all(value: write_value)
end
protected
def refresh_custom_fields_from_db
target = Hash.new

View File

@ -8,6 +8,10 @@ describe HasCustomFields do
before do
DB.exec("create temporary table custom_fields_test_items(id SERIAL primary key)")
DB.exec("create temporary table custom_fields_test_item_custom_fields(id SERIAL primary key, custom_fields_test_item_id int, name varchar(256) not null, value text)")
DB.exec(<<~SQL)
CREATE UNIQUE INDEX ON custom_fields_test_item_custom_fields (custom_fields_test_item_id)
WHERE NAME = 'rare'
SQL
class CustomFieldsTestItem < ActiveRecord::Base
include HasCustomFields
@ -28,7 +32,7 @@ describe HasCustomFields do
Object.send(:remove_const, :CustomFieldsTestItemCustomField)
end
it "simple modification of custom fields" do
it "allows simple modification of custom fields" do
test_item = CustomFieldsTestItem.new
expect(test_item.custom_fields["a"]).to eq(nil)
@ -67,7 +71,7 @@ describe HasCustomFields do
expect(test_item.custom_fields["a"]).to eq("0")
end
it "reload loads from database" do
it "reloads from the database" do
test_item = CustomFieldsTestItem.new
test_item.custom_fields["a"] = 0
@ -87,7 +91,7 @@ describe HasCustomFields do
expect(test_item.custom_fields["a"]).to eq("1")
end
it "double save actually saves" do
it "actually saves on double save" do
test_item = CustomFieldsTestItem.new
test_item.custom_fields = { "a" => "b" }
test_item.save
@ -165,7 +169,7 @@ describe HasCustomFields do
expect((before_ids - after_ids).size).to eq(1)
end
it "simple modifications don't interfere" do
it "doesn't allow simple modifications to interfere" do
test_item = CustomFieldsTestItem.new
expect(test_item.custom_fields["a"]).to eq(nil)
@ -214,7 +218,6 @@ describe HasCustomFields do
end
it "will not fail to load custom fields if json is corrupt" do
field_type = "bad_json"
CustomFieldsTestItem.register_custom_field_type(field_type, :json)
@ -268,6 +271,23 @@ describe HasCustomFields do
expect(test_item.reload.custom_fields).to eq(expected)
end
describe "create_singular" do
it "creates new records" do
item = CustomFieldsTestItem.create!
item.create_singular('hello', 'world')
expect(item.reload.custom_fields['hello']).to eq('world')
end
it "upserts on a database constraint error" do
item0 = CustomFieldsTestItem.new
item0.custom_fields = { "rare" => "gem" }
item0.save
item0.create_singular('rare', "diamond")
expect(item0.reload.custom_fields['rare']).to eq("diamond")
end
end
describe "upsert_custom_fields" do
it 'upserts records' do
test_item = CustomFieldsTestItem.create