So I'm writing a form object that deals with a specific thing - price - which is going to be included in several different underlying resources. (The Price is a separate model that may or may not need to be created/updated with the underlying resource that belongs_to
it; this 'pricing form object' handles this logic).
It populates itself in three different ways:
When instantiated for showing an
edit
form for an existing resource, it populates itself using data from the resource.When instantiated for showing a
new
form for a not-yet-existing resource, it populates itself using some defaults.When instantiated for saving a form (on
create
orupdate
), it populates itself with the form parameters.
So, slightly simplified:
def initialize(resource: nil, params: {})
@resource = resource
if params.empty?
resource.price.nil? ? set_defaults : set_from_resource
else
set_from_params(params)
end
end
This works fine. And it's nice and DRY - most of the logic can be shared between the three cases.
But... it feels a bit weird, y'know? Having a single object populate itself in several different ways depending on what task it has to do. It's not a pattern I've seen much elsewhere, which makes me worry if there's a reason for that.
One possible thing to do would be to split it up into a form object and a price service object. But this doesn't feel like it can solve the problem. For one thing, I can't make the form object only populate itself from the resource (leaving the service to populate from the params and save to the resource), since the form object needs to populate from the params in the case of a failed save, as the form is re-shown. For another, the form object needs access to any errors set on the Price model on save, so it can show them to the user -- which is simple at the moment (I can clone the errors from the price model to the form object), but with a Price service object the form object's going to have to request the errors off of it. Either I'm going to be duplicating most of the logic, or the two objects are going to end up so tightly coupled it'd have been pointless splitting them.
Is there a pattern I'm missing that solves this elegantly? Or should I stop worrying about populating an object in several different ways?