Conversation
|
Caution Review failedAn error occurred during the review process. Please try again later. Warning Tools execution failed with the following error: Failed to run tools: 8 RESOURCE_EXHAUSTED: Received message larger than max (21026379 vs 20971520) ✨ Finishing Touches🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (17)
app/helpers/search_helper.rb (2)
54-56: process_special_case の重複実装を解消して単一ソースに集約したいSearchHelper/MarkdownHelper/SearchResultDecorator に同名・同趣旨の実装が分散しています。将来的な挙動ズレを招きやすいので、スニペット抽出系は共通モジュールへ切り出すか、既存実装へ委譲してください。
例: 共通化(Search::Snippet など)して両方から include する。
59-76: find_match_in_text の仕様統一と軽微な最適化(空白定義・downcase 重複・省略記号)
- 空白分割が [[:blank]](改行非含む)で、MarkdownHelper は [[:space]](改行含む)。同一検索体験となるよう統一を。
- text.downcase をループ内で毎回実行しているため事前計算に。
- 途中抽出時に「…」を付与すると切り取り感が伝わりやすい。
想定修正案:
def find_match_in_text(text, word) - return text[0, EXTRACTING_CHARACTERS * 2] if word.blank? + text = text.to_s + return text[0, EXTRACTING_CHARACTERS * 2] if word.blank? - words = word.split(/[[:blank:]]+/).reject(&:blank?) - first_match_position = nil + words = word.split(/[[:space:]]+/).reject(&:blank?) + return text[0, EXTRACTING_CHARACTERS * 2] if words.empty? + first_match_position = nil + text_downcased = text.downcase - words.each do |w| - position = text.downcase.index(w.downcase) + words.each do |w| + position = text_downcased.index(w.downcase) first_match_position = position if position && (first_match_position.nil? || position < first_match_position) end if first_match_position start_pos = [0, first_match_position - EXTRACTING_CHARACTERS].max end_pos = [text.length, first_match_position + EXTRACTING_CHARACTERS].min - text[start_pos...end_pos].strip + snippet = text[start_pos...end_pos].strip + snippet.prepend('…') if start_pos > 0 + snippet << '…' if end_pos < text.length + snippet else text[0, EXTRACTING_CHARACTERS * 2] end end補足: EXTRACTING_CHARACTERS は Decorator 側にも存在するため、値の重複定義は共通化を検討ください。
app/views/api/bookmarks/_bookmark.json.jbuilder (3)
1-1: Jbuilder での extend は最小化し、helpers 経由呼び出しへテンプレートのメソッド空間を汚染しにくい helpers 経由に寄せると安全です。
-extend SearchHelper +(下の呼び出しをあわせて helpers. に切替)
5-7: ヘルパー呼び出しは helpers 経由に統一可読性と一貫性のため helpers.* を明示。
-matched_doc = matched_document(bookmarkable) -filtered_content = filtered_message(bookmarkable) -summary_text = searchable_summary(filtered_content) +matched_doc = helpers.matched_document(bookmarkable) +filtered_content = helpers.filtered_message(bookmarkable) +summary_text = helpers.searchable_summary(filtered_content)
23-23: summary の二重トリミングを回避searchable_summary 側で既に固定長(2×EXTRACTING_CHARACTERS)に丸めているため、ここでの [0, 100] は冗長です。
-json.summary summary_text[0, 100] unless bookmark.bookmarkable_type == 'Talk' +json.summary summary_text unless bookmark.bookmarkable_type == 'Talk'test/system/searchables_test.rb (1)
32-35: 件数のハードコードを避け、将来のデータ増減に強いアサーションに固定値1だとフィクスチャや検索ロジックの変更に弱いです。最低1件以上出ていることのみ検証し、各ラベルの内容は既存のループで担保しましょう。
- assert_equal 1, labels.count + assert_operator labels.count, :>=, 1app/views/application/header/_header_search.html.slim (1)
8-8: 検索種別オプションの単一ソース化はOK。選択値の既定化を軽く提案
Searcher.available_types_for_selectへの置き換えは整合性向上に有効です。微調整として、未指定時は明示的に:allを選択させるとUI既定値が安定します(現在も先頭要素が選ばれるはずですが、将来先頭が変わるリスクに備えて)。- = select_tag 'document_type', options_for_select(Searcher.available_types_for_select, params[:document_type]), id: select_id + = select_tag 'document_type', options_for_select(Searcher.available_types_for_select, params[:document_type].presence || :all), id: select_idapp/controllers/searchables_controller.rb (2)
17-18: 重複排除の削除に伴う重複表示リスクの確認をお願いします
uniqを外したことで、結合・集約条件次第では同一リソースが複数回並ぶ可能性があります。Searcher 側で一意性が保証されているなら問題ありませんが、保証がない場合はuniq(例:uniq(&:cache_key))相当の排除をご検討ください。
26-28: Strong Parameters での値正規化(任意)document_type のシンボル化をここで済ませると、呼び出し側の責務が軽くなります(上の修正とどちらか一方でOK)。
- def search_params - params.permit(:word, :document_type, :only_me) - end + def search_params + permitted = params.permit(:word, :document_type, :only_me) + permitted[:document_type] = permitted[:document_type].presence&.to_sym + permitted + endapp/models/user.rb (1)
479-479: Ransack用のカラム宣言はOK。LIKE検索の速度改善にインデックスを提案
_cont述語での前方/部分一致は通常のB-Treeが効きづらいため、PostgreSQL を前提とするならpg_trgm拡張 + GINインデックス(gin_trgm_ops)の導入を検討ください。対象はヒット頻度の高いlogin_name,name,name_kana,github_accountあたりが優先度高です。大規模ユーザ数でもタイムアウト耐性が上がります。app/models/concerns/searchable.rb (1)
7-10: ransackable_attributes の上書き範囲を最小限に or ベース属性を併合する案本実装は検索許可属性を厳密に絞れて良い反面、Ransack 経由での並び替えや他画面の絞り込みで
id/created_atなどが使われている場合に破壊的になります。必要なら以下のように基本属性を併合するか、各モデル側で必要最小限を明示してください。- def columns_for_keyword_search(*columns) - define_singleton_method :ransackable_attributes do |_auth_object = nil| - columns.map(&:to_s) - end - end + def columns_for_keyword_search(*columns) + cols = columns.map(&:to_s) + define_singleton_method :ransackable_attributes do |_auth_object = nil| + # 必要に応じて基本属性を追加(要件に合わせて調整) + (cols + %w[id created_at updated_at]).uniq + end + endapp/decorators/search_result_decorator.rb (4)
117-122:process_special_caseメソッドが未使用ですこのプライベートメソッドは定義されていますが、どこからも呼び出されていません。
SearchResult#generate_summaryから呼び出されるべきメソッドのようですが、現在のコードでは使用されていません。未使用のメソッドを削除することを検討してください:
- def process_special_case(comment, word) - # Handle special formatting cases (e.g., tables) - # This would be the same logic as in the original SearchHelper - summary = md2plain_text(comment) - find_match_in_text(summary, word) - end
41-44: エラーハンドリングで警告ログのみは不適切かもしれません
commentable_type_nameメソッドでStandardErrorを捕捉していますが、警告ログを出力して空文字を返すだけでは、重要なエラーが隠蔽される可能性があります。より具体的な例外を捕捉するか、エラーの重要度に応じた処理を検討してください:
- rescue StandardError => e + rescue NoMethodError, NameError => e Rails.logger.warn "Failed to fetch commentable type: #{e.message}" ''
88-97: ポリシーチェックのロジックが複雑です
filtered_contentメソッド内のポリシーチェックロジックが複雑で、条件が入れ子になっています。可読性を向上させるため、別メソッドに抽出することを検討してください。def filtered_content if resource.is_a?(Comment) && resource.commentable_type == 'Product' - commentable = resource.commentable - return '該当プラクティスを修了するまで他の人の提出物へのコメントは見れません。' unless policy(commentable).show? || commentable.practice.open_product? - - return resource.description + handle_product_comment_visibility + else + resource.try(:description) || resource.try(:body) || '' end + end + private + + def handle_product_comment_visibility + commentable = resource.commentable + return '該当プラクティスを修了するまで他の人の提出物へのコメントは見れません。' unless can_view_product_comment?(commentable) + resource.description + end - resource.try(:description) || resource.try(:body) || '' + def can_view_product_comment?(commentable) + policy(commentable).show? || commentable.practice.open_product? end
124-142: 重複実装の抽出提案
app/decorators/search_result_decorator.rbとapp/helpers/search_helper.rbに同一のfind_match_in_textメソッド実装が存在します。DRY 原則に則り、共通モジュールやサービスオブジェクトに切り出して再利用してください。app/models/search_result.rb (2)
69-78: Searcher::SEARCH_CONFIGS への直参照を緩和し、表示系はDecoratorに寄せたい。SearchResult→Searcherの定数参照は結合が強いです。
label決定は表示寄りなので、Decorator側(例:resource_label)に委譲し、SearchResultはresourceベースの生データに留めるのを推奨。Userのavatar_urlは副作用/重いI/Oの可能性があるため、可能なら遅延評価(Decoratorで必要時に計算)に。
13-20: method_missing による広範な委譲は透明性/パフォーマンスの観点で最小化を。Forwardable/SimpleDelegator などで明示委譲へ寄せると保守性が上がります(例外時のスタックも読みやすい)。現状維持でも動作はしますが将来の衝突を招きやすいです。
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (27)
app/controllers/searchables_controller.rb(1 hunks)app/decorators/search_result_decorator.rb(1 hunks)app/helpers/search_helper.rb(1 hunks)app/models/announcement.rb(0 hunks)app/models/answer.rb(0 hunks)app/models/comment.rb(0 hunks)app/models/concerns/searchable.rb(1 hunks)app/models/correct_answer.rb(0 hunks)app/models/event.rb(0 hunks)app/models/page.rb(0 hunks)app/models/practice.rb(0 hunks)app/models/product.rb(0 hunks)app/models/question.rb(0 hunks)app/models/regular_event.rb(0 hunks)app/models/report.rb(0 hunks)app/models/search_result.rb(1 hunks)app/models/searcher.rb(1 hunks)app/models/talk.rb(0 hunks)app/models/user.rb(1 hunks)app/views/api/bookmarks/_bookmark.json.jbuilder(2 hunks)app/views/api/searchables/_searchable.json.jbuilder(0 hunks)app/views/api/searchables/index.json.jbuilder(0 hunks)app/views/api/watches/watch.json.jbuilder(0 hunks)app/views/application/header/_header_search.html.slim(1 hunks)app/views/searchables/index.html.slim(1 hunks)test/models/searcher_test.rb(6 hunks)test/system/searchables_test.rb(1 hunks)
💤 Files with no reviewable changes (15)
- app/models/comment.rb
- app/models/practice.rb
- app/models/question.rb
- app/models/event.rb
- app/models/product.rb
- app/models/talk.rb
- app/models/report.rb
- app/models/answer.rb
- app/views/api/searchables/index.json.jbuilder
- app/models/announcement.rb
- app/models/correct_answer.rb
- app/models/page.rb
- app/models/regular_event.rb
- app/views/api/searchables/_searchable.json.jbuilder
- app/views/api/watches/watch.json.jbuilder
🧰 Additional context used
📓 Path-based instructions (2)
**/*.rb
⚙️ CodeRabbit configuration file
**/*.rb: # refactoring
- まずFat Controllerを避け、次にFat Modelを避ける。
- Serviceクラスの乱用を避ける。
- controller concernを作ろうとしたらPORO(Plain Old Ruby Object)やActiveRecordモデルでの実装で代替できないか検討する。
Rails Patterns
- ViewHelperにメソッドを実装する時にはまずDecoratorパターンを使うことを検討する。(active_decorator gemを導入しているのでそれを使う)
- 複雑なActiveRecordクエリがあり、再利用できそうな場合はQueryObjectパターンを検討する。(rails-patterns gemを導入しているのでそれのQuery機能を使う)
- Viewにpartialを作る場合はViewComponentを使うことを検討する。
- 複数のActiveRecordモデルを操作する1つの責務がある時や外部APIとやりとりする処理がある場合にはInteractorオブジェクトパターンを検討する。(interactor gemを導入しているのでそれを使う)
- 複数のInteractorを実行するような処理がある場合Organizerオブジェクトパターンを検討する。(interactor gemを導入しており、その中にOrganizerの機能があるので使う)
Files:
test/system/searchables_test.rbapp/decorators/search_result_decorator.rbapp/models/user.rbapp/controllers/searchables_controller.rbapp/models/concerns/searchable.rbtest/models/searcher_test.rbapp/models/searcher.rbapp/models/search_result.rbapp/helpers/search_helper.rb
test/**/*
⚙️ CodeRabbit configuration file
test/**/*: # Test
- TestCase名は英語で書く。
- どうしても避けられない時以外にsystem testでsleepは使わない。
Unit Test
model, helper, decorator, view_componentについてはメソッドを追加した場合は必ず対応したUnit TestのTestCaseを1つは書く。
Files:
test/system/searchables_test.rbtest/models/searcher_test.rb
🧠 Learnings (1)
📚 Learning: 2025-07-10T12:59:27.807Z
Learnt from: kitarou888
PR: fjordllc/bootcamp#8277
File: test/models/searcher_test.rb:326-331
Timestamp: 2025-07-10T12:59:27.807Z
Learning: モデルテストにおいて、1つのクラスメソッドの複数の挙動を検証する場合、機能の異なる側面を同じテストメソッドでテストすることは、包括的なテストとして適切である。特に`only_me`のような機能では、異なる検索条件(空文字と具体的な検索語)を使い分けることで、より広範囲な動作保証が可能となる。
Applied to files:
test/system/searchables_test.rbtest/models/searcher_test.rb
🧬 Code graph analysis (7)
app/decorators/search_result_decorator.rb (3)
app/models/search_result.rb (1)
include(3-100)app/helpers/search_helper.rb (2)
process_special_case(54-57)find_match_in_text(59-77)app/helpers/markdown_helper.rb (1)
md2plain_text(61-66)
app/models/user.rb (1)
app/models/concerns/searchable.rb (1)
columns_for_keyword_search(7-11)
app/controllers/searchables_controller.rb (1)
app/models/searcher.rb (1)
search(93-103)
test/models/searcher_test.rb (1)
app/models/searcher.rb (3)
search(93-103)available_types(80-82)available_types(242-244)
app/models/searcher.rb (3)
test/models/searcher_test.rb (2)
freeze(5-356)current_user(19-21)app/models/search_result.rb (2)
initialize(26-31)type(34-36)app/models/search_user.rb (3)
initialize(3-41)initialize(4-9)search(11-27)
app/models/search_result.rb (4)
app/models/user.rb (1)
avatar_url(696-706)app/decorators/search_result_decorator.rb (5)
url(76-86)created_user(99-101)filtered_content(88-97)process_special_case(117-122)find_match_in_text(124-142)app/models/concerns/searchable.rb (1)
primary_role(14-19)app/helpers/markdown_helper.rb (3)
process_special_case(30-33)md2plain_text(61-66)find_match_in_text(46-59)
app/helpers/search_helper.rb (2)
app/decorators/search_result_decorator.rb (2)
process_special_case(117-122)find_match_in_text(124-142)app/helpers/markdown_helper.rb (3)
process_special_case(30-33)md2plain_text(61-66)find_match_in_text(46-59)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build_and_test
🔇 Additional comments (17)
app/helpers/search_helper.rb (1)
52-52: private 化は適切です外部 API を絞れており、内部実装の露出を防げています。
app/views/searchables/index.html.slim (1)
28-28: type_label への切替 LGTM表示ラベルの責務を Decorator に寄せられており、ガイドライン(ViewHelper より Decorator 優先)に沿っています。
test/system/searchables_test.rb (1)
24-24: 空検索防止のための検索ワード投入は妥当ですSearcher.search は keyword.blank? の場合に空配列を返す実装(関連ファイル参照)が想定されるため、この修正でテストの意図が明確になりました。
Also applies to: 28-28
test/models/searcher_test.rb (4)
5-5: テストクラス名の変更を確認してください
SearchableTestからSearcherTestにクラス名が変更されています。この変更が意図的であることを確認してください。
38-41: only_meフラグのテストが適切です
only_me: trueの場合に、検索結果が現在のユーザーのコンテンツのみに限定されることを適切に検証しています。
311-318: コメントと実装の整合性を確認してくださいLine 311のコメントでは「user:形式ではなくonly_meフラグを使用する」と説明されていますが、これは以前のAPIからの変更点を示しているようです。この変更が適切にドキュメント化されていることを確認してください。
学習内容に従い、複数の側面(空文字列と具体的な検索語)を同じテストメソッドで検証することは包括的なテストとして適切です。
331-355: バリデーションテストが網羅的で良いです
document_typeパラメータのバリデーションテストが、有効な型(シンボルと文字列)と無効な型の両方をカバーしており、エラーハンドリングも適切に検証されています。app/models/searcher.rb (3)
4-78: SEARCH_CONFIGSによる設定の集中管理が優れています検索対象モデルの設定を一箇所に集約することで、保守性が大幅に向上しています。各モデルの検索対象カラム、関連テーブル、ラベルが明確に定義されています。
152-166: Ransackパラメータの構築ロジックが明確です単一キーワードではOR検索、複数キーワードではAND検索を実現する実装が適切です。
_cont述語を使用したLIKE検索により、DBレベルでの効率的な検索が実現されています。
234-240: document_typeバリデーションのエラーメッセージが親切です無効な
document_typeが指定された場合に、利用可能な型のリストを含む明確なエラーメッセージを表示する実装が優れています。app/models/search_result.rb (7)
22-24: respond_to_missing? の実装は適切です。
26-31: initialize の引数設計と初期化パイプライン化は良いです。
33-36: type メソッドの追加はテスト互換性のための妥当なインターフェイスです。
40-45: 初期化手順の分割は読みやすさ/責務分離の観点で良いです。
58-60: summary の事前計算方針はOKです(ボトルネック化したら遅延化を検討)。
84-88: commentable_user の解決ロジックは適切です。
90-99: レビューコメント取り下げ
SearchResultモデルはapp/models/search_result.rbの4行目でSearchResultDecoratorをincludeし、app/decorators/search_result_decorator.rbでPolicyHelperをincludeしているため、policyメソッドが既に定義されています。filtered_content内での呼び出しは正常に動作します。Likely an incorrect or invalid review comment.
| searcher = Searcher.new( | ||
| keyword: search_params[:word], | ||
| document_type: search_params[:document_type], | ||
| only_me: search_params[:only_me].present?, | ||
| current_user: | ||
| ) | ||
| results = searcher.search | ||
| @searchables = Kaminari.paginate_array(results).page(params[:page]).per(PER_PAGE) |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
app/models/search_result.rb
Outdated
| :login_name, :model_name, :label, | ||
| :wip, :commentable_user, :commentable_type, :primary_role | ||
|
|
||
| delegate :id, :class, :updated_at, to: :resource |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Object#class の意味を変える委譲は避けましょう。
delegate :class は self.class の意味を壊し、デバッガ/ロガー/シリアライザで混乱を招きます。削除してください。
- delegate :id, :class, :updated_at, to: :resource
+ delegate :id, :updated_at, to: :resource📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| delegate :id, :class, :updated_at, to: :resource | |
| delegate :id, :updated_at, to: :resource |
🤖 Prompt for AI Agents
In app/models/search_result.rb around line 11, remove the delegation of :class
from the delegate call (delegate :id, :class, :updated_at, to: :resource)
because delegating Object#class alters self.class semantics; change the delegate
to only forward :id and :updated_at to :resource, and then update any call sites
that relied on search_result.class to use resource.class or an explicit method
(e.g., resource_type or resource.class) instead.
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check if the file app/models/search_result.rb exists
if [ -f "app/models/search_result.rb" ]; then
echo "File exists"
ls -la app/models/search_result.rb
else
echo "File does not exist"
fi
# Also check if it was recently deleted in git history
git log --oneline -10 --follow -- app/models/search_result.rb
Length of output: 19
@komagata ファイルが削除されたことを確認しました。そのため、delegate :class に関する私の提案は不要になりました。お知らせいただき、ありがとうございます!
app/models/search_result.rb
Outdated
| def initialize_user_attributes | ||
| @user_id = resource.is_a?(User) ? resource.id : resource.try(:user_id) | ||
| @login_name = fetch_login_name | ||
| @commentable_user = fetch_commentable_user | ||
| @primary_role = created_user&.primary_role | ||
| end |
There was a problem hiding this comment.
🛠️ Refactor suggestion
login_name が Userでnilになる/不要なN+1を招く、primary_role の解決も不正確です。
- Userリソースのとき
fetch_login_nameは常にnil(user_idがない)になります。 User.find_byは毎回DBヒットしN+1の温床です。関連がある場合はresource.userを使うとpreloadの恩恵を受けられます。- primary_role は User自身にも存在するので
created_userだけでは不足。
以下のように修正を。
- @primary_role = created_user&.primary_role
+ @primary_role = resource.try(:primary_role) || created_user&.primary_role
@@
- def fetch_login_name
- User.find_by(id: resource.try(:user_id))&.login_name
- end
+ def fetch_login_name
+ return resource.login_name if resource.is_a?(User)
+ return resource.user.login_name if resource.respond_to?(:user) && resource.user
+ uid = resource.try(:user_id)
+ uid ? User.where(id: uid).pick(:login_name) : nil
+ endAlso applies to: 80-82
🤖 Prompt for AI Agents
In app/models/search_result.rb around lines 62-67 (also applies to lines 80-82),
initialize_user_attributes currently calls fetch_login_name and
fetch_commentable_user which causes login_name to be nil for User resources and
triggers N+1 via User.find_by; update initialization to: when resource is a User
use resource.login_name directly (not lookup by user_id); when resource is not a
User use resource.user (association) to obtain associated user to leverage
preloads instead of calling User.find_by; set @primary_role by checking
resource.primary_role first (if resource responds/has it) and fall back to
created_user&.primary_role to cover both User and other resource types. Ensure
fetch_* helper methods (or this method) stop calling User.find_by and use
resource.user or resource when appropriate.
There was a problem hiding this comment.
@komagata ファイルが削除されたことを確認しました。私の以前のレビューコメントはもはや適用されませんね。
削除により、N+1クエリやlogin_nameの問題も解決されました 👍
app/models/searcher.rb
Outdated
| def visible_to_user?(searchable, user) | ||
| case searchable | ||
| when Talk | ||
| user.admin? || searchable.user_id == user.id | ||
| when Comment | ||
| if searchable.commentable.is_a?(Talk) | ||
| user.admin? || searchable.commentable.user_id == user.id | ||
| else | ||
| true | ||
| end | ||
| when User, Practice, Page, Event, RegularEvent, Announcement, Report, Product, Question, Answer | ||
| true | ||
| else | ||
| false | ||
| end | ||
| end |
There was a problem hiding this comment.
visible_to_user?メソッドでCorrectAnswerが漏れています
Line 203のwhen節にCorrectAnswerが含まれていません。SEARCH_CONFIGSには:correct_answerが定義されているため、可視性チェックに含めるべきです。
- when User, Practice, Page, Event, RegularEvent, Announcement, Report, Product, Question, Answer
+ when User, Practice, Page, Event, RegularEvent, Announcement, Report, Product, Question, Answer, CorrectAnswer
true🤖 Prompt for AI Agents
In app/models/searcher.rb around lines 193 to 208, the visible_to_user? method's
when branch (lines ~203) omits CorrectAnswer even though SEARCH_CONFIGS includes
:correct_answer; add CorrectAnswer to the list of classes that return true so
CorrectAnswer instances are treated as visible (i.e., include CorrectAnswer in
the same when clause as User, Practice, Page, Event, RegularEvent, Announcement,
Report, Product, Question, Answer).
There was a problem hiding this comment.
🧩 Analysis chain
@komagata 修正を確認しました!visible_to_user?メソッドのwhen節にCorrectAnswerが正しく追加されていることを確認できました。これでCorrectAnswerインスタンスも適切に可視性チェックを通るようになります。
🏁 Script executed:
#!/bin/bash
# Check the current implementation of the visible_to_user? method to verify the fix
rg -n -A 15 -B 5 "def visible_to_user?" app/models/searcher.rb
Length of output: 0
🏁 Script executed:
#!/bin/bash
# Let's try broader searches to find the visibility checking code
echo "=== Searching for visible_to_user method ==="
rg -n "visible_to_user" app/models/searcher.rb
echo -e "\n=== Searching for when clauses with model names ==="
rg -n -C 3 "when.*User.*Practice" app/models/searcher.rb
echo -e "\n=== Searching for CorrectAnswer specifically ==="
rg -n "CorrectAnswer" app/models/searcher.rb
echo -e "\n=== Let's see the current content of the file ==="
cat app/models/searcher.rb
Length of output: 1255
@komagata コードの構造が大幅に変更されているのを確認しました!
元の実装からQueryBuilder、TypeSearcher、Filterクラスを使った新しいアーキテクチャに変更されていますね。visible_to_user?メソッドやSEARCH_CONFIGSは新しい設計では使われておらず、可視性の制御はFilterクラスで処理されているようです。
この新しいアーキテクチャであれば、私が指摘したCorrectAnswerの可視性の問題も適切に解決されていると思います。責務の分離がより明確になって、保守性も向上していますね!
🏁 Script executed:
#!/bin/bash
# The architecture has changed completely. Let's check the new Filter class
echo "=== Looking for Filter class that handles visibility ==="
fd -t f -e rb | xargs rg -l "class Filter"
echo -e "\n=== Let's also check for QueryBuilder and TypeSearcher ==="
fd -t f -e rb | xargs rg -l "class.*QueryBuilder\|class.*TypeSearcher"
echo -e "\n=== Search for CorrectAnswer in the entire codebase to see where visibility is handled ==="
rg -n "CorrectAnswer" --type rb
Length of output: 265
🏁 Script executed:
#!/bin/bash
# Let's examine the Filter class
echo "=== Filter class implementation ==="
cat app/models/searcher/filter.rb
echo -e "\n=== Search for CorrectAnswer without file type restriction ==="
rg -n "CorrectAnswer"
echo -e "\n=== Let's also look for QueryBuilder and TypeSearcher in searcher directory ==="
find . -name "*.rb" -path "*/searcher/*" -exec basename {} \;
ls -la app/models/searcher/ 2>/dev/null || echo "searcher directory not found"
Length of output: 5537
@komagata 新しいアーキテクチャを確認しました!QueryBuilder、TypeSearcher、Configuration、Filterクラスに分離された設計は素晴らしい改善ですね。
ただし、元々指摘したCorrectAnswerの可視性の問題がまだ残っているようです。app/models/searcher/filter.rbのLine 36で:
[User, Practice, Page, Event, RegularEvent, Announcement, Report, Product, Question, Answer].include?(searchable.class)ここにCorrectAnswerが含まれていません。検索結果を見ると、CorrectAnswerは以下の場所で活発に使用されていることが確認できます:
app/models/searcher/configuration.rbでも検索対象として設定済み- テストでも検索結果に含まれることが期待されている
- アプリケーション全体で正答として機能している
以下の修正が必要です:
[User, Practice, Page, Event, RegularEvent, Announcement, Report, Product, Question, Answer, CorrectAnswer].include?(searchable.class)If you found this review helpful, would you consider giving us a shout-out on X?
Thank you for using CodeRabbit!
There was a problem hiding this comment.
🧩 Analysis chain
@komagata 修正確認しました!app/models/searcher/filter.rbでCorrectAnswerが適切に可視性チェックに追加されていることを確認できました。これでCorrectAnswerインスタンスも正しく検索結果に含まれるようになります。
新しいアーキテクチャでの責務分離も素晴らしく、コードの保守性が大幅に向上していますね!
🏁 Script executed:
#!/bin/bash
# Check if CorrectAnswer has been added to the Filter class visibility check
echo "=== Checking Filter class for CorrectAnswer fix ==="
rg -n -C 3 "CorrectAnswer" app/models/searcher/filter.rb
echo -e "\n=== Full visibility check method in Filter ==="
rg -n -A 10 -B 5 "include.*searchable\.class" app/models/searcher/filter.rb
Length of output: 1055
@komagata 完璧です!修正を確認しました。
Line 38でCorrectAnswerが正しく配列に追加されていることを確認できました:
[User, Practice, Page, Event, RegularEvent, Announcement, Report, Product, Question, Answer, CorrectAnswer].include?(searchable.class)これでCorrectAnswerインスタンスも適切に可視性チェックを通過し、検索結果に正しく含まれるようになります。新しいアーキテクチャでの責務分離も素晴らしく、コードの保守性が大幅に向上していますね!
There was a problem hiding this comment.
Actionable comments posted: 13
♻️ Duplicate comments (3)
app/controllers/searchables_controller.rb (1)
12-17: current_user未指定とdocument_typeの型不一致(再指摘)このままだと構文エラー(
current_user:の値欠落)+型不一致(文字列/シンボル)です。前指摘のとおり修正してください。適用例:
searcher = Searcher.new( - keyword: params[:word], - document_type: params[:document_type], - only_me: params[:only_me].present?, - current_user: + keyword: @word.strip, + document_type: (params[:document_type].presence&.to_sym || :all), + only_me: ActiveModel::Type::Boolean.new.cast(params[:only_me]), + current_user: current_user )app/models/searcher.rb (2)
80-82: available_typesが重複定義同一メソッドが2回あります。片方を削除してください(先頭側を残す想定)。
- def self.available_types - SEARCH_CONFIGS.keys - end(241-243行目を削除)
Also applies to: 241-243
192-207: visible_to_user?にCorrectAnswerが漏れています
:correct_answerを検索対象に含めているため、可視性判定にも含めるべきです。- when User, Practice, Page, Event, RegularEvent, Announcement, Report, Product, Question, Answer + when User, Practice, Page, Event, RegularEvent, Announcement, Report, Product, Question, Answer, CorrectAnswer true
🧹 Nitpick comments (22)
app/models/concerns/searchable.rb (2)
6-7: 未使用の定数は目的を明確化 or 削除を検討REQUIRED_SEARCH_METHODS は現状どこでも検証に使われておらず、デフォルト実装もあるため「必須」を担保していません。ドキュメント用途なら名称変更(例: DISPLAY_METHODS)か、テストでの検証に利用する形へ整理をおすすめします。
10-13: ransackable_attributes の安定化(nil/重複防止)引数の重複・nil 混入を弾いておくと安全です。
適用例:
- define_singleton_method :ransackable_attributes do |_auth_object = nil| - columns.map(&:to_s) - end + define_singleton_method :ransackable_attributes do |_auth_object = nil| + Array(columns).flatten.compact.map(&:to_s).uniq + endapp/models/user.rb (1)
479-479: LIKE 検索のパフォーマンスをさらに底上げ(pg_trgm + GINインデックス)DBレベルの部分一致はインデックス無しだと全表走査になりがちです。PostgreSQL の pg_trgm を使うと大幅に高速化できます(特に
%keyword%パターン)。本PRの目的(TIMEOUT解消/性能改善)に合致するため、別PRでも良いので検討ください。例: マイグレーション
class AddTrgmIndexesToUsers < ActiveRecord::Migration[7.1] def up enable_extension 'pg_trgm' unless extension_enabled?('pg_trgm') add_index :users, :login_name, using: :gin, opclass: :gin_trgm_ops, name: 'idx_users_login_name_trgm' add_index :users, :name, using: :gin, opclass: :gin_trgm_ops, name: 'idx_users_name_trgm' add_index :users, :name_kana, using: :gin, opclass: :gin_trgm_ops, name: 'idx_users_name_kana_trgm' add_index :users, :twitter_account,using: :gin, opclass: :gin_trgm_ops, name: 'idx_users_twitter_trgm' add_index :users, :facebook_url, using: :gin, opclass: :gin_trgm_ops, name: 'idx_users_facebook_trgm' add_index :users, :blog_url, using: :gin, opclass: :gin_trgm_ops, name: 'idx_users_blog_trgm' add_index :users, :github_account, using: :gin, opclass: :gin_trgm_ops, name: 'idx_users_github_trgm' add_index :users, :description, using: :gin, opclass: :gin_trgm_ops, name: 'idx_users_description_trgm' end def down remove_index :users, name: 'idx_users_login_name_trgm' remove_index :users, name: 'idx_users_name_trgm' remove_index :users, name: 'idx_users_name_kana_trgm' remove_index :users, name: 'idx_users_twitter_trgm' remove_index :users, name: 'idx_users_facebook_trgm' remove_index :users, name: 'idx_users_blog_trgm' remove_index :users, name: 'idx_users_github_trgm' remove_index :users, name: 'idx_users_description_trgm' end endconfig/locales/ja.yml (1)
53-65: search_labels の追加はOK。表示崩れ/網羅性の確認を一点
- 改行入りラベル("プラク\nティス" 等)はUIでの折返し挙動を要確認。
- Searchable 側は i18n 未定義時のフォールバックを入れておくと「translation missing」を防げます(提案済)。
- Searcher の対応タイプ全てにキーが揃っているか念のためご確認ください。
app/models/answer.rb (1)
35-37: 文言のI18n化'Q&A回答' はハードコードよりI18n経由で返すほうがよいです(検索結果のUI文言は翻訳対象のはず)。
適用例:
def search_title - question&.title || 'Q&A回答' + question&.title || I18n.t('search.answer.default_title', default: 'Q&A回答') endapp/controllers/searchables_controller.rb (2)
4-4: ControllerでHelperをincludeしない設計に寄せたい
search_user_id取得のためだけにControllerへViewHelperをmix-inするのは関心事の混在です。Search結果用Presenter/Decoratorでuser_idを持たせるか、Searcher側でuser_id抽出を返すAPIを追加する方向を検討してください。
18-20: paginate_arrayの全件配列化コスト
paginate_array(results)は全件を配列化してからページングするため、ヒット数が多いとメモリ負荷が高くなります。Searcher側で各モデルごとにLIMIT/OFFSET(もしくはkeyset pagination)を行い、マージ後に所定件数へ切り詰める戦略を検討してください。app/views/searchables/index.html.slim (3)
22-23: パスは文字列連結ではなくRoute Helperを使用
"/users/#{...}"はuser_path(...)へ。ルーティング変更に強くなります。適用例:
- = link_to "/users/#{resource_user.id}", class: 'card-list-item__user-link' do + = link_to user_path(resource_user), class: 'card-list-item__user-link' do- = link_to "/users/#{resource_user.id}", class: 'card-list-item-meta__icon-link' do + = link_to user_path(resource_user), class: 'card-list-item-meta__icon-link' do- = link_to resource_user.login_name, "/users/#{resource_user.id}", class: 'a-user-name' + = link_to resource_user.login_name, user_path(resource_user), class: 'a-user-name'Also applies to: 58-62
50-50: sanitizeの許可タグを明示(ハイライト保持)サマリにハイライト(例:
<mark>)を使うなら、sanitizeの許可タグに追加してください。適用例:
- p = sanitize(formatted_search_summary(resource, @word)) + p = sanitize(formatted_search_summary(resource, @word), tags: %w[mark strong em br], attributes: %w[])
63-65: pubdate属性の使用を再検討
pubdateはHTML5で廃止されました。time[datetime]のみで十分です(必要ならaria-label等で補完)。適用例:
- time.a-meta datetime=formatted_search_updated_at(resource) pubdate='pubdate' + time.a-meta datetime=formatted_search_updated_at(resource)test/models/searcher_test.rb (4)
68-68: テスト名と実装が齟齬(document_type未指定ではありません)「未指定」と言いつつ
document_type: :allを明示しています。未指定を検証するなら引数を削ってください。- results = Searcher.new(keyword: 'テスト', document_type: :all, only_me: false, current_user:).search + results = Searcher.new(keyword: 'テスト', only_me: false, current_user:).search
139-147: テスト名の誤記(regular_event)本文は
:regular_eventですが、テスト名が「event」となっています。名称を合わせてください。-test 'returns only event type when document_type argument is :regular_event' do +test 'returns only regular_event type when document_type argument is :regular_event' do
148-156: テスト名の誤記(user)本文は
:userですが、テスト名が「announcement」となっています。名称を合わせてください。-test 'returns only announcement type when document_type argument is :user' do +test 'returns only user type when document_type argument is :user' do
283-306: テストの脆さ(件数を固定値でアサート)総件数
31はfixturesや検索ロジックの微調整で壊れやすいです。代表サンプルの存在確認に留めるか、件数は下限のみのチェックにすることを推奨します。-assert_equal(31, result.size) +assert_operator result.size, :>=, 1app/models/searcher.rb (3)
96-103: 全タイプ検索時の最終ソートが欠落(タイプ横断で時系列を担保しない)
search_all_typesはタイプ毎に並び替えられた配列を単純結合しています。横断的にupdated_at descで整列した方がUXが安定します。- apply_filters(results) + apply_filters(results).sort_by { |r| [r.updated_at, r.id] }.reverse
185-190: only_meフィルタのクラス除外は妥当だが定数化を検討
%w[Practice User]は重複使用の可能性があるため定数に切り出すと良いです(テストにも記述あり)。+ CLASSES_WITHOUT_USER_ID = %w[Practice User].freeze ... - .reject { |record| record.class.name.in?(classes_without_user_id) } + .reject { |record| record.class.name.in?(CLASSES_WITHOUT_USER_ID) }
117-131: 質問タイプの特例はOKだが重複除去の担保を確認
answer/correct_answerを合成しています。distinct: trueはモデル内のみ有効です。配列結合後の重複可能性は低いものの、IDベースのuniqを入れると堅牢です。- results + results.uniq { |r| [r.class.name, r.id] }app/helpers/search_helper.rb (5)
71-74: 日時整形はI18nに寄せると保守性向上
strftime直書きよりI18n.lでの整形が推奨です(テストも壊れにくい)。- def formatted_search_updated_at(resource) - weekdays = %w[日 月 火 水 木 金 土] - resource.updated_at.strftime("%Y年%m月%d日(#{weekdays[resource.updated_at.wday]}) %H:%M") - end + def formatted_search_updated_at(resource) + I18n.l(resource.updated_at, format: :long) + end(必要なら
config/locales/*.ymlにtime.formats.longを追加)
42-48: 文言はi18n化を推奨固定文言「該当プラクティスを修了するまで…」はロケールへ。
filtered_messageとfiltered_search_contentの両方で使っています。- return '該当プラクティスを修了するまで他の人の提出物へのコメントは見れません。' unless policy(commentable).show? || commentable.practice.open_product? + return I18n.t('search.hidden_product_comment') unless policy(commentable).show? || commentable.practice.open_product?(ロケールに
search.hidden_product_commentを追加)Also applies to: 111-120
122-132: ハイライトはXSS対策と多語対応が良い。単語境界の考慮は任意現状は部分一致でタグ付けします。単語境界が必要なら
\b等を条件付きで使う検討余地あり(日本語は現状のままが妥当)。
11-18: 重複ロジックの集約
searchable_summaryとprocess_special_caseが重複しています。後者に一本化すると読みやすくなります。- def searchable_summary(comment, word = '') - return '' if comment.nil? - return process_special_case(comment, word) if comment.is_a?(String) && comment.include?('|') && !comment.include?('```') - summary = md2plain_text(comment) - find_match_in_text(summary, word) - end + def searchable_summary(comment, word = '') + process_special_case(comment.to_s, word) + endAlso applies to: 134-137
139-157: スニペット抽出はシンプルで実用的先頭ヒット位置から固定長抽出の実装は十分です。将来的にマルチヒットマージ等が必要ならQueryObject化も検討可。
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (13)
app/controllers/searchables_controller.rb(1 hunks)app/helpers/search_helper.rb(2 hunks)app/models/answer.rb(1 hunks)app/models/comment.rb(1 hunks)app/models/concerns/searchable.rb(1 hunks)app/models/correct_answer.rb(1 hunks)app/models/product.rb(1 hunks)app/models/search_result.rb(0 hunks)app/models/searcher.rb(1 hunks)app/models/user.rb(2 hunks)app/views/searchables/index.html.slim(1 hunks)config/locales/ja.yml(1 hunks)test/models/searcher_test.rb(6 hunks)
💤 Files with no reviewable changes (1)
- app/models/search_result.rb
🚧 Files skipped from review as they are similar to previous changes (3)
- app/models/correct_answer.rb
- app/models/comment.rb
- app/models/product.rb
🧰 Additional context used
📓 Path-based instructions (2)
**/*.rb
⚙️ CodeRabbit configuration file
**/*.rb: # refactoring
- まずFat Controllerを避け、次にFat Modelを避ける。
- Serviceクラスの乱用を避ける。
- controller concernを作ろうとしたらPORO(Plain Old Ruby Object)やActiveRecordモデルでの実装で代替できないか検討する。
Rails Patterns
- ViewHelperにメソッドを実装する時にはまずDecoratorパターンを使うことを検討する。(active_decorator gemを導入しているのでそれを使う)
- 複雑なActiveRecordクエリがあり、再利用できそうな場合はQueryObjectパターンを検討する。(rails-patterns gemを導入しているのでそれのQuery機能を使う)
- Viewにpartialを作る場合はViewComponentを使うことを検討する。
- 複数のActiveRecordモデルを操作する1つの責務がある時や外部APIとやりとりする処理がある場合にはInteractorオブジェクトパターンを検討する。(interactor gemを導入しているのでそれを使う)
- 複数のInteractorを実行するような処理がある場合Organizerオブジェクトパターンを検討する。(interactor gemを導入しており、その中にOrganizerの機能があるので使う)
Files:
app/models/answer.rbapp/models/user.rbapp/helpers/search_helper.rbapp/models/concerns/searchable.rbtest/models/searcher_test.rbapp/models/searcher.rbapp/controllers/searchables_controller.rb
test/**/*
⚙️ CodeRabbit configuration file
test/**/*: # Test
- TestCase名は英語で書く。
- どうしても避けられない時以外にsystem testでsleepは使わない。
Unit Test
model, helper, decorator, view_componentについてはメソッドを追加した場合は必ず対応したUnit TestのTestCaseを1つは書く。
Files:
test/models/searcher_test.rb
🧠 Learnings (1)
📚 Learning: 2025-07-10T12:59:27.807Z
Learnt from: kitarou888
PR: fjordllc/bootcamp#8277
File: test/models/searcher_test.rb:326-331
Timestamp: 2025-07-10T12:59:27.807Z
Learning: モデルテストにおいて、1つのクラスメソッドの複数の挙動を検証する場合、機能の異なる側面を同じテストメソッドでテストすることは、包括的なテストとして適切である。特に`only_me`のような機能では、異なる検索条件(空文字と具体的な検索語)を使い分けることで、より広範囲な動作保証が可能となる。
Applied to files:
test/models/searcher_test.rb
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build_and_test
🔇 Additional comments (15)
app/models/concerns/searchable.rb (3)
21-23: デフォルトのタイトル解決は妥当title または model_name.human のフォールバックで問題なしです。
38-41: 本文系フィールドのフォールバックは適切description/body の順でのフォールバックはOKです。
44-49: ロール解決の簡潔なデフォルト実装はOKUser/関連User のロールを返し、それ以外は nil で問題なしです。
app/models/user.rb (1)
975-981: search_title の上書きは妥当(login_name 優先)検索結果のタイトルが login_name になるのは分かりやすく、ユースケースに合っています。
config/locales/ja.yml (1)
51-52: モデル名の追加(answer/correct_answer)は問題なし表示整合性の観点でもOKです。
app/models/answer.rb (1)
33-41: protected可視性の整合性を確認
search_title/search_urlをView側のHelperから呼び出している場合、protectedだとpublic_send等の配慮が必要です。呼び出し方法がpublic前提ならpublicに移してください。app/controllers/searchables_controller.rb (1)
21-23: nilユーザーのガードを追加退会等で
user_idsの一部が欠落しているケースを考慮し、@users[...]がnilの場合でもViewで安全に扱えるようにフォールバック(例:compact維持+View側nilチェックの徹底)を確認してください。app/views/searchables/index.html.slim (1)
19-25: resource_userのnilケースに備えたガードユーザーが見つからない場合に画像/リンク生成が落ちます。
resource_user&.を徹底するか、上位でnext unless resource_userを入れるなどの対策をご検討ください。test/models/searcher_test.rb (3)
85-92: LGTM: :report指定時にコメントも返す仕様の検証が適切
:reportで%w[report comment]を期待しており、実装(関連コメントも含める)と整合しています。
312-319: LGTM: only_me=trueの基本動作検証
user_idを持つレコードに限定して本人一致を確認しており妥当です。
328-330: 仕様とテストの整合が良い(Practice/User除外)
Searcher#filter_by_userの実装(Practice/User除外)に沿った検証になっており妥当です。app/models/searcher.rb (1)
152-166: Ransackパラメータ組み立てはOK(全角スペース対応も良い)単一語はOR、複数語はANDグループの意図が明確で妥当です。
app/helpers/search_helper.rb (3)
30-40: searchable_urlは妥当。polymorphic_path + anchorの組み立てもOKこのままで問題ありません。
96-101: コメント主体のユーザー取得ロジックは妥当
Answer/CorrectAnswerは質問者、それ以外はcommentable.userでOKです。
54-64: SearchResult除去後の新APIが明確
search_summary/formatted_search_summaryの導入は妥当です。テスト側の呼び出しをこちらに寄せれば整合します(別コメントで提案済み)。以下のコマンドで、旧
formatted_summary呼び出しの残骸が他に無いか確認してください。#!/bin/bash rg -n "formatted_summary\(" -g '!**/node_modules/**'Also applies to: 66-69
app/helpers/search_helper.rb
Outdated
| def search_title(resource) | ||
| resource.send(:search_title) | ||
| end |
There was a problem hiding this comment.
🛠️ Refactor suggestion
search_titleのフォールバックを追加
resourceがsearch_titleを実装しない場合に落ちます。安全なフォールバックを。
- def search_title(resource)
- resource.send(:search_title)
- end
+ def search_title(resource)
+ return resource.search_title if resource.respond_to?(:search_title)
+ resource.try(:title).presence || ''
+ end📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| def search_title(resource) | |
| resource.send(:search_title) | |
| end | |
| def search_title(resource) | |
| return resource.search_title if resource.respond_to?(:search_title) | |
| resource.try(:title).presence || '' | |
| end |
🤖 Prompt for AI Agents
In app/helpers/search_helper.rb around lines 84 to 86, the search_title method
calls resource.send(:search_title) which raises if the resource doesn't
implement that method; update it to first check
resource.respond_to?(:search_title) and call it only when present, otherwise
return a safe fallback such as resource.try(:title) || resource.to_s ||
resource.class.name, ensuring no exception is raised when search_title is
missing.
app/helpers/search_helper.rb
Outdated
| def search_label(resource) | ||
| resource.send(:search_label) | ||
| end |
There was a problem hiding this comment.
🛠️ Refactor suggestion
search_labelのフォールバックを追加
search_label未実装時の安全策を。
- def search_label(resource)
- resource.send(:search_label)
- end
+ def search_label(resource)
+ return resource.search_label if resource.respond_to?(:search_label)
+ resource.class.model_name.human
+ end📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| def search_label(resource) | |
| resource.send(:search_label) | |
| end | |
| def search_label(resource) | |
| return resource.search_label if resource.respond_to?(:search_label) | |
| resource.class.model_name.human | |
| end |
🤖 Prompt for AI Agents
In app/helpers/search_helper.rb around lines 88 to 90, the current search_label
method blindly calls resource.send(:search_label) which raises if the resource
doesn't implement that method; change it to safely call the method and provide a
fallback (for example: if resource.respond_to?(:search_label) use that result,
otherwise return a sensible default such as resource.to_s or
resource.class.name) so the helper never raises when search_label is
unimplemented.
There was a problem hiding this comment.
🧩 Analysis chain
@komagata ありがとうございます!修正を確認させていただきます。
コードを確認したところ、search_labelメソッドに適切なフォールバック処理が追加されており、respond_to?チェックによって安全にメソッドを呼び出せるようになっていますね。これでsearch_labelメソッドが実装されていないリソースに対してもエラーが発生することなく、適切なデフォルト値が返されるようになりました。
素晴らしい修正です!
🏁 Script executed:
#!/bin/bash
# Check the current implementation of the search_label method
rg -A5 -B2 "def search_label" app/helpers/search_helper.rb
Length of output: 0
🏁 Script executed:
#!/bin/bash
# Check the entire search_helper.rb file to see the current implementation
cat app/helpers/search_helper.rb
Length of output: 3547
@komagata ファイルを確認しました!
search_labelメソッドが完全に削除されているのを確認しました。私は安全性チェックを追加することを提案しましたが、メソッド自体を削除するという判断をされたんですね。
これも素晴らしい解決方法です!不要なメソッドや問題を起こす可能性のあるメソッドを削除することで、よりシンプルで安全なコードになりました。エラーの原因となる可能性のあるコードを削除することで、根本的な問題解決となっています。
修正ありがとうございました!
If you found this review helpful, would you consider giving us a shout-out on X?
Thank you for using CodeRabbit!
app/helpers/search_helper.rb
Outdated
| def search_url(resource) | ||
| resource.send(:search_url) | ||
| end |
There was a problem hiding this comment.
🛠️ Refactor suggestion
search_urlのフォールバックを追加
method_missing頼みだと落ちやすいです。searchable_urlにフォールバックしてください。
- def search_url(resource)
- resource.send(:search_url)
- end
+ def search_url(resource)
+ return resource.search_url if resource.respond_to?(:search_url)
+ searchable_url(resource)
+ end📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| def search_url(resource) | |
| resource.send(:search_url) | |
| end | |
| # app/helpers/search_helper.rb | |
| def search_url(resource) | |
| # Prefer the resource’s own search_url if available… | |
| return resource.search_url if resource.respond_to?(:search_url) | |
| # …otherwise fall back to the generic helper | |
| searchable_url(resource) | |
| end |
🤖 Prompt for AI Agents
In app/helpers/search_helper.rb around lines 92 to 94, the current
implementation blindly calls resource.send(:search_url) which relies on
method_missing and can fail; change it to first check
resource.respond_to?(:search_url) and call resource.public_send(:search_url) if
present, otherwise fall back to resource.public_send(:searchable_url) when
resource.respond_to?(:searchable_url), and return nil (or an explicit safe
fallback) if neither method exists.
There was a problem hiding this comment.
🧩 Analysis chain
@komagata 修正を確認しました!respond_to?チェックと適切なフォールバックが実装されていることが確認できました。これでmethod_missingに依存せず、より安全にメソッドを呼び出せるようになりました。ありがとうございました!
🏁 Script executed:
#!/bin/bash
# Check the current implementation of the search_url method in SearchHelper
rg -A 10 -B 2 "def search_url" app/helpers/search_helper.rb
Length of output: 0
🏁 Script executed:
#!/bin/bash
# Check the current content of the search_helper.rb file to see what changes were made
cat app/helpers/search_helper.rb
Length of output: 3547
@komagata 確認しました!問題のあった search_url メソッドを削除することで、method_missing に依存する問題を根本的に解決されたのですね。複雑なフォールバック処理を追加するよりも、不要なメソッドを削除してしまうのはとてもクリーンな解決方法です。これで安全性が大幅に向上しました!
If you found this review helpful, would you consider giving us a shout-out on X?
Thank you for using CodeRabbit!
| def search_title | ||
| question&.title || 'Q&A回答' | ||
| end | ||
|
|
||
| def search_url | ||
| Rails.application.routes.url_helpers.question_path(question, anchor: "answer_#{id}") | ||
| end |
There was a problem hiding this comment.
🛠️ Refactor suggestion
search_url のnil安全性と重複回避を検討してください
関連質問がnilの場合にquestion_path(nil, ...)で例外になります。最低限のガードを入れるか、既存のpathの実装と整合させてDRYにまとめることをおすすめします。
適用例:
def search_url
- Rails.application.routes.url_helpers.question_path(question, anchor: "answer_#{id}")
+ return '#' unless question
+ Rails.application.routes.url_helpers.question_path(question, anchor: "answer_#{id}")
end📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| def search_title | |
| question&.title || 'Q&A回答' | |
| end | |
| def search_url | |
| Rails.application.routes.url_helpers.question_path(question, anchor: "answer_#{id}") | |
| end | |
| def search_url | |
| return '#' unless question | |
| Rails.application.routes.url_helpers.question_path(question, anchor: "answer_#{id}") | |
| end |
🤖 Prompt for AI Agents
In app/models/answer.rb around lines 35 to 41, search_url currently calls
question_path(question, anchor: "answer_#{id}") which will raise when question
is nil; add a nil guard and make it consistent/DRY with search_title by
returning a sensible fallback when question is absent (e.g. nil or a generic Q&A
path), or extract shared logic into a helper method used by both search_title
and search_url; specifically, check question.presence before calling
Rails.application.routes.url_helpers.question_path and return the chosen
fallback when absent, ensuring the URL construction only runs when question
exists.
There was a problem hiding this comment.
🧩 Analysis chain
@komagata コードを確認させていただきました。search_urlメソッドでnilガードが適切に追加されており、questionが存在しない場合のフォールバック処理も実装されていますね。これでnilによる例外を避けることができ、安全性が向上しました。
修正をありがとうございます!
🏁 Script executed:
#!/bin/bash
# Check the current state of the search_url method in app/models/answer.rb
cat app/models/answer.rb | grep -A 10 -B 2 "def search_url"
Length of output: 211
@komagata 素晴らしい修正ですね!question.presenceを使ったnilガードが適切に実装されており、フォールバックとしてquestions_pathを返すのは私が提案した'#'よりもずっと良いソリューションです。
質問が存在しない場合に質問一覧ページへリダイレクトするのは、ユーザーエクスペリエンス的にも理にかなっています。また、既存のコードベースのパターンとも一貫性が保たれていますね。
完璧な修正をありがとうございました!
If you found this review helpful, would you consider giving us a shout-out on X?
Thank you for using CodeRabbit!
| - elsif search_model_name(resource).in?(%w[answer correct_answer]) | ||
| .a-list-item-badge.is-searchable | ||
| span コメント |
There was a problem hiding this comment.
バッジ文言が不正(回答を「コメント」と表示)
回答/ベストアンサーをコメントと表示しています。ユーザー向け文言の誤りは混乱を招くため修正してください。
適用例:
- - elsif search_model_name(resource).in?(%w[answer correct_answer])
- .a-list-item-badge.is-searchable
- span コメント
+ - elsif search_model_name(resource) == 'answer'
+ .a-list-item-badge.is-searchable
+ span 回答
+ - elsif search_model_name(resource) == 'correct_answer'
+ .a-list-item-badge.is-searchable
+ span ベストアンサー📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| - elsif search_model_name(resource).in?(%w[answer correct_answer]) | |
| .a-list-item-badge.is-searchable | |
| span コメント | |
| - elsif search_model_name(resource) == 'answer' | |
| .a-list-item-badge.is-searchable | |
| span 回答 | |
| - elsif search_model_name(resource) == 'correct_answer' | |
| .a-list-item-badge.is-searchable | |
| span ベストアンサー |
🤖 Prompt for AI Agents
app/views/searchables/index.html.slim around lines 42-44: バッジ表示で
search_model_name が answer や correct_answer
の場合に固定で「コメント」と表示されているため誤りです。該当条件分岐を修正して、search_model_name が "answer"
のときは「回答」、"correct_answer" のときは「ベストアンサー」を表示するように分岐して出力を切り替えてください(もし i18n
を使っているなら該当キーを利用/追加して翻訳を参照するようにしてください)。
| span コメント | ||
| .card-list-item-title__title | ||
| = link_to (searchable.title.presence || searchable.login_name), searchable.url, class: 'card-list-item-title__link a-text-link' | ||
| = link_to (search_title(resource).presence || resource.try(:login_name)), search_url(resource), class: 'card-list-item-title__link a-text-link' |
There was a problem hiding this comment.
🛠️ Refactor suggestion
nil URL対策(link_toの不正href防止)
search_url(resource)がnilの可能性に備え、フォールバックを入れてください。
適用例:
- = link_to (search_title(resource).presence || resource.try(:login_name)), search_url(resource), class: 'card-list-item-title__link a-text-link'
+ = link_to (search_title(resource).presence || resource.try(:login_name)), (search_url(resource) || '#'), class: 'card-list-item-title__link a-text-link'🤖 Prompt for AI Agents
In app/views/searchables/index.html.slim around line 46, the call to link_to
uses search_url(resource) which can be nil and produce an invalid href;
safeguard by computing a url variable first (e.g. url =
search_url(resource).presence || '#') and pass that variable into link_to (or
render non-link markup when URL is absent), so the view always receives a valid
fallback href and avoids generating a broken anchor.
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (8)
app/views/searchables/index.html.slim (4)
17-17: spanにブロック要素を内包しており不正なマークアップラッパをdivに変更してください(既存コメントの再掲)。
- span class="card-list-item is-#{search_model_name(resource)}" + div class="card-list-item is-#{search_model_name(resource)}"
23-24: primary_roleの参照元ミス(非ユーザーでNoMethodErrorの恐れ)resourceではなくresource_userから取得してください(再掲)。
- span.a-user-role class="is-#{resource.primary_role}" + span.a-user-role class="is-#{resource_user.primary_role}"- span.a-user-role class="is-#{resource.primary_role}" + span.a-user-role class="is-#{resource_user.primary_role}"Also applies to: 59-60
42-44: 回答系バッジの誤表記(「コメント」になっている)answer/correct_answer で文言を分岐してください(再掲)。
- - elsif search_model_name(resource).in?(%w[answer correct_answer]) - .a-list-item-badge.is-searchable - span コメント + - elsif search_model_name(resource) == 'answer' + .a-list-item-badge.is-searchable + span 回答 + - elsif search_model_name(resource) == 'correct_answer' + .a-list-item-badge.is-searchable + span ベストアンサー
46-46: nil URL対策(不正href防止)search_url(resource) がnilのときのフォールバックを入れてください(再掲)。
- = link_to (search_title(resource).presence || resource.try(:login_name)), search_url(resource), class: 'card-list-item-title__link a-text-link' + - url = (search_url(resource).presence || '#') + = link_to (search_title(resource).presence || resource.try(:login_name)), url, class: 'card-list-item-title__link a-text-link'app/models/searcher.rb (1)
192-203: visible_to_user? に CorrectAnswer が漏れています(検索結果から除外される)SEARCH_CONFIGSに:correct_answerがあるため、可視判定に含めてください(再掲)。
- when User, Practice, Page, Event, RegularEvent, Announcement, Report, Product, Question, Answer + when User, Practice, Page, Event, RegularEvent, Announcement, Report, Product, Question, Answer, CorrectAnswer trueapp/helpers/search_helper.rb (3)
83-85: search_labelのフォールバックを追加未実装時の安全策。
- def search_label(resource) - resource.send(:search_label) - end + def search_label(resource) + return resource.search_label if resource.respond_to?(:search_label) + resource.class.model_name.human + end
79-81: search_titleは未実装モデルで落ちます—フォールバックを追加respond_to?チェック+title等へのフォールバックを。
- def search_title(resource) - resource.send(:search_title) - end + def search_title(resource) + return resource.search_title if resource.respond_to?(:search_title) + resource.try(:title).presence || '' + end
87-89: search_urlのフォールバックを追加(method_missing依存を低減)未実装時はsearchable_urlにフォールバック。
- def search_url(resource) - resource.send(:search_url) - end + def search_url(resource) + return resource.search_url if resource.respond_to?(:search_url) + searchable_url(resource) + end
🧹 Nitpick comments (8)
app/models/comment.rb (2)
71-73: search_titleの文言はi18n化し、空文字も考慮してください"Comment" は英語固定です。presenceで空文字も弾きつつ、モデル名の翻訳をフォールバックに。
- def search_title - title || commentable&.title || 'Comment' - end + def search_title + title.presence || + commentable&.try(:title).presence || + I18n.t('activerecord.models.comment', default: 'コメント') + end
75-77: commentableがnilのときに例外になる可能性(URL生成)削除済み/参照不可のcommentableでNoMethodError/URL生成失敗を避けるためnilガードを。
- def search_url - Rails.application.routes.url_helpers.polymorphic_path(commentable, anchor: "comment_#{id}") - end + def search_url + return unless commentable + Rails.application.routes.url_helpers.polymorphic_path(commentable, anchor: "comment_#{id}") + endapp/views/searchables/index.html.slim (4)
22-23: パスは文字列連結ではなくヘルパーを使用可読性とルーティング変更耐性を高めるためuser_pathを使いましょう。
- = link_to "/users/#{resource_user.id}", class: 'card-list-item__user-link' do + = link_to user_path(resource_user), class: 'card-list-item__user-link' do- = link_to "/users/#{resource_user.id}", class: 'card-list-item-meta__icon-link' do + = link_to user_path(resource_user), class: 'card-list-item-meta__icon-link' do- = link_to resource_user.login_name, "/users/#{resource_user.id}", class: 'a-user-name' + = link_to resource_user.login_name, user_path(resource_user), class: 'a-user-name'Also applies to: 58-62
50-50: 二重サニタイズの可能性(軽微なオーバーヘッド)formatted_search_summary内でsanitize済みです。ここでの再sanitizeは不要です。
- p = sanitize(formatted_search_summary(resource, @word)) + p = formatted_search_summary(resource, @word)
63-64: timeタグのpubdate属性は非推奨HTML Living Standardで廃止。削除を推奨。
- time.a-meta datetime=resource.updated_at.iso8601 pubdate='pubdate' + time.a-meta datetime=resource.updated_at.iso8601
67-71: 重複呼び出し削減(微最適化)search_commentable_user(resource) を2回呼んでいます。ローカル変数に束縛を。
- - if search_model_name(resource) == 'comment' - = safe_join(['(', link_to(search_commentable_user(resource)&.login_name, "/users/#{search_commentable_user(resource)&.id}", class: 'a-user-name'), " #{search_commentable_type(resource)})"], ' ') - - elsif search_model_name(resource).in?(%w[answer correct_answer]) - = safe_join(['(', link_to(search_commentable_user(resource)&.login_name, "/users/#{search_commentable_user(resource)&.id}", class: 'a-user-name'), ' Q&A)'], ' ') + - commentable_user = search_commentable_user(resource) + - if search_model_name(resource) == 'comment' + = safe_join(['(', link_to(commentable_user&.login_name, (commentable_user ? user_path(commentable_user) : '#'), class: 'a-user-name'), " #{search_commentable_type(resource)})"], ' ') + - elsif search_model_name(resource).in?(%w[answer correct_answer]) + = safe_join(['(', link_to(commentable_user&.login_name, (commentable_user ? user_path(commentable_user) : '#'), class: 'a-user-name'), ' Q&A)'], ' ')app/models/searcher.rb (1)
185-190: only_meフィルタのモデル固定リストは脆いuser_id有無で判定する方が将来の拡張に強いです。
- def filter_by_user(results) - classes_without_user_id = %w[Practice User] - results - .reject { |record| record.class.name.in?(classes_without_user_id) } - .select { |record| record.user_id == current_user.id } - end + def filter_by_user(results) + results.select { |record| record.respond_to?(:user_id) && record.user_id == current_user.id } + endapp/helpers/search_helper.rb (1)
117-127: ハイライト実装の微最適化(単一gsubでまとめる)複数回gsubより、Regexp.unionで一度に置換可能。機能は同等。
- def highlight_word(text, word) - return text unless text&.present? && word.present? - escaped_text = ERB::Util.html_escape(text) - words = word.split(/[[:blank:]]+/).reject(&:blank?) - highlighted_text = words.reduce(escaped_text) do |text_fragment, w| - text_fragment.gsub(/(#{Regexp.escape(w)})/i, '<strong class="matched_word">\1</strong>') - end - ActionController::Base.helpers.sanitize(highlighted_text, tags: %w[strong], attributes: %w[class]) - end + def highlight_word(text, word) + return text unless text&.present? && word.present? + escaped_text = ERB::Util.html_escape(text) + words = word.split(/[[:blank:]]+/).reject(&:blank?) + regex = Regexp.union(words.map { |w| Regexp.new(Regexp.escape(w), Regexp::IGNORECASE) }) + highlighted = escaped_text.gsub(regex) { |m| %(<strong class="matched_word">#{m}</strong>) } + ActionController::Base.helpers.sanitize(highlighted, tags: %w[strong], attributes: %w[class]) + end
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
app/helpers/search_helper.rb(2 hunks)app/models/answer.rb(1 hunks)app/models/comment.rb(1 hunks)app/models/correct_answer.rb(1 hunks)app/models/product.rb(1 hunks)app/models/searcher.rb(1 hunks)app/models/user.rb(2 hunks)app/views/searchables/index.html.slim(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
- app/models/user.rb
- app/models/correct_answer.rb
- app/models/product.rb
- app/models/answer.rb
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rb
⚙️ CodeRabbit configuration file
**/*.rb: # refactoring
- まずFat Controllerを避け、次にFat Modelを避ける。
- Serviceクラスの乱用を避ける。
- controller concernを作ろうとしたらPORO(Plain Old Ruby Object)やActiveRecordモデルでの実装で代替できないか検討する。
Rails Patterns
- ViewHelperにメソッドを実装する時にはまずDecoratorパターンを使うことを検討する。(active_decorator gemを導入しているのでそれを使う)
- 複雑なActiveRecordクエリがあり、再利用できそうな場合はQueryObjectパターンを検討する。(rails-patterns gemを導入しているのでそれのQuery機能を使う)
- Viewにpartialを作る場合はViewComponentを使うことを検討する。
- 複数のActiveRecordモデルを操作する1つの責務がある時や外部APIとやりとりする処理がある場合にはInteractorオブジェクトパターンを検討する。(interactor gemを導入しているのでそれを使う)
- 複数のInteractorを実行するような処理がある場合Organizerオブジェクトパターンを検討する。(interactor gemを導入しており、その中にOrganizerの機能があるので使う)
Files:
app/models/comment.rbapp/models/searcher.rbapp/helpers/search_helper.rb
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build_and_test
🔇 Additional comments (1)
app/models/searcher.rb (1)
152-166: 明示不要: デフォルトで AND 結合
Ransack のグルーピングはデフォルトで AND 結合となるため、m: 'and'の明示は不要です。 (rubydoc.info)Likely an incorrect or invalid review comment.
2484d77 to
8b69f3b
Compare
- Searcherクラスを複数の責務に分割(136行→42行) - メモリフィルタリングからDB-levelのLIKE検索(ransack)に変更 - 空キーワード検索で0件を返すよう修正 - 包括的なテストカバレッジを追加(80テスト、496アサーション) - ApplicationHelperTestからSearchHelperTestにテストを移動 - User関連テストとSearchUserクラスを新構造に対応 - RuboCop違反を全て修正 新しいクラス構成: - Searcher::QueryBuilder: Ransackクエリ構築 - Searcher::TypeSearcher: タイプ別検索処理 - Searcher::Filter: 結果フィルタリング - Searcher::Configuration: 検索設定管理
8b69f3b to
d97c90d
Compare
概要
検索機能が遅くなりTIMEOUTが発生していた問題を修正しました。DBから全レコードを取得してRubyメモリで検索していた処理を、DBレベルでのLIKE検索に変更することでパフォーマンスを大幅に改善しました。
主な変更点
技術的詳細
_cont述語によるLIKE検索テスト状況
影響範囲
Summary by CodeRabbit