PIYO - Tech & Life -

GrapeのAPIのエンドポイントをrake routes的に出力する

API gem Grape Rake Ruby Rails

RailsアプリケーションにGrapeを使って定義したエンドポイントはrake routesには表示されません。表示されるのはroutes.rbでマウントしたAPIのルートのみ。

そこで自前のrake routesを定義してGrapeのAPIもroutesに表示してあげることにしました。

参考

lib/tasks/routes.rake

上のリンクから持ってきたものを少し変更し、task名をroutesとしています。

desc "API Routes"
task routes: :environment do
  API::Base.routes.each do |api|
    method = api.route_method.ljust(10)
    path = api.route_path
    puts "     #{method} #{path}"
  end
end

この状態で$ rake routesとすると先にGrapeのエンドポイントが出力され、続いてRailsのroutesが出力されます。

こんな感じ。揃っていなくて気持ち悪いですね。

     GET        /blogs(.:format)
     GET        /blogs/:id(.:format)
     GET        /blogs/:blog_id/posts(.:format)
     GET        /blogs/:blog_id/posts/:id(.:format)
        Prefix Verb   URI Pattern                              Controller#Action
    blog_posts GET    /blogs/:blog_id/posts(.:format)          posts#index
               POST   /blogs/:blog_id/posts(.:format)          posts#create
 new_blog_post GET    /blogs/:blog_id/posts/new(.:format)      posts#new
edit_blog_post GET    /blogs/:blog_id/posts/:id/edit(.:format) posts#edit
     blog_post GET    /blogs/:blog_id/posts/:id(.:format)      posts#show
               PATCH  /blogs/:blog_id/posts/:id(.:format)      posts#update
               PUT    /blogs/:blog_id/posts/:id(.:format)      posts#update
               DELETE /blogs/:blog_id/posts/:id(.:format)      posts#destroy
         blogs GET    /blogs(.:format)                         blogs#index
               POST   /blogs(.:format)                         blogs#create
      new_blog GET    /blogs/new(.:format)                     blogs#new
     edit_blog GET    /blogs/:id/edit(.:format)                blogs#edit
          blog GET    /blogs/:id(.:format)                     blogs#show
               PATCH  /blogs/:id(.:format)                     blogs#update
               PUT    /blogs/:id(.:format)                     blogs#update
               DELETE /blogs/:id(.:format)                     blogs#destroy
           api        /api                                     API

デフォルトのroutesタスクを再定義しよう

Railsのデフォルトタスクはlib/tasks以下のファイルの読み込み後に行われるため、lib/tasks/routes.rakeファイルではデフォルトタスクの再定義はできません。

Rubyだからメタに書けばできないことはないんだろうけど、いろいろ試してまだ答えに行き着いていません。今後の課題。

別タスクで無理矢理解決

実際のroutesタスクはruby/2.1.0/gems/railties-4.1.0/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

このソースを流用して、Grape APIの結果も含めて出力すりゃいいじゃんということで、ソースはこうなりました。DRY?なにそれ状態。

desc "Routes with API routes"
task all_routes: :environment do
  Rails.application.require_environment!

  require 'action_dispatch/routing/inspector'
  all_routes = Rails.application.routes.routes
  inspector = ActionDispatch::Routing::RoutesInspector.new(all_routes)
  output = inspector.format(ActionDispatch::Routing::ConsoleFormatter.new, ENV['CONTROLLER'])
  puts output

  first_line = output.lines.first
  verb_index = first_line.index("Verb")
  pattern_index = first_line.index("URI Pattern")
  
  API.routes.each do |api|
    method = api.route_method
    .ljust(pattern_index-verb_index-1) # "GET"     => "GET    "
    .rjust(pattern_index-1)            # "GET    " => "                GET    "
    
    path = api.route_path
    puts "#{method} #{path}"
  end
end

ほんで、$ rake all_routesってやるとこういう出力が得られるようになります。

        Prefix Verb   URI Pattern                              Controller#Action
    blog_posts GET    /blogs/:blog_id/posts(.:format)          posts#index
               POST   /blogs/:blog_id/posts(.:format)          posts#create
 new_blog_post GET    /blogs/:blog_id/posts/new(.:format)      posts#new
edit_blog_post GET    /blogs/:blog_id/posts/:id/edit(.:format) posts#edit
     blog_post GET    /blogs/:blog_id/posts/:id(.:format)      posts#show
               PATCH  /blogs/:blog_id/posts/:id(.:format)      posts#update
               PUT    /blogs/:blog_id/posts/:id(.:format)      posts#update
               DELETE /blogs/:blog_id/posts/:id(.:format)      posts#destroy
         blogs GET    /blogs(.:format)                         blogs#index
               POST   /blogs(.:format)                         blogs#create
      new_blog GET    /blogs/new(.:format)                     blogs#new
     edit_blog GET    /blogs/:id/edit(.:format)                blogs#edit
          blog GET    /blogs/:id(.:format)                     blogs#show
               PATCH  /blogs/:id(.:format)                     blogs#update
               PUT    /blogs/:id(.:format)                     blogs#update
               DELETE /blogs/:id(.:format)                     blogs#destroy
           api        /api                                     API
               GET    /blogs(.:format)
               GET    /blogs/:id(.:format)
               GET    /blogs/:blog_id/posts(.:format)
               GET    /blogs/:blog_id/posts/:id(.:format)

さっきより全然いい。コードは気持ち悪いけど結果はOKです。とりあえずこれでいこうと思います。