2012-10-08 47 views
0

我重寫了ActiveRecord中的屬性訪問器,將格式爲「hh:mm:ss」的字符串轉換爲秒。這裏是我的代碼:提高屬性訪問器中的錯誤覆蓋?

class Call < ActiveRecord::Base 
    attr_accessible :duration 

    def duration=(val) 
    begin 
     result = val.to_s.split(/:/) 
      .map { |t| Integer(t) } 
      .reverse 
      .zip([60**0, 60**1, 60**2]) 
      .map { |i,j| i*j } 
      .inject(:+) 
    rescue ArgumentError 
     #TODO: How can I correctly report this error? 
     errors.add(:duration, "Duration #{val} is not valid.") 
    end 
    write_attribute(:duration, result) 
    end 

    validates :duration, :presence => true, 
         :numericality => { :greater_than_or_equal_to => 0 } 

    validate :duration_string_valid 

    def duration_string_valid 
    if !duration.is_valid? and duration_before_type_cast 
     errors.add(:duration, "Duration #{duration_before_type_cast} is not valid.") 
    end 
    end 
end 

我想在驗證過程中有意義地報告此錯誤。代碼示例中包含了前兩個想法。

  1. 添加到存取器覆蓋內部的錯誤 - 但我不確定它是否是一個很好的解決方案。
  2. 使用驗證方法duration_string_valid。檢查其他驗證是否失敗並報告duration_before_type_cast。在這種情況下,duration.is_valid?不是一個有效的方法,我不確定如何檢查持續時間是否已通過其他驗證。
  3. 我可以在duration =(val)中設置一個實例變量,並在duration_string_valid之內報告它。

我很想反饋一下這是否是一種很好的方法來執行此操作,以及如何改進錯誤報告。

回答

5

第一個,清理你的代碼。將字符串移到持續時間轉換器到服務層。裏面的lib/目錄中創建StringToDurationConverter

# lib/string_to_duration_converter.rb 
class StringToDurationConverter 
    class << self 
    def convert(value) 
     value.to_s.split(/:/) 
     .map { |t| Integer(t) } 
     .reverse 
     .zip([60**0, 60**1, 60**2]) 
     .map { |i,j| i*j } 
     .inject(:+) 
    end 
    end 
end 

,添加自定義DurationValidator驗證

# lib/duration_validator.rb 
class DurationValidator < ActiveModel::EachValidator 
    # implement the method called during validation 
    def validate_each(record, attribute, value) 
    begin 
     StringToDurationConverter.convert(value) 
    resque ArgumentError 
     record.errors[attribute] << 'is not valid.' 
    end 
    end 
end 

而且你的模型將看起來像這樣:

class Call < ActiveRecord::Base 
    attr_accessible :duration 

    validates :duration, :presence => true, 
         :numericality => { :greater_than_or_equal_to => 0 }, 
         :duration => true 

    def duration=(value) 
    result = StringToDurationConverter.convert(value) 
    write_attribute(:duration, result) 
    end 
end 
+0

偉大的答案。但是我們不能在持續時間上有兩個相互衝突的驗證器 - DurationValidator和NumericalityValidator。 Duration是一個可由DurationValidator驗證的散列。我們不能同時使用數字驗證器來驗證持續時間爲秒數。 – Salil