1

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 or update), 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?

1

Your data source source for populating the form should always be a domain object (what you're calling a "resource"). I'm not sure how Ruby on Rails deals with this, but I am sure that RoR has a solution already. It probably goes something like this:

  • 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 a newly-created resource, already populated with some defaults.

  • When instantiated for saving a form (on create or update)... Well, that's already covered by the first two bullet points.

So there aren't three ways to populate the form. There's only one.

  • Thanks, but I'm afraid that doesn't help much - my question was unclear, apologies. To be clearer: the parts of the form that directly relate to the main resource - which is most of it - does bind straight to that resource in the usual way. The form object handles a second resource - the thing I referred to as the Price model - which may or may not need to be created/updated when the main resource is (logic deciding that is in the form object). The main resource (which could be one of a dozen things which have a price) just holds a reference to the secondary resource (a price_id). – Simon Woolf Mar 12 '15 at 17:52
  • Have a look at the ViewModel concept. – Robert Harvey Mar 12 '15 at 17:57
  • Thanks. If I'm understand the ViewModel concept right, it's basically exactly what I've done with what I called the form object -- an object whose state can be set by either the view or the model, can be read by the view, and can save to the model. So I guess I should conclude that I should stop worrying and this pattern is fine... – Simon Woolf Mar 16 '15 at 17:02
  • It sounds fine to me. The differences between the ViewModel and other model objects is that the ViewModel contains information that is specific to the view. For example, an Invoice View Model will contain customer information, product information, pricing information, and so forth. It can also contain validation logic. – Robert Harvey Mar 16 '15 at 17:16
0

If I'm understanding you correctly, you may want to break up your form into smaller more modular components. Is what you're saying is that a piece of your form will be updated or changed? If so, you can have a separate presenter responsible for binding that resource/model to the view.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.