diff --git a/README.md b/README.md index 0ab77de..c9f5d8e 100644 --- a/README.md +++ b/README.md @@ -148,6 +148,14 @@ class Account < ApplicationRecord end ``` +Custom index can be handled with a Hash containing a literal key : + +```ruby +class Account < ApplicationRecord + upsert_keys literal: 'md5(my_long_field)' +end +``` + ## Tests Make sure to have an upsert_test database: diff --git a/lib/active_record_upsert/active_record/persistence.rb b/lib/active_record_upsert/active_record/persistence.rb index ca91a38..4e65e75 100644 --- a/lib/active_record_upsert/active_record/persistence.rb +++ b/lib/active_record_upsert/active_record/persistence.rb @@ -75,7 +75,7 @@ def upsert_keys(*keys) options = keys.extract_options! keys = keys.first if keys.size == 1 # support single string/symbol, multiple string/symbols, and array return if keys.nil? - @_upsert_keys = Array(keys).map(&:to_s) + @_upsert_keys = Array(keys) @_upsert_options = options end diff --git a/lib/active_record_upsert/arel/crud.rb b/lib/active_record_upsert/arel/crud.rb index ce030c7..1d831c9 100644 --- a/lib/active_record_upsert/arel/crud.rb +++ b/lib/active_record_upsert/arel/crud.rb @@ -12,9 +12,11 @@ module Arel module Crud def compile_upsert(upsert_keys, upsert_options, upsert_values, insert_values, wheres) + # Support non-attribute key (like `md5(my_attribute)``) + target = self[upsert_keys.kind_of?(Hash) ? ::Arel::Nodes::SqlLiteral.new(upsert_keys[:literal]) : upsert_keys.join(',')] on_conflict_do_update = OnConflictDoUpdateManager.new - on_conflict_do_update.target = self[upsert_keys.join(',')] + on_conflict_do_update.target = target on_conflict_do_update.target_condition = upsert_options[:where] on_conflict_do_update.wheres = wheres on_conflict_do_update.set(upsert_values) diff --git a/spec/active_record/key_spec.rb b/spec/active_record/key_spec.rb index a798b50..2e01ba7 100644 --- a/spec/active_record/key_spec.rb +++ b/spec/active_record/key_spec.rb @@ -43,8 +43,9 @@ module ActiveRecord end end context 'different ways of setting keys' do - let(:attrs) { {make: 'Ford', name: 'Focus'} } + let(:attrs) { {make: 'Ford', name: 'Focus', long_field: SecureRandom.uuid} } before { Vehicle.create(attrs) } + it 'works with multiple symbol args' do Vehicle.upsert_keys :make, :name upserted = Vehicle.new(**attrs, wheels_count: 1) @@ -85,6 +86,14 @@ module ActiveRecord expect(upserted.wheels_count).to eq(1) expect(upserted.id).to eq(v.id) end + it 'works with a literal' do + v = Vehicle.create + Vehicle.upsert_keys literal: 'md5(long_field)' + upserted = Vehicle.new(id: v.id, long_field: attrs[:long_field]) + upserted.upsert + expect(upserted.long_field).to eq(attrs[:long_field]) + expect(upserted.id).to eq(v.id) + end end context 'when the record is not new' do diff --git a/spec/dummy/db/migrate/20160419103547_create_vehicles.rb b/spec/dummy/db/migrate/20160419103547_create_vehicles.rb index d5e35ac..4d493e4 100644 --- a/spec/dummy/db/migrate/20160419103547_create_vehicles.rb +++ b/spec/dummy/db/migrate/20160419103547_create_vehicles.rb @@ -4,10 +4,12 @@ def change t.integer :wheels_count t.string :name t.string :make + t.string :long_field t.timestamps t.index [:make, :name], unique: true + t.index 'md5(long_field)', unique: true end end end diff --git a/spec/dummy/db/structure.sql b/spec/dummy/db/structure.sql index f3aa339..3ae40ee 100644 --- a/spec/dummy/db/structure.sql +++ b/spec/dummy/db/structure.sql @@ -45,6 +45,7 @@ CREATE TABLE accounts ( -- CREATE SEQUENCE accounts_id_seq + AS integer START WITH 1 INCREMENT BY 1 NO MINVALUE @@ -89,6 +90,7 @@ CREATE TABLE my_records ( -- CREATE SEQUENCE my_records_id_seq + AS integer START WITH 1 INCREMENT BY 1 NO MINVALUE @@ -121,6 +123,7 @@ CREATE TABLE vehicles ( wheels_count integer, name character varying, make character varying, + long_field character varying, created_at timestamp without time zone NOT NULL, updated_at timestamp without time zone NOT NULL ); @@ -131,6 +134,7 @@ CREATE TABLE vehicles ( -- CREATE SEQUENCE vehicles_id_seq + AS integer START WITH 1 INCREMENT BY 1 NO MINVALUE @@ -227,6 +231,13 @@ CREATE UNIQUE INDEX index_my_records_on_wisdom ON my_records USING btree (wisdom CREATE UNIQUE INDEX index_vehicles_on_make_and_name ON vehicles USING btree (make, name); +-- +-- Name: index_vehicles_on_md5_long_field; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX index_vehicles_on_md5_long_field ON vehicles USING btree (md5((long_field)::text)); + + -- -- PostgreSQL database dump complete -- @@ -237,3 +248,5 @@ INSERT INTO "schema_migrations" (version) VALUES ('20160419103547'), ('20160419124138'), ('20160419124140'); + + diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 9b77bce..503c531 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,6 +1,7 @@ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__) require 'active_record' require 'database_cleaner' +require 'securerandom' # Configure Rails Environment ENV['RAILS_ENV'] = 'test'