Skip to content

Add polymorphic-parent-resources skill - #42

Merged
OutlawAndy merged 2 commits into
mainfrom
add-polymorphic-parent-resources-skill
Aug 6, 2026
Merged

Add polymorphic-parent-resources skill#42
OutlawAndy merged 2 commits into
mainfrom
add-polymorphic-parent-resources-skill

Conversation

@OutlawAndy

Copy link
Copy Markdown
Member

Ships a skill for Rolemodel::ResourceFor::ControllerExtension, added to rolemodel_rails in 2.4.0 (#205), so the pattern gets found and adopted instead of sitting in a gem README.

Why cross-links, not just a new skill

The discovery problem is that nobody searches for resource_for — they have never heard of it. Two existing skills are where developers and agents already are when they hit this problem, and both currently point the wrong way:

  • routing-patterns already taught the route concern (commentable_type: parent_resource.name.classify) and then stopped at "Controllers can access these via params[:commentable_type]." It taught you to pass the parent type and never said what to do with it. That dead end now points at the controller half.
  • controller-patterns actively taught the thing this replaces. Its decision tree routed "Nested under parent resource? → Nested RESTful Controller Pattern", and the example hard-codes Order.find(params[:order_id]) with no note about multiple parents. An agent following it faithfully generates exactly the per-parent duplication the pattern exists to remove.

grep -r "resource_for\|rolemodel_rails" skills/ returned zero hits across all 26 skills before this PR.

What's here

New skillskills/polymorphic-parent-resources/ (SKILL.md at 195 lines, under the repo's ~200 convention):

  • Models → routing → controller → views, with the only:/except: split under shallow nesting
  • The rolemodel_rails >= 2.4.0 gate, with bundle info plus a runtime check, and a temporary local concern for projects pinned to an older version
  • The type allowlist for cases where a *_type arrives from user input rather than a route default
  • A symptom → cause → fix gotchas table
  • references/retrofit.md — audit commands, consolidation order (one parent at a time), and verification for apps that already have duplicated per-parent controllers

Cross-linksrouting-patterns (dead-end bullet replaced, shallow caveat, checklist item), controller-patterns (decision-tree branch, two Common Mistakes rows, warning on the Nested Resource example, Agent Instructions branch, review checkbox), plus README.md and AGENTS.md listings and the "How skills work together" section.

A routing trap worth a look

Verifying the gem README's own example turned up something it does not document. Applying a concern with member actions to several parents under shallow draws /comments/:id once per parent, and Rails keeps only the first route's name:

name="comment" defaults={commentable_type: "Widget", ...}
name=nil       defaults={commentable_type: "Estimate", ...}   # unreachable

GET /comments/7 -> {commentable_type: "Widget", id: "7"}       # always Widget

So on member actions params[:commentable_type] is present but is always the first-drawn parent, whatever the record's real parent is. That makes scoping the parent before_action to collection actions load-bearing for correctness — the failure mode is either a silent wrong-parent read or a confusing 404 from find(nil), not just an untidy callback. Both the new skill and routing-patterns document it.

The gem README currently explains the split as "member actions carry no parent id," which is true but undersells it. Happy to open a follow-up there if you agree it's worth restating.

Review notes

  • Frontmatter validated as parseable YAML across every skill in the repo (an earlier draft had a : inside the plain-scalar description, which would have made the skill fail to load).
  • MD013 line-length warnings from markdownlint-cli2 defaults are untouched baseline — the repo has no markdownlint config and existing skills violate it identically.
  • Pre-existing and left alone: the README table lists testing-patterns, but the directory is skills/tdd/.

🤖 Generated with Claude Code

Documents the pattern for serving a child resource that hangs off many
different parents -- comments, reports, duplications, attachments -- from
a single controller, rather than one namespaced controller per parent.
Pairs a route concern passing the parent class name as a route default
with `resource_for`, which ships in rolemodel_rails 2.4.0.

The skill covers models, routing, controller, and views; the gem version
gate with a runtime check and a temporary local fallback for pinned
projects; the type allowlist for user-supplied types; and a gotchas
table. references/retrofit.md holds the audit commands, consolidation
order, and verification steps for adopting the pattern in an app that
already has duplicated per-parent controllers.

Adds cross-references from the two skills developers already reach for,
since nobody searches for a method they have not heard of.
routing-patterns previously taught the route concern and then stopped at
"controllers can access these via params[:commentable_type]" -- that
dead end now points at the controller half. controller-patterns gains a
decision-tree branch, Common Mistakes rows, and review checklist items,
because its Nested Resource example was the template being copied per
parent.

Also documents a routing trap the gem README does not mention: applying
a concern with member actions to several parents under `shallow` draws
the member path once per parent, and only the first keeps the route
name. Every member request therefore resolves with the first-drawn
parent's `*_type` default regardless of the record's real parent, so
scoping the parent before_action to collection actions is load-bearing
for correctness, not just tidiness.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings August 6, 2026 16:15

Copilot AI left a comment

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.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds a new Rails skill documenting the “polymorphic parent resources” pattern (route concern + resource_for) and updates existing routing/controller skills and repository indexes to reference it.

Changes:

  • Introduces polymorphic-parent-resources skill docs plus a retrofit guide for consolidating per-parent controllers.
  • Updates routing-patterns and controller-patterns to call out the pattern and the shallow-member-route *_type default trap.
  • Adds the new skill to README.md and AGENTS.md.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
skills/routing-patterns/SKILL.md Adds guidance linking route *_type defaults to the controller-side resource_for pattern and documents a shallow routing caveat.
skills/polymorphic-parent-resources/references/retrofit.md New retrofit guide with audit/consolidation steps and verification commands.
skills/polymorphic-parent-resources/SKILL.md New skill documenting the end-to-end pattern, prerequisites, gotchas, and controller template.
skills/controller-patterns/SKILL.md Updates decision tree/checklists to steer multi-parent child resources to the new skill.
README.md Adds the new skill to the skills list and cross-skill recommendations.
AGENTS.md Adds the new skill to the Rails Backend agent list.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread skills/polymorphic-parent-resources/SKILL.md
Comment thread skills/polymorphic-parent-resources/references/retrofit.md
Comment thread skills/polymorphic-parent-resources/SKILL.md Outdated
Copilot AI review requested due to automatic review settings August 6, 2026 17:38
@OutlawAndy
OutlawAndy merged commit f3e04c8 into main Aug 6, 2026
1 check failed
@OutlawAndy
OutlawAndy deleted the add-polymorphic-parent-resources-skill branch August 6, 2026 17:40

Copilot AI left a comment

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.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated no new comments.

Suppressed comments (4)

skills/polymorphic-parent-resources/SKILL.md:118

  • :unprocessable_content is not a valid Rails status symbol for 422 responses; Rails uses :unprocessable_entity. As written, this example will raise (or send an unintended status) in many Rails versions. Update the snippet to use status: :unprocessable_entity (or status: 422).
      render :new, status: :unprocessable_content

skills/polymorphic-parent-resources/references/retrofit.md:42

  • These grep commands rely on \\| alternation without enabling extended regex, which is not portable (notably on BSD/macOS grep). Use grep -E (or a single regex like resources :(comments|generated_reports|duplications) / (_comments_path|_comment_path)) so the commands work consistently across environments.
grep -n 'resources :comments\|resources :generated_reports\|resources :duplications' config/routes.rb

skills/polymorphic-parent-resources/references/retrofit.md:112

  • These grep commands rely on \\| alternation without enabling extended regex, which is not portable (notably on BSD/macOS grep). Use grep -E (or a single regex like resources :(comments|generated_reports|duplications) / (_comments_path|_comment_path)) so the commands work consistently across environments.
grep -rn '_comments_path\|_comment_path' app spec | grep -v 'polymorphic'

skills/routing-patterns/SKILL.md:57

  • The new lines change list structure/formatting: the prior controller note was nested under the custom-parameters bullet, but line 55 is now a top-level item and line 57 breaks out of the list entirely. If this section is intended to keep the controller guidance tied to the metadata bullet, consider nesting line 55 (and optionally the 'Read...' note) under line 54, or format the note as a consistent callout (e.g., blockquote) to avoid accidental outline breakage.
- Use `parent_resource.name.classify` to dynamically pass the parent context type to controllers
- Specify `only:` or `except:` to limit actions when appropriate
- Custom parameters (like `commentable_type`, `resource_type`) are passed as metadata to controllers
- Controllers turn that metadata back into the parent record with `resource_for(:commentable_type)`, provided by the `rolemodel_rails` gem (>= 2.4.0)

**Read the `polymorphic-parent-resources` skill before writing the controller.** Passing a `*_type` default is only half the pattern — that skill covers the controller side, the required `before_action` scoping under shallow nesting (member routes inherit the wrong `*_type`), and how to consolidate existing per-parent controllers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants