rista’s blog

株式会社リスタの技術?ブログ

ActiveRecordで保存時に自動で全角→半角等のノーマライズ処理をする

テキストを入力するフォームを作るとみなさんホントにいろんな文字を入れてくれます。
全角英数字やら各種記号、不要な空白・改行から、『-』(全角ハイフンマイナス)や『 』(EM SPACE)のようなややこしいものまで。

あまり無秩序だと見た目的にもよろしくないし、データ検索する時にパッと見つけられなくて困ります。
あとSJISに変換するときにも困ります。(EXCEL CSVもうイヤだー)

で、ある時カッとなって俺のDBに変なデータはいれさせねえ!と、保存時に自動でノーマライズする処理を作りました。

実装

こういうConcernを作ってます。

module StringNormalizable
  extend ActiveSupport::Concern

  # validate前にかってに変換
  included do
    before_validation :normalize_changed_attributes
  end

  private

  # string, textで変更のあったカラムを変換
  def normalize_changed_attributes
    changed_attributes.each do |key, _|
      self[key] = normalize(self[key]) if self[key].is_a?(String) && self[key].present?
    end
  end

  def normalize(str)
    str.strip                                    # 前後の空白除去
       .tr("0-9a-zA-Z  ()-−", "0-9a-zA-Z  ()-") # 全角英数字を半角に
       .gsub("\t", ' ').gsub(/ +/, " ")          # 空白を単一半角スペースに統一
       .gsub("\r\n", "\n").gsub("\r", "\n")      # 改行を\nに統一
       .unicode_normalize(:nfkc)                 # ㌧㌦とか
  end
end

使ってみる

例えばこういうモデルに対して、

bin/rails g model articles title:string body:text
bin/rake db:migrate

includeしてやると、

class Article < ApplicationRecord
  include StringNormalizable
end

レコード作成、更新時に自動でノーマライズしてくれます。

pry(main)> article = Article.create(title: "  ㌧\t\t㌦  ", body: "(AB)\r\n123")
pry(main)> article.title
=> "トン ドル"
pry(main)> article.body
=> "(AB)\n" + "123"

pry(main)> article.title = "㈱ Ⅲ"
=> "㈱ Ⅲ"
pry(main)> article.save
pry(main)> article.title
=> "(株) III"

実際に使ってみて

処理としては特に問題なく動いています。

ただユーザーが入力するところでは全角系などは意識的に使ってる人も多く、『勝手に変換しないでほしい』という声が多かったので、今はもっと限定的な処理にしています。

まとめ

EXCEL CSV(SJIS)の取り込み・ダウンロード機能が辛い。