2013-04-16 3 views
2

j'ai une certaine configuration STI comme ceci:STI, délégué et devient

class Document < ActiveRecord::Base 
    attr_accessible :name, description 

    # Basic stuff omitted 
end 

class OriginalDocument < Document 
    has_many :linked_documents, foreign_key: :original_document_id, dependent: :destroy 
end 

class LinkedDocument < Document 
    belongs_to :original_document 

    # Delegation, because it has the same attributes, except the name 
    delegate :description, to: :original_document 
end 

Maintenant, je veux dup le LinkedDocument et le stocker comme OriginalDocument, avec son propre nom et conserver les valeurs d'attributs de duplication. Cependant, mes approches échouent car quelque part, le duplicata veut toujours accéder à ses méthodes déléguées dans les callbacks after_ *.

class LinkedDocument < Document 
    def unlink_from_parent 
    original = self.original_document 

    copy = self.becomes OriginalDocument 
    copy.original_document_id = nil 
    copy.description = original.description 
    copy.save 
    end 
end 

Ceci déclenche un RuntimeError: LinkedDocument#description delegated to original_document.description, but original_document is nil.

L'exécution d'un copy.type = 'OriginalDocument' supplémentaire pour appliquer des éléments ne fonctionnera pas, car la requête de sauvegarde implique le type; UPDATE documents SET [...] WHERE documents.type IN('OriginalDocument') [...]. Cela échoue, car au moment de la transaction, l'objet est toujours de type LinkedDocument.

Ce qui serait une manière propre de copier un objet et de le laisser devenir un autre? J'ai pensé à appeler update_column pour type et tous les attributs que je veux copier, mais avant de le faire de manière si inélégante, je voulais demander ici.

Répondre

1

Je vais ajouter ma solution ici, au cas où personne n'en aurait une meilleure. J'espère que cela aidera quelqu'un.

Pour laisser l'objet devenir un autre sans avoir de mauvaises requêtes parce que la clause where vérifie le mauvais type, j'ai manuellement mis à jour la colonne de type sans invoquer de callback avant d'appeler become.

# This is for rails3, where +update_column+ does not trigger 
# validations or callbacks. For rails4, use 
# 
# self.update_columns {type: 'OriginalDocument'} 
# 
self.update_column :type, 'OriginalDocument' 
document = self.becomes OriginalDocument 

Maintenant, pour les missions, il y avait deux problèmes: d'abord, les poseurs d'attributs peuvent déclencher en quelque sorte une exception à cause des délégations. Deuxièmement, les attributs que je voulais assigner massivement n'étaient pas listés par ex. attr_accessible intentionnellement parce qu'ils étaient des attributs internes. J'ai donc eu recours à une boucle avec une instruction update_column laide qui produisait beaucoup trop de requêtes (puisque rails3 n'a pas de update_columns).

original.attributes.except('id', 'name', 'original_document_id').each do |k,v| 
    document.update_column k.to_sym, v 
end