Duplicating a dependency tree

Hey guys,

I posted this in the Ecto-Talk Google Group, but wasn’t sure if anyone ever goes there, these days, so figured I’d pop it on here, too.

So, I have some complex relations for objects (meaning there are a lot of relations and deeply nested). I now need to provide a duplication feature, where all objects in the tree are duplicated but the relationships remain (update of id’s throughout with secure inserts without breaking foreign key relationships). What’s the best way to do this functionally?

Let’s say my model represents a website. This site has pages. Each page has a header, footer and multiple rows. Each row has one or more columns. Each column has styles (Colour object, font etc.)

I need to be able to say site |> duplicate and have every nested object also duplicated having each of their foreign keys updated.

I have an implementation, but I’m sure it could be much simpler thanks to FP.

Thanks,
Lee

Hmm, the way I’d do it is to make functions for duplicating each part then coalesce them together.

Like if you have Page, a Page has a Header, Footer, and Row (multiple of Row), etc… I’d just call each from within the top:

def duplicate(%Page{} = page) do
  Page.changeset(%Page{},
    header: duplicate(page.header),
    footer: duplicate(page.footer),
    rows: duplicate(page.rows),
    etc...
  )
end

def duplicate(%Header{} = header) do blah end

def duplicate(%Footer{} = footer) do blah end

def duplicate([%Row{}|_rest] = rows) do
  Enum.map(&duplicate/1, rows)
end

Or something like that. Just have to remember to alter it if any field is added/removed/changes due to lack of type system enforcing it via field mappers or so.

That’s how I’m currently doing it. Like a to_string function, every model object has one and knows how to call duplicate on its children to build out a new version of itself. I just wasn’t sure if there was a simpler solution. I guess this is it :slight_smile:

Thanks for clarifying. At least I know I’m not wasting energy with my method.

There are other ways sure, but that is the traditional method, traditional because it is explicit. With some metaprogramming you could bind some things and make sure a field is never forgotten and such, but that is a ton of work (for admittedly useful benefit, but that should be a library, maybe one already exists). As Python says, explicit is better than implicit. :slight_smile:

What got me thinking about this is that, if there was a tree traversal, when finding a leaf node you could check for the availability of struct and meta and duplicate it, leaving out the id. Then, work your way up each branch, like an AST walker, duplicating and submitting as you go. Not sure how useful that would be as a library, but it seems like it could be made into a macro.

Lee

Actually, I might just give it a go for a bit of a fun exercise. I’m sure someone else would find it useful.

At the very least it is an awesome learning opportunity, or better the community gets an awesome new library! ^.^

I’ve made a ton of libraries over time, a lot just to learn with in a huge variety of languages. It is always fun to push ones knowledge. :slight_smile: