LoginSignup
3
0

More than 5 years have passed since last update.

ActiveRecordで全てのString型カラムの文字コードをバリデーションする。

Last updated at Posted at 2017-07-28

:ice_cream: :relaxed: :shaved_ice:

レガシープロジェクトでは、DBにShift_JISやEUC-JPなど、UTF-8文字コードを使っていることがあります。

でも、我らがActiveRecordはDBにRead/Writeする時に文字コードを自動変換してくれるので、文字化けが起こることはありません!

しかし、もちろん、Shift_JISに含まれない文字を保存することはできないので、
フォームに「⛄️」を入力してしまったときには、バリデーションエラーにしなければなりません。

カスタムバリデータ

そこで、文字コードをチェックするためのバリデータを作りました。

文字コードチェックは、全ての文字列型カラムで行わなければなりませんが、
いちいちカラムを指定するのは面倒なので、自動で検出するようにしています。

class DatabaseEncodingValidator < ActiveModel::Validator
  def initialize(options, &block)
    super(options, &block)
    @encoding = options[:encoding]
    raise "'encoding' parameter is required" if @encoding.blank?
  end

  def validate(record)
    string_attributes(record).each do |attribute|
      value = record.read_attribute_for_validation(attribute)
      next if value.blank?

      begin
        value.encode(@encoding)
      rescue Encoding::UndefinedConversionError => e
        record.errors.add(
          attribute, :encoding_incompatible_char,
          encoding: @encoding.to_s.upcase,
          error_char: e.error_char
        )
      end
    end
  end

  private

  def string_attributes(record)
    record.class.columns.select { |c| c.type == :string }.map(&:name)
  end
end

使い方

ApplicationRecordに仕込めば、全モデルで文字コードチェックが行われるようになります。

# app/models/application_record.rb
class ApplicationRecord < ActiveRecord::Base
  ...

  # 全てのモデルクラスで、全ての文字列型カラムの、バリデーションを行う
  validates_with DatabaseEncodingValidator, encoding: Encoding::Shift_JIS
end

メッセージを日本語化するために、ja.ymlにも追加します。

# config/locale/ja.yml
---
ja:
  errors:
    messages:
      encoding_incompatible_char: に%{encoding}では使用できない文字「%{error_char}」が含まれています

補足と課題

DBの文字コードは ActiveRecord::Base.connection_config[:encoding]でも取得できるのですが、encoding: Encoding::Shift_JISと明示するようにしています。例えば Oracle DB では文字コードが "sjis" (JA16SJISTILDE) でも、実際にはCP932の文字を保存することができたりするためです。

また、カラムが文字列型かどうかを record.class.columns で判定していますが、全てのケースでうまく動くかは自信がありません。もっと一般的な判定方法があるかもしれません。

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0