私は「ショートショート」という短編小説の投稿サイトの運営をしているのですが、ある日ユーザーの方からこんなお問い合わせがありました。
「3000字以内で書いているのに投稿できません」と。
(ちなみにその投稿画面には、javascriptで文字数をカウントして表示しています。)
そのカウントを見る限り3000字以内であるにも関わらず、文字数オーバーのバリーデーションエラーが出てしまうようです。
Rails側では以下のようにvalidationを書いていました。
1 2 3 4 |
class Post < ApplicationRecord validates :content, length: {maximum: 3000} #以下省略 end |
これは文字通り、contentは最大3000字ですよーというバリデーションです。
なので、3000字以内であれば本来エラーが出るはずはありません。
原因を調べてみたところ、改行文字が原因であることがわかりました。
投稿ページでは、改行をすると文字数のカウントが1増えます。(これはjavascript側の話です)
一方、Rails側では改行文字が \r\n となっており、それが2文字として換算されていることが分かりました。(\はバックスラッシュのことです。)
これが原因でエラーが起きてしまうことが判明したので、それに対処する方法を考えたのですが、対処方法は大きく分けて2つあるのかなと思います。
まず1つ目はjavascriptの方をいじる。
現在、改行されるたびに文字数カウントが1増えるようにユーザーに表示していますが、これをどうにかして2増えるようにするという方法です。
しかし、これだとユーザーからすると違和感があります。改行したら2文字増えるというのは自然ではありません。
なので今回はRails側で対処することにしました。すなわち、 \r\n という改行文字を一文字として換算するように変えました。
既存のバリデーションではうまく対応できそうになかったので、独自のバリデーションを作ってそれを適用させました。
ちなみに独自のバリデーションの作り方は非常に簡単です。
以下の例の上半分は既存のバリデーションを使う場合、下半分が独自のバリデーションを使う場合です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
#Railsで用意されているバリデーションを使う場合 #validatesの後にカラム名と適用するバリデーションを書いていく。 #この例の場合だと、lengthというのがもともとRails側が用意してくれているものです。 #ただしこの場合だと先ほども述べたとおり改行文字が2文字換算されてしまいます。 class Post < ApplicationRecord validates :content, length: {maximum: 3000} end #一方、独自のバリデーションを指定する場合は、validateの後に独自のバリデーションを書いていきます #validatesではなくvalidateなので注意してください。 #独自のバリデーションの定義も書く必要があります。 class Post < ApplicationRecord validate content_length def content_length content_for_validation = content.gsub(/\r\n/,"a") if content_for_validation.length > 3000 errors.add(:content, "は3000文字以内で入力してください。") end end end |
独自のバリデーションの方では、contentに含まれる \r\n という改行文字を一旦aという文字に変換しています。
こうすることでもともと2文字として換算されていたものが1文字として換算されます。(aという文字を選んだ理由は特にありません。)
また、保存されるcontentの中身を直接変えるのではなく、content_for_validationという変数に入れることで、バリデーションが通った後はちゃんと元のcontent(改行文字がちゃんと入っているcontent)が保存されるようになっています。
これで完成です!
もちろん、このバリデーションを他のmodelでも使うよーという場合や、独自のバリデーションをいっぱい使うよーという場合には、それ用のモジュールを作ってもいいかもしれません。
以上でRailsで改行文字が2文字と換算されてバリデーションエラーが出た時の対処方法の説明を終わります!