2009-11-19 2 views

Antwort

8

Sie können ein gesamtes AR :: B-Objekt einfrieren, indem Sie @readonly auf true (in einer Methode) festlegen. Dadurch werden jedoch alle Attribute gesperrt.

Die Art, wie ich empfehlen würde, ist durch Attribut Setter-Methoden definiert, die zu Super, bevor er für den aktuellen Zustand überprüfen:

class Post < ActiveRecord::Base 
    def author=(author) 
    super unless self.published? 
    end 

    def content=(content) 
    super unless self.published? 
    end 
end 

[EDIT] Oder für eine große Menge von Attributen:

class Post < ActiveRecord::Base 
    %w(author content comments others).each do |method| 
    class_eval <<-"end_eval", binding, __FILE__, __LINE__ 
     def #{method}=(val) 
     super unless self.published? 
     end 
    end_eval 
    end 
end 

Welche würde ich befürworten ziehen in ein Plugin mit anderen zu teilen, und fügen Sie eine schöne DSL für den Zugriff wie: disable_attributes :author, :content, :comments, :when => :published?

+0

Danke für das Feedback Colin. Für eine große Anzahl von Attributen nehme ich an, dass ich eine Klasse eval machen könnte, um diese Setter zu überschreiben, indem ich ein Array von Attributen übergebe, die gesperrt werden müssen, wie? –

+0

Genau. Ich überlegte, ob ich es als class_eval schreiben sollte, entschied mich aber aus Gründen der Lesbarkeit dagegen. Ich werde es aber für andere angehen. –

3

Sie könnten ac hinzufügen ustom-Validierung, um Änderungen an Attributen zu blockieren, wenn Sie sich in einem bestimmten Status befinden. Sie könnten Dinge direkt in die Validierung einprogrammieren. Aber ich bevorzuge den leicht robusteren Ansatz mit Konstanten, die eine Whitelist definieren (eine Liste von Attributen, die sich in einem Zustand ändern dürfen) oder eine Blacklist (Liste von Attributen, die sich in einem Zustand nicht ändern dürfen).

Hier ist ein Beispiel für beide Ansätze. Bei jedem Ansatz wird davon ausgegangen, dass in Ihrem Modell eine Statusmethode vorhanden ist, die den aktuellen/neuen Status als Zeichenfolge zurückgibt. Ansatz

White List

WhiteListStateLockMap = { 
    "state_1" => [ 
    "first_attribute_allowed_to_change_in_state_1", 
    "second_attribute_allowed_to_change_in_state_1", 
    ... 
    ], 
    "state_2" => [ 
    "first_attribute_allowed_to_change_in_state_2", 
    "second_attribute_allowed_to_change_in_state_2", 
    ... 
    ], 
    ... 
} 

validates :state_lock 

def state_lock 
    # ensure that all changed elements are on the white list for this state. 
    unless changed & WhiteListStateLockMap[state] == changed 
    # add an error for each changed attribute absent from the white list for this state. 
    (changed - WhiteListStateLockMap[state]).each do |attr| 
     errors.add attr, "Locked while #{state}" 
    end 
    end 
end 

Black List Ansatz

BlackListStateLockMap = { 
    "state_1" => [ 
    "first_attribute_not_allowed_to_change_in_state_1, 
    "second_attribute_not_allowed_to_change_in_state_1, 
    ... 
    ], 
    "state_2" => [ 
    "first_attribute_not_allowed_to_change_in_state_2", 
    "second_attribute_not_allowed_to_change_in_state_2", 
    ... 
    ], 
    ... 
} 

validates :state_lock 

def state_lock 
    # ensure that no changed attributes are on the black list for this state. 
    unless (changed & BlackListStateLockMap[state]).empty? 
    # add an error for all changed attributes on the black list for this state. 
    (BlackListStateLockMap[state] & changed).each do |attr| 
     errors.add attr, "Locked while #{state}" 
    end 
    end 
end 
+0

Nachdem ich geschrieben hatte, erkannte ich, dass meine Lösung im Wesentlichen ähnlich zu dieser war, außer anders geschrieben. Ich glaube, meine ist expliziter, aber das ist auch gut, weil es deklarativer ist. –

+0

EmFi - danke für die Antwort.Dies ist etwas fortgeschritten für mein Ruby-Wissen, in Bezug auf die zwei Linien, die das einzelne Ampersand betreffen, nicht sicher, was genau dort passiert, es sei denn, dies ist ein && - Ich erhalte tatsächlich einen Fehler beim Testen: undefinierte Methode & 'for {}: Hash –

+1

& ist der Schnittmengenoperator für Arrays. http://ruby-doc.org/core/classes/Array.html#M002212. Array A & Array B gibt die für beide Arrays gemeinsamen Elemente zurück. Wenn A und B == A sind, dann sind alle Elemente von A in B. Außerdem erhalten Sie den Fehler wegen eines Tippfehlers, der mehrmals kopiert und eingefügt wurde. Alle Instanzen von Änderungen sollten sich geändert haben. Ich habe die Lösung aktualisiert, um dies zu reflektieren – EmFi

14

Bearbeiten Attribute, die nicht bearbeitet werden sollte ein Überprüfungsfehler ist:

class Post < ActiveRecord::Base 
    validate :lock_down_attributes_when_published 

    private 

    def lock_down_attributes_when_published 
    return unless published? 

    message = "must not change when published" 
    errors.add(:title, message) if title_changed? 
    errors.add(:published_at, message) if published_at_changed? 
    end 
end 

Thi s verwendet die in 2.2 oder so eingeführten Erweiterungen ActiveRecord::Dirty.

+0

Danke für das Feedback François, sieht dies wie eine andere gute Möglichkeit, dies zu tun. –

0

Wenn der bestimmte Zustand nur persisted? ist, dann ist attr_readonly die beste Option.

attr_readonly(*attributes)öffentliche

Attributes listed as readonly will be used to create a new record but update operations will ignore these fields.

Um zu testen (mit freundlicher Genehmigung von THAiSi):

class MyModel < ActiveRecord::Base 
    attr_readonly :important_type_thingie 
end 

#RSpec 
describe MyModel do 
its('class.readonly_attributes') { should include "important_type_thingie" } 

it "should not update the thingie" do 
    m = create :my_model, :important_type_thingie => 'foo' 
    m.update_attributes :important_type_thingie => 'bar' 
    m.reload.important_type_thingie.should eql 'foo' 
end 
end 
Verwandte Themen