├── .github └── workflows │ └── main.yml ├── .gitignore ├── Gemfile ├── MIT-LICENSE ├── README.rdoc ├── Rakefile ├── lib ├── tasks │ └── traceroute.rake └── traceroute.rb ├── test ├── app.rb ├── test_helper.rb ├── traceroute_test.rb ├── traceroute_with_engine_test.rb └── yaml_test.rb └── traceroute.gemspec /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | strategy: 8 | matrix: 9 | ruby_version: [ruby-head, '3.1', '3.0', '2.7'] 10 | rails_version: [edge, '7.0', '6.1'] 11 | 12 | include: 13 | - ruby_version: '2.6' 14 | rails_version: '6.1' 15 | 16 | - ruby_version: '3.0' 17 | rails_version: '6.0' 18 | - ruby_version: '2.7' 19 | rails_version: '6.0' 20 | - ruby_version: '2.6' 21 | rails_version: '6.0' 22 | 23 | - ruby_version: '2.7' 24 | rails_version: '5.2' 25 | - ruby_version: '2.6' 26 | rails_version: '5.2' 27 | 28 | - ruby_version: '2.6' 29 | rails_version: '5.1' 30 | 31 | - ruby_version: '2.6' 32 | rails_version: '5.0' 33 | 34 | runs-on: ubuntu-24.04 35 | 36 | env: 37 | RAILS_VERSION: ${{ matrix.rails_version }} 38 | 39 | steps: 40 | - uses: actions/checkout@v3 41 | 42 | - uses: ruby/setup-ruby@v1 43 | with: 44 | ruby-version: ${{ matrix.ruby_version }} 45 | bundler-cache: true 46 | rubygems: ${{ matrix.ruby_version < '2.6' && 'default' || 'latest' }} 47 | bundler: ${{ (startsWith(matrix.rails_version, '4.') || startsWith(matrix.rails_version, '3.')) && '1' || '2' }} 48 | continue-on-error: ${{ (matrix.ruby_version == 'ruby-head') || (matrix.allow_failures == 'true') }} 49 | 50 | - run: bundle exec rake 51 | continue-on-error: ${{ (matrix.ruby_version == 'ruby-head') || (matrix.rails_version == 'edge') || (matrix.allow_failures == 'true') }} 52 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | .bundle 3 | Gemfile.lock 4 | gemfiles/*.lock 5 | pkg/* 6 | *.log 7 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source 'https://rubygems.org' 4 | 5 | # Specify your gem's dependencies in traceroute.gemspec 6 | gemspec 7 | 8 | if ENV['RAILS_VERSION'] == 'edge' 9 | gem 'rails', git: 'https://github.com/rails/rails.git' 10 | elsif ENV['RAILS_VERSION'] 11 | gem 'rails', "~> #{ENV['RAILS_VERSION']}.0" 12 | else 13 | gem 'rails' 14 | end 15 | -------------------------------------------------------------------------------- /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Akira Matsuda 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.rdoc: -------------------------------------------------------------------------------- 1 | = Traceroute 2 | 3 | A Rake task that helps you find dead routes and unused actions in your Rails 3+ app. 4 | 5 | 6 | == Features 7 | 8 | This Rake task investigates your Rails application's routes definition, then shows you the unused routes and unreachable action methods. 9 | 10 | 11 | == Supported versions 12 | 13 | * Ruby 1.8.7, 1.9.2, 1.9.3, 2.0, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 3.0, 3.1 (trunk) 14 | 15 | * Rails 3.0.x, 3.1.x, 3.2.x, 4.0.x, 4.1.x, 5.0.x, 5.1.x, 5.2.x, 6.0, 6.1, 7.0 (master) 16 | 17 | 18 | == Install 19 | 20 | Put this line in your Gemfile: 21 | gem 'traceroute' 22 | 23 | Then bundle: 24 | % bundle 25 | 26 | 27 | == Usage 28 | 29 | Just run the following command in your Rails app directory. 30 | % rake traceroute 31 | 32 | If you want the rake task to fail when errors are found. 33 | 34 | % FAIL_ON_ERROR=1 rake traceroute 35 | 36 | == What's gonna happen then? 37 | 38 | Consider you have the following routes.rb and a controller: 39 | 40 | # config/routes.rb 41 | YourRailsApp::Application.routes.draw do 42 | resources :users, :only => [:index, :show, :new, :create] 43 | match 'products/:id/purchase' => 'catalog#purchase', :as => :purchase 44 | end 45 | 46 | # app/controllers/users_controller.rb 47 | class UsersController < ApplicationController 48 | def index 49 | @users = User.page(params[:page]) 50 | end 51 | 52 | def index2 53 | end 54 | 55 | def show 56 | @user = User.find(params[:id]) 57 | end 58 | end 59 | 60 | Running the Rake task will print something like this for you: 61 | 62 | Unused routes (3): 63 | users#create 64 | users#new 65 | catalog#purchase 66 | Unreachable action methods (1): 67 | users#index2 68 | 69 | OMG super helpful, isn't it? 70 | 71 | == How do I tell it to ignore routes? 72 | 73 | Some gems out there that inject routes or actions into our app for testing. Jasmine-Rails does this. They can give you false negatives when running this in development mode. It can be useful to ignore said routes or actions. 74 | 75 | Create a .traceroute.yaml (or .traceroute.yml or .traceroute) file in your root directory. 76 | 77 | # .traceroute.yaml 78 | ignore_unreachable_actions: 79 | - ^jasmine_rails\/ 80 | ignore_unused_routes: 81 | - ^users#index 82 | 83 | Both yaml headers accept a list of regexes. 84 | 85 | 86 | == FAQ 87 | 88 | Q: It makes a fuss over the default route at the very bottom. WTF? 89 | A: Please do not use that. Did you read the comment in your routes.rb? Actually this task will help you a lot to remove that evil route. 90 | 91 | Q: "command not found: traceroute" Did you mean: tracert 92 | A: I'm afraid you're using the wrong operating system. 93 | 94 | 95 | == Questions, Feedback 96 | 97 | Feel free to message me on Github (amatsuda) or Twitter (@a_matsuda) ☇3☇3☇3 98 | 99 | 100 | == Contributing to Traceroute 101 | 102 | * Fork, patch, then send me a pull request. 103 | 104 | 105 | == Copyright 106 | 107 | Copyright (c) 2011 Akira Matsuda. See MIT-LICENSE file for further details. 108 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler' 2 | Bundler::GemHelper.install_tasks 3 | 4 | task :default => :test 5 | require 'rake/testtask' 6 | Rake::TestTask.new(:test) do |test| 7 | test.libs << 'lib' << 'test' 8 | test.pattern = 'test/**/*_test.rb' 9 | test.verbose = true 10 | end 11 | -------------------------------------------------------------------------------- /lib/tasks/traceroute.rake: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | desc 'Prints out unused routes and unreachable action methods' 4 | task :traceroute => :environment do 5 | traceroute = Traceroute.new Rails.application 6 | traceroute.load_everything! 7 | 8 | unless ENV['UNREACHABLE_ACTION_METHODS_ONLY'] 9 | unused_routes = traceroute.unused_routes 10 | puts "Unused routes (#{unused_routes.count}):" 11 | unused_routes.each {|route| puts " #{route}"} 12 | end 13 | 14 | puts unless (ENV['UNREACHABLE_ACTION_METHODS_ONLY'] || ENV['UNUSED_ROUTES_ONLY']) 15 | 16 | unless ENV['UNUSED_ROUTES_ONLY'] 17 | unreachable_action_methods = traceroute.unreachable_action_methods 18 | puts "Unreachable action methods (#{unreachable_action_methods.count}):" 19 | unreachable_action_methods.each {|action| puts " #{action}"} 20 | end 21 | 22 | if ENV['FAIL_ON_ERROR'] && ((!ENV['UNREACHABLE_ACTION_METHODS_ONLY'] && unused_routes.any?) || (!ENV['UNUSED_ROUTES_ONLY'] && unreachable_action_methods.any?)) 23 | fail "Unused routes or unreachable action methods detected." 24 | end 25 | end 26 | 27 | namespace :traceroute do 28 | desc 'Prints out unused routes' 29 | task :unused_routes => :environment do 30 | ENV['UNUSED_ROUTES_ONLY'] = '1' 31 | Rake::Task[:traceroute].invoke 32 | end 33 | 34 | desc 'Prints out unreachable action methods' 35 | task :unreachable_action_methods => :environment do 36 | ENV['UNREACHABLE_ACTION_METHODS_ONLY'] = '1' 37 | Rake::Task[:traceroute].invoke 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /lib/traceroute.rb: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | # frozen_string_literal: true 3 | 4 | require 'yaml' 5 | 6 | class Traceroute 7 | VERSION = Gem.loaded_specs['traceroute'].version.to_s 8 | 9 | WILDCARD_ROUTES = %r[^/?:controller\(?/:action].freeze 10 | 11 | class Railtie < ::Rails::Railtie 12 | rake_tasks do 13 | load File.join(File.dirname(__FILE__), 'tasks/traceroute.rake') 14 | end 15 | end 16 | 17 | def initialize(app) 18 | @app = app 19 | 20 | @ignored_unreachable_actions = [] 21 | @ignored_unused_routes = [/^\/cable$/] 22 | 23 | @ignored_unused_routes << %r{^#{@app.config.assets.prefix}} if @app.config.respond_to? :assets 24 | 25 | config_filename = %w(.traceroute.yaml .traceroute.yml .traceroute).detect {|f| File.exist?(f)} 26 | if config_filename && (config = YAML.load_file(config_filename)) 27 | (config['ignore_unreachable_actions'] || []).each do |ignored_action| 28 | @ignored_unreachable_actions << Regexp.new(ignored_action) 29 | end 30 | 31 | (config['ignore_unused_routes'] || []).each do |ignored_action| 32 | @ignored_unused_routes << Regexp.new(ignored_action) 33 | end 34 | end 35 | end 36 | 37 | def load_everything! 38 | @app.eager_load! 39 | ::Rails::InfoController rescue NameError 40 | ::Rails::WelcomeController rescue NameError 41 | ::Rails::MailersController rescue NameError 42 | @app.reload_routes! 43 | 44 | Rails::Engine.subclasses.each(&:eager_load!) 45 | end 46 | 47 | def unused_routes 48 | routed_actions - defined_action_methods 49 | end 50 | 51 | def unreachable_action_methods 52 | defined_action_methods - routed_actions 53 | end 54 | 55 | def defined_action_methods 56 | @defined_action_methods ||= [ActionController::Base, (ActionController::API if defined?(ActionController::API))].compact.map do |klass| 57 | klass.descendants.map do |controller| 58 | controller.action_methods.reject {|a| (a =~ /\A(_conditional)?_callback_/) || (a == '_layout_from_proc')}.map do |action| 59 | "#{controller.controller_path}##{action}" 60 | end 61 | end.flatten 62 | end.flatten.reject {|r| @ignored_unreachable_actions.any? { |m| r.match(m) } } 63 | end 64 | 65 | def routed_actions 66 | @routed_actions ||= routes.map do |r| 67 | if r.requirements[:controller].present? && r.requirements[:action].present? 68 | "#{r.requirements[:controller]}##{r.requirements[:action]}" 69 | elsif (String === r.path) && (WILDCARD_ROUTES =~ r.path) 70 | %Q["#{r.path}" ⚠️ This is a legacy wild controller route that's not recommended for RESTful applications.] 71 | elsif WILDCARD_ROUTES =~ r.path.spec.to_s 72 | %Q["#{r.path.spec}" ⚠️ This is a legacy wild controller route that's not recommended for RESTful applications.] 73 | else 74 | ((String === r.path) && r.path.to_s) || r.path.spec.to_s # unknown routes 75 | end 76 | end.compact.flatten.reject {|r| @ignored_unused_routes.any? { |m| r.match(m) } } 77 | end 78 | 79 | def routes 80 | collect_routes @app.routes.routes 81 | end 82 | 83 | def collect_routes(routes) 84 | routes = routes.each_with_object([]) do |r, tmp_routes| 85 | next if (ActionDispatch::Routing::Mapper::Constraints === r.app) && (%w[ActionDispatch::Routing::PathRedirect ActionDispatch::Routing::OptionRedirect ActionDispatch::Routing::Redirect].include?(r.app.app.class.name)) 86 | 87 | if r.app.is_a?(ActionDispatch::Routing::Mapper::Constraints) && r.app.app.respond_to?(:routes) 88 | engine_routes = r.app.app.routes 89 | if engine_routes.is_a?(ActionDispatch::Routing::RouteSet) 90 | tmp_routes.concat collect_routes(engine_routes.routes) 91 | end 92 | else 93 | tmp_routes << r 94 | end 95 | end 96 | 97 | routes.reject! {|r| r.app.is_a?(ActionDispatch::Routing::Redirect)} 98 | 99 | routes 100 | end 101 | end 102 | -------------------------------------------------------------------------------- /test/app.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rails' 4 | require 'action_controller/railtie' 5 | 6 | require 'traceroute' 7 | 8 | module DummyApp 9 | class Application < Rails::Application 10 | config.root = File.expand_path('..', __FILE__) 11 | config.eager_load = false 12 | config.secret_key_base = '1234567890' 13 | end 14 | end 15 | 16 | class ApplicationController < ActionController::Base; end 17 | 18 | class UsersController < ApplicationController 19 | def index; end 20 | def index2; end 21 | def show; end 22 | end 23 | 24 | module Admin; class ShopsController < ApplicationController 25 | def index; end 26 | def create; end 27 | end; end 28 | 29 | if defined?(ActionController::API) 30 | class ApiController < ActionController::API; end 31 | 32 | module Api; class BooksController < ApiController 33 | def index; end 34 | def create; end 35 | end; end 36 | end 37 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'bundler/setup' 4 | require 'minitest/autorun' 5 | require 'rails' 6 | require 'traceroute' 7 | require_relative 'app' 8 | 9 | Minitest::Test.class_eval do 10 | def assert_defined_action_methods(*actions) 11 | assert_equal actions.sort, @traceroute.defined_action_methods.reject {|a| a =~ /^rails/}.sort 12 | end 13 | 14 | def assert_routed_actions(*actions) 15 | assert_equal actions.sort, @traceroute.routed_actions.sort 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /test/traceroute_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative 'test_helper' 4 | 5 | module TracerouteTest 6 | class BasicTest < Minitest::Test 7 | def setup 8 | @traceroute = Traceroute.new Rails.application 9 | end 10 | 11 | def test_defined_action_methods 12 | assert_defined_action_methods 'users#index', 'users#show', 'users#index2', 'admin/shops#create', 'admin/shops#index', 'api/books#create', 'api/books#index' 13 | end 14 | 15 | def test_routed_actions 16 | assert_empty @traceroute.routed_actions 17 | end 18 | end 19 | 20 | class RoutedActionsTest < Minitest::Test 21 | def setup 22 | DummyApp::Application.routes.draw do 23 | resources :users, :only => [:index, :show, :new, :create] 24 | 25 | get '/', to: redirect('/users'), constraints: lambda { true } 26 | 27 | namespace :admin do 28 | resources :shops, :only => :index 29 | end 30 | 31 | namespace :api do 32 | resources :books, :only => :index 33 | end 34 | end 35 | 36 | @traceroute = Traceroute.new Rails.application 37 | end 38 | 39 | def teardown 40 | DummyApp::Application.routes.clear! 41 | end 42 | 43 | def test_routed_actions 44 | assert_routed_actions 'admin/shops#index', 'api/books#index', 'users#index', 'users#show', 'users#new', 'users#create' 45 | end 46 | end 47 | 48 | class TracerouteRakeTests < Minitest::Test 49 | def setup 50 | require 'rake' 51 | load "./lib/tasks/traceroute.rake" 52 | @fail_on_error_was = ENV['FAIL_ON_ERROR'] 53 | end 54 | 55 | def teardown 56 | ENV['FAIL_ON_ERROR'] = @fail_on_error_was 57 | Rake::Task.clear 58 | DummyApp::Application.routes.clear! 59 | end 60 | 61 | def test_rake_task_fails_when_unreachable_action_method_detected 62 | ENV['FAIL_ON_ERROR']="1" 63 | Rake::Task[:traceroute].execute 64 | rescue => e 65 | assert_includes e.message, "Unused routes or unreachable action methods detected." 66 | end 67 | 68 | def test_rake_task_fails_when_unused_route_detected 69 | DummyApp::Application.routes.draw do 70 | resources :users, :only => [:index, :show, :new, :create] do 71 | member do 72 | get :index2 73 | end 74 | end 75 | 76 | namespace :admin do 77 | resources :shops, :only => [:index, :create] 78 | end 79 | 80 | namespace :api do 81 | resources :books, :only => [:index, :create] 82 | end 83 | 84 | namespace :rails do 85 | resources :mailers, only: ["index"] do 86 | member do 87 | get :preview 88 | end 89 | end 90 | end 91 | end 92 | 93 | begin 94 | ENV['FAIL_ON_ERROR'] = "1" 95 | Rake::Task[:traceroute].execute 96 | rescue => e 97 | assert_includes e.message, "Unused routes or unreachable action methods detected." 98 | end 99 | end 100 | end 101 | end 102 | -------------------------------------------------------------------------------- /test/traceroute_with_engine_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative 'test_helper' 4 | 5 | module TestEngine 6 | class Engine < ::Rails::Engine 7 | isolate_namespace TestEngine 8 | end 9 | 10 | class TasksController < ApplicationController; end 11 | end 12 | 13 | module EngineTestsCondition 14 | def setup 15 | TestEngine::TasksController.class_eval { def index() end } 16 | super 17 | end 18 | 19 | def teardown 20 | super 21 | TestEngine::TasksController.send :undef_method, :index 22 | TestEngine::TasksController.clear_action_methods! 23 | end 24 | end 25 | 26 | module TracerouteWithEngineTest 27 | class BasicTest < Minitest::Test 28 | prepend EngineTestsCondition 29 | 30 | def setup 31 | @traceroute = Traceroute.new Rails.application 32 | end 33 | 34 | def test_defined_action_methods 35 | assert_defined_action_methods 'admin/shops#create', 'admin/shops#index', 'api/books#create', 'api/books#index', 'test_engine/tasks#index', 'users#index', 'users#index2', 'users#show' 36 | end 37 | 38 | def test_routed_actions 39 | assert_empty @traceroute.routed_actions 40 | end 41 | end 42 | 43 | class RoutedActionsTest < Minitest::Test 44 | prepend EngineTestsCondition 45 | 46 | def setup 47 | DummyApp::Application.routes.draw do 48 | resources :posts, :only => [:index, :show, :new, :create] 49 | end 50 | 51 | @traceroute = Traceroute.new Rails.application 52 | end 53 | 54 | def teardown 55 | DummyApp::Application.routes.clear! 56 | end 57 | 58 | def test_routed_actions 59 | assert_routed_actions 'posts#index', 'posts#show', 'posts#new', 'posts#create' 60 | end 61 | end 62 | 63 | class EngineTest < Minitest::Test 64 | prepend EngineTestsCondition 65 | 66 | def setup 67 | TestEngine::Engine.routes.draw do 68 | resources :tasks, only: :index 69 | 70 | root to: redirect(path: '/tasks') 71 | end 72 | 73 | Rails.application.routes_reloader.route_sets << DummyApp::Application.routes 74 | DummyApp::Application.routes.draw do 75 | resources :posts, only: [:index, :show] 76 | 77 | mount TestEngine::Engine => '/test_engine' 78 | end 79 | 80 | @traceroute = Traceroute.new Rails.application 81 | end 82 | 83 | def teardown 84 | DummyApp::Application.routes.clear! 85 | end 86 | 87 | def test_defined_action_methods 88 | assert_defined_action_methods 'admin/shops#create', 'admin/shops#index', 'api/books#create', 'api/books#index', 'test_engine/tasks#index', 'users#index', 'users#index2', 'users#show' 89 | end 90 | 91 | def test_routed_actions 92 | assert_routed_actions 'posts#index', 'posts#show', 'test_engine/tasks#index' 93 | end 94 | end 95 | end 96 | -------------------------------------------------------------------------------- /test/yaml_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative 'test_helper' 4 | 5 | module JasmineRails 6 | class SpecRunner < ApplicationController; end 7 | end 8 | 9 | module YamlTestsCondition 10 | def setup 11 | super 12 | JasmineRails::SpecRunner.class_eval { def index() end } 13 | end 14 | 15 | def teardown 16 | super 17 | JasmineRails::SpecRunner.send :undef_method, :index 18 | JasmineRails::SpecRunner.clear_action_methods! 19 | end 20 | end 21 | 22 | class DotFileTest < Minitest::Test 23 | prepend YamlTestsCondition 24 | 25 | def setup 26 | File.open ".traceroute.yaml", "w" do |file| 27 | file.puts 'ignore_unreachable_actions:' 28 | file.puts '- ^jasmine_rails\/' 29 | file.puts 'ignore_unused_routes:' 30 | file.puts '- ^users' 31 | end 32 | 33 | DummyApp::Application.routes.draw do 34 | resources :users, :only => [:index, :show, :new, :create] 35 | 36 | namespace :admin do 37 | resources :shops, :only => :index 38 | end 39 | 40 | namespace :api do 41 | resources :books, :only => :index 42 | end 43 | end 44 | 45 | @traceroute = Traceroute.new Rails.application 46 | end 47 | 48 | def teardown 49 | DummyApp::Application.routes.clear! 50 | 51 | File.delete ".traceroute.yaml" 52 | end 53 | 54 | def test_unreachable_actions_are_ignored 55 | refute @traceroute.defined_action_methods.include? 'jasmine_rails/spec_runner#index' 56 | end 57 | 58 | def test_used_routes_are_ignored 59 | assert_routed_actions 'admin/shops#index', 'api/books#index' 60 | end 61 | end 62 | 63 | class EmptyFileTest < Minitest::Test 64 | prepend YamlTestsCondition 65 | 66 | def setup 67 | File.open ".traceroute.yaml", "w" do |file| 68 | end 69 | 70 | DummyApp::Application.routes.draw do 71 | resources :users, :only => [:index, :show, :new, :create] 72 | 73 | namespace :admin do 74 | resources :shops, :only => :index 75 | end 76 | namespace :api do 77 | resources :books, :only => :index 78 | end 79 | end 80 | 81 | @traceroute = Traceroute.new Rails.application 82 | end 83 | 84 | def teardown 85 | DummyApp::Application.routes.clear! 86 | 87 | File.delete ".traceroute.yaml" 88 | end 89 | 90 | def test_empty_yaml_file_is_handled_the_same_as_no_file 91 | assert_defined_action_methods 'users#index', 'users#show', 'users#index2', 'admin/shops#create', 'admin/shops#index', 'api/books#create', 'api/books#index', 'jasmine_rails/spec_runner#index' 92 | end 93 | 94 | def test_property_with_no_key 95 | assert_routed_actions 'admin/shops#index', 'api/books#index', 'users#index', 'users#show', 'users#new', 'users#create' 96 | end 97 | end 98 | 99 | class InvalidFileTest < Minitest::Test 100 | prepend YamlTestsCondition 101 | 102 | def setup 103 | File.open ".traceroute.yml", "w" do |file| 104 | file.puts 'ignore_unreachable_actions:' 105 | file.puts 'ignore_unused_routes:' 106 | end 107 | 108 | DummyApp::Application.routes.draw do 109 | resources :users, :only => [:index, :show, :new, :create] 110 | 111 | namespace :admin do 112 | resources :shops, :only => :index 113 | end 114 | 115 | namespace :api do 116 | resources :books, :only => :index 117 | end 118 | end 119 | 120 | @traceroute = Traceroute.new Rails.application 121 | end 122 | 123 | def teardown 124 | DummyApp::Application.routes.clear! 125 | 126 | File.delete ".traceroute.yml" 127 | end 128 | 129 | def test_empty_yaml_file_is_handled_the_same_as_no_file 130 | assert_defined_action_methods 'users#index', 'users#show', 'users#index2', 'admin/shops#create', 'admin/shops#index', 'api/books#create', 'api/books#index', 'jasmine_rails/spec_runner#index' 131 | end 132 | 133 | def test_property_with_no_key 134 | assert_routed_actions 'admin/shops#index', 'api/books#index', 'users#index', 'users#show', 'users#new', 'users#create' 135 | end 136 | end 137 | 138 | class FilenameSupportTest < Minitest::Test 139 | prepend YamlTestsCondition 140 | 141 | def test_yml_supported 142 | File.open ".traceroute.yml", "w" do |file| 143 | file.puts 'ignore_unreachable_actions:' 144 | file.puts '- ^jasmine_rails\/' 145 | file.puts 'ignore_unused_routes:' 146 | file.puts '- ^users' 147 | end 148 | 149 | @traceroute = Traceroute.new Rails.application 150 | 151 | refute @traceroute.defined_action_methods.include? 'jasmine_rails/spec_runner#index' 152 | 153 | File.delete ".traceroute.yml" 154 | end 155 | 156 | def test_no_extension_supported 157 | File.open ".traceroute", "w" do |file| 158 | file.puts 'ignore_unreachable_actions:' 159 | file.puts '- ^jasmine_rails\/' 160 | file.puts 'ignore_unused_routes:' 161 | file.puts '- ^users' 162 | end 163 | 164 | @traceroute = Traceroute.new Rails.application 165 | 166 | refute @traceroute.defined_action_methods.include? 'jasmine_rails/spec_runner#index' 167 | 168 | File.delete ".traceroute" 169 | end 170 | end 171 | -------------------------------------------------------------------------------- /traceroute.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | $:.push File.expand_path("../lib", __FILE__) 3 | 4 | Gem::Specification.new do |s| 5 | s.name = "traceroute" 6 | s.version = '0.8.1' 7 | s.platform = Gem::Platform::RUBY 8 | s.authors = ['Akira Matsuda'] 9 | s.email = ['ronnie@dio.jp'] 10 | s.homepage = 'https://github.com/amatsuda/traceroute' 11 | s.summary = 'A Rake task that helps you find the dead routes and actions for your Rails 3 app' 12 | s.description = "This Rake task investigates the application's routes definition, then tells you unused routes and unreachable action methods" 13 | 14 | s.files = `git ls-files`.split("\n") 15 | s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") 16 | s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } 17 | s.extra_rdoc_files = ['README.rdoc'] 18 | s.require_paths = ['lib'] 19 | 20 | s.licenses = ['MIT'] 21 | 22 | s.add_dependency 'rails', ['>= 3.0.0'] 23 | end 24 | --------------------------------------------------------------------------------