【Rails】ルーティング情報をフィルタリングするコマンド作成

最初に

rails routesコマンドといって、ルーティングの一覧をだすコマンドがある。
(※rake routesではない)

しかし、ActiveStorage周りのルーティングで、めちゃくちゃ幅をとったりして、とても読みにくい。
特定のキーワードでフィルタリングした上で、幅を絞ると見やすくなるのでは、と思い実装した。

探せば同じ目的の機能がありそうだけど、実装も簡単だし、ちゃっちゃと実装した。 lsコマンドのlオプション付きのを実装したことがあるから簡単だった。

コード

以下のコードをrakeファイルに追記する。
使い方は、コードの先頭に書いたとおり。

# rake routes F=devise
# rake routes V=GET F=devise
# rake routes V1=PATCH V2=PUT
# rake routes F1=devise F2=users
# rake routes F1=devise OP=AND F2=users
desc 'Print out all defined routes with filtering'
task :routes do
  routes = `rails routes`
  routes = routes.split("\n")
  routes.shift

  routes = routes.select{
    _1.index(ENV["V"]&.upcase || '') &&
    _1.index(ENV["V1"]&.upcase || '') &&
    _1.index(ENV["V2"]&.upcase || '')
  }

  if ENV["OP"]&.start_with?("A") || ENV["AND"] || ENV["A"]
    routes = routes.select{ _1.index(ENV["F"]) } if ENV["F"]
    routes = routes.select{ _1.index(ENV["F1"]) } if ENV["F1"]
    routes = routes.select{ _1.index(ENV["F2"]) } if ENV["F2"]
  else
    r1 = routes.select{ ENV["F"] and _1.index(ENV["F"]) }
    r2 = routes.select{ ENV["F1"] and _1.index(ENV["F1"]) }
    r3 = routes.select{ ENV["F2"] and _1.index(ENV["F2"]) }
    routes = r1 | r2 | r3
  end

  routes.map! do |route|
    route.gsub!("(.:format)", "")
    row = route.split
    row.unshift("") if row.size == 3
    row
  end

  prefixes, verbs, uris, actions = routes.transpose
  max_prefix_size = prefixes.map(&:size).max
    max_verb_size =    verbs.map(&:size).max
     max_uri_size =     uris.map(&:size).max
  max_action_size =  actions.map(&:size).max

  puts routes.map!{ |prefix, verb, uri, action|
    "#{prefix.rjust max_prefix_size} #{verb.ljust max_verb_size} #{uri.ljust max_uri_size} #{action.ljust max_action_size}"
  }
end

サッと書いたり修正したりしたので、いつもよりバグってるかもしれない。

コード解説

routesコマンド

task :routes do

この部分で、routesサブコマンドで呼び出すものを定義している。 実際にはRakefileにあり、rake routesで呼び出す。

ルーティング一覧を取得する

routes = `rails routes`
routes = routes.split("\n")
routes.shift

Railsから直接取れるのだろうが、Railsの機能を調べるのが面倒そうだったので、 `rails routes`で直接コマンドを叩いて1回取得して加工することにした。 これをやると、コマンドを2回叩いてることになるので、その分遅くなっているかもしれない。

そのあと、改行"\n"で分割して行単位の配列にして、routes.shiftでヘッダー部分を取り除いている。

Verbで絞る

routes = routes.select{
    _1.index(ENV["V"]&.upcase || '') &&
    _1.index(ENV["V1"]&.upcase || '') &&
    _1.index(ENV["V2"]&.upcase || '')
  }

Rakeファイルでの引数の取り方がわからなかったので、コマンドの呼び出し時に環境変数を指定してもらって、 環境変数コマンドライン引数代わりにしている。

V=GETとすると、GETのルーティングのみに絞れる。 V1=PATCH V2=PUTとすると、PATCHかPUTのルーティングのみに絞れる。

upcaseをつけて、V=getのように小文字でも絞れるようにした。

AND検索

if ENV["OP"]&.start_with?("A") || ENV["AND"] || ENV["A"]

OP=ANDAND=trueと書くと、AND検索になる。

rake routes F1=users F2=posts OP=AND

3列のものを4列に揃える

rails routesで取れる情報は、path/urlヘルパーメソッドがあったりなかったりする。 いったん行データをsplitで配列して、サイズが3(つまり、3列)なら、空文字列を入れる形で4列にする。

  routes.map! do |route|
    route.gsub!("(.:format)", "")
    row = route.split
    row.unshift("") if row.size == 3
    row
  end

あと、"(.:format)"の文字列は、あっても価値が低く思うので、取っ払うことにした。

transposeして、列幅を求めて出力

全部のデータを4列にしたのもあり、transposeでデータを転置して、列ごとに取り出せる。 列ごとに取り出して、列ごとの文字列の最大値を求めることで、必要な列幅がわかる。

で、最後にrjustljustを使って、右や左に寄せたりして、出力して終わり。