I have a complicated relationship where I have multiple models require addresses. This usually means using a polymorphic relationship like so:
class Address < ApplicationRecord
belongs_to :addressable, polymorphic: true
end
class User < ApplicationRecord
# no issue, its "addressable" so just use this line of code
has_one :address, as: :addressable
end
class Account < ApplicationRecord
# Ok the issue here is that I need exactly TWO addresses though
# One is for billing and one if a physical address where an event will
# physically take place.
has_one :billing_address, class_name: "Address", as: :addressable
has_one :site_address, class_name: "Address", as: :addressable
end
The problem with this is ...
a = Account.first
a.billing_address #returns an address
a.site_address #returns the same address
How can I get the account to differentiate between two addresses? I know this isn't really a limitation of polymorphism but rather a software design problem that I need to solve. I'm wondering if maybe I need to treat Address
as an abstract model and derive BillingAddress
and SiteAddress
from it and maybe have something like this:
class Address < ApplicationRecord
# see active_record-acts_as gem for how this mixin works
# https://github.com/hzamani/active_record-acts_as
actable
belongs_to :addressable, polymorphic: true
end
class User < ApplicationRecord
# no issue, its "addressable" so just use this line of code
has_one :address, as: :addressable
end
class BillingAddress < ApplicationRecord
acts_as :address
end
class SiteAddress < ApplicationRecord
acts_as :address
end
class Account < ApplicationRecord
has_one :billing_address
has_one :site_address
end
This might be good to do because I also have an Event
model which requires a site address so I could do this as well:
class Event < ApplicationRecord
has_one :site_address
end
Is this over engineering? At the risk of sounding too subjective, what are your thoughts on this? Is there a better way to do this?
What separates the address categories? You mention that you may have a billing address and a site address.
If for example the categories are determined by an attribute called 'category', then all you have to do is set a condition on the association declarations on the addressable:
class Account < ApplicationRecord
has_one :billing_address, -> { where category: 'billing' }, class_name: "Address", as: :addressable
has_one :site_address, -> { where category: 'site' }, class_name: "Address", as: :addressable
end