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 if v.is_a?(Array) && field_type != :json
v.each { |subv| _custom_fields.create!(name: k, value: subv) } v.each { |subv| _custom_fields.create!(name: k, value: subv) }
else else
_custom_fields.create!( create_singular(k, v, field_type)
name: k,
value: v.is_a?(Hash) || field_type == :json ? v.to_json : v
)
end end
end end
end end
@ -252,7 +249,16 @@ module HasCustomFields
end end
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 def refresh_custom_fields_from_db
target = Hash.new target = Hash.new

View File

@ -8,6 +8,10 @@ describe HasCustomFields do
before do before do
DB.exec("create temporary table custom_fields_test_items(id SERIAL primary key)") 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("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 class CustomFieldsTestItem < ActiveRecord::Base
include HasCustomFields include HasCustomFields
@ -28,7 +32,7 @@ describe HasCustomFields do
Object.send(:remove_const, :CustomFieldsTestItemCustomField) Object.send(:remove_const, :CustomFieldsTestItemCustomField)
end end
it "simple modification of custom fields" do it "allows simple modification of custom fields" do
test_item = CustomFieldsTestItem.new test_item = CustomFieldsTestItem.new
expect(test_item.custom_fields["a"]).to eq(nil) 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") expect(test_item.custom_fields["a"]).to eq("0")
end end
it "reload loads from database" do it "reloads from the database" do
test_item = CustomFieldsTestItem.new test_item = CustomFieldsTestItem.new
test_item.custom_fields["a"] = 0 test_item.custom_fields["a"] = 0
@ -87,7 +91,7 @@ describe HasCustomFields do
expect(test_item.custom_fields["a"]).to eq("1") expect(test_item.custom_fields["a"]).to eq("1")
end end
it "double save actually saves" do it "actually saves on double save" do
test_item = CustomFieldsTestItem.new test_item = CustomFieldsTestItem.new
test_item.custom_fields = { "a" => "b" } test_item.custom_fields = { "a" => "b" }
test_item.save test_item.save
@ -165,7 +169,7 @@ describe HasCustomFields do
expect((before_ids - after_ids).size).to eq(1) expect((before_ids - after_ids).size).to eq(1)
end end
it "simple modifications don't interfere" do it "doesn't allow simple modifications to interfere" do
test_item = CustomFieldsTestItem.new test_item = CustomFieldsTestItem.new
expect(test_item.custom_fields["a"]).to eq(nil) expect(test_item.custom_fields["a"]).to eq(nil)
@ -214,7 +218,6 @@ describe HasCustomFields do
end end
it "will not fail to load custom fields if json is corrupt" do it "will not fail to load custom fields if json is corrupt" do
field_type = "bad_json" field_type = "bad_json"
CustomFieldsTestItem.register_custom_field_type(field_type, :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) expect(test_item.reload.custom_fields).to eq(expected)
end 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 describe "upsert_custom_fields" do
it 'upserts records' do it 'upserts records' do
test_item = CustomFieldsTestItem.create test_item = CustomFieldsTestItem.create