2012-05-08 1 views
0

У меня есть модель User, где я включаю номер мобильного телефона. Номер мобильного телефона будет сохранен в базе данных в международном формате. Поскольку пользователи часто не знают об этом и не любят международные форматы, я сменил геттер и сеттеры, чтобы пользователь мог вводить цифры в локальном формате, и они отображают локальный формат, если международный префикс отсутствует.Странные вещи случаются, когда я переоцениваю геттеры и сеттеры моей модели ActiveRecord

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! 

Все, что вам нужно сделать, это использовать 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