2016-01-25 34 views
0

之間不必要的重複,我有責任屬性驗證兩類:減少兩班

class NameValidator < ActiveModel::EachValidator 
    def validate_each(record, attribute, value) 
    message = options.fetch(:message, I18n.t('errors.attributes.name.invalid')) 
    record.errors[attribute] << message unless NameValidator.valid_name?(value) 
    end 

    def self.valid_name?(name) 
    name =~ /\A[a-z][\w\p{Blank}]+\z/i 
    end 
end 

,第二個

class EmailValidator < ActiveModel::EachValidator 
    def validate_each(record, attribute, value) 
    message = options.fetch(:message, I18n.t('errors.attributes.email.invalid')) 
    record.errors[attribute] << message unless EmailValidator.valid_email?(value) 
    end 

    def self.valid_email?(email) 
    email =~ /\[email protected]+\..+\z/i 
    end 
end 

,他們基本上是相同的。我應該使用受保護的實用方法從一個類繼承它們嗎?

+0

恕我直言,這將是一個很好的問題http://codereview.stackexchange.com/ –

回答

1

可以簡化這一進一步

class PatternValidator < ActiveModel::EachValidator 
    def validate_each(record, attribute, value) 
    message = options.fetch(:message) || kind 
    record.errors[attribute] << message unless value =~ validation_pattern 
    end 
end 

class NameValidator < PatternValidator 
    def validation_pattern; /\A[a-z][\w\p{Blank}]+\z/i end 
end 

class EmailValidator < PatternValidator 
    def validation_pattern; /\[email protected]+\..+\z/i end 
end 

EachValidator有#因此它會添加:name或:email作爲失敗消息,除非被覆蓋。然後,您可以讓國際化域名按照rails guide中記錄的標準級聯進行查找。

+0

這是美麗的,謝謝。 –

0

爲了清晰起見,請將它們保持分開。這些方法足夠小,因爲抽象的混淆會讓事情變得更多而不是更不明顯。

如果您開始有3,4,5,6個或更多類似的驗證器,其中此模式開始顯而易見,則添加抽象可能會使其更易於理解,更改,依賴或移除。

+0

這就是事情,有更多的驗證工具在我的項目中提取。郵政標題驗證,例如.. –

+0

如何使用內置的ActiveModel格式驗證程序? http://edgeguides.rubyonrails.org/active_record_validations.html#format – Glenn

+0

不,我提取這個驗證器,以避免這種情況 –

1

只有當一個類明顯是另一個類的特例時才使用繼承。在你的例子中,這兩個類似乎是平等的。然後,使用mixin,而不是繼承。

代碼中反對通用validate_each的一個小點是NameValidator.valid_name?EmailValidator.valid_email?的硬編碼。你需要在兩個類中使用的通用代碼中使它們相同。首先,你不需要給出不同的名字valid_name?valid_email?。他們的差異應該通過使用相應的類來吸收。使用通用名稱。其次,您不需要對接收器進行硬編碼。相反,使用self.class。但不是使用類方法來實現,而是使用實例方法。

module ValidatorModule 
    def validate_each(record, attribute, value) 
    message = options.fetch(:message, I18n.t("errors.attributes.#{attribute}.invalid")) 
    record.errors[attribute] << message unless valid?(value) 
    end 
end 

class NameValidator < ActiveModel::EachValidator 
    include ValidatorModule 
    def attribute; "name" end 
    def valid?(value); value =~ /\A[a-z][\w\p{Blank}]+\z/i end 
end 

class EmailValidator < ActiveModel::EachValidator 
    include ValidatorModule 
    def attribute; "email" end 
    def valid?(value); value =~ /\[email protected]+\..+\z/i end 
end 

如果你認爲驗證總是有一個簡單的正則表達式完成,你可以走一步:

module ValidatorModule 
    def validate_each(record, attribute, value) 
    message = options.fetch(:message, I18n.t("errors.attributes.#{attribute}.invalid")) 
    record.errors[attribute] << message unless value =~ validation_pattern 
    end 
end 

class NameValidator < ActiveModel::EachValidator 
    include ValidatorModule 
    def attribute; "name" end 
    def validation_pattern; /\A[a-z][\w\p{Blank}]+\z/i end 
end 

class EmailValidator < ActiveModel::EachValidator 
    include ValidatorModule 
    def attribute; "email" end 
    def validation_pattern; /\[email protected]+\..+\z/i end 
end