Homebrew Immutable Attribute

by winson, 7 months ago.

最近寫點小程式,客戶拜託我其中某個table不要用Rails內建的key,這不是新增一個key就好的問題,而是要用指定的格式當key。

好,從設計的角度來看,格式如何先不管,這個key的值除了生成的那一刻之外,往後都不可隨意變更,不只從view無法變更,甚至要防止程式不小心修改到,這才有資格作為一個key。

先看Rails是否內建防止某個欄位遭到修改的保護機制。這時候,第一個想到的自然是attr_protected:

class User < ActiveRecord::Base
  attr_protected :my_key
end
查一下Rail的文獻,會發現這個attr_protected跟他親戚attr_accessible是用來防止批次指定的(mass-assignment),也就是說以下幾種指定方式都可防止掉:
user = User.new :my_key => 12345
user = User.create :my_key => 12345
user.attributes = { :my_key => 12345 }
user.update_attributes :my_key => 12345
而attr_protected跟attr_accessible的差別在於:
  • attr_protected防止批次指定
  • attr_accessible容許批次指定
講白一點,attr_protected是負面表列,attr_accessible是正面表列;attr_protected後面跟著的屬性是不可批次指定;而attr_accessible則是列出可以批次指定的屬性,其餘沒列出的都不可以被批次指定

對於作為一個key而言,這樣的保護夠嗎?要偷改他的值,至少還有以下幾種方式:

user.my_key = 12345
user.update_attribute :my_key, 12345
因為這兩種方式都不是批次指定,所以不在這兩個保鏢attr_protected、attr_accessible的保護範圍之內。

回過頭來看一下需求:

這個key的值除了生成的那一刻之外,往後都不可隨意變更。
所以,我只要去限制可以塞值給my_key的時機就好,其他一律不准!!

因為最近愛上了module,所以嘗試寫些自己的module:

module UntouchableModel
  def attr_immutable(attr_name)
    define_method "#{attr_name}=" do |new_value|
      if new_record?
        write_attribute attr_name, new_value
      else
        raise ActiveRecord::RecordNotSaved
      end
    end
  end
end
至於怎麼使用直接看扣比較快:
class User < ActiveRecord::Base
  include UntouchableModel
  attr_immutable :my_key
end