🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Ruby on Rails 6

Ruby on Rails 6は、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

Q&A

解決済

1回答

3444閲覧

Rails ツイート投稿のバリデーション(文字数)について

shawn_709

総合スコア13

Ruby on Rails 6

Ruby on Rails 6は、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

0グッド

0クリップ

投稿2020/12/28 07:59

編集2021/01/04 07:56

ご回答いただけるとありがたいです。よろしくお願いします!

実現したいこと

ツイート機能があるアプリを作成しています。
ツイートを投稿する時、400文字以内というバリデーションを設定しています。
しかし以下の条件のように、入力する文字に合わせて文字カウントを変更したいです。
・全角英数字と日本語は2文字扱い(全て日本語で入力した場合、最大文字数が200文字)
・半角英数字と改行は1文字扱い

現状

model

1#tweet 2validates :content, length: { maximum: 400 }

###試したこと・調べたこと(1) #解決済
改行が2文字でカウントされてバリデーションが正しく動作しない件
を参考に、改行を1文字カウントになるように実装。

model

1#tweet 2class Tweet < ApplicationRecord 3 belongs_to :group 4 belongs_to :user 5 has_one_attached :image 6 7 validates :content, presence: true, unless: :was_attached? 8 validates :content, length: { maximum: 50 } 9 10 def was_attached? 11 self.image.attached? 12 end 13end

の状態でツイート投稿ページで以下のように入力したら投稿できなかった。
改行が2文字カウントされていることを確認できた。

1234567890↵ (↵ = 改行) 1234567890↵ 1234567890↵ 1234567890↵ 123456

そこで以下のように実装した。

model

1#tweet 2(省略) 3 validates :content, presence: true, unless: :was_attached? 4 validates :content, correct_line_break: { maximum: 50 } #この部分を変更 5(省略)

modelsディレクトリ内にvalidatorsディレクトリを作成しその中にcorrect_line_break_validator.rbファイルを作成し、以下を記入。

model

1#validators/correct_line_break_validator.rb 2class CorrectLineBreakValidator < ActiveModel::EachValidator 3 def validate_each(record, attribute, value) 4 text_length = value&.count("^\r\n") || 0 5 break_length = value&.scan("\r\n")&.length || 0 6 correct_text_length = text_length + break_length 7 record.errors.add(attribute, "#{options[:maximum]}文字以内で入力してください") if correct_text_length > options[:maximum] 8 end 9end

以下のエラー文が出た。

ArgumentError in TweetsController#new Unknown validator: 'CorrectLineBreakValidator'

そこでcorrect_line_break_validator.rbをmodelsディレクトリの直下に配置
エラー文がなくなり、実装することができた。

###試したこと・調べたこと(2) #未解決
全角1文字を半角2文字としてカウントするvalidator
文字数制限を3に設定して実装。

model

1#tweet 2validates :content, correct_line_break: { maximum: 3 }

modelsディレクトリ内にlength_with_wide_char_validator.rbファイルを作成

model

1#length_with_wide_char_validator.rb 2class LengthWithWideCharValidator < ActiveModel::EachValidator 3 def validate_each(object, attribute, value) 4 count = 0 5 value.split(//).each do |v| 6 v.bytesize > 1 ? count += 2 : count += 1 7 end 8 end 9end 10 11#tweet 12class Tweet < ApplicationRecord 13 belongs_to :group 14 belongs_to :user 15 has_one_attached :image 16 17 validates :content, presence: true, unless: :was_attached? 18 validates :content, correct_line_break: { maximum: 3 } 19 validates :content, length_with_wide_char: { maximum: 3 } 20 21 def was_attached? 22 self.image.attached? 23 end 24end

以上の記述をして、以下のように入力。

1234 ※半角数字4文字は投稿できない。(maximum: 3が有効だと確認できる)
あいう ※全角3文字でも投稿できる。

以上のことから全角1文字が半角2文字扱いできていないことが確認できた。
他にも

model

1#application_record.rb 2require 'length_with_wide_char_validator' #これを記述 3 4class ApplicationRecord < ActiveRecord::Base 5 self.abstract_class = true 6end

をしたが「全角3文字」でも投稿できてしまいました。全角1文字を半角2文字扱いできるように実装したいです。

以上になります。ご助言いただけると幸いです。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

m.ts10806

2020/12/28 09:05

理解できないままというのはよろしくありませんが、 「以上の2つを見て実装してみましたが」を実際の実装してみた内容を提示されてないと「元記事の意図を汲めているか」「動作できるように組んでいるか」は誰にも判断できません。 あと「全く違う形のやり方があればそちらでも構いません」もあくまで「自身がやってみたコードで特に問題なく動かせていること」が前提でないと単なる作業依頼になるので、表現の使いどころは気を付けたほうが良いです。
guest

回答1

0

ベストアンサー

その制限「入力する文字に合わせて文字カウントを変更したい」は何のために必要ですか?
「何のために」⇐
制限を掛けるのは、それをしないとなにか困ったことが起きるのでそれを防ぐために行います。shownさんは何を警戒しているのでしょう?
全角・半角という用語が使われ始めた頃の (1)「等幅フォントしかない。全角は半角の2倍の場所を取る」(2)「半角は1Byte、全角は2Byteでメモリーも倍使う」という時代でしたら意味がある制限でしたが、プロポーショナルが普通の昨今では(1)は意味ほぼない。全角と半角の数字やアルファベットパット見で区別できます?
メモリー消費もUTF-8な今は2倍ではなく3倍ですので、「2文字扱い」では意味不明。
databaseのfieldのサイズ制限も今はbyteではなく文字数になっている(はず)
改行はOSによりcode、byte数が異なるのが厄介ですが、Webアプリですとネットワークの標準になおして送られてくる(と思ってます。未確認)ので手を打つ必要は無いのでは?
5文字10文字位の制限でしたら1byteの評価違いが問題になるかと思いますが、400文字ということは、「べらぼうに大きい記事は書かないでね」という位の意味でしょうから、、、、、

ということで私としては
「その辺りは気にしない」
をお薦めします。

「何のために」が実はあるのでしたらそれを開示いただき「なるほど」と感じたら取り組み直します。

追記
def validate_each の最後の式が each なので、その結果は必ず "falseではない" となります。
例えば value="1あ"だとすると、["1", "あ"]が返ります。
ので、最後に count < 4 を入れましょう。

ただ、これ私の好みではないです。
count = value.size + value.gsub(/[\t-~]/,'').size
文字数を数え、さらに半角を除いてから数え直して足す。

\r\nを一文字扱いにしたいなら value = value.gsub(/\r\n/,"\n") してから。
[\t-~] は 0x09から0x7eまでの文字。画面に入力ならこの範囲しか入らないのでこれで良いかと。

投稿2020/12/29 00:25

編集2021/01/04 08:24
winterboum

総合スコア23567

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

shawn_709

2021/01/04 01:58

ご指摘ありがとうございます。返事が遅くなりすみません。 意図としましては、メモリなどではなく、大文字と小文字の文字量のことを意識したいです。 英語で投稿すると、漢字を使う日本語より文字数が多くなります。 そこでバリデーションで400文字制限にして小文字を1文字、日本語を2文字扱いにすることで、同じ内容で同じぐらいの文字量を投稿することができると考えたからです。 Twitterも同じような設定なので、真似をしたいと思っています。 説明足らずで申し訳ありません。
winterboum

2021/01/04 04:40

なるほど。 で何が問題なのでしょう。解決策は見つけて実装しているということは? よくわからない というのは うまく行っているかどうかわからない のかなぜそれで良いのかわからないのか どちらでしょう
shawn_709

2021/01/04 07:56

すみません。修正したので、ご確認いただけるとありがたいです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問