From ee53cefb94db5a47d8bd8c617221098cf018ec56 Mon Sep 17 00:00:00 2001 From: lgebhardt Date: Sat, 13 Jan 2024 12:49:05 -0500 Subject: [PATCH] Rework ResourceIdentity <=> operator add tests for ResourceIdentity, including that comparison does not allocate memory --- jsonapi-resources.gemspec | 1 + lib/jsonapi/resource_identity.rb | 13 ++++- test/unit/resource/resource_identity_test.rb | 58 ++++++++++++++++++++ 3 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 test/unit/resource/resource_identity_test.rb diff --git a/jsonapi-resources.gemspec b/jsonapi-resources.gemspec index f6b2f8af..1a0aa561 100644 --- a/jsonapi-resources.gemspec +++ b/jsonapi-resources.gemspec @@ -29,6 +29,7 @@ Gem::Specification.new do |spec| spec.add_development_dependency 'database_cleaner' spec.add_development_dependency 'hashie' spec.add_development_dependency 'sorted_set' + spec.add_development_dependency 'memory_profiler' spec.add_dependency 'activerecord', '>= 5.1' spec.add_dependency 'railties', '>= 5.1' spec.add_dependency 'concurrent-ruby' diff --git a/lib/jsonapi/resource_identity.rb b/lib/jsonapi/resource_identity.rb index 9466ba0c..58936d95 100644 --- a/lib/jsonapi/resource_identity.rb +++ b/lib/jsonapi/resource_identity.rb @@ -13,6 +13,8 @@ module JSONAPI # rid = ResourceIdentity.new(PostResource, 12) # class ResourceIdentity + include Comparable + # Store the identity parts as an array to avoid allocating a new array for the hash method to work on def initialize(resource_klass, id) @identity_parts = [resource_klass, id] @@ -41,7 +43,16 @@ def hash end def <=>(other_identity) - self.id <=> other_identity.id + return nil unless other_identity.is_a?(ResourceIdentity) + + case self.resource_klass.name <=> other_identity.resource_klass.name + when -1 + -1 + when 1 + 1 + else + self.id <=> other_identity.id + end end # Creates a string representation of the identifier. diff --git a/test/unit/resource/resource_identity_test.rb b/test/unit/resource/resource_identity_test.rb new file mode 100644 index 00000000..8d4ee6cf --- /dev/null +++ b/test/unit/resource/resource_identity_test.rb @@ -0,0 +1,58 @@ +require File.expand_path('../../../test_helper', __FILE__) +require 'memory_profiler' + +class ResourceIdentity < ActiveSupport::TestCase + + def test_can_generate_a_consistent_hash_for_comparison + rid = JSONAPI::ResourceIdentity.new(PostResource, 12) + assert_equal(rid.hash, [PostResource, 12].hash) + end + + def test_equality + rid = JSONAPI::ResourceIdentity.new(PostResource, 12) + rid2 = JSONAPI::ResourceIdentity.new(PostResource, 12) + assert_equal(rid, rid2) # uses == internally + assert rid.eql?(rid2) + end + + def test_inequality + rid = JSONAPI::ResourceIdentity.new(PostResource, 12) + rid2 = JSONAPI::ResourceIdentity.new(PostResource, 13) + refute_equal(rid, rid2) + end + + def test_sorting_by_resource_class_name + rid = JSONAPI::ResourceIdentity.new(CommentResource, 13) + rid2 = JSONAPI::ResourceIdentity.new(PostResource, 13) + rid3 = JSONAPI::ResourceIdentity.new(SectionResource, 13) + assert_equal([rid2, rid3, rid].sort, [rid, rid2, rid3]) + end + + def test_sorting_by_id_secondarily + rid = JSONAPI::ResourceIdentity.new(PostResource, 12) + rid2 = JSONAPI::ResourceIdentity.new(PostResource, 13) + rid3 = JSONAPI::ResourceIdentity.new(PostResource, 14) + + assert_equal([rid2, rid3, rid].sort, [rid, rid2, rid3]) + end + + def test_to_s + rid = JSONAPI::ResourceIdentity.new(PostResource, 12) + assert_equal(rid.to_s, 'PostResource:12') + end + + def test_comparisons_return_nil_for_non_resource_identity + rid = JSONAPI::ResourceIdentity.new(PostResource, 13) + rid2 = "PostResource:13" + assert_nil(rid <=> rid2) + end + + def test_comparisons_allocate_no_new_memory + rid = JSONAPI::ResourceIdentity.new(PostResource, 13) + rid2 = JSONAPI::ResourceIdentity.new(PostResource, 13) + allocation_report = MemoryProfiler.report do + rid == rid2 + end + assert_equal 0, allocation_report.total_allocated + end +end