【问题标题】:Is there a way for Ruby accessors to return something other than the set variable?Ruby 访问器有没有办法返回 set 变量以外的东西?
【发布时间】:2008-10-13 14:06:34
【问题描述】:

我想检查写入器访问器。我的第一个想法是返回一个布尔值。

class MyClass
  def var=(var)
    @var = var
    # some checking
    return true
  end
end

m = MyClass.new
retval = (m.var = 'foo')

=> "foo"

我可以在写入访问器中设置返回值吗?如果是,我怎样才能得到这个值?

【问题讨论】:

  • 你能提供一个什么样的检查的例子吗?我对你的意思有点迷茫。但我有一种预感,这很有可能,只是需要澄清一下。
  • 仅仅因为某事是可能的并不意味着它是一个好主意。 ;)
  • 引发异常绝对是 ruby​​ 方法。
  • 您似乎要求的内容可能会变得不直观,而不是 Ruby 方式。您可以通过使用您的表单向自己证明这一点,然后在一些代码中使用它。是的,你会知道你必须以不同的方式做事,但是当你编写代码时,你应该注意它是否感觉像其他 Ruby 方法。如果感觉不一样,那就不要这样做。语言中的一致行为非常重要,并且延伸到我们如何编写自己的代码。

标签: ruby class accessor


【解决方案1】:

我会使用 set_var(var) 而不是您正在尝试做的事情,假设属性编写器可以正常工作。您正在尝试做的事情对于下一个使用您的代码的穷人来说是非标准且不明显的。 (可能只是你自己)如果发送了错误的输入或发生了相当异常的事情,我会抛出异常。

你想要这种行为

Correct
>>temp = object.var = 7
=> 7 

Wrong
>>temp = object.var = 7
=> false

= 运算符应始终返回传递给它的值。 Ruby 使用隐式返回,这在编程语言中并不常见。使用method=() 时仔细检查返回值。

【讨论】:

    【解决方案2】:
    class Test
      def var=(var)
        @var = var
        return true
      end
    end
    
    t1, t2 = Test.new, Test.new
    
    t1.var = 123 # evaluates to 123
    
    # Why is it impossible to return something else:
    t1.var = t2.var = 456
    

    如评论中所述:我相信不可能更改返回值以允许链式分配。无论如何,更改返回值可能会让大多数 Ruby 程序员出乎意料。

    免责声明:我测试了上面的代码,但没有发现任何明确的引用来验证我的陈述。

    更新

    class Test
      def method_missing(method, *args)
        if method == :var=
          # check something
          @var = args[0]
          return true
        else
          super(method, *args)
        end
      end
    
      def var
        @var
      end
    end
    
    t = Test.new
    t.var = 123 # evaluates to 123
    t.does_not_exists # NoMethodError
    

    这似乎真的不可能!上面的示例表明返回值var= 方法完全无关。您无法更改此行为 - 这是 Ruby 分配工作的基本方式。

    更新 2:找到解决方案

    您可以引发异常!

    class Test
      def var=(var)
        raise ArgumentError if var < 100 # or some other condition
        @var = var
      end
      def var
        @var
      end
    end
    
    t = Test.new
    t.var = 123 # 123
    t.var = 1 # ArgumentError raised
    t.var # 123
    

    【讨论】:

    • 异常引发绝对是这样做的方法。
    【解决方案3】:

    此外,根据您的示例,要清理内容,您可以执行以下操作:

    class MyClass
      attr_accessor :var
    end
    
    m = MyClass.new
    m.var = "Test"
    puts m.var # => "Test"
    

    关于您的问题,您是在问是否可以返回您为对象的值设置的值以外的值?

    更新

    查看这个项目:Validatable。它将类似于 ActiveRecord 的验证添加到您的类属性中。

    快速场景:

    class Person
      include Validatable
      validates_presence_of :name
      attr_accessor :name
    end
    
    class PersonPresenter
      include Validatable
      include_validations_for :person
      attr_accessor :person
    
      def initialize(person)
        @person = person
      end
    end
    
    presenter = PersonPresenter.new(Person.new)
    presenter.valid? #=> false
    presenter.errors.on(:name) #=> "can't be blank"
    

    【讨论】:

    • 我需要在保存值之前进行一些检查,这就是我不使用 attr_accessor 的原因。是的,你明白我的问题。一个更好的问题可能是“类应该如何响应无效值?”
    【解决方案4】:

    我知道这是对派对的迟到……

    很难知道你想对课程做什么。您提到要在保存...到数据库之前对其进行检查?文件?

    如果属性值不正确,您希望发生什么?

    通过使用布尔返回,您可以有效地“隐藏”错误(因为您忘记必须检查 setter 的返回值的奇怪事情)。

    虽然你可以按照别人的建议去做

    • 使用验证器插件
    • 引发错误

    你也可以考虑一个isValid?像许多最终被持久化的类一样的方法。然后save就可以检查对象的状态并抛出错误。

    底线:

    • 不要做你发布的事情
    • 请务必抛出错误(通过任何已发布的解决方案)

    帮助推动解决方案的一种方法是考虑首先编写您正在寻找的行为(即BDD)。然后,通过 TDD -ing 使用 RSpec 进一步了解类应该做什么,以获得需要出现以支持所需行为的所需类“合同”。

    【讨论】:

    • 感谢您的回答。 现在看起来确实是个坏主意,所以从那时起我可能已经在 Ruby 中学到了一些东西 ;-)
    猜你喜欢
    • 2011-11-15
    • 2012-01-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-11
    • 2012-04-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多