通过操纵而不是分配来绕过编写器访问器

时间:2012-11-25 11:38:57

标签: ruby

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

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

通过使用读者访问器给我的引用,我能够操作电子邮件实例变量,而无需通过编写器访问器。

相同的原则将适用于任何允许操作的数据类型,而无需使用=

直接指定新值

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

我是这门语言的新手,我正试图了解最佳实践。

3 个答案:

答案 0 :(得分:2)

使测试通过(并保护@email变量)的选项是暴露副本。

def email
  @email.dup
end

答案 1 :(得分:1)

你可以冻结编写器中的值,这样你就可以通过编写器分配新的,但已经分配的是不可变的:

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

答案 2 :(得分:1)

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

更好的是,除非你真的想要做正确的事,否则不要写一封电子邮件regexp。

请改用宝石: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验证方法。

相关问题