Skip to content

動画一覧にタグ一覧と絞り込み機能を追加#8590

Merged
komagata merged 9 commits intomainfrom
feature/add-list-of-movies-by-tag
Aug 25, 2025
Merged

動画一覧にタグ一覧と絞り込み機能を追加#8590
komagata merged 9 commits intomainfrom
feature/add-list-of-movies-by-tag

Conversation

@reckyy
Copy link
Copy Markdown
Contributor

@reckyy reckyy commented Apr 29, 2025

Issue

概要

下記を追加しました。

  • 動画一覧画面にタグ一覧の表示
  • タグで絞り込んだ動画一覧の表示

動画機能は本番環境にデプロイされていますが、まだ全機能が実装されておらず使用段階ではないため隠匿されています。なので、動作確認の際は直接URLを入力してページを開いてください。

変更確認方法

  1. feature/add-list-of-movies-by-tagをローカルに取り込む
  2. http://localhost:3000/movies にアクセス
  3. 一覧下部にタグ一覧が表示されていることを確認
  4. 任意のタグをクリックすると、そのタグがついた動画一覧が表示されることを確認

Screenshot

動画一覧ページ

変更前 変更後
スクリーンショット 2025-04-30 10 51 44 スクリーンショット_2025-04-30_10_50_49

タグで絞り込んだページ

変更前 変更後
なし スクリーンショット 2025-04-30 10 51 02

Summary by CodeRabbit

  • 新機能

    • 映画一覧をタグで絞り込み可能にし、タグコンテキストを表示、ページング(更新順)に対応。
    • ヘッダーにタグ名と件数を表示、meta説明をタグに応じて切替、サイドバーにタグ一覧を追加。
    • 管理者向けにタグ名編集・統合のモーダルUIと即時反映するインページ編集(JS)を追加。
    • タグ専用のURLルートを追加。
  • バグ修正

    • タグ編集で同名への更新時に送信ボタンを無効化。
  • テスト

    • タグ検索・編集・権限制御・統合をカバーするシステムテスト、コンポーネントテストとフィクスチャを追加。

@reckyy reckyy self-assigned this Apr 29, 2025
@reckyy reckyy marked this pull request as ready for review April 30, 2025 02:03
@reckyy
Copy link
Copy Markdown
Contributor Author

reckyy commented Apr 30, 2025

@riq0h
お疲れ様です!
こちらのPRのレビューをお願いしたく、ご連絡いたしました。
お手隙の際にご対応いただけると嬉しいです。全く急ぎではないです。
もし、ご都合悪ければ仰ってください!
よろしくお願いいたします。

@reckyy reckyy requested a review from riq0h April 30, 2025 02:09
Copy link
Copy Markdown
Contributor

@riq0h riq0h left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

確認しました! OKと思われるのでApproveさせていただきました🙇

@reckyy reckyy force-pushed the feature/add-list-of-movies-by-tag branch from 2c65d1d to 4ae5f99 Compare June 15, 2025 12:50
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Jun 15, 2025

Walkthrough

映画一覧にタグ絞り込みが追加され、MoviesController#indexがタグコンテキスト(@tag, @tags)を渡すようになりました。Movieモデルにby_tagスコープが追加され、タグ編集用のコンポーネント/JS/ルート/テスト/フィクスチャが導入されています。

Changes

Cohort / File(s) 変更内容概要
コントローラ
app/controllers/movies_controller.rb
index@tag = Tag.find_by(name: params[:tag])@tags = Movie.all.all_tagsを設定し、Movie.by_tag(params[:tag]).includes(:user).order(updated_at: :desc, id: :desc).page(params[:page]).per(PAGER_NUMBER)で絞り込むように変更
モデル
app/models/movie.rb
scope :by_tag, ->(tag) { tag.present? ? tagged_with(tag) : all } を追加
ビュー
app/views/movies/index.html.slim
タグがある場合のmeta説明とヘッダー(「タグ『name』の動画(件数)」)を追加。管理者向けにTag::EditComponentを表示し、ページ下部にタグ一覧のサイドナビを追加
ルーティング
config/routes.rb
get "movies/tags/:tag", to: "movies#index", as: :movies_tag, tag: /.+/, format: "html" を追加
タグ編集コンポーネント
app/components/tag/edit_component.rb, app/components/tag/edit_component.html.slim
Tag::EditComponentクラスとタグ名変更モーダルのテンプレートを追加
JavaScript
app/javascript/tag_edit.js, app/javascript/packs/application.js
タグ編集モーダルの表示・入力検証・PATCH /api/tags/:id の送信と成功時のタグ一覧URL更新処理を追加。application.jsに../tag_edit.jsをimport
システムテスト
test/system/movie/tags_test.rb
タグ検索、ページ遷移なしでのタグ更新、管理者のタグ編集、非管理者制限、タグ名変更・マージ・同名更新不可ケースを検証するシステムテストを追加
コンポーネントテスト
test/components/tag/edit_component_test.rb
Tag::EditComponenttag_id/tag_nameを検証する単体テストを追加
テストフィクスチャ
test/fixtures/acts_as_taggable_on/taggings.yml
映画に対するタグ付けフィクスチャを3件追加(movie1: game, movie2: beginner, movie2: cat

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Browser
    participant MoviesController
    participant TagModel
    participant MovieModel
    participant JS
    participant API

    Note over User,Browser: ユーザが一覧またはタグページにアクセス
    User->>Browser: GET /movies or /movies/tags/:tag
    Browser->>MoviesController: request (params[:tag])
    MoviesController->>TagModel: Tag.find_by(name: params[:tag]) (if present)
    MoviesController->>MovieModel: Movie.by_tag(params[:tag]).includes(:user).order(...).page(...)
    MovieModel-->>MoviesController: movies
    MoviesController-->>Browser: render index (movies, tags, tag)

    Note over Browser,JS: 管理者がタグ編集を開く
    Browser->>JS: click "タグ名変更" → show modal
    JS->>API: PATCH /api/tags/:id { tag: { name } }
    API-->>JS: 200 OK
    JS->>Browser: update URL to /movies/tags/:new_name (replace current tag page)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • komagata

Poem

ぴょんと飛んでタグを見つけたよ 🐇
映画に名を添えて数を数え、
こっそりモーダルで名前変える、
JSが道を直してページを繋ぎ、
テストがぴょんと結果を守る。

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 RuboCop (1.76.1)
app/components/tag/edit_component.rb

rubocop-minitest extension supports plugin, specify plugins: rubocop-minitest instead of require: rubocop-minitest in /.rubocop.yml.
For more information, see https://docs.rubocop.org/rubocop/plugin_migration_guide.html.
rubocop-capybara extension supports plugin, specify plugins: rubocop-capybara instead of require: rubocop-capybara in /.rubocop.yml.
For more information, see https://docs.rubocop.org/rubocop/plugin_migration_guide.html.
Unable to find gem rubocop-fjord; is the gem installed? Gem::MissingSpecError
/var/lib/gems/3.1.0/gems/rubocop-1.79.2/lib/rubocop/config_loader_resolver.rb:309:in rescue in gem_config_path' /var/lib/gems/3.1.0/gems/rubocop-1.79.2/lib/rubocop/config_loader_resolver.rb:293:in gem_config_path'
/var/lib/gems/3.1.0/gems/rubocop-1.79.2/lib/rubocop/config_loader_resolver.rb:84:in block (2 levels) in resolve_inheritance_from_gems' /var/lib/gems/3.1.0/gems/rubocop-1.79.2/lib/rubocop/config_loader_resolver.rb:82:in reverse_each'
/var/lib/gems/3.1.0/gems/rubocop-1.79.2/lib/rubocop/config_loader_resolver.rb:82:in block in resolve_inheritance_from_gems' /var/lib/gems/3.1.0/gems/rubocop-1.79.2/lib/rubocop/config_loader_resolver.rb:76:in each_pair'
/var/lib/gems/3.1.0/gems/rubocop-1.79.2/lib/rubocop/config_loader_resolver.rb:76:in resolve_inheritance_from_gems' /var/lib/gems/3.1.0/gems/rubocop-1.79.2/lib/rubocop/config_loader.rb:58:in load_file'
/var/lib/gems/3.1.0/gems/rubocop-1.79.2/lib/rubocop/config_store.rb:29:in options_config=' /var/lib/gems/3.1.0/gems/rubocop-1.79.2/lib/rubocop/cli.rb:162:in act_on_options'
/var/lib/gems/3.1.0/gems/rubocop-1.79.2/lib/rubocop/cli.rb:47:in block in run' /var/lib/gems/3.1.0/gems/rubocop-1.79.2/lib/rubocop/cli.rb:82:in profile_if_needed'
/var/lib/gems/3.1.0/gems/rubocop-1.79.2/lib/rubocop/cli.rb:43:in run' /var/lib/gems/3.1.0/gems/rubocop-1.79.2/exe/rubocop:19:in <top (required)>'
/usr/local/bin/rubocop:25:in load' /usr/local/bin/rubocop:25:in

'

app/controllers/movies_controller.rb

rubocop-minitest extension supports plugin, specify plugins: rubocop-minitest instead of require: rubocop-minitest in /.rubocop.yml.
For more information, see https://docs.rubocop.org/rubocop/plugin_migration_guide.html.
rubocop-capybara extension supports plugin, specify plugins: rubocop-capybara instead of require: rubocop-capybara in /.rubocop.yml.
For more information, see https://docs.rubocop.org/rubocop/plugin_migration_guide.html.
Unable to find gem rubocop-fjord; is the gem installed? Gem::MissingSpecError
/var/lib/gems/3.1.0/gems/rubocop-1.79.2/lib/rubocop/config_loader_resolver.rb:309:in rescue in gem_config_path' /var/lib/gems/3.1.0/gems/rubocop-1.79.2/lib/rubocop/config_loader_resolver.rb:293:in gem_config_path'
/var/lib/gems/3.1.0/gems/rubocop-1.79.2/lib/rubocop/config_loader_resolver.rb:84:in block (2 levels) in resolve_inheritance_from_gems' /var/lib/gems/3.1.0/gems/rubocop-1.79.2/lib/rubocop/config_loader_resolver.rb:82:in reverse_each'
/var/lib/gems/3.1.0/gems/rubocop-1.79.2/lib/rubocop/config_loader_resolver.rb:82:in block in resolve_inheritance_from_gems' /var/lib/gems/3.1.0/gems/rubocop-1.79.2/lib/rubocop/config_loader_resolver.rb:76:in each_pair'
/var/lib/gems/3.1.0/gems/rubocop-1.79.2/lib/rubocop/config_loader_resolver.rb:76:in resolve_inheritance_from_gems' /var/lib/gems/3.1.0/gems/rubocop-1.79.2/lib/rubocop/config_loader.rb:58:in load_file'
/var/lib/gems/3.1.0/gems/rubocop-1.79.2/lib/rubocop/config_store.rb:29:in options_config=' /var/lib/gems/3.1.0/gems/rubocop-1.79.2/lib/rubocop/cli.rb:162:in act_on_options'
/var/lib/gems/3.1.0/gems/rubocop-1.79.2/lib/rubocop/cli.rb:47:in block in run' /var/lib/gems/3.1.0/gems/rubocop-1.79.2/lib/rubocop/cli.rb:82:in profile_if_needed'
/var/lib/gems/3.1.0/gems/rubocop-1.79.2/lib/rubocop/cli.rb:43:in run' /var/lib/gems/3.1.0/gems/rubocop-1.79.2/exe/rubocop:19:in <top (required)>'
/usr/local/bin/rubocop:25:in load' /usr/local/bin/rubocop:25:in

'

config/routes.rb

rubocop-minitest extension supports plugin, specify plugins: rubocop-minitest instead of require: rubocop-minitest in /.rubocop.yml.
For more information, see https://docs.rubocop.org/rubocop/plugin_migration_guide.html.
rubocop-capybara extension supports plugin, specify plugins: rubocop-capybara instead of require: rubocop-capybara in /.rubocop.yml.
For more information, see https://docs.rubocop.org/rubocop/plugin_migration_guide.html.
Unable to find gem rubocop-fjord; is the gem installed? Gem::MissingSpecError
/var/lib/gems/3.1.0/gems/rubocop-1.79.2/lib/rubocop/config_loader_resolver.rb:309:in rescue in gem_config_path' /var/lib/gems/3.1.0/gems/rubocop-1.79.2/lib/rubocop/config_loader_resolver.rb:293:in gem_config_path'
/var/lib/gems/3.1.0/gems/rubocop-1.79.2/lib/rubocop/config_loader_resolver.rb:84:in block (2 levels) in resolve_inheritance_from_gems' /var/lib/gems/3.1.0/gems/rubocop-1.79.2/lib/rubocop/config_loader_resolver.rb:82:in reverse_each'
/var/lib/gems/3.1.0/gems/rubocop-1.79.2/lib/rubocop/config_loader_resolver.rb:82:in block in resolve_inheritance_from_gems' /var/lib/gems/3.1.0/gems/rubocop-1.79.2/lib/rubocop/config_loader_resolver.rb:76:in each_pair'
/var/lib/gems/3.1.0/gems/rubocop-1.79.2/lib/rubocop/config_loader_resolver.rb:76:in resolve_inheritance_from_gems' /var/lib/gems/3.1.0/gems/rubocop-1.79.2/lib/rubocop/config_loader.rb:58:in load_file'
/var/lib/gems/3.1.0/gems/rubocop-1.79.2/lib/rubocop/config_store.rb:29:in options_config=' /var/lib/gems/3.1.0/gems/rubocop-1.79.2/lib/rubocop/cli.rb:162:in act_on_options'
/var/lib/gems/3.1.0/gems/rubocop-1.79.2/lib/rubocop/cli.rb:47:in block in run' /var/lib/gems/3.1.0/gems/rubocop-1.79.2/lib/rubocop/cli.rb:82:in profile_if_needed'
/var/lib/gems/3.1.0/gems/rubocop-1.79.2/lib/rubocop/cli.rb:43:in run' /var/lib/gems/3.1.0/gems/rubocop-1.79.2/exe/rubocop:19:in <top (required)>'
/usr/local/bin/rubocop:25:in load' /usr/local/bin/rubocop:25:in

'

  • 3 others

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/add-list-of-movies-by-tag

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.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🔭 Outside diff range comments (1)
app/controllers/movies_controller.rb (1)

11-18: 🛠️ Refactor suggestion

@movies が nil になる可能性があります

上記 by_tag の仕様に依存しているため、現状 params[:tag] が無い通常の /movies で 500 エラーになります。
モデル側を修正するか、コントローラ側でフォールバックする処理を追加してください。

-    @movies = Movie.by_tag(params[:tag])
+    @movies = Movie.by_tag(params[:tag])

(モデル修正が入ればこの行はそのままで可)

🧹 Nitpick comments (4)
app/controllers/movies_controller.rb (1)

12-13: タグ一覧取得の N+1 & 並び順

Movie.all.all_tags は全ムービーをロードしてからタグを集計するためメモリ負荷が高いです。
ActsAsTaggableOn には ActsAsTaggableOn::Tag.for_context(:tags) を直接呼ぶ方法があります。加えて order(:name) を付けると UI が安定します。

app/views/movies/index.html.slim (2)

25-37: ヘッダが二重定義になっています

既存 .page-main-header に加えてタグ用ヘッダをもう一つ描画しているため、同一ページに同クラスの要素が 2 つ並びます。CSS の想定外挙動やアクセシビリティに影響するので、
・既存ヘッダを条件分岐で置き換える
・もしくはタグ用情報だけを既存ヘッダ内に差し込む
など構造を一本化してください。


49-61: タグ一覧リンクの表示順を安定化

視認性向上のため @tags を名前昇順で並べ替えると UX が向上します。

test/system/movie/tags_test.rb (1)

51-62: マージ後の件数ハードコードはフィクスチャ変更で壊れやすい

assert_text "タグ「#{new_tag.name}」の動画(2)"2 はタグ付け件数に依存するハードコードです。
今後フィクスチャや実装を変更すると値がずれテストが落ちるリスクがあります。件数ではなく、対象動画タイトルの一部を確認するなど、より堅牢なアサーションに置き換えると保守性が向上します。

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 024fd00 and 4ae5f99.

📒 Files selected for processing (6)
  • app/controllers/movies_controller.rb (1 hunks)
  • app/models/movie.rb (1 hunks)
  • app/views/movies/index.html.slim (3 hunks)
  • config/routes.rb (1 hunks)
  • test/fixtures/acts_as_taggable_on/taggings.yml (1 hunks)
  • test/system/movie/tags_test.rb (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build_and_test
🔇 Additional comments (4)
test/fixtures/acts_as_taggable_on/taggings.yml (1)

105-118: タグ付けフィクスチャの追加は問題なし
Movie へのタグ付与がテストに必要な最小限の行追加で済んでおり、他既存データとの衝突もありません。

config/routes.rb (1)

122-122: ルート追加は妥当です
/movies/tags/:tag を先に定義しているため RESTful resources :movies との衝突も回避されています。

app/views/movies/index.html.slim (1)

2-5: meta description は OK
タグ有無でメタを分岐させる実装は問題ありません。

test/system/movie/tags_test.rb (1)

18-25: Ajax 完了待ちが無い場合、テストが不安定になる可能性

click_on '保存' 後に非同期リクエストが走る場合、即座に assert_text '追加タグ' を実行すると要素がまだ描画されておらず失敗するケースがあります。
assert_text は一定時間リトライしますが、処理が長い場合は assert_no_css('.is-loading') 等でロード完了を明示的に待つと安定します。
必要に応じてご検討ください。

@machida machida assigned thmz337 and unassigned reckyy Jul 7, 2025
@machida
Copy link
Copy Markdown
Member

machida commented Jul 7, 2025

@thmz337
こちらのreckyyさんの続きをお願いします:pray:
coderabbitaiのレビュー対応をしたら、メンターレビューに進めてくださいー

@thmz337
Copy link
Copy Markdown
Contributor

thmz337 commented Jul 10, 2025

@machida

お疲れ様です!こちら現状把握も兼ねて動作確認を行っていたのですが、
添付の画像のように画面を一定以上広げると、タグ一覧が動画一覧に被ってしまうようです。

image

こちらは仕様になるのでしょうか。メンターレビューに進んでも良いかご確認いただけますと幸いです。

@machida
Copy link
Copy Markdown
Member

machida commented Jul 10, 2025

@thmz337 こちらは僕の方で修正しますー レビューは進めてくださいー

@thmz337
Copy link
Copy Markdown
Contributor

thmz337 commented Jul 11, 2025

@machida
ご確認ありがとうございます。作業進めさせていだきます。

@thmz337 thmz337 requested a review from komagata July 12, 2025 11:08
@thmz337
Copy link
Copy Markdown
Contributor

thmz337 commented Jul 13, 2025

@komagata @machida

お疲れ様です。こちら修正が完了しましたので、レビューをお願いできますでしょうか。
よろしくお願いいたします。

@komagata
Copy link
Copy Markdown
Member

@thmz337 conflictの修正をお願いします。

@thmz337 thmz337 force-pushed the feature/add-list-of-movies-by-tag branch from 6d44136 to be28986 Compare July 19, 2025 02:56
@komagata
Copy link
Copy Markdown
Member

@thmz337 コミットのところのアイコンがデフォルトになっているようです。

image

@thmz337
Copy link
Copy Markdown
Contributor

thmz337 commented Jul 25, 2025

@komagata

コミットのところのアイコンがデフォルトになっているようです。

修正しましたので、ご確認よろしくお願いいたします。

@komagata
Copy link
Copy Markdown
Member

@thmz337 commit内容とメッセージが合っていないように思います。

スクリーンショット 2025-07-28 16 04 59

これですとこのPRでは具体的に実装した部分がないように見えちゃうかなと思います。

@thmz337 thmz337 force-pushed the feature/add-list-of-movies-by-tag branch from 6f08986 to 3563dc6 Compare July 28, 2025 12:49
@thmz337
Copy link
Copy Markdown
Contributor

thmz337 commented Jul 28, 2025

@komagata

お疲れ様です。修正しましたので、ご確認よろしくお願いいたします。

.page-main-header__end
.page-main-header-actions
.page-main-header-actions__items
= react_component('Tags/TagEditButton', tagId: @tag.id, tagName: @tag.name)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Vue.jsとReactを外すIssueを他で進めているところなので極力新しいReactの部分は作らず、バニラJSで行きたい感じです。

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@komagata

確認ありがとうございます。こちらも同様のissueで対応した方が良いでしょうか。切り分けた方が良いでしょうか。

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@thmz337 こちらはIssueの内容から想定される内容なのでこのPRで実装して大丈夫です。

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@komagata

確認ありがとうございます。承知しました。

@thmz337 thmz337 force-pushed the feature/add-list-of-movies-by-tag branch from 89275df to 7128291 Compare August 4, 2025 15:04
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
app/javascript/tag_edit.js (1)

67-69: エラーハンドリングの改善を検討してください

現在はコンソールにエラーをログ出力するだけですが、ユーザーにエラーメッセージを表示することを検討してください。

     .catch((error) => {
       console.warn(error)
+      alert('タグの更新に失敗しました。再度お試しください。')
     })
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 89275df and 7128291.

📒 Files selected for processing (11)
  • app/components/tag/edit_component.html.slim (1 hunks)
  • app/components/tag/edit_component.rb (1 hunks)
  • app/controllers/movies_controller.rb (1 hunks)
  • app/javascript/packs/application.js (1 hunks)
  • app/javascript/tag_edit.js (1 hunks)
  • app/models/movie.rb (1 hunks)
  • app/views/movies/index.html.slim (3 hunks)
  • config/routes.rb (1 hunks)
  • test/components/tag/edit_component_test.rb (1 hunks)
  • test/fixtures/acts_as_taggable_on/taggings.yml (1 hunks)
  • test/system/movie/tags_test.rb (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • app/javascript/packs/application.js
🚧 Files skipped from review as they are similar to previous changes (6)
  • app/controllers/movies_controller.rb
  • app/models/movie.rb
  • config/routes.rb
  • app/views/movies/index.html.slim
  • test/fixtures/acts_as_taggable_on/taggings.yml
  • test/system/movie/tags_test.rb
🧰 Additional context used
📓 Path-based instructions (3)
**/*.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/components/tag/edit_component.rb
  • test/components/tag/edit_component_test.rb
**/*.js

⚙️ CodeRabbit Configuration File

**/*.js: - どうしても避けられない時以外に新規にVue.js, Reactのコードを追加しない。

  • fetchメソッドの代わりにrequest.jsを使う。

Files:

  • app/javascript/tag_edit.js
test/**/*

⚙️ CodeRabbit Configuration File

test/**/*: # Test

  • TestCase名は英語で書く。
  • どうしても避けられない時以外にsystem testでsleepは使わない。

Unit Test

model, helper, decorator, view_componentについてはメソッドを追加した場合は必ず対応したUnit TestのTestCaseを1つは書く。

Files:

  • test/components/tag/edit_component_test.rb
🧠 Learnings (2)
📓 Common learnings
Learnt from: ryufuta
PR: fjordllc/bootcamp#8992
File: app/models/correct_answer_notifier.rb:23-23
Timestamp: 2025-07-30T07:26:36.599Z
Learning: PRのスコープを維持することが重要であり、バグ修正PRにおいて、修正対象以外の機能(例:ルーティングテスト)の追加提案は適切ではない。そのような改善が必要な場合は別PRで対応すべきである。
📚 Learning: fjordllc/bootcampプロジェクトでは、hタグ(見出し)の文言は日本語でハードコーディングする方針が確立されており、i18n対応は行わない。例:app/views/welcome/logo...
Learnt from: jun-kondo
PR: fjordllc/bootcamp#0
File: :0-0
Timestamp: 2025-07-23T20:42:19.974Z
Learning: fjordllc/bootcampプロジェクトでは、hタグ(見出し)の文言は日本語でハードコーディングする方針が確立されており、I18n対応は行わない。例:app/views/welcome/logo.html.slimでh2、h3タグが日本語でハードコーディングされている。

Applied to files:

  • app/components/tag/edit_component.html.slim
⏰ 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 (7)
test/components/tag/edit_component_test.rb (1)

14-20: 基本的なテストケースは適切です

tag_idtag_nameの属性が正しく公開されていることを確認するテストは適切です。シンプルなコンポーネントには十分なテストカバレッジです。

app/components/tag/edit_component.rb (1)

3-10: ViewComponentパターンに従った適切な実装です

シンプルでわかりやすいコンポーネント実装になっています。attr_readerを使用してデータを公開し、コンストラクタで必要な情報を抽出する設計は適切です。

app/components/tag/edit_component.html.slim (2)

1-4: モーダルトリガーボタンの実装は適切です

アイコンと日本語ラベルを使用したボタンの実装は、プロジェクトのガイドラインに従って適切に作成されています。


15-19: データ属性の設定が適切です

JavaScriptとの連携に必要なdata-tag-iddata-initial-tag-name属性が正しく設定されており、コンポーネントとの連携が適切に実装されています。

app/javascript/tag_edit.js (3)

34-41: フォームバリデーションロジックが適切です

入力値の変更に応じて保存ボタンの有効/無効を切り替える実装が適切に動作します。空文字列と初期値のチェックも正しく行われています。


52-66: API呼び出しの実装が適切です

CSRF トークンの設定、適切なHTTPヘッダー、JSON形式でのデータ送信など、セキュリティとベストプラクティスに従った実装になっています。


43-50: URL置換ロジックは現状のままで問題ありません

アプリ内のタグ一覧ページ(articles, pages, questions, movies, users など)はすべて*/tags/:tagというパス構造を共有しており、

location.pathname.replace(
  `/tags/${encodeURIComponent(initialTagName)}`,
  `/tags/${encodeURIComponent(tagName)}`
)

による文字列置換で正しく動作します。

– 将来的にページネーションやクエリパラメータを維持したい場合は、以下のようにlocation.searchを付与する実装を検討してください。

const url = new URL(location.href);
url.pathname = url.pathname.replace(
  `/tags/${encodeURIComponent(initialTagName)}`,
  `/tags/${encodeURIComponent(tagName)}`
);
location.href = url.toString();

Likely an incorrect or invalid review comment.

@thmz337 thmz337 force-pushed the feature/add-list-of-movies-by-tag branch from ae84fa4 to 8e9e383 Compare August 6, 2025 15:39
@thmz337 thmz337 force-pushed the feature/add-list-of-movies-by-tag branch from 8e9e383 to c7c4993 Compare August 12, 2025 00:48
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

♻️ Duplicate comments (1)
test/system/movie/tags_test.rb (1)

6-15: 順序非依存の比較に修正済みで安定性向上

expected_titles.sortactual_titles.sort の比較により、DBやビューの並びの差異で落ちない堅牢なテストになっています。良いです。

🧹 Nitpick comments (5)
app/views/movies/index.html.slim (2)

25-37: タグ見出しと管理用編集UIの挿入はOKだが、アクセシビリティの向上余地あり

実装自体は妥当です。アクセシビリティ向上のため、現在表示中のタグ名を示す手がかり(例: 見出しのすぐ後に visually-hidden な補足)を検討しても良いです。


50-63: タグ一覧リンクに aria-current を付与して現在のタグを明示しよう

現在選択中のタグがある場合、該当リンクに aria-current="page" を付与すると支援技術で文脈が伝わりやすくなります。

以下のような条件付き属性付与を検討してください。

-                      = link_to tag.name, movies_tag_path(tag.name), class: 'page-tags-nav__item-link'
+                      = link_to tag.name,
+                                movies_tag_path(tag.name),
+                                class: 'page-tags-nav__item-link',
+                                aria: (@tag&.name == tag.name ? { current: 'page' } : nil)
test/system/movie/tags_test.rb (1)

64-71: 同値更新で変更ボタンがdisabledになる動作の検証は適切

UI仕様どおりの検証で良いです。余裕があれば、空文字入力時もdisabledになることの検証を追加するとより堅牢になります。

app/javascript/tag_edit.js (2)

22-26: キャンセル時に保存ボタンのdisabledを戻す

キャンセルで値は初期化されますが、keyup が発火しないためボタン状態が更新されません。キャンセル時にdisabledへ戻してください。

   cancelButton?.addEventListener('click', (e) => {
     e.preventDefault()
     tagForm.value = initialTagName
+    if (tagSaveButton) tagSaveButton.disabled = true
     setShowModal(false)
   })

34-41: disabled属性の操作はプロパティを用いると簡潔で安全

setAttribute/removeAttribute ではなく、disabled プロパティでのON/OFFが簡潔です。

-    if (tagValue === initialTagName || tagValue === '') {
-      tagSaveButton.setAttribute('disabled', true)
-    } else {
-      tagSaveButton.removeAttribute('disabled')
-    }
+    const disabled = tagValue === initialTagName || tagValue === ''
+    if (tagSaveButton) tagSaveButton.disabled = disabled
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8e9e383 and c7c4993.

📒 Files selected for processing (11)
  • app/components/tag/edit_component.html.slim (1 hunks)
  • app/components/tag/edit_component.rb (1 hunks)
  • app/controllers/movies_controller.rb (1 hunks)
  • app/javascript/packs/application.js (1 hunks)
  • app/javascript/tag_edit.js (1 hunks)
  • app/models/movie.rb (1 hunks)
  • app/views/movies/index.html.slim (3 hunks)
  • config/routes.rb (1 hunks)
  • test/components/tag/edit_component_test.rb (1 hunks)
  • test/fixtures/acts_as_taggable_on/taggings.yml (1 hunks)
  • test/system/movie/tags_test.rb (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (6)
  • app/models/movie.rb
  • app/javascript/packs/application.js
  • app/components/tag/edit_component.rb
  • test/components/tag/edit_component_test.rb
  • app/components/tag/edit_component.html.slim
  • app/controllers/movies_controller.rb
🧰 Additional context used
📓 Path-based instructions (3)
**/*.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:

  • config/routes.rb
  • test/system/movie/tags_test.rb
test/**/*

⚙️ CodeRabbit Configuration File

test/**/*: # Test

  • TestCase名は英語で書く。
  • どうしても避けられない時以外にsystem testでsleepは使わない。

Unit Test

model, helper, decorator, view_componentについてはメソッドを追加した場合は必ず対応したUnit TestのTestCaseを1つは書く。

Files:

  • test/fixtures/acts_as_taggable_on/taggings.yml
  • test/system/movie/tags_test.rb
**/*.js

⚙️ CodeRabbit Configuration File

**/*.js: - どうしても避けられない時以外に新規にVue.js, Reactのコードを追加しない。

  • fetchメソッドの代わりにrequest.jsを使う。

Files:

  • app/javascript/tag_edit.js
🧠 Learnings (1)
📚 Learning: 2025-06-30T01:07:46.192Z
Learnt from: e-yanagita-gs
PR: fjordllc/bootcamp#8771
File: app/controllers/welcome_controller.rb:57-57
Timestamp: 2025-06-30T01:07:46.192Z
Learning: プロジェクトでは、仕様上データの存在が保証されている場合、nil処理を省略することがある。特にFAQ表示のような特定機能に特化したPRでは、データの存在を前提とした実装が採用される。

Applied to files:

  • app/views/movies/index.html.slim
🧬 Code Graph Analysis (1)
app/javascript/tag_edit.js (1)
app/javascript/components/Tags/TagEditModal.jsx (1)
  • tagName (6-6)
⏰ 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 (3)
test/fixtures/acts_as_taggable_on/taggings.yml (1)

112-126: 映画タグ付けフィクスチャ追加の確認完了

  • test/fixtures/acts_as_taggable_on/tags.yml に catgamebeginner が定義済み
  • test/fixtures/movies.yml に movie1movie2 が定義済み
  • 他のフィクスチャ命名規則や ERB ブロック配置も問題なし

これにより、追加されたタグ付けフィクスチャは整合性が取れており、問題ありません。
Approveします。

config/routes.rb (1)

123-123: タグ別動画一覧のルーティング追加は既存パターンに準拠しており問題なし

  • pages_tag, questions_tag と同様に tag: /.+/format: "html" を指定しており一貫性があります。
  • resources :movies より前に宣言されているため、意図した優先度でマッチします。
app/views/movies/index.html.slim (1)

2-5: メタディスクリプションの条件分岐は適切

タグ有無で説明文を出し分ける実装は適切です。XSS面でもSlimのエスケープが効く前提で問題はありません。

Comment on lines +39 to +49
test 'admin updates a tag to a new name' do
old_tag = acts_as_taggable_on_tags('beginner')
new_tag_name = '上級者'

visit_with_auth movies_tag_path(old_tag.name, all: 'true'), 'komagata'
click_button 'タグ名変更'
fill_in('tag[name]', with: new_tag_name)
click_button '変更'

assert_text "タグ「#{new_tag_name}」の動画(1)"
end
Copy link
Copy Markdown

@coderabbitai coderabbitai bot Aug 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

タグ名変更後のURLとクエリ維持を検証するアサーションを追加しよう

現状のJS実装では、タグ名変更後に location.pathname を直接置換しており、?all=true 等のクエリが失われる懸念があります。テストでURL維持を検証し、リグレッションを防ぎましょう。

以下のようなアサーション追加を検討ください。

   visit_with_auth movies_tag_path(old_tag.name, all: 'true'), 'komagata'
   click_button 'タグ名変更'
   fill_in('tag[name]', with: new_tag_name)
   click_button '変更'

   assert_text "タグ「#{new_tag_name}」の動画(1)"
+  assert_current_path movies_tag_path(new_tag_name, all: 'true')

以下のスクリプトでビューとJS実装を検索し、タグ名変更後のURL遷移処理を確認しましょう。


🏁 Script executed:

#!/bin/bash
# 'タグ名変更'を含むビューを検索
rg -n "タグ名変更" -n app/views
# location.pathname を直接操作している JS コードを検索
rg -n "location\.pathname" -n app/javascript

Length of output: 659


クエリパラメータを維持したURL遷移をテストでカバーしましょう

現在のJS実装では、タグ名変更後に
location.href = location.pathname.replace(…)
を使っているため、?all=true などのクエリが消えてしまいます。
リグレッションを防ぐため、システムテストにURL保持のアサーションを追加してください。

修正ポイント例:

  • app/javascript/tag_edit.js(46行目付近)
  • app/javascript/components/Tags/TagEditModal.jsx(16行目付近)
  • test/system/movie/tags_test.rb

提案するテストの追加例:

   visit_with_auth movies_tag_path(old_tag.name, all: 'true'), 'komagata'
   click_button 'タグ名変更'
   fill_in('tag[name]', with: new_tag_name)
   click_button '変更'

   assert_text "タグ「#{new_tag_name}」の動画(1)"
+  assert_current_path movies_tag_path(new_tag_name, all: 'true')
📝 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.

Suggested change
test 'admin updates a tag to a new name' do
old_tag = acts_as_taggable_on_tags('beginner')
new_tag_name = '上級者'
visit_with_auth movies_tag_path(old_tag.name, all: 'true'), 'komagata'
click_button 'タグ名変更'
fill_in('tag[name]', with: new_tag_name)
click_button '変更'
assert_text "タグ「#{new_tag_name}」の動画(1)"
end
test 'admin updates a tag to a new name' do
old_tag = acts_as_taggable_on_tags('beginner')
new_tag_name = '上級者'
visit_with_auth movies_tag_path(old_tag.name, all: 'true'), 'komagata'
click_button 'タグ名変更'
fill_in('tag[name]', with: new_tag_name)
click_button '変更'
assert_text "タグ「#{new_tag_name}」の動画(1)"
assert_current_path movies_tag_path(new_tag_name, all: 'true')
end
🤖 Prompt for AI Agents
In test/system/movie/tags_test.rb around lines 39 to 49, the system test
exercises renaming a tag but does not assert that query parameters (e.g.
?all=true) are preserved after the redirect; add an assertion after the rename
that the browser URL still contains the original query param (for example assert
current_url.include?('?all=true') or assert_match(/\?all=true$/,
URI.parse(current_url).query.to_s)) so the test fails if the JS change drops
query strings; keep the rest of the test same.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

こちらについて、現在の実装で問題ないことを確認しました。

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@thmz337 承知いたしました。実装を確認していただき、ありがとうございます。現在の実装で問題ないとのことですので、追加のアサーションは不要ですね。

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
app/javascript/tag_edit.js (1)

7-14: tagEditModal/tagForm が存在しない場合の null 参照クラッシュ(早期リターンで防止)

tagEditModal が見つからないケースや、モーダル内に対象の入力要素がないケースで querySelector / dataset 参照時に例外になります。早期 return でガードしてください。

以下のように修正を提案します。

-  const tagEditModal = document.querySelector('.edit-tag-modal')
-  const tagForm = tagEditModal.querySelector('.a-text-input')
+  const tagEditModal = document.querySelector('.edit-tag-modal')
+  if (!tagEditModal) return
+  const tagForm = tagEditModal.querySelector('.a-text-input')
+  if (!tagForm) return
   const cancelButton = tagEditModal.querySelector('.cancel-change-tag-button')
   const tagSaveButton = tagEditModal.querySelector('.save-tag-button')

   const initialTagName = tagForm.dataset.initialTagName
   const tagId = tagForm.dataset.tagId
🧹 Nitpick comments (3)
app/javascript/tag_edit.js (3)

36-43: 保存ボタンが存在しない DOM に対するガード(軽微な堅牢化)

keyup ハンドラ内で tagSaveButton を直接操作しているため、ボタンが見つからない DOM 構造だと例外になります。optional chaining で安全に扱いましょう。

-    if (tagValue === initialTagName || tagValue === '') {
-      tagSaveButton.setAttribute('disabled', true)
-    } else {
-      tagSaveButton.removeAttribute('disabled')
-    }
+    if (tagValue === initialTagName || tagValue === '') {
+      tagSaveButton?.setAttribute('disabled', true)
+    } else {
+      tagSaveButton?.removeAttribute('disabled')
+    }

30-34: クリック連打対策と入力値の前後空白除去(UX/堅牢性の向上)

保存時に前後空白を除去し、通信中は二重送信を防ぐためボタンを無効化するのが無難です。updateTagawait し、最小限のガードを入れておくと事故を防げます。

-  tagSaveButton?.addEventListener('click', async (e) => {
+  tagSaveButton?.addEventListener('click', async (e) => {
     e.preventDefault()
-    const tagName = tagForm.value
-    updateTag(tagName)
+    const tagName = tagForm.value.trim()
+    tagSaveButton.setAttribute('disabled', true)
+    try {
+      await updateTag(tagName)
+    } finally {
+      // 遷移する場合は実行されませんが、保険として
+      tagSaveButton.removeAttribute('disabled')
+    }
   })

45-52: 履歴を汚したくない場合は location.replace の利用を検討

相対パスで location.href を代入してもクエリ/ハッシュは保持される点は把握済み(学習メモの通り)なので実装はそのままでOKです。そのうえで、保存後の戻る履歴を増やしたくない場合は location.replace(...) を使うと UX が向上します。

-    location.href = location.pathname.replace(
+    location.replace(location.pathname.replace(
       `/tags/${encodeURIComponent(initialTagName)}`,
       `/tags/${encodeURIComponent(tagName)}`
-    )
+    ))

補足: 既存のクエリ/ハッシュは相対遷移で維持されます(本PRのスレッドでの検証・学習に基づく前提)。

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c7c4993 and 5561e28.

📒 Files selected for processing (1)
  • app/javascript/tag_edit.js (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.js

⚙️ CodeRabbit Configuration File

**/*.js: - どうしても避けられない時以外に新規にVue.js, Reactのコードを追加しない。

  • fetchメソッドの代わりにrequest.jsを使う。

Files:

  • app/javascript/tag_edit.js
🧠 Learnings (1)
📚 Learning: 2025-08-12T15:34:46.372Z
Learnt from: thmz337
PR: fjordllc/bootcamp#8590
File: app/javascript/tag_edit.js:46-49
Timestamp: 2025-08-12T15:34:46.372Z
Learning: JavaScriptで `location.href` に相対パスを代入した場合、ブラウザは自動的に現在のクエリパラメータ(search)とハッシュフラグメント(hash)を保持する。`location.pathname.replace(...)` の結果を `location.href` に代入しても、クエリやハッシュは失われない。

Applied to files:

  • app/javascript/tag_edit.js
🧬 Code Graph Analysis (1)
app/javascript/tag_edit.js (1)
app/javascript/components/Tags/TagEditModal.jsx (1)
  • tagName (6-6)
⏰ 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/javascript/tag_edit.js (1)

1-1: request.js の採用はガイドライン準拠で良いです

fetch ではなく @rails/request.jspatch を使っている点、チーム方針に合致しています。

Comment on lines +54 to +73
const updateTag = async (tagName) => {
await patch(`/api/tags/${tagId}`, {
body: JSON.stringify({ tag: { name: tagName } }),
contentType: 'application/json'
})
.then(() => {
updateTagList(tagName)
})
.catch((error) => {
console.warn(error)
})
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

レスポンスの成否判定と await/then 混在解消(エラー時に遷移しない)

現状は patch のレスポンス成否を見ずに常に updateTagList を呼ぶため、422 等の失敗でも画面遷移してしまいます。また await.then() が混在しています。res.ok を確認して成功時のみ遷移、try/catch に一本化しましょう。

-  const updateTag = async (tagName) => {
-    await patch(`/api/tags/${tagId}`, {
-      body: JSON.stringify({ tag: { name: tagName } }),
-      contentType: 'application/json'
-    })
-    .then(() => {
-      updateTagList(tagName)
-    })
-    .catch((error) => {
-      console.warn(error)
-    })
-  }
+  const updateTag = async (tagName) => {
+    try {
+      const res = await patch(`/api/tags/${tagId}`, {
+        body: JSON.stringify({ tag: { name: tagName } }),
+        contentType: 'application/json'
+      })
+      if (res && res.ok) {
+        updateTagList(tagName)
+      } else {
+        console.warn('Failed to update tag:', res?.status, res?.statusText)
+      }
+    } catch (error) {
+      console.warn(error)
+    }
+  }
📝 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.

Suggested change
const updateTag = async (tagName) => {
await patch(`/api/tags/${tagId}`, {
body: JSON.stringify({ tag: { name: tagName } }),
contentType: 'application/json'
})
.then(() => {
updateTagList(tagName)
})
.catch((error) => {
console.warn(error)
})
}
const updateTag = async (tagName) => {
try {
const res = await patch(`/api/tags/${tagId}`, {
body: JSON.stringify({ tag: { name: tagName } }),
contentType: 'application/json'
})
if (res && res.ok) {
updateTagList(tagName)
} else {
console.warn('Failed to update tag:', res?.status, res?.statusText)
}
} catch (error) {
console.warn(error)
}
}
🤖 Prompt for AI Agents
In app/javascript/tag_edit.js around lines 54–65, the function mixes await with
.then()/.catch() and always calls updateTagList even if the PATCH failed; change
it to a single async/await flow inside a try/catch, await the patch call into a
response variable, check response.ok and call updateTagList(tagName) only on
success, and in the catch or non-ok branch log or surface the error and do not
navigate/update the UI.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
app/javascript/tag_edit.js (2)

7-11: モーダル/入力要素が存在しない場合の null 参照をガード(再指摘)

.edit-tag-modal やその子要素が存在しないと querySelector チェーンで例外になります。早期 return で防ぎましょう。

以下の差分を適用してください。

-  const tagEditModal = document.querySelector('.edit-tag-modal')
-  const tagForm = tagEditModal.querySelector('.a-text-input')
-  const cancelButton = tagEditModal.querySelector('.cancel-change-tag-button')
-  const tagSaveButton = tagEditModal.querySelector('.save-tag-button')
+  const tagEditModal = document.querySelector('.edit-tag-modal')
+  if (!tagEditModal) return
+  const tagForm = tagEditModal.querySelector('.a-text-input')
+  if (!tagForm) return
+  const cancelButton = tagEditModal.querySelector('.cancel-change-tag-button')
+  const tagSaveButton = tagEditModal.querySelector('.save-tag-button')

54-65: await と then の混在/レスポンス未検査により失敗時も遷移してしまう

PATCH が 4xx/5xx でも常に updateTagList が呼ばれてしまいます。async/await に統一し res.ok を確認して成功時のみ更新してください。入力も送信前に trim しましょう。

-  const updateTag = async (tagName) => {
-    await patch(`/api/tags/${tagId}`, {
-      body: JSON.stringify({ tag: { name: tagName } }),
-      contentType: 'application/json'
-    })
-      .then(() => {
-        updateTagList(tagName)
-      })
-      .catch((error) => {
-        console.warn(error)
-      })
-  }
+  const updateTag = async (tagName) => {
+    const name = tagName.trim()
+    if (!name) return
+    try {
+      const res = await patch(`/api/tags/${tagId}`, {
+        body: JSON.stringify({ tag: { name } }),
+        contentType: 'application/json'
+      })
+      if (res?.ok) {
+        updateTagList(name)
+      } else {
+        console.warn('Failed to update tag:', res?.status, res?.statusText)
+      }
+    } catch (error) {
+      console.warn(error)
+    }
+  }

任意で、保存押下時に二重送信防止のため一時的に保存ボタンを disabled にする対応も検討ください。

🧹 Nitpick comments (3)
app/javascript/tag_edit.js (3)

19-23: モーダル表示時に入力へフォーカスを移動(UX向上)

開いた瞬間に入力できるようにフォーカスを当てると操作性が上がります。

   tagChangeButton?.addEventListener('click', (e) => {
     e.preventDefault()
     setShowModal(true)
+    tagForm.focus()
   })

36-43: 保存ボタンの活性/非活性判定に trim を適用し、null セーフに

前後空白のみの入力を弾き、要素が欠けていても落ちないようにしておくと堅牢です。

-  tagForm?.addEventListener('keyup', (e) => {
-    const tagValue = e.target.value
-    if (tagValue === initialTagName || tagValue === '') {
-      tagSaveButton.setAttribute('disabled', true)
-    } else {
-      tagSaveButton.removeAttribute('disabled')
-    }
-  })
+  tagForm?.addEventListener('keyup', (e) => {
+    const tagValue = e.target.value.trim()
+    const initial = initialTagName?.trim?.() ?? initialTagName
+    if (tagValue === '' || tagValue === initial) {
+      tagSaveButton?.setAttribute('disabled', true)
+    } else {
+      tagSaveButton?.removeAttribute('disabled')
+    }
+  })

加えて、初期表示時にも同じ判定を適用したい場合は、上記ロジックを関数化して DOM 構築後に一度呼び出すのがよいです。


45-52: 履歴を汚さずに遷移したい場合は location.replace を検討

現在の実装でも動作はしますが、戻る操作で古い URL に戻らせたくない場合は replace が適しています。なお、クエリ/ハッシュ保持の件は先の検証結果(相対パス代入で保持される)に従って本件は問題ありません。

-    location.href = location.pathname.replace(
-      `/tags/${encodeURIComponent(initialTagName)}`,
-      `/tags/${encodeURIComponent(tagName)}`
-    )
+    location.replace(
+      location.pathname.replace(
+        `/tags/${encodeURIComponent(initialTagName)}`,
+        `/tags/${encodeURIComponent(tagName)}`
+      )
+    )
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5561e28 and 4f20392.

📒 Files selected for processing (1)
  • app/javascript/tag_edit.js (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.js

⚙️ CodeRabbit Configuration File

**/*.js: - どうしても避けられない時以外に新規にVue.js, Reactのコードを追加しない。

  • fetchメソッドの代わりにrequest.jsを使う。

Files:

  • app/javascript/tag_edit.js
🧠 Learnings (1)
📚 Learning: 2025-08-12T15:34:46.372Z
Learnt from: thmz337
PR: fjordllc/bootcamp#8590
File: app/javascript/tag_edit.js:46-49
Timestamp: 2025-08-12T15:34:46.372Z
Learning: JavaScriptで `location.href` に相対パスを代入した場合、ブラウザは自動的に現在のクエリパラメータ(search)とハッシュフラグメント(hash)を保持する。`location.pathname.replace(...)` の結果を `location.href` に代入しても、クエリやハッシュは失われない。

Applied to files:

  • app/javascript/tag_edit.js
🧬 Code Graph Analysis (1)
app/javascript/tag_edit.js (1)
app/javascript/components/Tags/TagEditModal.jsx (1)
  • tagName (6-6)
⏰ 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/javascript/tag_edit.js (1)

3-3: turbo:load へのフックは不要です (Turbo/Turbolinks 未使用)
リポジトリ内を以下の手順で確認したところ、

  • @hotwired/turbo-railsturbolinks の import
  • ビュー内の data-turbo / data-turbolinks 属性
    いずれも検出されませんでした。
    そのため、DOMContentLoaded へのみフックする実装で問題ありません。

@thmz337
Copy link
Copy Markdown
Contributor

thmz337 commented Aug 13, 2025

@komagata

お疲れ様です。動画タグの修正フォームを、 ReactからVanillaJSに移行しました。
ご確認をお願いできますでしょうか。

以上よろしくお願いいたします🙇‍♀️

@thmz337 thmz337 force-pushed the feature/add-list-of-movies-by-tag branch from 5f99935 to a96c96d Compare August 21, 2025 13:19
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (7)
app/models/movie.rb (2)

25-26: after_create_commit における on: :create は冗長(かつ無視される)

after_create_commit は「create時のcommit後」に限定された専用マクロのため、on: :create オプションは不要です(ActiveSupportのコールバックオプション正規化でも利用されず読み手の混乱要因)。削ってシンプルにしてください。

-  after_create_commit :start_transcode_job, on: :create
+  after_create_commit :start_transcode_job

29-34: after_commit内での再スローはUX劣化の恐れ(保存成功後でも500になり得る)

after_create_commit はトランザクション確定後に実行されます。ここで例外を再スローすると、レコード保存は成功しているのにリクエストは500で落ちる可能性があります(キューアダプタの一時障害等)。非同期処理の起動失敗はログ+エラートラッキングに留め、リクエストは成功させる運用が実務的です。

検討案A(推奨: 例外は握りつぶし、監視に委譲)

 def start_transcode_job
   TranscodeJob.perform_later(self)
-rescue StandardError => e
-  Rails.logger.error("Failed to enqueue TranscodeJob for Movie #{id}: #{e.message}")
-  raise
+rescue StandardError => e
+  Rails.logger.error("Failed to enqueue TranscodeJob for Movie #{id}: #{e.class}: #{e.message}")
+  # 監視サービスがあれば通知する(例:Sentry/Rails.error.report など)
+  Rails.error.report(e, context: { movie_id: id }) if Rails.respond_to?(:error)
+  nil
 end

検討案B(厳格運用: ジョブ起動失敗は保存も失敗扱いにしたい)

  • after_create_commit ではなく before/after_save + throw(:abort) を使う、もしくは明示的にサービス層で「保存+ジョブ登録」をトランザクションにまとめて扱う(今回の要件ではAが妥当と判断)。

どちらの方針がプロダクトポリシーに合うか確認お願いします。

app/views/movies/index.html.slim (3)

2-5: メタディスクリプションの文言はi18n化を検討

SEO文言がハードコードされています。将来の文言調整や多言語対応を見据えて config/locales/ja.yml へ退避を推奨します。キー例:views.movies.index.meta.description_with_tag / views.movies.index.meta.description_default


25-37: ヘッダーが二重化しているためレイアウト崩れ再発の懸念あり

上部に既存の「動画」ヘッダー(Line 11-24)があり、タグ選択時に二つ目の .page-main-header を追加しています。ブレークポイントによっては高さ・余白計算や「被り」問題が再発しやすい構成です。既存ヘッダー内のタイトル・アクション領域を条件分岐で切替える構成へ寄せると安全です。

  • 想定案:既存ヘッダー内の h1@tag.present? ? "タグ「#{@tag.name}」の動画(#{@movies.total_count})" : "動画" に切替、管理者時の編集コンポーネントは .page-main-header__end に条件表示。
  • 可能であればデザイン責務の影響が小さいコンポーネント(ViewComponent)化も検討。

スタイル修正済とのことですが、以下の解像度で再確認をお願いします: 1280px, 1440px, 1920px。


50-62: 現在選択中タグの可視化とa11y改善(aria-current, 選択スタイル)

タグナビに現在選択中のタグを視覚的・音声的に示すとUXが向上します。aria-current="page" と選択クラス付与を提案します。また、タグリンクであることを明示する rel="tag" も付与可能です。

-                      = link_to tag.name, movies_tag_path(tag.name), class: 'page-tags-nav__item-link'
+                      = link_to tag.name,
+                                movies_tag_path(tag.name),
+                                class: ['page-tags-nav__item-link', (@tag&.name == tag.name ? 'is-current' : nil)].compact.join(' '),
+                                aria: { current: (@tag&.name == tag.name ? 'page' : nil) },
+                                rel: 'tag'

補足:

  • 「すべて」を選択状態にできるよう、未選択時のナビ上部に movies_path への「すべて」リンクを追加するのも有効です(任意)。
app/controllers/movies_controller.rb (2)

11-12: params未指定時の無駄なDBヒット回避

params[:tag] が空のときでも find_by(name: nil) を叩くため軽微ですが無駄なクエリになります。presenceガードを。

-    @tag = ActsAsTaggableOn::Tag.find_by(name: params[:tag])
+    @tag = ActsAsTaggableOn::Tag.find_by(name: params[:tag]) if params[:tag].present?

13-17: 一覧クエリはOK。タグ一覧取得の負荷をモニタリング推奨

Movie.by_tag(...).includes(:user).order(...).page(...).per(...) の組み合わせは問題ありません。
一方、@tags = Movie.all.all_tags はデータ量によっては重くなる可能性があるため、キャッシュ(例:フラグメントキャッシュ or タグの最終更新時刻ベース)か、用途に応じて「使用数の多い順・上位N件」などの絞り込みを検討してください。

  • まずはログ/メトリクスでレスポンスタイムと発行SQLを確認してボトルネックかを検証するのがよいです。
📜 Review details

Configuration used: .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.

📥 Commits

Reviewing files that changed from the base of the PR and between 5f99935 and a96c96d.

📒 Files selected for processing (7)
  • app/components/tag/edit_component.html.slim (1 hunks)
  • app/components/tag/edit_component.rb (1 hunks)
  • app/controllers/movies_controller.rb (1 hunks)
  • app/javascript/packs/application.js (1 hunks)
  • app/javascript/tag_edit.js (1 hunks)
  • app/models/movie.rb (1 hunks)
  • app/views/movies/index.html.slim (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • app/components/tag/edit_component.rb
  • app/javascript/tag_edit.js
  • app/javascript/packs/application.js
  • app/components/tag/edit_component.html.slim
🧰 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/controllers/movies_controller.rb
  • app/models/movie.rb
🧠 Learnings (3)
📚 Learning: 2025-07-12T09:56:13.360Z
Learnt from: thmz337
PR: fjordllc/bootcamp#8590
File: app/models/movie.rb:22-24
Timestamp: 2025-07-12T09:56:13.360Z
Learning: Rails scopeでnilやfalseを返すと、NoMethodErrorは発生せず、代わりにModel.allが返される。これによりメソッドチェインは継続するが、全レコードが読み込まれるため意図しない動作となる可能性がある。

Applied to files:

  • app/models/movie.rb
📚 Learning: 2025-06-30T01:07:46.192Z
Learnt from: e-yanagita-gs
PR: fjordllc/bootcamp#8771
File: app/controllers/welcome_controller.rb:57-57
Timestamp: 2025-06-30T01:07:46.192Z
Learning: プロジェクトでは、仕様上データの存在が保証されている場合、nil処理を省略することがある。特にFAQ表示のような特定機能に特化したPRでは、データの存在を前提とした実装が採用される。

Applied to files:

  • app/models/movie.rb
  • app/views/movies/index.html.slim
📚 Learning: 2025-07-07T12:46:01.650Z
Learnt from: masyuko0222
PR: fjordllc/bootcamp#8277
File: test/models/searcher_test.rb:0-0
Timestamp: 2025-07-07T12:46:01.650Z
Learning: テストにおいて、`result.all? { |condition| }` でコレクションの全要素が条件を満たすことを確認できれば、その条件を満たさない要素が含まれていないことも論理的に保証されるため、追加のアサーションは冗長となる。

Applied to files:

  • app/models/movie.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/movie.rb (1)

23-23: by_tagスコープは意図が明確でOK(常にRelationを返す)

tag.present? ? tagged_with(tag) : all により常に ActiveRecord::Relation を返す実装になっており、チェーン安全性が担保されています。以前の議論点(nil時にallへフォールバックする暗黙挙動への依存)も回避できています。

@thmz337 thmz337 force-pushed the feature/add-list-of-movies-by-tag branch from a96c96d to b049bf5 Compare August 21, 2025 15:35
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
app/models/movie.rb (1)

23-23: by_tagは常にActiveRecord::Relationを返すよう明示しましょう(過去コメントの再提示)

tag が未指定/空のときに nil を返すと、Rails の仕様で暗黙に all へフォールバックします。動作はたまたま期待どおりでも、意図がコードに表れず将来のリスクになります。present? 判定で明示的に all を返す方が安全です。以前のスレッドで合意されていた修正が現状のコードには反映されていないようなので、再度提案します。

適用diff:

-  scope :by_tag, ->(tag) { tagged_with(tag) if tag }
+  scope :by_tag, ->(tag) { tag.present? ? tagged_with(tag) : all }
🧹 Nitpick comments (4)
app/components/tag/edit_component.rb (1)

6-10: nil想定が不要ならそのままでOK/必要なら引数ガードを追加

本プロジェクトでは「仕様上存在が保証されるデータはnil処理を省略」する方針があるため(学習メモ参照)、このままでも問題ありません。一方で将来的に呼び出し元が増える可能性を考慮するなら、軽い引数ガードを入れて早期に失敗させるとデバッグ性が上がります。

例(任意):

   def initialize(tag:)
+    raise ArgumentError, "tag is required" if tag.nil?
     @tag_id = tag.id
     @tag_name = tag.name
   end
app/controllers/movies_controller.rb (1)

11-17: タグ一覧取得方法とタグN+1回避のオプショナルリファクタ提案

– 現状、Movie.all.all_tags は acts_as_taggable_on gem が提供するメソッドですが、内部実装によっては大量レコードをロードしてしまう可能性があります。標準 API の Movie.tags_on(:tags)(件数不要)や、件数情報も合わせて取得したい場合は Movie.tag_counts_on(:tags) の利用を検討してください。
– ビュー内で movie.tag_list.each を呼び出しているため、タグ取得時にレコードごとに追加クエリが発行される可能性があります。コントローラの @movies に対して includes(:tags) を追加し、タグ関連を事前ロードすることで N+1 問題を防止できます。

対象箇所

  • app/controllers/movies_controller.rb(11–17行)
  • app/views/movies/_movie_list.html.slim(38–40行、movie.tag_list.each

提案diff

--- a/app/controllers/movies_controller.rb
+++ b/app/controllers/movies_controller.rb
@@ -11,7 +11,9 @@ class MoviesController < ApplicationController
     @tag = ActsAsTaggableOn::Tag.find_by(name: params[:tag])
-    @tags = Movie.all.all_tags
+    # タグ一覧(件数不要)
+    @tags = Movie.tags_on(:tags)
+    # 件数情報も必要なら → Movie.tag_counts_on(:tags)

     @movies = Movie.by_tag(params[:tag])
-                   .includes(:user)
+                   .includes(:user, :tags)
                    .order(updated_at: :desc, id: :desc)
                    .page(params[:page])
                    .per(PAGER_NUMBER)
app/components/tag/edit_component.html.slim (1)

26-28: キャンセル要素はbutton要素にしてキーボード操作可能に

現在は div 相当のノードです。インタラクティブ要素は button にすることで、Enter/Space で操作でき、デフォルトのフォーカス管理やアクセシビリティが改善されます。

-              .cancel-change-tag-button.card-main-actions__muted-action
-                | キャンセル
+              button.cancel-change-tag-button.card-main-actions__muted-action type="button"
+                | キャンセル
test/components/tag/edit_component_test.rb (1)

14-21: レンダリング結果の属性も検証して回 regress を防ぎましょう

現在は attr_reader の値のみをテストしています。テンプレートの for 属性や data-* 属性が意図どおり出力されるかも合わせて検証すると、今回の label 属性のような不具合を早期検出できます。

例(追記テスト案):

def test_template_wires_label_and_input
  doc = Capybara.string(rendered_content)
  input = doc.find('input#tag_name', visible: :all)
  label = doc.find('label.a-form-label[for="tag_name"]', visible: :all)
  assert_equal @tag.name, input[:value]
  assert_equal @tag.id.to_s, input['data-tag-id']
  assert_equal @tag.name, input['data-initial-tag-name']
end
📜 Review details

Configuration used: .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.

📥 Commits

Reviewing files that changed from the base of the PR and between a96c96d and b049bf5.

📒 Files selected for processing (11)
  • app/components/tag/edit_component.html.slim (1 hunks)
  • app/components/tag/edit_component.rb (1 hunks)
  • app/controllers/movies_controller.rb (1 hunks)
  • app/javascript/packs/application.js (1 hunks)
  • app/javascript/tag_edit.js (1 hunks)
  • app/models/movie.rb (1 hunks)
  • app/views/movies/index.html.slim (3 hunks)
  • config/routes.rb (1 hunks)
  • test/components/tag/edit_component_test.rb (1 hunks)
  • test/fixtures/acts_as_taggable_on/taggings.yml (1 hunks)
  • test/system/movie/tags_test.rb (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (6)
  • app/javascript/packs/application.js
  • test/system/movie/tags_test.rb
  • config/routes.rb
  • app/javascript/tag_edit.js
  • test/fixtures/acts_as_taggable_on/taggings.yml
  • app/views/movies/index.html.slim
🧰 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/movie.rb
  • app/controllers/movies_controller.rb
  • test/components/tag/edit_component_test.rb
  • app/components/tag/edit_component.rb
test/**/*

⚙️ CodeRabbit configuration file

test/**/*: # Test

  • TestCase名は英語で書く。
  • どうしても避けられない時以外にsystem testでsleepは使わない。

Unit Test

model, helper, decorator, view_componentについてはメソッドを追加した場合は必ず対応したUnit TestのTestCaseを1つは書く。

Files:

  • test/components/tag/edit_component_test.rb
🧠 Learnings (3)
📚 Learning: 2025-07-12T09:56:13.360Z
Learnt from: thmz337
PR: fjordllc/bootcamp#8590
File: app/models/movie.rb:22-24
Timestamp: 2025-07-12T09:56:13.360Z
Learning: Rails scopeでnilやfalseを返すと、NoMethodErrorは発生せず、代わりにModel.allが返される。これによりメソッドチェインは継続するが、全レコードが読み込まれるため意図しない動作となる可能性がある。

Applied to files:

  • app/models/movie.rb
📚 Learning: 2025-06-30T01:07:46.192Z
Learnt from: e-yanagita-gs
PR: fjordllc/bootcamp#8771
File: app/controllers/welcome_controller.rb:57-57
Timestamp: 2025-06-30T01:07:46.192Z
Learning: プロジェクトでは、仕様上データの存在が保証されている場合、nil処理を省略することがある。特にFAQ表示のような特定機能に特化したPRでは、データの存在を前提とした実装が採用される。

Applied to files:

  • app/models/movie.rb
📚 Learning: 2025-07-07T12:46:01.650Z
Learnt from: masyuko0222
PR: fjordllc/bootcamp#8277
File: test/models/searcher_test.rb:0-0
Timestamp: 2025-07-07T12:46:01.650Z
Learning: テストにおいて、`result.all? { |condition| }` でコレクションの全要素が条件を満たすことを確認できれば、その条件を満たさない要素が含まれていないことも論理的に保証されるため、追加のアサーションは冗長となる。

Applied to files:

  • app/models/movie.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

@komagata
Copy link
Copy Markdown
Member

@thmz337

#8590 (comment)

上記の他にも複数の対応が行われていない指摘があるようです。やりとりをお願いします。

@thmz337 thmz337 force-pushed the feature/add-list-of-movies-by-tag branch from b049bf5 to c4acebd Compare August 22, 2025 12:47
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
config/routes.rb (1)

125-125: ルート順序の確認結果
config/routes.rb 上で

  • get "movies/tags/:tag" が125行目
  • resources :movies が136行目
    に定義されており、タグ用ルートは resources :movies よりも前に置かれています。現状問題ありません。CIで順序入れ替えを検知する仕組みは、必要に応じて後ほど検討してください。

System Test のエッジケース追加提案
test/system/movie/tags_test.rb には基本的なタグ検索テストが含まれていますが、以下のようなエッジケースもカバーしておくとリグレッション防止に役立ちます。

  • 「C++」や「a.b」のようにピリオド・プラス記号を含むタグ
  • 「日本語タグ」などマルチバイト文字を含むタグ

これらを1〜2件、movies_tag_path を使った visit テストとして追加しておくことをおすすめします。

app/javascript/packs/application.js (1)

87-87: (任意)必要ページのみ動的 import にしてバンドルを軽量化

  • tag_edit.js が映画一覧やタグ編集 UI に限定されるなら、DOM 存在確認後の動的 import にすると初期ロードをわずかに削減できます。既存のグローバル読込方針を崩したくない場合はスルーでOKです。

対象行の差分(静的 import を外す):

- import '../tag_edit.js'

DOM 監視での遅延読込例(既存の DOMContentLoaded ハンドラ内に追記):

// 例: 既存の DOMContentLoaded の中に追記
document.addEventListener('DOMContentLoaded', () => {
  // ...既存処理...
  const hasTagEdit = document.querySelector('[data-component="tag-edit"], .js-tag-edit');
  if (hasTagEdit) {
    import('../tag_edit.js');
  }
});
📜 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.

📥 Commits

Reviewing files that changed from the base of the PR and between b049bf5 and c4acebd.

📒 Files selected for processing (11)
  • app/components/tag/edit_component.html.slim (1 hunks)
  • app/components/tag/edit_component.rb (1 hunks)
  • app/controllers/movies_controller.rb (1 hunks)
  • app/javascript/packs/application.js (1 hunks)
  • app/javascript/tag_edit.js (1 hunks)
  • app/models/movie.rb (1 hunks)
  • app/views/movies/index.html.slim (3 hunks)
  • config/routes.rb (1 hunks)
  • test/components/tag/edit_component_test.rb (1 hunks)
  • test/fixtures/acts_as_taggable_on/taggings.yml (1 hunks)
  • test/system/movie/tags_test.rb (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • test/fixtures/acts_as_taggable_on/taggings.yml
🚧 Files skipped from review as they are similar to previous changes (8)
  • app/components/tag/edit_component.rb
  • test/components/tag/edit_component_test.rb
  • test/system/movie/tags_test.rb
  • app/controllers/movies_controller.rb
  • app/javascript/tag_edit.js
  • app/views/movies/index.html.slim
  • app/components/tag/edit_component.html.slim
  • app/models/movie.rb
🧰 Additional context used
📓 Path-based instructions (2)
**/*.js

⚙️ CodeRabbit configuration file

**/*.js: - どうしても避けられない時以外に新規にVue.js, Reactのコードを追加しない。

  • fetchメソッドの代わりにrequest.jsを使う。

Files:

  • app/javascript/packs/application.js
**/*.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:

  • config/routes.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 (3)
config/routes.rb (1)

125-125: タグ絞り込みルートの追加は既存パターンと整合していて妥当です

  • pages_tag / questions_tag と同じ構造(/movies/tags/:tag + format: "html" + tag: /.+/)で統一できており、ビューから movies_tag_path(tag.name) を使いやすい命名です。
app/javascript/packs/application.js (2)

87-87: tag_edit.js の読込追加は既存パックの方針に沿っており問題ありません

  • tag.js に隣接して編集用 tag_edit.js を取り込む配置は妥当です。副作用ベースの初期化が多いパック構成に一貫しています。

87-87: 確認完了: @rails/request.js 利用および副作用スコープは要件を満たしています

  • import { patch } from '@rails/request.js' のインポートを確認済み
  • fetch() の直接呼び出しは存在せず、すべて @rails/request.js 経由でリクエストを実行
  • DOMContentLoaded 内で要素取得後に if (!…) return による存在チェックを行い、対象要素がないページで副作用が発生しないよう制御

以上のとおり問題ありませんので、このままマージしてください。

@thmz337
Copy link
Copy Markdown
Contributor

thmz337 commented Aug 22, 2025

@komagata

お疲れ様です。ご確認ありがとうございます。

上記の他にも複数の対応が行われていない指摘があるようです。やりとりをお願いします。

こちら申し訳ございません。やり取りを行いましたので、ご確認よろしくお願いいたします。

Copy link
Copy Markdown
Member

@komagata komagata left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

確認させて頂きました。OKです〜🙆‍♂️

@@ -0,0 +1,74 @@
import { patch } from '@rails/request.js'
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

request.js使っているのナイスです〜

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@komagata

ありがとうございます!

@komagata komagata merged commit c652032 into main Aug 25, 2025
8 checks passed
@komagata komagata deleted the feature/add-list-of-movies-by-tag branch August 25, 2025 07:25
@github-actions github-actions bot mentioned this pull request Aug 25, 2025
24 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants