提供给Rails验证功能的参数有时会丢失

时间:2012-06-20 19:52:29

标签: ruby ruby-on-rails-2

我遇到了一个非常奇怪的问题,验证器只有在包含此模块时才会使用。运行某些测试时,我会按预期得到验证失败。但是,错误不是RecordInvalid;相反,它是ArgumentError: wrong number of arguments (0 for 1),在这种情况下完全没有意义。

这是包含验证码的mixin。

module AccountStateIntegrityMixin
  def self.included(base)
    base.class_eval do
      base.validate :account_state_integrity
    end
  end

  def account_state_integrity
    history = self.account_state_history_entries.order("start ASC").all
    return if history.blank?

    vald_methods = [
      :end_with_nil,
      :no_self_transitions,
      :no_overlaps_or_gaps
    ]

    vald_methods.each do |m|
      p "history = #{history.inspect}"
      if !self.send(m, history)
        return
      end
    end
  end

  def no_self_transitions(*args)
    p "no_self_transitions"
    p args
    history = args[0]
    # I want everything except the last element. I want to compare each
    # element with its successor
    history[0..-2].each_with_index do |entry, i|
      next_elem = history[i+1]
      if entry.account_state_id == next_elem.account_state_id
        self.errors.add(:account_state_history, "has a self-transition entries with " +
                        "IDs #{entry.id} and #{next_elem.id}")
        self.errors.add(:no_self_transitions, "violated")
        return false
      end
    end

    true
  end

  def end_with_nil(*args)  # ArgumentError was being thrown here
    p "end_with_nil"
    p args
    history = args[0]
    last = history.last  # with debugging statement, NoMethodError here

    if last.end != nil
      self.errors.add(:account_state_history, "is missing a nil ending. Offending " +
                      "entry has ID = #{last.id}")
      self.errors.add(:end_with_nil, "violated")
      return false
    end

    true
  end

  def no_overlaps_or_gaps(*args)
    p "no_overlaps_or_gaps"
    p args
    history = args[0]
    # Again, everything except the last element
    history[0..-2].each_with_index do |entry, i|
      next_elem = history[i+1]
      if next_elem.start != entry.end
        self.errors.add(:account_state_history, "has an overlap or gap between " +
                        "entries with IDs #{entry.id} and #{next_elem.id}")
        self.errors.add(:no_overlaps_or_gaps, "violated")
        return false
      end
    end

    true
  end
end

您可能已经注意到,我添加了一些打印语句来帮助我在测试期间调试某些变量的状态。我还更改了方法参数以获取可变数量的参数,因此我可以看到实际发送到方法的内容。 (注1:由于进行了这些更改以使事情更易于调试,因此错误已更改为MethodError: You have a nil object when you didn't expect it!。注意2:方法签名更改主要包括def method(history) -> def method(*args)和设置{{ 1}})以下是这些语句的结果:

history = args[0]

鉴于"history = [#<AccountStateHistoryEntry id: 1007, account_id: 684, ... >]" "end_with_nil" [[#<AccountStateHistoryEntry id: 1007, account_id: 684, ... >]] "history = [#<AccountStateHistoryEntry id: 1007, account_id: 684, ... >]" "no_self_transitions" [[#<AccountStateHistoryEntry id: 1007, account_id: 684, ... ]] "history = [#<AccountStateHistoryEntry id: 1007, account_id: 684, ... >]" "no_overlaps_or_gaps" [[#<AccountStateHistoryEntry id: 1007, account_id: 684, ... >]] "history = [#<AccountStateHistoryEntry id: 1007, account_id: 684, ... >, <AccountStateHistoryEntry id: 1008, account_id: 684, ... >]" "end_with_nil" [[#<AccountStateHistoryEntry id: 1007, account_id: 684, ... >, #<AccountStateHistoryEntry id: 1008, account_id: 684, ... >]] "end_with_nil" [] 方法的内容,我看不出account_state_integrity突然错过end_with_nil中明确传递给它的参数的任何理由。

如果这个错误是由一个已经修复过的遗留错误引起的,我会说清楚:我们使用旧版本的Ruby(1.8.7)和Rails(2.3.14)。我们将在不久的将来迁移到更新版本的Ruby和Rails。

编辑:单元测试的完整堆栈跟踪输出

account_state_integrity

1 个答案:

答案 0 :(得分:2)

在向属性添加错误时,问题是ActiveRecord::Errors.add

end_with_nil不是属性。您需要使用ActiveRecord::Errors.add_to_base代替。

您也应该更正其他errors.add行。

发生的事情是Shoulda(您的测试运行员)注意到名为end_with_nil的属性存在错误,并且它会尝试为失败的测试生成一条消息,如下所示:

"You have an error for end_with_nil: value was #{record.end_with_nil}, 
 when it should have been xyz".

..因此end_with_nil在没有参数的情况下被调用,一切都失败了。