当我覆盖我的 ActiveRecord 模型的 getter 和 setter 时会发生奇怪的事情



我有一个User型号,其中包含一个手机号码。手机号码将以国际格式保存在数据库中。由于用户通常不知道这一点并且不喜欢国际格式,因此我更改了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" 

跆拳道?我的属性变了。这是否仅限于吸气器功能?

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

不。它甚至在数据库模型中设置属性。

如果我访问该属性两次,该属性会发生变化。我无法访问write_attribute.为什么我的属性会改变?

考虑这个简化的例子:

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

最新更新