mcpField
R
routing-patterns

routing-patterns

github.com/rolemodel开发746 次安装3 天前更新
代码仓库

关于 routing-patterns

routing-patterns 是供 AI Agent 审查、生成和更新 Rails 路由的技能,涵盖可复用 concern、浅层嵌套及路由配置模式,帮助维护应用路由。

主要功能

  1. 01可复用路由 concern
  2. 02浅层嵌套路由
  3. 03明确限制资源操作
  4. 04嵌套与单数资源
  5. 05自定义参数和解析器
  6. 06基于约束的路由

适用场景

  1. 01审查 Rails 路由文件
  2. 02生成可复用的资源路由
  3. 03缩短深层嵌套 URL

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/rolemodel/rolemodel-skills --skill routing-patterns

安装后在 Claude Code 中输入/routing-patterns确认已加载。

详细介绍

路由最佳实践

目的

此技能可帮助 AI Agent 按照专业模式和最佳实践审查、生成和更新 Rails 路由。内容涵盖 RESTful 资源路由、用于代码复用的路由关注项、浅层嵌套策略,以及可应用于任何 Rails 应用的高级路由配置。

背景

此技能涵盖:

  • 遵循 RESTful 约定的 Rails 路由
  • 遵循 DRY(Don't Repeat Yourself,不要重复自己)原则的 路由关注项
  • 避免 URL 过长的 浅层嵌套
  • 灵活路由所需的 自定义参数和元数据
  • 用于多态路径的 自定义路由解析器
  • 用于授权的 基于约束的路由
  • 用于维护路由文件的 组织模式

最佳实践

1. 使用路由关注项复用行为

在 routes.rb 顶部定义关注项,用于多个资源共享的行为。这能让路由保持 DRY 且易于维护。

示例:可评论资源

concern :commentable do
  resources :comments, commentable_type: parent_resource.name.classify
end

示例:可复制资源

concern :duplicatable do
  resources :duplications, only: %i[create], resource_type: parent_resource.name.classify
end

动态表单使用 :turbo_form 关注项,该关注项由 turbo_form gem 的引擎定义。请直接应用,不要自行定义。参见 dynamic-forms 技能。

要点:

  • 关注项将通用的嵌套资源模式提取为可复用模块
  • 使用 parent_resource.name.classify,动态地将父级上下文类型传递给控制器
  • 适当时使用 only: 或 except: 限制操作
  • 自定义参数(例如 commentable_type、resource_type)会作为元数据传递给控制器
  • 控制器通过 rolemodel_rails gem(>= 2.4.0)提供的 resource_for(:commentable_type),将这些元数据还原为父级记录

编写控制器前,请先阅读 polymorphic-parent-resources 技能。 传递 *_type 默认值只是整个模式的一部分——该技能介绍了控制器端的处理方式、浅层嵌套下必需的 before_action 作用域(成员路由会继承错误的 *_type),以及如何整合现有的按父级划分的控制器。

2. 将关注项应用于资源

通过 concerns: 选项和符号数组应用关注项:

resources :products, concerns: %i[duplicatable turbo_form]
resources :articles, concerns: %i[commentable duplicatable turbo_form]

优点:

  • 消除重复的嵌套资源定义
  • 共享行为只需在一处更新
  • 能清楚地看出哪些资源共享通用功能

3. 浅层嵌套策略

使用 scope shallow: true 包装器为所有嵌套资源启用浅层嵌套,避免 URL 因嵌套层级过深而变得冗长。

scope shallow: true do
  resources :projects do
    resources :tasks do
      resources :comments
        # comments routes are shallow - only :index and :create are nested
        # show/edit/update/destroy use /comments/:id instead of /projects/:project_id/tasks/:task_id/comments/:id
      end
    end
  end
end

优点:

  • 成员操作(show、edit、update、destroy)的 URL 更短、更简洁
  • 只有集合操作(index、create)仍嵌套在父级之下
  • 单个资源的 URL 更易于收藏和分享
  • 需要完整嵌套时,可通过 shallow: false 覆盖

生成的路由:

# Nested (collection routes)
GET    /projects/:project_id/tasks                tasks#index
POST   /projects/:project_id/tasks                tasks#create
GET    /projects/:project_id/tasks/new            tasks#new

# Shallow (member routes)
GET    /tasks/:id                                 tasks#show
GET    /tasks/:id/edit                            tasks#edit
PATCH  /tasks/:id                                 tasks#update
DELETE /tasks/:id                                 tasks#destroy

覆盖示例:

concern :assembly do
  # Keep full nesting when parent context is always needed
  resources :assembly_items, only: %i[show], param: :kind, shallow: false
end

注意事项——浅层成员路由和按父级设置的默认值。 将包含成员操作的关注项应用于多个父级时,Rails 会为每个父级绘制浅层成员路径(/tasks/:id)。只有第一个路由会保留路由名称,其余都是无法访问的重复路由。因此,该关注项上的任何自定义默认值(例如 commentable_type),在每个成员请求中都会解析为第一个绘制的父级,无论记录实际属于哪个父级。控制器不得在成员操作中读取 *_type 参数;请将该 before_action 限定在集合操作中。参见 polymorphic-parent-resources。

4. 使用 only: 和 except: 限制操作

始终明确资源提供哪些操作。这有助于提升安全性、性能和代码清晰度。

# Only specific actions
resources :duplications, only: %i[create]                 # Only create action needed
resources :previews, only: %i[show update]                # Only show and update

# All except specific actions
resources :automations, except: %i[show]                  # All standard actions except show
resources :notes, except: %i[show]                        # Create/edit/destroy but no individual view
resources :widgets, except: %i[index show]                # No collection or individual views

原因:

  • 避免未使用的路由使 rails routes 输出变得杂乱
  • 阻止访问尚未实现的控制器操作
  • 清楚地向其他开发者说明意图
  • 不公开不必要的端点,缩小攻击面

5. 嵌套资源

对于属于某个父资源的资源,请进行适当嵌套:

resources :companies do
  resources :employees, except: %i[index show]
end

resources :products do
  resources :reviews, except: %i[index]
  resources :variants, except: %i[index show]
end

常见模式:

  • 父资源默认拥有完整的 CRUD 操作
  • 嵌套资源通常不包含 index(在父资源的 show 页面中显示)
  • 嵌套资源通常不包含 show(在父资源视图中内联编辑或编辑)
  • 嵌套资源的创建、更新和销毁操作在父资源上下文中执行

6. 单数资源

对于每个父资源只有一个的资源,请使用单数形式的 resource:

resources :users do
  resource :profile, only: %i[show edit update]  # Only one profile per user
  resource :settings, only: %i[edit update]      # Only one settings per user
  resource :avatar, only: %i[show update]        # Only one avatar per user
end

要点:

  • 单数资源没有 index 操作
  • URL 不需要 :id 参数(例如,使用 /users/1/profile,而不是 /users/1/profiles/1)
  • 非常适合一对一关系或单例资源

7. 集合路由和成员路由

使用 on: :collection 或 on: :member 添加自定义路由:

resources :products do
  # Collection routes (no :id needed)
  get :search, on: :collection              # /products/search
  post :bulk_import, on: :collection        # /products/bulk_import

  # Member routes (requires :id)
  post :duplicate, on: :member              # /products/:id/duplicate
  patch :archive, on: :member               # /products/:id/archive
  get :preview, on: :member                 # /products/:id/preview
end

# In a concern
concern :archivable do
  patch :archive, on: :member
  patch :unarchive, on: :member
end

要点:

  • 集合路由作用于整个集合(没有 :id 参数)
  • 成员路由作用于单个资源(需要 :id 参数)
  • 使用合适的 HTTP 动词(读取用 GET,创建用 POST,更新用 PATCH/PUT,删除用 DELETE)

8. 自定义参数

使用 param: 覆盖默认参数名称:

resources :products, param: :slug  # Uses :slug instead of :id

# In concerns
concern :categorizable do
  resources :categories, only: %i[show], param: :slug
end

结果:

  • URL 会变为 /products/:slug,而不是 /products/:id
  • 控制器接收的是 params[:slug],而不是 params[:id]
  • 示例:/products/vintage-leather-jacket,而不是 /products/123
  • 适用于 SEO 友好的 URL,或使用非数字标识符的情况

9. 默认选项

为资源设置默认选项:

resources :categories, defaults: { subcategory: false }

这些默认值可通过 params[:subcategory] 获取。

10. 自定义路由解析器

为多态路径辅助方法定义自定义解析器:

resolve 'Bulk::Accessories' do |form|
  form.persisted? ? [form.accessory] : [form.tank, :accessories]
end

resolve 'AssemblyItem' do |item|
  [item.host, item]
end

**用法:**这样可以让 url_for(@form_object) 或 link_to(@assembly_item) 正常工作。

11. 会话路由

对相关的身份验证操作,使用带有 scope 的 controller 块:

controller :sessions do
  get :login, action: :new
  delete :logout, action: :destroy

  scope :auth do
    get :failure
    match 'ADFS/callback', action: :create, via: %i[get post], as: :adfs_callback
  end
end

12. 使用约束挂载引擎

使用身份验证约束挂载管理引擎:

# Allow access only if user is logged in and is admin
mount PgHero::Engine, at: :pghero,
  constraints: -> env {
    env.session[:user_id].present? &&
    User.find_by(id: env.session[:user_id])&.admin?
  }

# Redirect to login if not authenticated
get :pghero, to: redirect('/login'), anchor: false,
  constraints: -> env { env.session[:user_id].blank? }

13. 健康检查路由

# Production health check
mount Health::Check::Engine, at: 'health-check' if Rails.env.production?

# Rails 7.1+ health check
get :up, to: 'rails/health#show', as: :rails_health_check

完整示例结构

以下是一个遵循所有最佳实践、组织良好的路由文件:

Rails.application.routes.draw do
  # 1. Define concerns first (reusable route patterns)
  concern :commentable do
    resources :comments, commentable_type: parent_resource.name.classify
  end

  concern :archivable do
    patch :archive, on: :member
    patch :unarchive, on: :member
  end

  concern :searchable do
    get :search, on: :collection
  end

  # 2. Main routes with shallow nesting
  scope shallow: true do
    # Top-level resources
    resources :users, except: %i[show] do
      resource :profile, only: %i[show edit update]
      resource :settings, only: %i[edit update]
    end

    resources :products, concerns: %i[commentable archivable searchable] do
      resources :reviews, except: %i[index]
      resources :variants, except: %i[index show]
    end

    # Nested resources
    resources :projects do
      resources :tasks, concerns: %i[commentable] do
        resource :assignment, only: %i[create destroy]
      end
    end
  end

  # 3. Session/authentication routes
  controller :sessions do
    get :login, action: :new
    post :login, action: :create
    delete :logout, action: :destroy
  end

  # 4. Admin routes with constraints
  namespace :admin do
    resources :users
    resources :settings, only: %i[index update]
  end

  # 5. Mounted engines (with constraints if needed)
  mount Sidekiq::Web, at: '/sidekiq', constraints: AdminConstraint.new

  # 6. Custom route resolvers (for polymorphic routing)
  resolve 'ProjectTask' do |task|
    [task.project, task]
  end

  # 7. Health checks
  get :up, to: 'rails/health#show', as: :rails_health_check

  # 8. Root route
  root 'dashboard#index'
end

审查清单

审查或生成路由时,请确认:

  • Concern 定义在文件顶部
  • Concern 遵循 DRY 原则,可在多个资源间复用
  • 自定义参数(如 commentable_type)使用 parent_resource.name.classify
  • 传递 *_type 默认值的 Concern 有对应的控制器,且该控制器使用 resource_for(参见 polymorphic-parent-resources)
  • 使用 scope shallow: true 启用浅层嵌套
  • 资源明确使用 only: 或 except: 限制操作
  • 嵌套资源遵循该模式(通常不包含 index/show)
  • 单数资源使用 resource,而不是 resources
  • 集合路由/成员路由使用 on: 参数
  • 需要时使用 param: 指定自定义参数名称
  • 为表单对象和多态模型定义路由解析器
  • 管理引擎配置了身份验证约束
  • 定义了根路由
  • 路由按逻辑顺序组织(concern → 资源 → 自定义路由 → 引擎 → 解析器 → 根路由)

应避免的反模式

❌ 不要重复嵌套资源模式:

resources :articles do
  resources :comments, commentable_type: 'Article'
end

resources :posts do
  resources :comments, commentable_type: 'Post'
end

✅ 改用 Concern:

concern :commentable do
  resources :comments, commentable_type: parent_resource.name.classify
end

resources :articles, concerns: %i[commentable]
resources :posts, concerns: %i[commentable]

❌ 不要在未启用 shallow 的情况下进行深层嵌套:

resources :companies do
  resources :projects do
    resources :tasks do
      # Results in /companies/:company_id/projects/:project_id/tasks/:id/edit
    end
  end
end

✅ 使用浅层嵌套:

scope shallow: true do
  resources :companies do
    resources :projects do
      resources :tasks  # edit becomes /tasks/:id/edit
    end
  end
end

❌ 不需要时不要保留所有操作:

resources :duplications  # Provides 7 REST actions but only need create

✅ 明确指定:

resources :duplications, only: %i[create]

❌ 单数资源不要使用复数形式:

resources :profiles, only: %i[show]  # There's only one profile per user

✅ 使用单数资源:

resource :profile, only: %i[show]

❌ 不要对上下文类型进行硬编码:

concern :commentable do
  resources :comments, commentable_type: 'Article'  # Only works for articles
end

✅ 使用 parent_resource:

concern :commentable do
  resources :comments, commentable_type: parent_resource.name.classify
end

高级模式

带有嵌套 Concern 的 Concern

Concern 可以引用其他 Concern,以构建高度可复用的路由模式:

concern :searchable do
  get :search, on: :collection
  get :autocomplete, on: :collection
end

concern :taggable do
  resources :tags, only: %i[index create destroy],
    concerns: %i[searchable],
    taggable_type: parent_resource.name.classify
end

resources :articles, concerns: %i[taggable]
# Articles get tags with search and autocomplete functionality

多个自定义参数

可以向 Concern 传递多个自定义参数:

concern :versioned do
  resources :versions,
    only: %i[index show],
    param: :version_number,
    shallow: false,
    versionable_type: parent_resource.name.classify
end

这些自定义参数可在控制器中作为路由元数据使用:

# In controller
def index
  @versionable_type = request.path_parameters[:versionable_type] # => "Article"
end

特定上下文的路由扩展

应用 concern 后,为特定资源添加其他路由:

resources :articles, concerns: %i[commentable] do
  # Add article-specific routes beyond the concern
  get :preview, on: :member
  post :publish, on: :member
end

按条件挂载引擎

根据环境按条件挂载引擎:

if Rails.env.production?
  mount HealthCheck::Engine, at: 'health-check'
end

unless Rails.env.production?
  mount LetterOpenerWeb::Engine, at: '/letter_opener'
end

常见路由模式

这些模式在 Rails 应用中很常见,可通过 concern 实现:

可评论的资源:

  • 模式:可以添加评论的资源
  • 实现:使用多态关联的嵌套评论资源
  • 应用于:文章、博客帖子、产品、任务等

可复制的资源:

  • 模式:可以克隆/复制的资源
  • 实现:为复制操作创建仅支持创建的嵌套资源
  • 应用于:模板、文档、配置等

可归档的资源:

  • 模式:可以归档/取消归档的资源
  • 实现:用于更改归档状态的成员路由
  • 应用于:项目、文档、记录等

可搜索的资源:

  • 模式:具有搜索/筛选功能的资源
  • 实现:用于搜索操作的集合路由
  • 应用于:产品、用户、文章等

有版本历史的资源:

  • 模式:具有版本历史的资源
  • 实现:嵌套版本资源,通常为只读
  • 应用于:文档、API 资源、配置等

可标记的资源:

  • 模式:可以添加标签/分类的资源
  • 实现:带有创建/销毁操作的嵌套标签
  • 应用于:文章、图片、书签等

AI Agent 使用说明

当被要求审查路由时:

  1. 检查路由是否按以下顺序组织:concern → 资源 → 自定义路由 → 引擎 → 解析器 → 根路由
  2. 验证 concern 定义正确且可复用
  3. 检查是否存在应使用浅层嵌套的深层嵌套
  4. 确保资源适当地使用 only:/except:
  5. 验证单数与复数资源的使用是否正确
  6. 检查是否有应提取为 concern 的重复模式
  7. 验证自定义解析器是否与模型关联关系匹配

当被要求生成新路由时:

  1. 判断该行为是否可复用 → 创建/使用 concern
  2. 检查资源是否应嵌套在父资源下
  3. 为嵌套资源启用浅层嵌套(除非明确需要深层嵌套)
  4. 根据所需操作指定 only: 或 except:
  5. 如果每个父资源只有一个子资源,则使用单数形式的 resource
  6. 根据需要添加自定义集合路由/成员路由
  7. 如果这是表单对象或多态模型,则创建路由解析器
  8. 对于类似功能,遵循现有的 concern 模式

当被要求更新路由时:

  1. 保持与现有模式一致
  2. 如果要为多个资源添加类似功能,则重构为使用 concern
  3. 更新现有 concern,而不是重复代码
  4. 保留浅层嵌套策略
  5. 按既定结构组织路由
  6. 如果模型关联关系发生变化,则更新自定义解析器

测试路由

更改后务必验证路由:

# List all routes
rails routes

# Search for specific routes
rails routes | grep products

# Show routes for a specific controller
rails routes -c products

# Show routes with expanded format
rails routes --expanded

# Filter by HTTP verb
rails routes -g POST

最后更新:2026 年 2 月

评论

支持 Markdown