diff --git a/lib/global_id/identification.rb b/lib/global_id/identification.rb index 759250c..bb26333 100644 --- a/lib/global_id/identification.rb +++ b/lib/global_id/identification.rb @@ -1,3 +1,5 @@ +require "active_support/core_ext/class/attribute" + class GlobalID # Mix `GlobalID::Identification` into any model with a `#find(id)` class # method. Support is automatically included in Active Record. @@ -26,6 +28,9 @@ class GlobalID # GlobalID::Locator.locate person_gid # # => # module Identification + def self.included(base) + base.class_attribute :global_id_column, default: :id, instance_writer: false + end # Returns the Global ID of the model. # diff --git a/lib/global_id/locator.rb b/lib/global_id/locator.rb index c9edc5e..01dc4ec 100644 --- a/lib/global_id/locator.rb +++ b/lib/global_id/locator.rb @@ -162,7 +162,11 @@ def locate(gid, options = {}) model_class = gid.model_class model_class = model_class.includes(options[:includes]) if options[:includes] - model_class.find gid.model_id + if model_class.global_id_column == :id + model_class.find gid.model_id + else + model_class.find_by model_class.global_id_column => gid.model_id + end end def locate_many(gids, options = {}) diff --git a/lib/global_id/uri/gid.rb b/lib/global_id/uri/gid.rb index ae729bd..359fd90 100644 --- a/lib/global_id/uri/gid.rb +++ b/lib/global_id/uri/gid.rb @@ -72,7 +72,7 @@ def parse(uri) # # URI::GID.create('bcx', Person.find(5), database: 'superhumans') def create(app, model, params = nil) - build app: app, model_name: model.class.name, model_id: model.id, params: params + build app: app, model_name: model.class.name, model_id: model.public_send(model.global_id_column), params: params end # Create a new URI::GID from components with argument check. diff --git a/test/cases/global_id_test.rb b/test/cases/global_id_test.rb index baf7bc1..1c924c1 100644 --- a/test/cases/global_id_test.rb +++ b/test/cases/global_id_test.rb @@ -45,6 +45,8 @@ class GlobalIDCreationTest < ActiveSupport::TestCase @person_namespaced_gid = GlobalID.create(Person::Child.new(4)) @person_model_gid = GlobalID.create(PersonModel.new(id: 1)) @cpk_model_gid = GlobalID.create(CompositePrimaryKeyModel.new(id: ["tenant-key-value", "id-value"])) + @ckm_model = ConfigurableKeyModel.new(id: "id-value", external_id: "external-id-value") + @ckm_model_gid = GlobalID.create(@ckm_model) end test 'find' do @@ -53,6 +55,7 @@ class GlobalIDCreationTest < ActiveSupport::TestCase assert_equal Person::Child.find(@person_namespaced_gid.model_id), @person_namespaced_gid.find assert_equal PersonModel.find(@person_model_gid.model_id), @person_model_gid.find assert_equal CompositePrimaryKeyModel.find(@cpk_model_gid.model_id), @cpk_model_gid.find + assert_equal ConfigurableKeyModel.find_by(external_id: @ckm_model_gid.model_id), @ckm_model_gid.find end test 'find with class' do @@ -60,6 +63,7 @@ class GlobalIDCreationTest < ActiveSupport::TestCase assert_equal Person.find(@person_uuid_gid.model_id), @person_uuid_gid.find(only: Person) assert_equal PersonModel.find(@person_model_gid.model_id), @person_model_gid.find(only: PersonModel) assert_equal CompositePrimaryKeyModel.find(@cpk_model_gid.model_id), @cpk_model_gid.find(only: CompositePrimaryKeyModel) + assert_equal ConfigurableKeyModel.find_by(external_id: @ckm_model_gid.model_id), @ckm_model_gid.find(only: ConfigurableKeyModel) end test 'find with class no match' do @@ -68,6 +72,7 @@ class GlobalIDCreationTest < ActiveSupport::TestCase assert_nil @person_namespaced_gid.find(only: String) assert_nil @person_model_gid.find(only: Float) assert_nil @cpk_model_gid.find(only: Hash) + assert_nil @ckm_model_gid.find(only: Hash) end test 'find with subclass' do @@ -140,6 +145,7 @@ class GlobalIDCreationTest < ActiveSupport::TestCase assert_equal 'gid://bcx/Person::Child/4', @person_namespaced_gid.to_s assert_equal 'gid://bcx/PersonModel/1', @person_model_gid.to_s assert_equal 'gid://bcx/CompositePrimaryKeyModel/tenant-key-value/id-value', @cpk_model_gid.to_s + assert_equal 'gid://bcx/ConfigurableKeyModel/external-id-value', @ckm_model_gid.to_s end test 'as param' do @@ -166,6 +172,7 @@ class GlobalIDCreationTest < ActiveSupport::TestCase assert_equal URI('gid://bcx/Person::Child/4'), @person_namespaced_gid.uri assert_equal URI('gid://bcx/PersonModel/1'), @person_model_gid.uri assert_equal URI('gid://bcx/CompositePrimaryKeyModel/tenant-key-value/id-value'), @cpk_model_gid.uri + assert_equal URI('gid://bcx/ConfigurableKeyModel/external-id-value'), @ckm_model_gid.uri end test 'as JSON' do @@ -183,6 +190,9 @@ class GlobalIDCreationTest < ActiveSupport::TestCase assert_equal 'gid://bcx/CompositePrimaryKeyModel/tenant-key-value/id-value', @cpk_model_gid.as_json assert_equal '"gid://bcx/CompositePrimaryKeyModel/tenant-key-value/id-value"', @cpk_model_gid.to_json + + assert_equal 'gid://bcx/ConfigurableKeyModel/external-id-value', @ckm_model_gid.as_json + assert_equal '"gid://bcx/ConfigurableKeyModel/external-id-value"', @ckm_model_gid.to_json end test 'model id' do @@ -191,6 +201,7 @@ class GlobalIDCreationTest < ActiveSupport::TestCase assert_equal '4', @person_namespaced_gid.model_id assert_equal '1', @person_model_gid.model_id assert_equal ['tenant-key-value', 'id-value'], @cpk_model_gid.model_id + assert_equal 'external-id-value', @ckm_model_gid.model_id end test 'model name' do @@ -199,6 +210,7 @@ class GlobalIDCreationTest < ActiveSupport::TestCase assert_equal 'Person::Child', @person_namespaced_gid.model_name assert_equal 'PersonModel', @person_model_gid.model_name assert_equal 'CompositePrimaryKeyModel', @cpk_model_gid.model_name + assert_equal 'ConfigurableKeyModel', @ckm_model_gid.model_name end test 'model class' do @@ -207,6 +219,7 @@ class GlobalIDCreationTest < ActiveSupport::TestCase assert_equal Person::Child, @person_namespaced_gid.model_class assert_equal PersonModel, @person_model_gid.model_class assert_equal CompositePrimaryKeyModel, @cpk_model_gid.model_class + assert_equal ConfigurableKeyModel, @ckm_model_gid.model_class assert_raise ArgumentError do GlobalID.find 'gid://bcx/SignedGlobalID/5' end diff --git a/test/cases/uri_gid_test.rb b/test/cases/uri_gid_test.rb index 3c3b3fa..cc706e4 100644 --- a/test/cases/uri_gid_test.rb +++ b/test/cases/uri_gid_test.rb @@ -6,6 +6,8 @@ class URI::GIDTest < ActiveSupport::TestCase @gid = URI::GID.parse(@gid_string) @cpk_gid_string = 'gid://bcx/CompositePrimaryKeyModel/tenant-key-value/id-value' @cpk_gid = URI::GID.parse(@cpk_gid_string) + @ckm_gid_string = 'gid://bcx/ConfigurableKeyModel/external-id-123' + @ckm_gid = URI::GID.parse(@ckm_gid_string) end test 'parsed' do @@ -13,6 +15,9 @@ class URI::GIDTest < ActiveSupport::TestCase assert_equal @gid.model_name, 'Person' assert_equal @gid.model_id, '5' assert_equal ["tenant-key-value", "id-value"], @cpk_gid.model_id + assert_equal @ckm_gid.app, 'bcx' + assert_equal @ckm_gid.model_name, 'ConfigurableKeyModel' + assert_equal @ckm_gid.model_id, 'external-id-123' end test 'parsed for non existing model class' do @@ -39,6 +44,11 @@ class URI::GIDTest < ActiveSupport::TestCase assert_equal @cpk_gid_string, URI::GID.create('bcx', model).to_s end + test 'create from a configurable key model' do + model = ConfigurableKeyModel.new(id: 'id-value', external_id: 'external-id-123') + assert_equal @ckm_gid_string, URI::GID.create('bcx', model).to_s + end + test 'build' do array = URI::GID.build(['bcx', 'Person', '5', nil]) assert array @@ -65,6 +75,21 @@ class URI::GIDTest < ActiveSupport::TestCase assert_equal("gid://bcx/CompositePrimaryKeyModel/tenant-key-value/id-value", array.to_s) end + # NOTE: I'm not sure if this test is valuable, it's pretty duplicative with the standard + # test path, but with a different value passed in for `model_id:` + test 'build with a configurable key model' do + array = URI::GID.build(['bcx', 'ConfigurableKeyModel', 'external-id-123', nil]) + gid = URI::GID.build( + app: 'bcx', + model_name: 'ConfigurableKeyModel', + model_id: 'external-id-123', + params: nil + ) + + assert_equal array, gid + assert_equal 'gid://bcx/ConfigurableKeyModel/external-id-123', array.to_s + end + test 'build with wrong ordered array creates a wrong ordered gid' do assert_not_equal @gid_string, URI::GID.build(['Person', '5', 'bcx', nil]).to_s end diff --git a/test/helper.rb b/test/helper.rb index 936fa89..92cfed3 100644 --- a/test/helper.rb +++ b/test/helper.rb @@ -8,6 +8,7 @@ require 'models/person' require 'models/person_model' require 'models/composite_primary_key_model' +require 'models/configurable_key_model' require 'json' diff --git a/test/models/configurable_key_model.rb b/test/models/configurable_key_model.rb new file mode 100644 index 0000000..95107e8 --- /dev/null +++ b/test/models/configurable_key_model.rb @@ -0,0 +1,28 @@ +require 'active_model' + +class ConfigurableKeyModel + include ActiveModel::Model + include GlobalID::Identification + + attr_accessor :id, :external_id + + self.global_id_column = :external_id + + class << self + def primary_key + :id + end + + def find(_id) + raise ".find is not supported for ConfigurableKeyModel" + end + + def find_by(external_id:) + new external_id: external_id, id: "id-value" + end + end + + def ==(other) + external_id == other.try(:external_id) + end +end