Tom*_*ord 4 ruby routes ruby-on-rails url-routing
有没有一种方法来验证config/routes.rb由定义和公开的所有控制器动作是否rake routes实际上都与现有的控制器动作相对应?
例如,假设我们有以下路由文件:
Application.routes.draw do
resources :foobar
end
Run Code Online (Sandbox Code Playgroud)
和以下控制器:
class FoobarsController < ApplicationController
def index
# ...
end
def show
# ...
end
end
Run Code Online (Sandbox Code Playgroud)
我想有一些办法自动检测到create,new,edit,update和destroy动作(如隐式路由定义)未映射到一个有效的控制器操作-这样我就可以修复routes.rb文件:
Application.routes.draw do
resources :foobar, only: [:index, :show]
end
Run Code Online (Sandbox Code Playgroud)
如果可以的话,对路线进行“完整性检查”。
这样的检查不一定是完美的。我可以轻松地手动验证任何误报。(尽管“完美”的检查是理想的,因为它可以包含在测试套件中!)
我在这里的动机是为了防止AbstractController::ActionNotFound由于狡猾的API请求而引发异常,因为在大型应用程序中意外地定义了其他路由。
我很好奇,以下是我的尝试。它仍然不准确,因为它还没有匹配正确的format。另外,某些路线有约束条件。我的代码尚未考虑。
rails console:
todo_skipped_routes = []
valid_routes = []
invalid_routes = []
Rails.application.routes.routes.each do |route|
controller_route_name = route.defaults[:controller]
action_route_name = route.defaults[:action]
if controller_route_name.blank? || action_route_name.blank?
todo_skipped_routes << route
next
end
# TODO: maybe Rails already has a "proper" way / method to constantize this
# copied over @max answer, because I forgot to consider namespacing
controller_class = "#{controller_route_name.sub('\/', '::')}_controller".camelcase.safe_constantize
is_route_valid = !controller_class.nil? && controller_class.instance_methods(false).include?(action_route_name.to_sym)
# TODO: check also if "format" matches / gonna be "responded to" properly by the controller-action
# check also "lambda" constraints, and `request.SOMEMETHOD` constraints (i.e. `subdomain`, `remote_ip`, `host`, ...)
if is_route_valid
valid_routes << route
else
invalid_routes << route
end
end
puts valid_routes
puts invalid_routes
# puts "friendlier" version
pp invalid_routes.map(&:defaults)
# => [
# {:controller=>"sessions", :action=>"somenonexistingaction"},
# {:controller=>"posts", :action=>"criate"},
# {:controller=>"yoosers", :action=>"create"},
# ]
Run Code Online (Sandbox Code Playgroud)
我也想知道其他答案,或者是否有适当的方法来做到这一点。另外,如果有人知道我的代码有所改进,请告诉我。谢谢 :)
对其他答案的巨大贡献 - 请在下面查看。但这是我在过去几年中在多个项目中最终使用的,并且它对我很有帮助。因此,我将其自我标记为可见性的可接受答案。
我将以下内容放入spec/routes/integrity_check_spec.rb:
require 'rails_helper'
RSpec.describe 'Integrity Check of Routes', order: :defined do # rubocop:disable RSpec/DescribeClass
Rails.application.routes.routes.sort_by { |r| r.defaults[:controller].to_s }.each do |route|
controller, action = route.defaults.slice(:controller, :action).values
# Some routes may have the controller assigned as a dynamic segment
# We need to skip them since we can't really test them in this way
next if controller.nil?
# Skip the built in Rails 5 active_storage routes
next if controller.split('/').first == 'active_storage'
# Skip built in Rails 6 action_mailbox routes
next if controller == 'rails/conductor/action_mailbox/inbound_emails'
ctrl_name = "#{controller.sub('\/', '::')}_controller".camelcase
ctrl_klass = ctrl_name.safe_constantize
it "#{ctrl_name} is defined and has corresponding action: #{action}, for #{route.name || '(no route name)'}" do
expect(ctrl_klass).to be_present
expect(ctrl_klass.new).to respond_to(action)
end
end
end
Run Code Online (Sandbox Code Playgroud)
注意事项: