🐦

RailsでTwitter風のハッシュタグ機能を実装してみる

2020/12/12に公開

はじめに

Twitter のようなハッシュタグ機能を Rails を使って実装してみます。
本記事では Rails TutorialSample apps をベースにハッシュタグ機能を追加しています。

ハッシュタグ機能の実装

routing の追加

パラメータで渡されたハッシュタグに一致する投稿を検索する場所として、search アクションを追加します。

routes.rb
resources :microposts, only: [:create, :destroy] do
  collection do
    get :search
  end
end

ハッシュタグの抽出・リンク化

次に投稿に含まれるハッシュタグを抽出してリンクにする処理を追加します。
ハッシュタグの抽出は正規表現を使っています。
抽出条件は以下のようにしました。(Twitter のハッシュタグはもっと複雑だと思いますが、今回は簡易にしました。正規表現は苦手なのでこれが限界でした。

  • #が先頭 or 空白文字の後 or 全角空白の後 にあること
  • #の後に 1 文字以上続くこと
  • #は最後 or 空白文字 or 全角空白 で終わること
microposts_helper.rb
module MicropostsHelper
  HASHTAG_REGEX = /(?<=\s| |^)#.+?(?=( |\s|$))/.freeze

  def link_to_hashtag(content)
    content.gsub(HASHTAG_REGEX) { |hashtag| link_to(hashtag, search_microposts_path(q: hashtag)) }
  end
end

View に表示できるようにする

上で追加したlink_to_hashtagを View で使っても以下のようにエスケープされてリンクにはなりません。

リンクがエスケープされた投稿

ですので、一手間加えます。html_with_link_to_hashtagというメソッドを追加しています。

microposts_helper.rb
module MicropostsHelper
  HASHTAG_REGEX = /(?<=\s| |^)#.+?(?=( |\s|$))/.freeze

  def link_to_hashtag(content)
    content.gsub(HASHTAG_REGEX) { |hashtag| link_to(hashtag, search_microposts_path(q: hashtag)) }
  end

  def html_with_link_to_hashtag(content)
    # 下の処理でaタグのhref属性を表示できるようにするので、ハッシュタグのaタグ以外が有効にならないように、エスケープしておく。
    html_escaped_content = h(content)
    content_with_hashtags = link_to_hashtag(html_escaped_content)
    # aタグのhref属性以外はサニタイズします。
    # aタグのhref属性はエスケープされずに表示されるようになります。
    sanitize content_with_hashtags, tags: ['a'], attributes: ['href']
  end
end
_micropost.html.erb
<%= html_with_link_to_hashtag(micropost.content) %>

これでハッシュタグのリンクだけが有効な状態で表示されます。

リンクが有効な状態で表示された投稿

ハッシュタグを検索できるようにする

最後にハッシュタグを使って検索できるようにします。

最初に追加した search アクションに検索する処理を追加します。

microposts_controller.rb
def search
  @microposts = Micropost.search_content_for(params[:q]).paginate(page: params[:page])
end
micropost.rb
scope :search_content_for, ->(query) { where('content like ?', "%#{query}%") }
search.html.erb
<div>
  <ol class="microposts">
    <%= render @microposts %>
  </ol>
  <%= will_paginate @microposts %>
</div>

これで完成です!
※見た目は適当です…!

ハッシュタグ検索の結果の画面

あとがき

  • 本記事では自前で実装してみましたが、twitter-text という gem があったので、ハッシュタグの抽出・リンク化はこれを使ってみても良さそうでした。

  • セキュリティを考慮してhtml_with_link_to_hashtagでエスケープ・サニタイズをしていますが、もう少しスッキリした処理にしたい気持ちです。 🤔

Discussion