From 147fb22ed30234ad111b835565897fdd2d936d45 Mon Sep 17 00:00:00 2001 From: Santiago Pais Date: Fri, 13 Mar 2026 14:25:00 -0300 Subject: [PATCH 1/2] Fix compatibility with Rails delegated_type belongs_to accepts an optional scope as the second argument. safe_polymorphic was not forwarding this argument correctly, causing an ArgumentError when used with APIs like delegated_type. --- CHANGELOG.md | 4 +- Gemfile.lock | 1 + lib/safe_polymorphic/associations.rb | 4 +- spec/safe_polymorphic/associations_spec.rb | 47 ++++++++++++++++++++++ spec/support/models.rb | 3 ++ spec/support/models/address.rb | 6 +++ spec/support/models/house_address.rb | 4 ++ spec/support/models/office_address.rb | 4 ++ spec/support/schema.rb | 22 ++++++++++ 9 files changed, 92 insertions(+), 3 deletions(-) create mode 100644 spec/support/models/address.rb create mode 100644 spec/support/models/house_address.rb create mode 100644 spec/support/models/office_address.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e1996f..a09cc3f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ ## [Unreleased] +- Fix compatibility with Rails `delegated_type` by accepting optional `scope` parameter in `safe_polymorphic` method signature + ## [0.1.0] - 2022-12-28 - Initial release @@ -11,4 +13,4 @@ ## [0.1.2] - 2022-12-28 -- Fix issue that forced to declare the I18n locale (`class_not_allowed`) in each project using this gem instead of using the default locale included in the gem \ No newline at end of file +- Fix issue that forced to declare the I18n locale (`class_not_allowed`) in each project using this gem instead of using the default locale included in the gem diff --git a/Gemfile.lock b/Gemfile.lock index d78c5dd..00b675e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -114,6 +114,7 @@ GEM PLATFORMS arm64-darwin-21 arm64-darwin-23 + arm64-darwin-24 x86_64-linux DEPENDENCIES diff --git a/lib/safe_polymorphic/associations.rb b/lib/safe_polymorphic/associations.rb index 322efab..a02c830 100644 --- a/lib/safe_polymorphic/associations.rb +++ b/lib/safe_polymorphic/associations.rb @@ -11,8 +11,8 @@ class << base end module ClassMethods - def safe_polymorphic(name, **options) - unsafe_polymorphic name, **options + def safe_polymorphic(name, scope = nil, **options) + unsafe_polymorphic name, scope, **options polymorphic = options[:polymorphic] return unless polymorphic.present? && polymorphic.class != TrueClass diff --git a/spec/safe_polymorphic/associations_spec.rb b/spec/safe_polymorphic/associations_spec.rb index 5560821..abc0d35 100644 --- a/spec/safe_polymorphic/associations_spec.rb +++ b/spec/safe_polymorphic/associations_spec.rb @@ -205,4 +205,51 @@ end end end + + describe 'with delegated_type' do + it 'should work alongside delegated_type without errors' do + expect(Address).to respond_to(:addressable_types) + expect(Address.respond_to?(:with_addressable_user)).to be true + expect(Address.respond_to?(:with_addressable_publisher)).to be true + end + + it 'should create an Address with a User addressable' do + user = User.first + address = Address.new(addressable: user) + + expect(address.addressable_type).to eq('User') + expect(address.addressable).to eq(user) + end + + it 'should create an Address with a Publisher addressable' do + publisher = Publisher.first + address = Address.new(addressable: publisher) + + expect(address.addressable_type).to eq('Publisher') + expect(address.addressable).to eq(publisher) + end + + it 'should have safe_polymorphic helper methods working' do + expect(Address.addressable_types).to match_array([User, Publisher]) + end + + it 'should reject invalid addressable types' do + other = OtherThing.create + address = Address.new(addressable: other) + + expect(address).to_not be_valid + expect(address.errors[:addressable_type]).to include('OtherThing is not an allowed class') + end + + it 'should provide scopes for safe_polymorphic' do + user = User.first + publisher = Publisher.first + + Address.create(addressable: user) + Address.create(addressable: publisher) + + expect(Address.with_addressable_user.count).to eq(1) + expect(Address.with_addressable_publisher.count).to eq(1) + end + end end diff --git a/spec/support/models.rb b/spec/support/models.rb index 1b45c65..54bd6f5 100644 --- a/spec/support/models.rb +++ b/spec/support/models.rb @@ -4,3 +4,6 @@ require_relative 'models/publisher' require_relative 'models/book' require_relative 'models/other_thing' +require_relative 'models/address' +require_relative 'models/house_address' +require_relative 'models/office_address' diff --git a/spec/support/models/address.rb b/spec/support/models/address.rb new file mode 100644 index 0000000..5b75664 --- /dev/null +++ b/spec/support/models/address.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +class Address < ActiveRecord::Base + belongs_to :addressable, polymorphic: [Publisher, User] + delegated_type :type, types: %w[House Office], dependent: :destroy +end diff --git a/spec/support/models/house_address.rb b/spec/support/models/house_address.rb new file mode 100644 index 0000000..00d9ccc --- /dev/null +++ b/spec/support/models/house_address.rb @@ -0,0 +1,4 @@ +# frozen_string_literal: true + +class HouseAddress < Address +end diff --git a/spec/support/models/office_address.rb b/spec/support/models/office_address.rb new file mode 100644 index 0000000..c623de2 --- /dev/null +++ b/spec/support/models/office_address.rb @@ -0,0 +1,4 @@ +# frozen_string_literal: true + +class OfficeAddress < Address +end diff --git a/spec/support/schema.rb b/spec/support/schema.rb index d531fa1..a17bea0 100644 --- a/spec/support/schema.rb +++ b/spec/support/schema.rb @@ -29,4 +29,26 @@ t.timestamps end + + create_table :addresses, force: true do |t| + t.integer :addressable_id + t.string :addressable_type + t.string :type + + t.timestamps + end + + create_table :house_addresses, force: true do |t| + t.integer :address_id + t.string :style + + t.timestamps + end + + create_table :office_addresses, force: true do |t| + t.integer :address_id + t.integer :floors + + t.timestamps + end end From 0876ae6ccbc1f7c6779ff40006aa7a49ab745bff Mon Sep 17 00:00:00 2001 From: Santiago Pais Date: Fri, 13 Mar 2026 17:42:29 -0300 Subject: [PATCH 2/2] Add suggestions --- spec/safe_polymorphic/associations_spec.rb | 24 +++++++++++++++++----- spec/support/models.rb | 2 -- spec/support/models/address.rb | 2 +- spec/support/models/house_address.rb | 4 ---- spec/support/models/office_address.rb | 4 ---- spec/support/schema.rb | 17 ++------------- 6 files changed, 22 insertions(+), 31 deletions(-) delete mode 100644 spec/support/models/house_address.rb delete mode 100644 spec/support/models/office_address.rb diff --git a/spec/safe_polymorphic/associations_spec.rb b/spec/safe_polymorphic/associations_spec.rb index abc0d35..216a866 100644 --- a/spec/safe_polymorphic/associations_spec.rb +++ b/spec/safe_polymorphic/associations_spec.rb @@ -207,6 +207,9 @@ end describe 'with delegated_type' do + before do + Book.create(title: 'Book', owner: User.first) + end it 'should work alongside delegated_type without errors' do expect(Address).to respond_to(:addressable_types) expect(Address.respond_to?(:with_addressable_user)).to be true @@ -215,7 +218,7 @@ it 'should create an Address with a User addressable' do user = User.first - address = Address.new(addressable: user) + address = Address.new(addressable: user, delegated: Book.first) expect(address.addressable_type).to eq('User') expect(address.addressable).to eq(user) @@ -223,7 +226,7 @@ it 'should create an Address with a Publisher addressable' do publisher = Publisher.first - address = Address.new(addressable: publisher) + address = Address.new(addressable: publisher, delegated: Book.first) expect(address.addressable_type).to eq('Publisher') expect(address.addressable).to eq(publisher) @@ -235,7 +238,7 @@ it 'should reject invalid addressable types' do other = OtherThing.create - address = Address.new(addressable: other) + address = Address.new(addressable: other, delegated: Book.first) expect(address).to_not be_valid expect(address.errors[:addressable_type]).to include('OtherThing is not an allowed class') @@ -245,11 +248,22 @@ user = User.first publisher = Publisher.first - Address.create(addressable: user) - Address.create(addressable: publisher) + Address.create(addressable: user, delegated: Book.first) + Address.create(addressable: publisher, delegated: Book.first) expect(Address.with_addressable_user.count).to eq(1) expect(Address.with_addressable_publisher.count).to eq(1) end + + it 'should provide class methods of delegated_type ' do + user = User.first + book = Book.first + + address = Address.create(addressable: user, delegated: book) + + expect(Address.with_addressable_user.first).to eq(address) + expect(address.addressable).to eq(user) + expect(address.delegated).to eq(book) + end end end diff --git a/spec/support/models.rb b/spec/support/models.rb index 54bd6f5..f62170b 100644 --- a/spec/support/models.rb +++ b/spec/support/models.rb @@ -5,5 +5,3 @@ require_relative 'models/book' require_relative 'models/other_thing' require_relative 'models/address' -require_relative 'models/house_address' -require_relative 'models/office_address' diff --git a/spec/support/models/address.rb b/spec/support/models/address.rb index 5b75664..4e7cc0b 100644 --- a/spec/support/models/address.rb +++ b/spec/support/models/address.rb @@ -2,5 +2,5 @@ class Address < ActiveRecord::Base belongs_to :addressable, polymorphic: [Publisher, User] - delegated_type :type, types: %w[House Office], dependent: :destroy + delegated_type :delegated, types: %w[Book], dependent: :destroy end diff --git a/spec/support/models/house_address.rb b/spec/support/models/house_address.rb deleted file mode 100644 index 00d9ccc..0000000 --- a/spec/support/models/house_address.rb +++ /dev/null @@ -1,4 +0,0 @@ -# frozen_string_literal: true - -class HouseAddress < Address -end diff --git a/spec/support/models/office_address.rb b/spec/support/models/office_address.rb deleted file mode 100644 index c623de2..0000000 --- a/spec/support/models/office_address.rb +++ /dev/null @@ -1,4 +0,0 @@ -# frozen_string_literal: true - -class OfficeAddress < Address -end diff --git a/spec/support/schema.rb b/spec/support/schema.rb index a17bea0..13d3064 100644 --- a/spec/support/schema.rb +++ b/spec/support/schema.rb @@ -33,21 +33,8 @@ create_table :addresses, force: true do |t| t.integer :addressable_id t.string :addressable_type - t.string :type - - t.timestamps - end - - create_table :house_addresses, force: true do |t| - t.integer :address_id - t.string :style - - t.timestamps - end - - create_table :office_addresses, force: true do |t| - t.integer :address_id - t.integer :floors + t.bigint :delegated_id, null: false + t.string :delegated_type, null: false t.timestamps end