Redmineの「いいね」機能(Reaction機能)の仕組みをGeminiに聞いてみた
Redmine Advent Calendar 2025
今回の記事は下記の「Redmine Advent Calendar 2025」の12/8の記事として作成しました。
12/7はHaru Iidaさんの「DockerでRedmineを楽々構築・運用する方法」という記事でした。
急に寒くなり今年も残り数日です。今年もRedmine Advent Calendarの時期になりました。Advent Calendarに参加してRedmine界隈を盛り上げていけたらと思っています。
Redmineの「いいね」機能(Reaction機能)の概要
Feature #42630 「Introduce reaction feature to issues, notes, news, and forums」でKatsuya HIDAKA さんが提供されたパッチファイルがRedmine6.1.0で組み込まれて「いいね」機能(Reaction機能)が実装されました。
Add-reaction-feature.patchの概要
Add-reaction-feature.patchがRedmine6.1.0で実装された「いいね」機能(Reaction機能)の元になっているパッチファイルになります。(実際に実装された機能は、多くの議論を重ねて改修が行われているため、このパッチファイルのままではありませんが、このパッチファイルを読むことで大まかに「いいね」機能の仕組みを理解することができます。)
Geminiにパッチの処理内容を聞いてみた
最近、急激に賢くなったと言われているGeminiに上記のAdd-reaction-feature.patchで各ファイルがどのような処理をしているのかを聞いてみました。
質問内容
https://www.redmine.org/attachments/34281
上記の情報はredmineにreactionボタンが導入されたプログラムソースが記録されています。
具体的にどのような処理をしているのか、プログラムソースの内容を説明してください。
以下、回答内容です。
このパッチファイル(Add-reaction-feature.patch)は、Redmineにリアクション(いいね!)機能を導入するためのプログラムソース変更を記録しています。
主に、リアクションを管理するための新しいモデルとコントローラ、そしてそれを表示するためのビューヘルパー、スタイル、アイコンが追加・変更されています。
以下に、プログラムソースが具体的にどのような処理を行っているかをファイルごとに説明します。
1. コア機能の実装 (モデルとロジック)
app/models/reaction.rb (新規)
-
Reactionモデルを定義します。 -
belongs_to :reactable, polymorphic: true:このリアクションが、**複数の種類のオブジェクト(チケット、ノート、ニュースなど)**に関連付けられるようにします(ポリモーフィック関連付け)。 -
belongs_to :user:リアクションを行ったユーザーに関連付けます。 -
validates :reactable_type:リアクション可能なオブジェクトの種類を制限します。
lib/redmine/acts_as_reactable.rb (新規)
-
acts_as_reactableというモジュールを定義し、リアクション可能なモデル(Issue,Journal,Message,Newsなど)にリアクション機能を追加する共通ロジックを提供します。 - このモジュールをインクルードしたモデルに、
has_many :reactionsという関連付けと、以下のメソッドが追加されます。-
reaction_count:リアクションの総数を取得します。 -
reaction_user_names:リアクションを行ったユーザー名のリストを取得します(ツールチップ表示などに使用)。 -
reaction_by(user):特定のユーザーが既に行ったリアクションを取得します。
-
lib/redmine/reaction.rb (新規)
- リアクション可能なモデルのリストを保持する定数
REACTABLE_TYPESなどを定義します。
db/migrate/..._create_reactions.rb (新規)
- リアクションデータを保存するためのデータベーステーブル**
reactions**を作成するマイグレーションファイルです。 -
reactable_id、reactable_type、user_idなどのカラムを持ち、インデックスが設定されます。
2. APIとデータ操作 (コントローラ)
app/controllers/reactions_controller.rb (新規)
- リアクションの作成と削除を処理する専用コントローラです。
-
before_actionで以下のチェックを行います。-
check_enabled:設定でリアクション機能が有効になっているか。 -
require_login:ユーザーがログインしているか。 -
set_object, :authorize_reactable:リアクション対象のオブジェクトを特定し、現在のユーザーがそれを閲覧可能か。
-
-
createアクション:- 現在のユーザーについて、対象オブジェクトに対するリアクションを
find_or_create_by!メソッドで作成します。
- 現在のユーザーについて、対象オブジェクトに対するリアクションを
-
destroyアクション:- 現在のユーザーのリアクションを見つけ、削除します。
app/controllers/messages_controller.rb / app/controllers/news_controller.rb (変更)
- メッセージやニュースのコメントを取得する際に、新しく定義されたスコープやメソッド(例:
load_with_reactions)を使用して、リアクションデータも一緒に効率よくロードするように変更されます。
3. 表示とユーザーインターフェース (ビューとアセット)
app/helpers/reactions_helper.rb (新規)
- ビューでリアクションボタンをレンダリングするためのヘルパーモジュールです。
-
reaction_button(object)メソッドが、以下の状態に応じて適切なボタンを生成します。-
リアクション済みの場合: 塗りつぶされたアイコン(
thumb-up-filled)を使用し、method: :delete(削除API)へのリンクを生成します。 -
未リアクションの場合: 枠線アイコン(
thumb-up)を使用し、method: :post(作成API)へのリンクを生成します。 - 未ログインの場合: 読み取り専用のボタンを生成し、アクションリンクは設定しません。
-
リアクション済みの場合: 塗りつぶされたアイコン(
- リアクションしたユーザー名を基にしたツールチップ(
title属性)も設定します。 -
remote: trueが設定されており、ボタンのクリックは**Ajax (非同期通信)**で行われます。
app/helpers/journals_helper.rb / messages_helper.rb / news_helper.rb (変更)
- これらのヘルパーに
ReactionsHelperをインクルードし、ジャーナル(ノート)、フォーラムメッセージ、ニュースなどの表示箇所でreaction_buttonを呼び出せるようにします。
app/assets/images/icons.svg (変更)
- リアクションボタンに使用する、**いいね!アイコン(親指を立てたアイコン)**のSVG定義が追加されます。
-
icon--thumb-up(アウトライン) -
icon--thumb-up-filled(塗りつぶし)
-
app/assets/stylesheets/application.css (変更)
- リアクションボタン(
.reaction-button)に関するCSSスタイルが追加されます。-
float: rightでコンテンツの右端に寄せる設定。 - リアクション済みのボタン(
.reaction-button.reacted)の色を青系(#126fa7)に、ホバー時に赤系(#c61a1a)に変化させるスタイルなどが定義されます。
-
app/assets/javascripts/application-legacy.js (変更)
-
setupHoverTooltips関数の変更と、removeHoverTooltips関数の追加により、リアクションボタンのように動的に追加される要素に対してもツールチップが正しく機能するように対応します。
追加で「reactions_helper.rb」の処理内容を確認してみた
ご提示いただいた app/helpers/reactions_helper.rb ファイルは、Redmineのリアクション(いいね!)機能のビュー側(HTML出力)を担うヘルパーモジュールです。
各関数が実行する処理は以下の通りです。
💡 ReactionsHelperの各関数の説明
1. reaction_button(object, reaction = nil)
- 役割: リアクションボタン全体をレンダリングします。ユーザーのログイン状態とリアクション済みかどうかによって、表示するボタンを振り分けます。
-
処理内容:
-
Setting.reactions_enabled?(リアクション機能が有効か)とobject.visible?(User.current)(対象オブジェクトが現在のユーザーに表示可能か)をチェックし、満たさない場合は描画を中止します。 - 現在のユーザーによる既存のリアクション(
reaction)と、総カウント(count)、リアクションしたユーザー名リスト(user_names)を取得します。 - ユーザー名リストからツールチップの文字列(
tooltip)を作成します。 - 現在のユーザーが ログインしているかどうか (
User.current.logged?) を判定します。- ログインしており、かつリアクション済み (
reaction&.persisted?) の場合、reaction_button_reactedを呼び出します(削除用ボタン)。 - ログインしているが、未リアクションの場合、
reaction_button_not_reactedを呼び出します(作成用ボタン)。 - ログインしていない場合、
reaction_button_readonlyを呼び出します(表示のみのボタン)。
- ログインしており、かつリアクション済み (
-
2. reaction_id_for(object)
- 役割: リアクションボタンのDOM要素を特定するための一意なID文字列を生成します。
-
処理内容:
- Redmineの組み込みヘルパー
dom_idを使用して、渡されたobjectに対する:reactionという接尾辞を持つID(例:reaction_issue_123)を生成します。これは主にJavaScriptでの操作に使われます。
- Redmineの組み込みヘルパー
3. reaction_button_reacted(object, reaction, count, tooltip)
- 役割: リアクション済みの状態のボタンをレンダリングします(リアクションを解除するためのボタン)。
-
処理内容:
-
reaction_button_wrapperを使ってボタン全体をラップします。 -
link_toヘルパーを使ってリンクを作成します。-
表示:
sprite_icon('thumb-up-filled', count)(塗りつぶされたいいねアイコンとカウント数)。 -
リンク先:
reaction_path(reaction, ...)(既存のリアクションの削除を指示するURL)。 -
動作:
remote: true, method: :deleteにより、このリンクがクリックされると、非同期(Ajax)でReactionsControllerのdestroyアクション(削除)が呼び出されます。 -
CSS:
reactedクラスが追加され、ボタンが青色などに変化します。
-
表示:
-
4. reaction_button_not_reacted(object, count, tooltip)
- 役割: 未リアクションの状態のボタンをレンダリングします(リアクションを追加するためのボタン)。
-
処理内容:
-
reaction_button_wrapperを使ってボタン全体をラップします。 -
link_toヘルパーを使ってリンクを作成します。-
表示:
sprite_icon('thumb-up', count)(枠線のみのいいねアイコンとカウント数)。 -
リンク先:
reactions_path(...)(新しいリアクションの作成を指示するURL)。 -
動作:
remote: true, method: :postにより、このリンクがクリックされると、非同期(Ajax)でReactionsControllerのcreateアクション(作成)が呼び出されます。
-
表示:
-
5. reaction_button_readonly(object, count, tooltip)
- 役割: ログインしていないユーザー向けに、読み取り専用のボタンをレンダリングします。
-
処理内容:
-
reaction_button_wrapperを使ってボタン全体をラップします。 -
link_toではなくtag.spanを使用するため、クリックしてもアクションは発生しません。 - 表示: 枠線のみのいいねアイコンとカウント数、およびツールチップが表示されます。
-
6. reaction_button_wrapper(object, &)
-
役割: リアクションボタンを囲むためのコンテナ
<span>タグを作成します。 -
処理内容:
-
reaction_id_for(object)を使って生成されたIDをdata-reaction-button-id属性として設定した<span>タグを作成し、ブロック内のコンテンツ(ボタン本体)をその中に入れます。これは、JavaScriptが特定のリアクションボタン要素を識別・操作するために使用されます。
-
7. build_reaction_tooltip(user_names, count)
- 役割: リアクションを行ったユーザー名リストから、ツールチップとして表示するテキストを作成します。
-
処理内容:
- リアクション数がゼロ (
count.zero?) の場合は何も返しません。 - リアクションしたユーザーの総数が表示制限(
DISPLAY_REACTION_USERS_LIMIT)を超えている場合、最初の数名の名前の後に**「他X名」**といったテキストを追加します(例: "Alice, Bob, and 3 other users")。 - 最後に、
to_sentenceを使って、ユーザー名を言語設定に従って整形し(例: 日本語なら「A、B、C」)、ツールチップの文字列として返します。
- リアクション数がゼロ (
感想
複数のファイルが複雑に関連しているMVCモデルのRailsの文法で書かれたプログラムソースもAIの助けを受けて各ファイルや各関数の処理内容の概要を把握できるので、すごい時代になったと思います。
AIの解答が100%正しくはない場合もありますが、プログラムソースだけを読むことに比べれば十分に概要の把握のスピードは向上できました。
この記事の作成者の紹介
山崎進
- Redmine、Jquery、JavaScript,Rails、Ruby、SQL、VBA、RPAの開発を行なっています。
- 自動化、業務の効率化に高い関心があります。
- 下記の媒体で情報を発信しています。Redmineのプラグインの開発などに対応が可能ですので、お気軽にご連絡ください。
* Qiita:https://qiita.com/ankosoft
* Twitter:https://twitter.com/yamasaki24
* Redmine Advent Calendarで記事投稿
* redmine.tokyoで講演多数
* Redmine Japan Vol.1 前夜祭、Redmine Japan Vol.3で講演
* https://ankosoft.co.jp/blog/
* https://technology.ankosoft.co.jp/
関連記事(2024年のRedmine Advent Calendarの記事です。)
* Redmine wikiマクロを作成する方法2(完了チケットの割合をグラフで表示)
* Redmine wikiマクロを作成する方法3(ステータスごとのチケットの割合をグラフで表示)
* Redmine wikiマクロを作成する方法4(ステータスごとのチケットの割合を一つのグラフで表示)
* Redmine wikiマクロを作成する方法5(特定のユーザーのチケット状態の確認)
* Redmine wikiマクロを作成する方法6(プロジェクトに所属する全てのユーザーのチケット状態の確認)
関連記事(2023年のRedmine Advent Calendarの記事です。)
* JqueryでRedmineのメニューにアイコンを入れる方法
* JqueryでRedmineのメニューを閉じたり開いたりする方法
* JqueryでRedmineの「活動ページ」をもっと便利に(タイトルを開閉したり、曜日を入れたり)
* JqueryでRedmineの「活動ページ」をもっと便利に②(フィルタリング機能と移動機能)
* JqueryでRedmineの「wikiページ」をもっと便利に(フィルタリング機能と移動機能)
* JqueryでRedmineの「チケットページ」をもっと便利に(年ごと、月ごと、四半期ごとの集計機能の開発)

