2009-11-19 88 views

回答

8

通過將@readonly設置爲true(在方法中),您可以凍結整個AR :: B對象,但會鎖定所有屬性。

我建議的方法是定義屬性setter方法傳遞給超之前檢查當前的狀態:

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

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

[編輯]或大量屬性:

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 

當然我會主張拉入插件與他人分享,並添加一個很好的DSL訪問像:disable_attributes :author, :content, :comments, :when => :published?

+0

感謝您的反饋科林。對於大量的屬性,我想我可以通過傳入需要被鎖定的屬性數組來完成一個類的eval來覆蓋這些設置器,呃? – 2009-11-19 20:28:34

+0

準確。我辯論是否應該把它寫成class_eval,但是爲了可讀性而決定不採用它。我會盡力爲別人着想。 – 2009-11-19 20:55:38

3

您可以添加ac如果您處於特定狀態,則使用ustom驗證來阻止對屬性的更改。你可以直接將驗證碼直接寫入代碼。但我更喜歡使用定義白名單的常量(允許在狀態中更改的屬性列表)或黑名單(不允許在狀態中更改的屬性列表)的更穩健的方法。

下面是兩種方法的示例。每種方法都假定模型中有一個狀態方法,以字符串形式返回當前/新狀態。

白名單方法

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 

黑名單方法

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

我發佈後,我意識到我的解決方案基本上與此類似,只是寫法不同。我相信我是更明確的,但這也是好的,因爲它更具說明性。 – 2009-11-19 23:39:24

+0

EmFi - 感謝您的迴應。這對於我的Ruby知識來說有點高級,就涉及單個&符號的兩行代碼而言,我不確定到底發生了什麼,除非這意味着是&& - 實際上我得到了一個測試錯誤:未定義的方法' &'for {}:哈希 – 2009-11-20 05:20:17

+1

&是數組的交集操作符。 http://ruby-doc.org/core/classes/Array.html#M002212。數組A和數組B返回兩個數組通用的元素。如果A&B == A,那麼A的所有元素都在B中。而且由於我的多次複製和粘貼的錯字,您也會收到錯誤。所有更改的實例都應該已經更改。我已經更新瞭解決方案,以反映這 – EmFi 2009-11-20 06:28:14

14

編輯屬性不應被編輯驗證錯誤:

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 

氏s使用了2.2左右的ActiveRecord::Dirty擴展。

+0

感謝您的反饋François,這看起來是另一個偉大的方式做到這一點。 – 2009-11-20 05:00:00

0

如果特定狀態只是persisted?,那麼attr_readonly是最佳選擇。

attr_readonly(*attributes)公共

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

爲了測試(的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