J'ai déjà fait quelque chose de similaire, mais avec des associations profondes à un seul niveau. Par exemple, des comptes qui avaient un ou plusieurs adresses, contacts, etc.
Vous devez vous assurer que vos associations avec d'autres modèles apparentés sont plutôt simples, de sorte qu'il n'y ait pas de boucles ou de pointeurs arrière.
Il est probablement préférable de l'enregistrer de manière à pouvoir la comparer facilement par la suite avec la version actuelle du modèle. Vous devrez exclure le champ "original" de la comparaison ;-)
Lors de la première sauvegarde du modèle, je ferais un clone profond du modèle et de ses associations et je le sauvegarderais dans un champ sérialisé 'original' dans le modèle principal.
Je pense que j'utiliserais le serialize
caractéristique d'ActiveRecord, par exemple :
Class YourMainModel
...
serialize :original # will serialize this ; make this TEXT field in DB
...
end
vous voulez vous assurer que le type de données du champ dans la base de données est un texte ou une chaîne, et non un champ binaire ! (cela causerait des bugs plus tard !)
Ensuite, pendant l'after_save du modèle, j'ajouterais du code pour faire quelque chose comme ceci :
# do this in the after_save - so the validations have run:
main_model_object.original ||= main_model_object.deep_clone # ||= to do this only once
main_model_object.save(:validate => false) if main_model_object.original_changed? # save if we added the "copy"
Il y a probablement d'autres façons de le faire, mais after_save a l'avantage de permettre l'exécution des validations.
Vous devrez vous assurer que tous les enregistrements associés sont créés au moment de la première sauvegarde, vous pourriez probablement faire un "formulaire monstre" avec Gem nested_forms.
Voir :
https://github.com/moiristo/deep_cloneable (Fourchette pour Rails 3)
https://github.com/openminds/deep_cloning (projet original)
Regardez les autres RailsCasts de Ryan :
http://railscasts.com/episodes/196-nested-model-form-part-1
http://railscasts.com/episodes/197-nested-model-form-part-2
J'espère que cela vous aidera
EDIT :
En y réfléchissant bien, pour limiter la taille de votre base de données et de ses vidages, pour simplifier votre schéma et pour conserver les originaux, vous pourriez stocker les originaux dans un magasin de documents distinct, par exemple en utilisant MongoDB.
Si vous n'avez pas besoin d'accéder fréquemment aux originaux, le stockage dans MongoDB sous la forme d'un document structuré pourrait être très avantageux et votre application principale serait moins compliquée.
Lorsque vous comparez des enregistrements, vous devez cloner profondément votre modèle modifié de la même manière que vous l'avez fait la première fois, puis rechercher l'enregistrement MongoDB via le champ "id" de l'enregistrement original, puis comparer les deux (les deux sont des clones profonds).
Avantage supplémentaire de cette solution : il serait plus difficile de modifier accidentellement l'original, car il n'est pas directement lié à l'enregistrement de la base de données SQL, par exemple, vous ne pourriez pas accidentellement faire object.original = something