やりたいこと
ユーザーのマイページやアカウント設定ページのルーティングをしたいです。
普通にRailsのresourcesメソッドに任せてしまうと、my_app.com/users/:idてな具合になってしまいます。
でも、ナウいQiitaとかnoteとかだとこんな感じになっています。
https://qiita.com/kakudaisuke
https://qiita.com/settings/account
https://note.com/kakudaisuke
https://note.com/settings/account
シンプルでかっこいいじゃないですか!
僕のアプリもこうしたい!
実装してみて、簡単にできたものの、細かいところの調整がやや面倒だったのでメモを。
環境
ruby 2.7.2
Rails 6.1.4.1
実装!
routes
Rails.application.routes.draw do
  root 'posts#index'
  #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  # users
  devise_for :users,
             only: %i[session password registration],
             controllers: { sessions: 'users/sessions',
                            password: 'users/passwords',
                            registrations: 'users/registrations' }
  get '/users', to: 'users#index'
  get '/settings', to: 'users#edit'
  resources :users, param: :account_name, only: %i[show new create update destroy], path: '/'
end
(ユーザーに関わる部分なので、なんとなくdeviseのルーティングも併せて載せておきましたが、今回の実装とは全く関係ないので無視で。)
resourcesメソッドのparamやpathというオプションを駆使すると簡単にやりたいことは大体できてしまいます。
いくつか注意点があります。
- 
indexをresoucesに含めてしまうと、ルートmyapp.com/がユーザー一覧になってしまうんですよね。ルートpathは別に設定したいので分けました。 - 
これ、順番が大事で、resources :usersを一番最後に書かないと、
/posts、/usersなどもaccount_nameだと判断されてしまいます。予期していないルーティングがされ「postsなんてユーザーいないぞ」とエラーに。 - 
それでも例えば
postsなんていうアカウント名のユーザーがいたら、やはり、マイページではなく、posts一覧にルーティングしてしまいます。そこで、今回はモデルのバリデーションで使われたくない名前を定義してブロックしました↓。 
reserved_words = %w(posts users my_app)
validates :account_name, exclusion: { in: reserved_words }
Model
param: :account_nameを有効にするには、モデル側でパラメーターにidじゃないのを使うよーと宣言してあげないといけません。
def to_param
  account_name
end
ドキュメント→https://railsdoc.com/page/to_param
Views
こんな感じで設定してあげます。
<%= link_to User.model_name.human, '/users' %>
<%= link_to 'アカウント設定', '/settings' %>
<%= link_to 'マイページ', user_path(current_user) %>
<%#= postのユーザー名からユーザーページへのリンク %>
<%= link_to(post.user.account_name, "/#{post.user.account_name}") %>
最後に
これでとりま自分がやりたいことは実現できました!
ただ、もっとなんかスマートな方法がある気もしています。。
何かご指摘などあればバシバシよろしくお願いします。