Skip to content
Permalink
Browse files

FIX: Upsert a custom field if a unique constraint fails

  • Loading branch information...
eviltrout committed Jul 4, 2019
1 parent c786342 commit 72bac61c90044407f7a2029b15f6ac043a8bccf0
Showing with 36 additions and 10 deletions.
  1. +11 −5 app/models/concerns/has_custom_fields.rb
  2. +25 −5 spec/components/concern/has_custom_fields_spec.rb
@@ -240,10 +240,7 @@ def save_custom_fields(force = false)
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 @@ def save_custom_fields(force = false)
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
@@ -8,6 +8,10 @@
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 @@ class CustomFieldsTestItemCustomField < ActiveRecord::Base
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 @@ class CustomFieldsTestItemCustomField < ActiveRecord::Base
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 @@ class CustomFieldsTestItemCustomField < ActiveRecord::Base
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 @@ class CustomFieldsTestItemCustomField < ActiveRecord::Base
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 @@ class CustomFieldsTestItemCustomField < ActiveRecord::Base
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 @@ class CustomFieldsTestItemCustomField < ActiveRecord::Base
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

1 comment on commit 72bac61

@discoursebot

This comment has been minimized.

Copy link

commented on 72bac61 Jul 11, 2019

This commit has been mentioned on Discourse Meta. There might be relevant details there:

https://meta.discourse.org/t/custom-field-casting-affected-by-recent-update/122746/1

Please sign in to comment.
You can’t perform that action at this time.