2009-07-28 4 views
0

J'ai entrepris de résoudre un problème que nous rencontrons, dans certaines circonstances; il se peut que nous devions rendre un attribut ou une relation immuable, c'est-à-dire qu'une fois l'attribut écrit, il est écrit pour toujours. attr_readonly ne convenait pas, car cela n'a pas de relations, donc j'ai essayé de prouver le concept ici:Relations et attributs d'enregistrements actifs immuables (Setters)

http://pastie.org/562329

Cela montre mes tests, un exemple d'implémentation (qu'idéalement je propose à un module) - Je ne suis pas sûr que ce soit un moyen acceptable de surcharger un setter de relations, j'ai étendu le test pour lire maintenant http://pastie.org/562417 qui peut être overkill, mais m'a aidé à comprendre comment customer_id se rapporte au client comme un attribut par rapport à une relation , mais je serais plus qu'heureux d'être corrigé!

Répondre

3

Je ne l'ai pas essayé, mais je pense que update_attribute(:customer_id => 1) cassera dans certains cas. En outre, je n'aime vraiment pas l'idée de substituer des setters d'attributs.

Une meilleure approche (à mon humble avis) serait de remplacer 'before_update' et d'en prendre un si rien n'était changé. Vous pouvez le faire en utilisant le module Dirty. Consultez ceci:

Je n'ai pas testé, mais il donnerait à peu près des lignes de:

class Order < ActiveRecord::Base 
    IMMUTABLE = %w{customer_id notes} 

    before_save do |record| 
    return false if IMMUTABLE.any? { |attr| record.changed.has_key?(attr) } 
    end 
end 

Bien sûr, vous aurez une meilleure idée sur la façon de gérer IMMUTABLE. Si vous mettez ceci dans un module, le before_save doit être dans le Module#included hook

+0

Stefan, avez-vous des preuves que la surcharge des setters d'attributs est mauvaise ou mal conseillée? Je suis d'accord avec votre réponse, c'est beaucoup plus propre que le mien, mais je ne trouve pas non plus de documentation que c'est la façon préférée! –

+0

Je ne pense pas que ce soit mauvais ou mal conseillé - je ne l'aime pas. Cela devient un peu compliqué quand vous avez une relation (vous devez remplacer foo = et foo_id =). En outre, tout ce que je peux penser peut être accompli par des observateurs et d'autres approches. En outre, dans votre cas particulier, vous pouvez vérifier votre solution avec record.name << "something". Il va contourner le mien et il contournera également overriding name =, puisque vous n'invoquez pas un setter, mais changez l'attribut en place. –

+0

[Cette réponse] (http://stackoverflow.com/questions/14779879/prevent-change-of-one-field-in-rails-model/14781183#14781183) comporte un ou deux exemples de validation de rails pour les attributs modifiés. – twelve17

-1

Vous pouvez définir la méthode setter de sorte qu'après la première écriture, la méthode se redéfinisse comme une opération non-op.

class Order < ActiveRecord::Base 
    def customer= c 
    if c 
     super 
     class << self 
     def customer= c 
     end 
     end 
    end 
    end 
end 
+0

Je n'aime pas cette idée. En plus d'être trop intelligent pour son propre bien, ça ne marchera pas. Lorsque vous rechargez l'objet, il aura perdu sa classe singleton et on peut le redéfinir. De plus, cela ne sera pas remarqué par update_attributes –

Questions connexes