[Rails]メンション
はじめに
正規表現の応用編としてコメントにユーザーのメンションを追加していきます。
環境
Rails 7.0.7
ruby 3.2.1
流れ
ユーザー、コメントなどを作成された前提で進めていきます。
- ユーザー名を正規表現でマッチする
- ユーザー名を抽出するヘルパーメソッドを作成する
ユーザー名を正規表現でマッチする
@username
のように投稿やコメントなどのテキスト内からメンションされたユーザーを検出するために正規表現を使用します。
ユーザー名のフォーマットにバリデーションをかけた場合、それに沿って正規表現を設定します。
例えば、ユーザー名がロマ字、数字、ハイフンとアンダーバーで構成される場合、/@([a-zA-Z0-9_]+)/
の正規表現になります。
今回はユーザー名に制限を設けてないため/@(\w+)/
を使います。
irb(main):001:0> "@username @user user".match(/@(\w+)/)
=> #<MatchData "@username" 1:"username">
複数のユーザーがメンションされる場合、ユーザー名の配列を取得できると便利ですね。
irb(main):002:0> "@username @user user".scan(/@(\w+)/)
=> [["username"], ["user"]]
irb(main):003:0> "@username @user user".scan(/@(\w+)/).flatten
=> ["username", "user"]
バリデーション用正規表現集を参考にしてみてください。
このサイトを正規表現のチェックにおすすめです!ユーザー名を抽出するヘルパーメソッドを作成する
コメントの作成や更新の直前にメンションがあるかどうかをチェックするためにヘルパーメソッドを作成します。
module MentionHelper
def parse_at_mentions(comment_body)
parsed_body = comment_body.dup
comment_body.scan(/@(\w+)/).each do |mention|
username = mention[0]
user = User.find_by(name: username)
if user
replacement = view_context.link_to("@#{mention[0]}", user_path(user))
parsed_body.gsub!("@#{mention[0]}", replacement)
end
end
parsed_body
end
end
parse_at_mentions
メソッドは、コメントのボディー内のメンション(例: @username
)を検出し、それらをリンクに置き換えます。
.dup
メソッドを使って、コメントのコピーを作成します。これは、parsed_body
への変更がオリジナルのコメントに影響を与えないようにするためです。
正規表現を使用しメンションの配列を取得します。
検出した各メンションに対して、@
記号を取り除いたユーザー名をusername
変数に格納します。
そして、User.find_by
を使用して、ユーザー名(name
)を指定して検索します。ユーザーが存在しない場合エラーを起こさないようにfind_by
を使います。
指定されたユーザー名のユーザーが見つかった場合、view_context.link_to
を使用して、置換用の文字列(replacement
)を作成します。
view_context
はビューファイル以外のファイルででリンクを生成するヘルパーメソッドです。
これは、メンションに@
マークとユーザー名を含むリンク(user_path(user)
)を生成します。
最後に、メンションを、gsub!
を使用して replacement
文字列に置き換えます。これにより、メンションがクリック可能なリンクに置き換えられます。
コメントコントローラー内にヘルパーメソッドを呼び出す
新規作成と更新のタイミングでメンションがあるかどうかをチェエクします。
@comment.body = parse_at_mentions(@comment.body)
ユーザーをメンションしてみます。
ユーザーが存在する場合、リンクを作成されることを確認します。
redcarpet
gemを使っているため、リンクにCSSを適用されています。
終わりに
正規表現のパターンマッチでユーザーをメンションすることができました!
今回はメンションを試したかっただけなので簡単にまとめました。
メンションされたユーザーに通知を送ることができるともっと便利かもしれないですね。
def notifiy_mentioned_users
mentioned_users = comment_body.scan(/@(\w+)/).flatten
mentioned_users.each do |user|
next if user.id == comment.user.id
Notification.create(
sender_id: comment.user.id,
recipient_id: user.id,
notifiable: self)
end
end
調べたらメンションのgemもありました。
Discussion