[Rails]TopicPath(パンくずリスト)を作る

プラグインや実装例が結構あるのかと思いきや、あまり見当たらなかったので考えてみた。
以下のような方向で作ってみる。

  • 履歴はセッションに保存する。
  • パスには現ページまでの履歴を表示し、直前のページまではリンクとして表示する。
  • TopicPathのリンクからページにとんだ場合は、それ以降の履歴をクリアする。

パス構築のメソッド

必要なメソッドは、以下の2つ。

  • 履歴の初期化
  • 履歴の追加

履歴はURL情報を保時する必要があるため、コントローラ、アクション、パラメータのハッシュ配列で保持することにする。作成した配列はセッション中に:topicというキーで保存する。
という訳で、以下のようなコード。

  def init_topic(label)
     item = { :label => label,
              :controller => controller_name,
              :action => @action_name,
              :params => {} }
     session[:topic] = Array[item]
  end

  def put_topic(label)
    item = { :label => label,
             :controller => controller_name,
             :action => @action_name,
             :params => params }
    session[:topic].push(item)
  end

引数のlabelはTopicPathに表示される文字列となる。
これらは、作成した各コントローラのアクションメソッドの中から呼ぶ必要があるため、ApplicationControllerクラスに定義する。
(ApplicationControllerは全てのコントローラクラスの基底クラスになる)

TopicPathの表示

Viewの中からは@session[:topic]で履歴情報が参照可能なため、以下のようなコードで表示できる。

<%  if @session[:topic] != nil then
      index = 0
      for topic in @session[:topic] %>
        <%= ' > ' unless index == 0 %>
<%      if index == @session[:topic].size - 1 then %>
        <%= topic[:label] %>
<%      else %>
        <%= link_to topic[:label], :action => 'topic', :id => index %>
<%      end
        index += 1
      end
    end %>

正直、あんまりきれいなコードではないなぁ...
ループの中でやりたいことは、

  • 要素間に ' > ' を出力する (-> 先頭要素を除き、要素の前に' > 'を出力する)
  • 最後の要素はリンクにしない

この2つなので、要素がコレクションの先頭/最後かを判別できればそのほうがいいんだけど...
安易にindexを用いて判断してしまった。

TopicPathからのジャンプ

上のコードで何気にリンク先アクションを'topic'と書いているが、そのアクションを実行するコードはApplicationController内に書いている。そうすることによって、現在のコントローラを意識せずに実行される。
topicアクションのコードは以下のとおり。

  def topic
    index = params[:id].to_i
    topiclist = session[:topic]
    item = topiclist[index]
    session[:topic] = topiclist[0..index-1]
    redirect_to :controller => item[:controller],
                :action => item[:action],
                :params => item[:params]
  end

パラメータとして渡されたインディクスに相当するリンク先情報をセッションから取得し、以降の履歴をクリアしてリンク先にredirectしている。

アプリケーションへの組込み

以上がTopicPath実装の仕組み。実際に作成するアプリケーション内で上記メソッドを呼ぶが、基本的に以下の2つを行えばよい。

  • ログイン時に履歴初期化 (init_topic呼出)
  • 履歴保存したいアクション内で put_topic を呼び出す
# ログイン時に初期化
  def login
    〜
    init_topic('Home')
    〜
  end

# アクション実行時にput_topic呼出
  def list
    〜
    put_topic('List')
    〜
  end

課題

とりあえず以上の形で実装できたが、TopicPathの大きさに制限を設けていないので、リンクをぐるぐるたどっているとエラいことになる。
ここら辺は割り切って、直近いくらかを表示するとか、繰り返しを排除するとか、いろいろやりかたが考えられるが、アプリケーションによっていろいろポリシーも変わってくると思う。