自由帳

既に知っていることも含めて再アウトプット用に書きます✍️

モデルのロード/バリデーション時に文字列型の属性値をNFC正規化する

NFD正規化されたUNICODE文字列が入力された時に、合字(濁点付きかな)のレンダリングが不自然になる問題を回避する

# app/models/application_record.rb
class ApplicationRecord < ActiveRecord::Base
  include UnicodeNormalizer
end
# frozen_string_literal: true

module UnicodeNormalizer
  extend ActiveSupport::Concern

  TARGET_COLUMN_TYPES = %i[string text].freeze
  EXCEPTIONAL_MODEL_NAMES = %w[Hoge].freeze

  included do
    after_find :normalize_columns
    before_validation :normalize_columns
  end

  private

  def normalize_columns
    # UNICODE正規化の対象外の場合、早期return
    return if EXCEPTIONAL_MODEL_NAMES.include?(self.class.to_s)

    target_attribute_names = self.class.columns_hash.select { |_k, v| TARGET_COLUMN_TYPES.include?(v.type) }.keys
    attributes.each do |k, v|
      next unless target_attribute_names.include?(k)
       # unicode_normalizeメソッドを持たない属性値(nil / serialize)をスキップ
      next unless v.respond_to?(:unicode_normalize)


      send("#{k}=", v.unicode_normalize(:nfc))
    rescue Encoding::CompatibilityError
      next
    end
  end
end