运行测试时的弃用问题:可比较#==将不再有#< =>的救援例外

时间:2016-01-04 03:54:38

标签: ruby-on-rails ruby ruby-on-rails-4

当我运行规范时,我收到以下错误:

.rbenv/versions/2.2.4/lib/ruby/gems/2.2.0/gems/activemodel-4.2.5/lib/active_model/validations/callbacks.rb:26: warning: Comparable#== will no more rescue exceptions of #<=> in the next release.
.rbenv/versions/2.2.4/lib/ruby/gems/2.2.0/gems/activemodel-4.2.5/lib/active_model/validations/callbacks.rb:26: warning: Return nil in #<=> if the comparison is inappropriate or avoid such comparison.

唯一的地方我&lt; =&gt;在我的一个模特中:

class PostCategory
  CONTEXTS = %w(regular)

  module Collections
    def all
      @all ||= AppConfig.post_categories.
       map { |c| new(c) }.
       sort
    end

    def in_context(context)
      @all.select { |e| e.in_context?(context) }
    end
  end

  extend Collections

  include Comparable

  attr_reader :name, :image_id

  def initialize(hash)
    @name          = hash.fetch('name')
    @image_id      = hash.fetch('image_id')
    @contexts      = hash.fetch('contexts', CONTEXTS)
  end

  def <=>(other) 
    @name <=> other.name
  end
end

我尝试在“def&lt; =&gt;(other)”之后添加下面的nil但是会产生排序问题。

2 个答案:

答案 0 :(得分:4)

由于this Ruby issue中讨论的更改,会出现此警告。它的要点是:当一个类包含Comparable并调用其==方法时,Comparable#==依次调用<=>方法。到目前为止,在Ruby版本中,如果<=>引发错误,==将吞下错误并返回false。这很好,因为开发人员不希望==引发错误,但这很糟糕,因为它可以隐藏<=>的问题。在将来的版本中,==将不再吞下<=>引发的错误,因此会出现警告。

如果查看堆栈跟踪中提到的行上的Rails source,您可以看到错误的来源:

define_callbacks :validation,
                 terminator: ->(_,result) { result == false },
                 # ...

在您的情况下,result是PostCategory类的一个实例。当上面的第二行调用result == false时,您的<=>方法会调用false.name,这会引发错误(因为false不响应name)。< / p>

解决方案是Comparable docs说:

  

如果其他对象无法比较,则<=>运算符应返回nil

像这样修改你的方法:

def <=>(other)
  return unless other.respond_to?(:name)
  @name <=> other.name
end

或者,如果您希望PostCategory的实例仅与PostCategory的其他实例(而不是响应name的任何实例)相比较:

def <=>(other)
  return unless other.is_a?(self.class)
  @name <=> other.name
end

答案 1 :(得分:0)

没有足够的细节来准确诊断问题,但以下是两个可以帮助您解决问题的提示。

•警告告诉您发动机救出<=>时发生异常。好的,为了避免它,我们首先应该了解它是什么:

def <=>(other) 
  @name <=> other.name
rescue => e
  puts e.inspect # or even e.backtrace
end

•现在,当你知道确切的问题是什么时,你可以优雅地处理它:

def <=>(other) 
  return nil unless @name.is_a?(String) && other.name.is_a?(String)
  @name <=> other.name
end

希望它有所帮助。