2012-05-08 28 views
0

我有一個User模型,其中包含一個手機號碼。手機號碼將以國際格式保存在數據庫中。因爲用戶通常不知道這一點,也不喜歡國際格式,所以我更改了getter和setter,以便用戶能夠以本地格式輸入數字,並且如果沒有國際前綴,他們將顯示本地格式。當我重寫我的ActiveRecord模型的getter和setter時發生了奇怪的事情

class User < ActiveRecord::Base 
    def mobile=(number) 
    # Strip whitespace, dashes and slashes first 
    [" ", "-", "/", "\\"].each do |particle| 
     number.gsub!(particle, "") 
    end 

    # Check if there is leading 00, this indicates a 
    # country code. 
    number.gsub!(/^00/,"+") 

    # Check if there is only one leading zero. If there is, 
    # treat as German number 
    number.gsub!(/^0/,"+49") 

    # Now write to attribute. Validate later. 
    puts "Writing: #{number}" 
    write_attribute(:mobile, number) 
    end 

    def mobile 
    number = read_attribute(:mobile) 
    puts "Reading: #{number}" 

    # If this is a German number, display as local number. 
    number.gsub!(/\+49/,"0") 

    number 
    end 
end 

現在,看起來這並不像預期的那樣工作。這是我的rails console會話:

> u = User.new(:mobile => "0163 12345") 
Writing: +4916312345 
=> #<User id: nil, mobile: "+4916312345", ...> 

這個按預期工作。所以,讓我們檢查一下吸氣劑:

> u.mobile 
Reading: +4916312345 
=> "016312345" 

看起來不錯。但再次檢查一遍:

> u.mobile 
Reading: 016312345 
=> "016312345" 

WTF?我的屬性改變了。這限於吸氣劑功能嗎?

> u 
=> #<User id: nil, mobile: "016312345", ...> 

否。它甚至在數據庫模型中設置屬性。

如果我訪問屬性兩次,屬性更改。我沒有訪問write_attribute。爲什麼我的屬性改變了?

+0

爲什麼你在這裏做這件事,而不是在驗證器或'before_save'過濾器?您正在使用的導軌版本爲 –

+0

? – efoo

回答

2

考慮該簡化的例子:

class User 
    def initialize 
    @attributes = { :mobile => "+4916312345" } 
    end 
    def read_attribute name 
    @attributes[name] 
    end 
end 

注意read_attribute返回的屬性的值,而不是一個複製值。

現在:

user = User.new 
mobile = user.read_attribute :mobile 
=> "+4916312345" 
mobile.gsub!(/\+49/,"0") 
=> "016312345" 
mobile = user.read_attribute :mobile 
=> "016312345" # because we modified it in place with gsub! 

所有你需要做的就是在你的getter使用gsub代替gsub!,因爲你永遠不會更換一次+49多在相同的字符串,你可能也只是使用sub

def mobile 
    number = read_attribute(:mobile) 
    puts "Reading: #{number}" 

    # If this is a German number, display as local number. 
    number.sub(/\+49/,"0") 
end 
相關問題