この文書では、人に見やすい形に成形されて表示される(その代わりプログラムで処理しづらい) rake routes
の出力結果を csv とかに変える方法について記します。
主に著者の環境である rails 3.2.22 を対象にしていきたいと思います。
Rails 3.2.22
さて、routes
タスクそのものは railties-3.2.22/lib/rails/tasks/routes.rake
で定義されています(ので抜粋しておきます)。
desc 'Print out all defined routes in match order, with names. Target specific controller with CONTROLLER=x.'
task :routes => :environment do
Rails.application.reload_routes!
all_routes = Rails.application.routes.routes
require 'rails/application/route_inspector'
inspector = Rails::Application::RouteInspector.new
puts inspector.format(all_routes, ENV['CONTROLLER']).join "\n"
end
タスクの前半2行をコピペして独自タスクを作るのが鮮やかそうではあります。
しかし、 all_routes
の中身は Journey::Routes
のインスタンスとなっており、よくわかりません。
(https://github.com/rails/rails/blob/v3.2.22/actionpack/actionpack.gemspec#L28 を見るとわかるけど、また別の依存 gem )
Journey を無視して Rails::Application::RouteInspector
を読むと #format_routes
という、とてもわかりやすいメソッドを見つけられるはずです。
module Rails
class Application
class RouteInspector # :nodoc:
def formatted_routes(routes)
name_width = routes.map{ |r| r[:name].length }.max
verb_width = routes.map{ |r| r[:verb].length }.max
path_width = routes.map{ |r| r[:path].length }.max
routes.map do |r|
"#{r[:name].rjust(name_width)} #{r[:verb].ljust(verb_width)} #{r[:path].ljust(path_width)} #{r[:reqs]}"
end
end
end
end
end
あとはこれにパッチを当てるだけですが、 rake routes
は Rake タスクなので、 lib
ディレクトリのロードがどうも怪しかったりします・・・ので、 lib/task/patch.rake
とかにこんなパッチを書けば良いでしょう。
module RouteFormatCSVPatch
def formatted_routes(routes)
routes.map do |r|
"#{r[:name]},#{r[:verb]},#{r[:path]},#{r[:reqs]}"
end
end
end
require 'rails/application/route_inspector'
Rails::Application::RouteInspector.send(:prepend, RouteFormatCSVPatch)
外部エンジンなどを使って route が足された場合などには正しい CSV にならない(外部エンジン表記が途中行に入る)ですが、まぁ、前よりかはましでしょう。
(完璧を目指すなら#formatted_routes_for_engines
にもパッチを当てること)
Rails 4.x
4系では、より拡張がしやすくなっています。
同じくタスクが定義されている railties-4.2.3/lib/rails/tasks/routes.rake
からソースを抜粋しましょう。
desc 'Print out all defined routes in match order, with names. Target specific controller with CONTROLLER=x.'
task routes: :environment do
all_routes = Rails.application.routes.routes
require 'action_dispatch/routing/inspector'
inspector = ActionDispatch::Routing::RoutesInspector.new(all_routes)
puts inspector.format(ActionDispatch::Routing::ConsoleFormatter.new, ENV['CONTROLLER'])
end
フォーマッターが別クラスで用意されているので、パッチを当てるより、独自フォーマッターを定義してやる手法の方が良さそうです。
例えば、こんな感じのタスクを lib/task/route.rake
とかに書くと良いでしょう。
namespace :routes do
class CSVFormatter
def initialize
@csv = []
@current_section = "Application"
end
def result
@buffer.join("\n")
end
def section_title(title)
@current_section = title
end
def section(routes)
routes.each do |r|
@buffer << "#{r[:name]},#{r[:verb]},#{r[:path]},#{r[:reqs]}"
end
end
def header(routes)
@buffer << "Prefix, Verb, URI Pattern, Controller#Action"
end
def no_routes
@buffer << ""
end
end
desc 'Print out all defined routes on CSV'
task csv: :environment do
all_routes = Rails.application.routes.routes
require 'action_dispatch/routing/inspector'
inspector = ActionDispatch::Routing::RoutesInspector.new(all_routes)
puts inspector.format(CSVFormatter.new, ENV['CONTROLLER'])
end
end