Compare 2 models including associations, sync changes

Hey everyone, i’m a bit stuck with choosing the path to solve this, would appreciate your thoughts

  • User has many products, each product has many prices.
  • Recipient (user2) copies a product including all prices from sender (user1)
  • Sender updates this product (changes attributes or prices)
  • Recipient can see the diff between his copy and sender’s copy, and can update his copy to match sender’s copy
  • Recipient can have his own products

there are a few ways to implement this:

  1. Create a nested hash with a product and all prices on each change, store it in Versions table (like paper_trail gem). Recipient links to a version in this table. Comparison can be done with hashdiff gem, syncing will be relinking to a newer version. Cons: recipient can have his own products, this will require two different implementations for own products and received products. Also in case he will need to change a received product - this will require some extra wires. Also each time product or price table needs to be updated - hashes in Versions table might need to be rebuilt

  2. Iterate through product and prices and compare them using ActiveModel::Dirty. Merge results in one hash to show diffs. Syncing will be iterating again with save! inside a transaction. Feels a bit brute force

    product2.attributes = product1.attributes.slice(attributes)

  3. Build nested hashes for both products and compare hashes using hashdiff. Syncing is sending nested hash to a form object that will parse this hash and apply changes to models

    Product.first.as_json(include: :prices)
    (There is also amoeba gem and deep_cloneable gem for building nested hashes)

The last option looks the most appealing at the moment, as this interface can be also used in API - it will work if data comes from another model or from external source.

I’d appreciate your thoughts. Am I missing something or overcomplicating things? Is there a better way to compare and sync models with associations?


How about you create a belongs_to :parent_product relationship on the copy? This way you always have a reference to the original product and from there it’s pretty straightforward to do any comparisons and merging you may want.