3

我写了一个非常简单的 User 类。实例变量 email 有一个 reader 访问器和我自己的 writer 访问器,它们使用正则表达式验证电子邮件地址。

class User
    attr_reader :email

    def email=(value)
        if (value =~ /^[a-z\d\-\_\+\.]+@([a-z\d\-]+\.)+[a-z]+$/)
            @email = value
        else
            # bonus question: is ArgumentError the right error type to use here?
            raise ArgumentError, "#{value} is not a valid email address."
        end
    end
end

我写了以下测试:

require 'test/unit'
require_relative '../lib/user'

class TC_UserTest < Test::Unit::TestCase
    def setup
        @user = User.new()
    end

    def test_email
        # using the writer accessor
        @user.email = 'user@example.com'
        # bypassing the writer accessor. evil.
        @user.email[4] = '#'
        assert_equal('user@example.com', @user.email)
    end
end

通过使用 reader 访问器给我的引用,我能够在不通过 writer 访问器的情况下操作 email 实例变量。

相同的原则适用于任何允许操作而不用 = 直接分配新值的数据类型

我是不是太热心了?我只想编写健壮的代码。有没有办法确保我的电子邮件地址只能使用 writer 访问器设置?

我是该语言的新手,我正在尝试了解最佳实践。

4

3 回答 3

2

使测试通过(并保护@email变量)的一个选项是公开一个副本。

def email
  @email.dup
end
于 2012-11-25T13:46:03.493 回答
1

您可以冻结 writer 中的值,这样您就可以通过 writer 分配新的值,但已经分配的值将是不可变的:

class User
  attr_reader :email

  def email=(value)
    if (value =~ /^[a-z\d\-\_\+\.]+@([a-z\d\-]+\.)+[a-z]+$/)
      # make email immutable:
      @email = value.freeze
    else
      # bonus question: is ArgumentError the right error type to use here?
      raise ArgumentError, "#{value} is not a valid email address."
    end
  end
end
于 2012-11-25T11:54:06.007 回答
1

为了做你想做的事,我的建议是将正则表达式移到它自己的验证方法中。

更好的是,除非你真的想把它做对,否则不要写电子邮件正则表达式。

改用 gem:https ://github.com/SixArm/sixarm_ruby_email_address_validation

设置电子邮件后,使用http://ruby-doc.org/core-1.9.3/Object.html#method-i-freeze将其冻结

奖励答案:是的,ArgumentError 通常是正确的错误类型。如果您使用的是 Rails,请考虑使用 Rails 验证方法。

于 2012-11-25T11:54:24.403 回答