Как проверить, что действия контроллера определены для всех маршрутов в приложении rails? - PullRequest
0 голосов
/ 19 октября 2018

Есть ли способ проверить, что все действия контроллера, как определено в config/routes.rb и выставлено rake routes, действительно соответствуют существующему действию контроллера?

Например, предположим, у нас есть следующие маршрутыfile:

Application.routes.draw do
  resources :foobar
end

И следующий контроллер:

class FoobarsController < ApplicationController
  def index
    # ...
  end

  def show
    # ...
  end
end

Я бы хотел иметь какой-то способ автоматического определения того, что create, new, editДействия update и destroy (как неявно определены в маршрутах) не сопоставлены действительному действию контроллера - поэтому я могу исправить файл routes.rb:

Application.routes.draw do
  resources :foobar, only: [:index, :show]
end

Проверка целостности«из маршрутов, если хотите.

Такая проверка не обязательно должна быть совершенной ;Я мог легко проверить любые ложные срабатывания вручную.(Хотя «идеальная» проверка была бы идеальной, так как она могла бы быть включена в набор тестов!)

Моя мотивация здесь состоит в том, чтобы не вызывать AbstractController::ActionNotFound исключений, вызванных хитрыми запросами API, поскольку дополнительные маршруты былинепреднамеренно определено (в большом приложении).

Ответы [ 2 ]

0 голосов
/ 19 октября 2018

Это основано на ответе Джея-Ар-Полидарио:

require 'test_helper'

class RoutesTest < ActionDispatch::IntegrationTest
  Rails.application.routes.routes.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 'active_storage' == controller.split('/').first 
    # Naive attempt to resolve the controller constant from the name
    # Replacing / with :: is for namespaces
    ctrl_name = "#{controller.sub('\/', '::')}_controller".camelcase
    ctrl = ctrl_name.safe_constantize
    # tagging SecureRandom.uuid on the end is a hack to ensure that each
    # test name is unique
    test "#{ctrl_name} controller exists - #{SecureRandom.uuid}" do
      assert ctrl, "Controller #{ctrl_name} is not defined for #{route.name}"
    end
    test "#{controller} has the action #{action} - #{SecureRandom.uuid}" do
      assert ctrl.respond_to?(action),
        "#{ctrl_name} does not have the action '#{action}' - #{route.name}"
    end if ctrl
  end
end

Однако я хотел бы спросить, действительно ли оно применимо для чего-либо, кроме самых тривиальных примеров.

0 голосов
/ 19 октября 2018

Мне стало любопытно, и вот моя попытка.Это все еще не точно, потому что это еще не соответствует 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"},
# ]

Мне также интересно узнать другие ответы или есть ли правильный способ сделать это.Кроме того, если кто-то знает улучшение по сравнению с моим кодом, пожалуйста, дайте мне знать.Спасибо:)

...