disposable icon indicating copy to clipboard operation
disposable copied to clipboard

Using a twin with nested properties to decorate a flat model

Open zavan opened this issue 8 years ago • 4 comments

Hey Nick, long time no talk, you may not even remember me haha! I've been busy with other stuff and had to pause my work using trailblazer, but I'm back at it now.

Here is the deal, suppose I have the following flat model (no nested objects):

Song = Struct.new(:title, :track, :length)

But from my form I receive this nested hash:

{
  song: {
    title: 'Roxanne',
    details: {
      track: 3,
      length: '4:10'
   }
}

So I have this form that maps nicely to the nested hash:

class SongForm < Reform::Form
  property :title

  property :details do
    property :track
    property :length
  end

  # validations...
end

Now, I don't want to do the mapping nested hash -> flat model directly in my form, the form should not know about this. So I want introduce a twin that the form will use to do the mapping and syncing, like this:

params = {
  song: {
    title: 'Roxanne',
    details: {
      track: 3,
      length: '4:10'
   }
}

twin = SongTwin.new(Song.new)
form = SongForm.new(twin)

if form.validate(params)
  # will sync to the twin and call save on it
  # the twin will sync to the model (mapping the nested hash to the flat model) and then call save it
  form.save
end

So I need something close to what I can do with nested in Representable (in fact, I stole this example from its documentation), but with the twin getters and setters, since I plan on using the twin to implement simple business logic:

class SongTwin < Disposable::Twin
  feature Sync
  feature Nested

  property :title

  nested :details do
    property :track
    property :length
  end

  # business logic using the getters and setters (including the nested ones) 
end

I believe that currently the simpler way to do this would be to override the sync method of the twin:

def sync
  super do |hash|
    model.title = hash[:title]
    model.track = hash[:details][:track]
    model.length = hash[:details][:length]
  end
end

But this is obviously not a nice solution and it doesn't work for reading...

This relates to this Reform issue: https://github.com/apotonick/reform/issues/277

zavan avatar Apr 13 '16 22:04 zavan

Maybe I could use the Parent feature to to delegate the nested twin setters/getters to the parent twin... But still, it would required lots of delegations for a big twin.

zavan avatar Apr 13 '16 22:04 zavan

Now, this is funny, because we were just needing the same thing with @danieldraper and came to the conclusion that something like nested, plus :getter and :setter could be helpful in Disposable! :laughing:

And, of course I remember you, good to have you back, my lost son! :beers:

apotonick avatar Apr 14 '16 00:04 apotonick

Cool, so we're in sync! Also, with this implemented in Disposable, it could be used in Reform too, right? (As in the Reform issue I referenced)

zavan avatar Apr 14 '16 00:04 zavan

Yes, a form object is simply a twin with validation semantics.

apotonick avatar Apr 14 '16 00:04 apotonick