Delegated Type supports customizeable foreign_type column

This commit is contained in:
Jason Karns 2023-02-11 16:09:56 -05:00
parent 2e81ed6d10
commit 1f61fd65ae
No known key found for this signature in database
GPG Key ID: 4F072FBC1ACA2746
4 changed files with 36 additions and 3 deletions

@ -1,3 +1,10 @@
* Respect `foreign_type` option to `delegated_type` for `{role}_class` method.
Usage of `delegated_type` with non-conventional `{role}_type` column names can now be specified with `foreign_type` option.
This option is the same as `foreign_type` as forwarded to the underlying `belongs_to` association that `delegated_type` wraps.
*Jason Karns*
* Add support for unique constraints (PostgreSQL-only).
```ruby

@ -192,6 +192,11 @@ module DelegatedType
# +role+ with an "_id" suffix. So a class that defines a
# <tt>delegated_type :entryable, types: %w[ Message Comment ]</tt> association will use "entryable_id" as
# the default <tt>:foreign_key</tt>.
# [:foreign_type]
# Specify the column used to store the associated object's type. By default this is inferred to be the passed
# +role+ with a "_type" suffix. A class that defines a
# <tt>delegated_type :entryable, types: %w[ Message Comment ]</tt> association will use "entryable_type" as
# the default <tt>:foreign_type</tt>.
# [:primary_key]
# Specify the method that returns the primary key of associated object used for the convenience methods.
# By default this is +id+.
@ -211,11 +216,11 @@ def delegated_type(role, types:, **options)
private
def define_delegated_type_methods(role, types:, options:)
primary_key = options[:primary_key] || "id"
role_type = "#{role}_type"
role_type = options[:foreign_type] || "#{role}_type"
role_id = options[:foreign_key] || "#{role}_id"
define_method "#{role}_class" do
public_send("#{role}_type").constantize
public_send(role_type).constantize
end
define_method "#{role}_name" do

@ -11,11 +11,12 @@
require "models/uuid_comment"
class DelegatedTypeTest < ActiveRecord::TestCase
fixtures :comments, :accounts
fixtures :comments, :accounts, :posts
setup do
@entry_with_message = Entry.create! entryable: Message.new(subject: "Hello world!"), account: accounts(:signals37)
@entry_with_comment = Entry.create! entryable: comments(:greetings), account: accounts(:signals37)
@entry_with_post = Entry.create! thing: posts(:welcome), account: accounts(:signals37)
if current_adapter?(:PostgreSQLAdapter)
@uuid_entry_with_message = UuidEntry.create! uuid: SecureRandom.uuid, entryable: UuidMessage.new(uuid: SecureRandom.uuid, subject: "Hello world!")
@ -28,6 +29,12 @@ class DelegatedTypeTest < ActiveRecord::TestCase
assert_equal Comment, @entry_with_comment.entryable_class
end
test "delegated class with custom foreign_type" do
assert_equal Message, @entry_with_message.thing_class
assert_equal Comment, @entry_with_comment.thing_class
assert_equal Post, @entry_with_post.thing_class
end
test "delegated type name" do
assert_equal "message", @entry_with_message.entryable_name
assert @entry_with_message.entryable_name.message?
@ -44,11 +51,21 @@ class DelegatedTypeTest < ActiveRecord::TestCase
assert_not @entry_with_comment.message?
end
test "delegated type predicates with custom foreign_type" do
assert @entry_with_post.post?
assert_not @entry_with_message.post?
assert_not @entry_with_comment.post?
end
test "scope" do
assert Entry.messages.first.message?
assert Entry.comments.first.comment?
end
test "scope with custom foreign_type" do
assert Entry.posts.first.post?
end
test "accessor" do
assert @entry_with_message.message.is_a?(Message)
assert_nil @entry_with_message.comment

@ -3,4 +3,8 @@
class Entry < ActiveRecord::Base
delegated_type :entryable, types: %w[ Message Comment ]
belongs_to :account, touch: true
# alternate delegation for custom foreign_key/foreign_type
delegated_type :thing, types: %w[ Post ],
foreign_key: :entryable_id, foreign_type: :entryable_type
end