@ledsun blog

Hのキーがhellで、Sのキーがslaveだ、と彼は思った。そしてYのキーがyouだ。

アクションメソッドが存在しないルーティングをみつけるスクリプト

すこし伝統があるRailsアプリケーションを修正しようとしました。 config/routes.rbにすでに使っていないルーティングが残っていることに気がつきました。 対応するアクションメソッドの実装がないため、使っていないルーティングだと判断しました。

温かみのある手作業でチェックしても良いのですが、自動的に検出できそうな気もします。 ぐぐってもいい情報がみつけられませんでした。 同僚に相談したところ、 ruby - How to verify controller actions are defined for all routes in a rails application? - Stack Overflow を教えてもらいました。 未解決ですが、回答によるとRails.application.routesからルーティング情報が取得できるようです。

これを元にアクションメソッドが存在しないルーティングを見つけるスクリプトを作成しました。

bad_informations = Rails.application.routes.set.map { |r| r.defaults }
                            .reject { |r| r[:controller].nil? }
                            .reject { |r| r[:controller].include? '/' }
                            .reduce([]) do |bad_informations, r|
                              controller = r[:controller] + '_controller'
                              action = r[:action]
                              begin
                                class_name = controller.singularize.camelcase
                                if class_name.constantize.instance_methods.include?(action.to_sym)
                                  # p "#{class_name}クラスの#{action}メソッドはあります"
                                else
                                  bad_informations << "#{class_name}クラスの#{action}メソッドはありません"
                                end
                              rescue NameError
                                bad_informations << "#{class_name}クラスはありません"
                              end

                              bad_informations
                            end
                            .sort.uniq

p *(bad_informations)

Railsの情報を参照するのでrails runnerから起動します。 例えば bin/rails runner hoge.rb です。 次のような出力をします。

"AnnotationsControllerクラスのcreate_from_tgzメソッドはありません"
"AnnotationsControllerクラスのcreate_project_annotations_rdfメソッドはありません"
"AnnotationsControllerクラスのeditメソッドはありません"
"AnnotationsControllerクラスのnewメソッドはありません"
"AnnotationsControllerクラスのshowメソッドはありません"
"AnnotationsControllerクラスのupdateメソッドはありません"
"DocsControllerクラスのsearchメソッドはありません"
"JobsControllerクラスのcreateメソッドはありません"
"JobsControllerクラスのeditメソッドはありません"
"JobsControllerクラスのnewメソッドはありません"
"MessagesControllerクラスのcreateメソッドはありません"
"MessagesControllerクラスのdestroyメソッドはありません"
"MessagesControllerクラスのeditメソッドはありません"
"MessagesControllerクラスのnewメソッドはありません"
"MessagesControllerクラスのupdateメソッドはありません"
"NoticesControllerクラスはありません"
"ProjectsControllerクラスのcreate_from_tgzメソッドはありません"
"ProjectsControllerクラスのdelete_reference_projectメソッドはありません"
"ProjectsControllerクラスのselect_reference_projectメソッドはありません"
"ProjectsControllerクラスのzip_uploadメソッドはありません"
"SqlsControllerクラスのcreateメソッドはありません"
"SqlsControllerクラスのdestroyメソッドはありません"
"SqlsControllerクラスのeditメソッドはありません"
"SqlsControllerクラスのnewメソッドはありません"
"SqlsControllerクラスのshowメソッドはありません"
"SqlsControllerクラスのupdateメソッドはありません"
"UsersControllerクラスのcreateメソッドはありません"
"UsersControllerクラスのdestroyメソッドはありません"
"UsersControllerクラスのeditメソッドはありません"
"UsersControllerクラスのnewメソッドはありません"
"UsersControllerクラスのupdateメソッドはありません"

Railsのルート情報には次のようにrails/info/propertieのようなルートが含まれています。

irb(main):001:0> Rails.application.routes.set.map { |a| a.defaults }.first
=> {:controller=>"rails/info", :action=>"properties"}

これらはアプリケーションのアクションメソッドと関係がありません。 雑に.reject { |r| r[:controller].include? '/' }として、/を含むコントローラーを除外しています。*1

追記

*1:元ネタのstackoverflowでは / を :: に置換しているので、その方がいいかもしれません。