66
41

More than 5 years have passed since last update.

Rails console で Rails routes を詳しく確認する方法

Posted at

Rails routes ともっと仲良く

Rails routes を探索する初心者RailsプログラマのためのTipsです。

  • Lv.0 config/routes.rbrake routes
  • Lv.1 rails consoleで モデルからどのURLが生成されるか調べる
  • Lv.2 rails consoleで URL 文字列からどのコントローラーが応答するのか調べる
  • Lv.3 デフォルトのWelcome page
  • Lv.4 全てにマッチするroutesを書く
  • Lv.5 サブドメイン

Lv.0 config/routes.rbrake routes

config/routes.rb
Rails.application.routes.draw do
  # comments...
end

rails new 直後の config/routes.rb はろいろ書いてあるけど、全てコメントなので、ルートの定義は何も書いてない状態ですよね。

定義を追加して(resources :groupsみたいな)確認する方法はターミナルからおなじみの

> ./bin/rake routes

Lv.0の状態だと、多分 rake routes が使えるだけなので、ちょっとroutesが混みいってきたり、狙ったroutesになってない!?となった時のデバッグがツライ・・

ここから徐々にレベルを上げていきます!

Lv.1 rails consoleで モデルからどのURLが生成されるか調べる

説明のため、Groupというモデルがあるとする。

config/routes.rb
Rails.application.routes.draw do
  resources :groups
end

モデルからどのパスが生成されるか知りたい時

> ./bin/rails console

irb> group = Group.first
irb> app.polymorphic_path(group)
=> "/groups/1"

↓polymorphic_pathでいろいろ調べる方法は以前書いたのでスキップ

Rails でリンクパスを生成する方法色々・・とRails console で 生成される path を確認したい時 - Qiita

Lv.2 rails consoleで URL 文字列からどのコントローラーが応答するのか調べる

Lv.1の逆をやりたい時。

"/groups/1" -> GroupsController#show

が知りたい。

> ./bin/rails console

irb> > Rails.application.routes.recognize_path('/groups/1')
=> {:controller=>"groups", :action=>"show", :id=>"1"}

Recognize routes in rails console Session - Stack Overflow

Lv.3 デフォルトのWelcome page

Lv.2の方法で調べてみると、/ をどのこが担当してるのかわかる。

> ./bin/rails console

irb> > Rails.application.routes.recognize_path('/')
=> {:controller=>"rails/welcome", :action=>"index"}

明示的に root to: を指定してない場合、 デフォルトのあの赤いページ (Welcome aboard) が出てくる

自分のコントローラーに対応させる場合

config/routes.rb
Rails.application.routes.draw do
  resources :groups
  root to: 'groups#index'
end
> ./bin/rails console

irb> > Rails.application.routes.recognize_path('/')
=> {:controller=>"groups", :action=>"index"}

rails/welcomeが応答してくれるのは、routes.rbに記載されたルールのうち、どれもマッチせず終了した場合

Lv.4 全てにマッチするroutesを書く

/以外のパスは、routes.rbどれにもマッチせずに応答するコントローラーないやん、となった場合、ルーティングのエラーが出る。

development環境なら、赤いRouting Errorのページ。

これを一旦キャッチして、なんかしたい場合、マッチしなかったルートを担当するメソッドに渡すルールを追加する。

config/routes.rb
Rails.application.routes.draw do
  resources :groups
  root to: 'groups#index'
  get '*unmatched_route', to: 'application#unmatched'
end

ルーティングのルールは上から順にマッチするので、全部を受け取るようなルールは一番下に書くこと!

app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  # ...
  def unmatched
    # なんかいろいろする
    # リダイレクト先があればリダイレクト
    redirect_to your_redirect_path if need_redirect?
    # ルーティングエラーを出すときはこんな感じ
    raise ActionController::RoutingError.new("No route matches #{params[:unmatched_route]}")
  end
end

上の雑な例では need_redirect? で何かのルールを判定してるものとする。
どのルーティングでここまで来ちゃったかは、受け取ったコントローラーのパラメータでわかる
params[:unmatched_route]
(config/routes.rbで書いたget '*unmatched_route'と一致)

蛇足

特にコントローラーで判断するべきルールがなくて、一律にリダイレクトすれば良い場合は、routes.rbの記載だけで対応できる。

config/routes.rb
  get '*unmatched_route' => redirect('/')

Lv.5 サブドメイン

全てのルーティングを www サブドメインの下で使いたい、場合。

config/routes.rb
Rails.application.routes.draw do
  constraints subdomain: 'www' do
    resources :groups
    root to: 'groups#index'
  end
  get '*unmatched_route', to: 'application#unmatched'
end

自分で作ってる全てのルーティングを www 下に置いてみました。
www なしでアクセスされた時、www 付きにリダイレクトするようにしてみます。

app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  # ...
  def unmatched
    raise_routing_error if request.subdomain.present?
    url_to_redirect = root_url + params['unmatched_route'].to_s
    route_hash = Rails.application.routes.recognize_path(url_to_redirect)
    raise_routing_error if route_hash[:controller] == 'application' && route_hash[:action] == 'unmatched'
    redirect_to url_to_redirect
  end

  private

  def raise_routing_error
    raise ActionController::RoutingError.new("No route matches #{params[:unmatched_route]}")
  end
end

これで、http://your.domain/groups -> http://www.your.domain/groups にリダイレクトできるようになります。

残る問題点・・

やったーできた〜〜と思ったんですが・・実は問題点が残っています。

http://your.domain/ にアクセスすると、亡霊のようにデフォルトの Welcome aboard が残っているのです・・・

Lv.2で得たスキルで確認してみます。

> ./bin/rails console

irb> > Rails.application.routes.recognize_path('/')
=> {:controller=>"rails/welcome", :action=>"index"}

きゃーー;;
これが出てくるということは、どのルールにもマッチしていない、ということ。

routes.rbの一番最後に書いたはずの、
get '*unmatched_route', to: 'application#unmatched'
はどうしちゃったのか・・・

解決策

get '(*unmatched_route)', to: 'application#unmatched'

マッチするパスに()を付けて省略可にすると、全てマッチするようになる。

補足

recognize_path の使い方を調べている途中で、

recognize_path is private API and should not be used in applications.

との記載を発見・・ (参考
あんまり積極的に利用すべきではなさそう・・・

修正してみた

app/controllers/application_controller.rb
  def unmatched
    raise_routing_error if request.subdomain.present?
    redirect_to root_url + params['unmatched_route'].to_s
  end

recognize_pathを使って、リダイレクト後に対応できるコントローラーがいるかどうかのチェックを外してみた。
サブドメインありでunmatched まできた場合は、どこにも転送できないのでエラーに。
それ以外の場合はとりあえず www 付きのURLに転送するように。
(リダイレクト後にまたunmatchedに来て、404になるという無駄が発生するけど・・)

補足2

一律www付きにリダイレクト、とかだと、Nginxとかでやることのが多いのかも。。(なので後半のTipsは参考までに。。)

66
41
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
66
41