Custom Rails Validator
Vừa rồi mình có gặp một yêu cầu là khi tạo name cho group thi không được có các từ bậy bạ, các từ thô tục trong đó. Nên mình đã tìm hiểu và viết một cái validation cho riêng trường hợp này. 1. Các class kế thừa khi tạo 1 class validator ActiveModel::Validator || ...
Vừa rồi mình có gặp một yêu cầu là khi tạo name cho group thi không được có các từ bậy bạ, các từ thô tục trong đó. Nên mình đã tìm hiểu và viết một cái validation cho riêng trường hợp này.
1. Các class kế thừa khi tạo 1 class validator
ActiveModel::Validator || ActiveModel::EachValidator
ActiveModel::Validator thì sẽ phải hiện thực function validate(record) nhận param là 1 record, và mình thực hiện validate trên record này. Khi hiện thực xong thì mình gọi validate bằng hàm validates_with.
Ví dụ:
class MyValidator < ActiveModel::Validator
def validate(record)
unless record.name.starts_with? 'X'
record.errors[:name] << 'Need a name starting with X please!'
end
end
end
class Person
include ActiveModel::Validations
validates_with MyValidator
end
_
ActiveModel::EachValidator thì phổ biến hơn, mình sẽ phải hiện thực hàm validate_each với 3 param (record, attribute, and value), đối với cách này thì mình có thể chỉ định attribute cần validate và biết được value của attribute đó. Và khi hiện thực xong mình có thể validate bằng method validates.
Ví dụ:
class EmailValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
unless value =~ /A([^@s]+)@((?:[-a-z0-9]+.)+[a-z]{2,})z/i
record.errors[attribute] << (options[:message] || "is not an email")
end
end
end
class Person < ApplicationRecord
validates :email, presence: true, email: true
end
2. Custom Validators
Sau khi mình tìm hiểu 1 chút về các class Validate thì quay trờ lại với yêu cầu đầu bài: khi tạo name cho group thi không được có các từ bậy bạ, các từ thô tục trong đó. Yêu cầu ở đây có attribute là field name và value của nó không được chứa các từ bậy bạ, chắc mình sẽ dùng class ActiveModel::EachValidator
Mình sẽ tạo class ở trong folder app/validators, nếu chưa có thì mình tạo folder validators
# app/validators/blacklist_validator.rb
# Validate list of words that can not be use in name field
class BlacklistValidator < ActiveModel::EachValidator
def validate_each record, attribute, value
converted_value = value.unicode_normalize(:nfkc).downcase
plain_text = Nokogiri::HTML(converted_value).text
BlackWord.pluck(:word).each do |word|
if plain_text.include? word
message = options[:message] || :words_in_blacklist
record.errors.add attribute, message, word: plain_text
break
end
end
end
end
Ở trên các "black words" mình được lưu trong bảng BlackWord của database, Nếu bạn muốn lưu các "black words" này vào file và đọc lên để validate thì bạn có thể tham khảo cách làm của trang sau đây: http://womanonrails.com/custom-rails-validators
Mình sẽ nói 1 chút về 2 hàng này
1. converted_value = value.unicode_normalize(:nfkc).downcase 2. plain_text = Nokogiri::HTML(converted_value).text
Hàng thứ nhấtđể mình tránh các dùng các kí tự unicode để qua mặt compare thì mình dùng hàm unicode_normalize với Unicode normalization là NFKC để convert các kí tự tiếng Nhật 1 byte (Hankaku - 半角) thành 2 bytes (Zenkaku - 全角). Hình như nó còn tác dụng khác mà mình không biết