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