食べログのようなメニューをRailsで再現する方法を考えてみた

[食べログ](http://tabelog.com/)のようなメニューをRailsで再現しようとして、どんな方法が良いのか悩みました。結論はないけれど、どんな方法で実現するのがいいのか？考えてみました

# 前提：したいこと

![image](https://qiita-image-store.s3.amazonaws.com/0/21321/d40058e8-ca9c-03b3-aedf-d00c3bd830a3.png)

* 例えば、口コミ一覧と口コミの詳細ページは
 * 現在地表示をしたい（CSSのclass名に `current`とか入れたい）
 * 現在地は、リンクを外したい（食べログはリンクになってますけれど...）
 * 口コミ数が0件だったら、非リンクにしたい
* 環境はRuby on Rails 4.2

## ルーティング

```rb
Rails.application.routes.draw do
  resources :shops do
    resources :accesses
    resources :official_photos
    resources :photos
    resources :reviews
  end
end
```

# 実現方法

## current_page? を使う

`current_page?` を使うと、controller名や、cotroller + action名など細かい指定で、アクセスしているページ（current page）が想定通りか判別してくれます。

* http://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-current_page-3F

### 課題

課題なのは、`/shops/:shop_id/photos/:photo_id` のようなURLの指定が難しいこと。というか、諦めた。

### ソース

```haml
%ul.menu
  %li
    .menu_item
      - unless current_page?(shop_path(@shop))
        = link_to '店舗', @shop
      - else
        店舗
  %li
    .menu_item
      - unless current_page?(shop_reviews_path(@shop))
        = link_to '口コミ', shop_reviews_path(@shop)
      - else
        口コミ
  %li
    .menu_item
      - unless current_page?(shop_photos_path(@shop))
        = link_to '写真', shop_photos_path(@shop)
      - else
        写真
```

## request.path から判定する

helperを用意して、`request.path` に指定した文字列が含まれているか判別するようにしてみました
`/shops/:shop_id/photos/:photo_id` のようなURLについても判別できるようになりました

### 課題

* なんかコレジャナイ感...
* `photo`と`offcial-photo`があったら、判定方法を変えないといけない
* `request.path.include?(string.to_s)` というのがいけてない気がする。あまりないかもしれないけれど、routes.rbを変えたらどうなるの？
* routes.rbで、`resources`は何か決まっているので、そこで判断してはどうだろう？

### ソース

```haml
%ul.menu
  %li
    .menu_item
      - unless current_page?(shop_path(@shop))
        = link_to '店舗', @shop
      - else
        店舗
  %li
    .menu_item
      - unless request_path_include?('reviews')
        = link_to '口コミ', shop_reviews_path(@shop)
      - else
        口コミ
  %li
    .menu_item
      - unless request_path_include?('photos')
        = link_to '写真', shop_photos_path(@shop)
      - else
        写真

```

### helperを用意

```ruby:app/helpers/application_helper.rb
module ApplicationHelper

  def request_path_include?(string)
    request.path.include?(string.to_s)
  end

end
```

## link_to_if_with_block を使う

課題はあるままだけれど、テンプレートで同じことを書いているので1回で書けるようにしてみた

### 課題

* 課題は残っている
* url_for() を調べたらまだ何か別な方法がありそう

### ソース

```haml
%ul.menu
  %li
    - shop_menu = current_page?(shop_path(@shop))
    .menu_item{style: shop_menu ? "color: red;" : ""}
      = link_to_if_with_block !shop_menu, @shop do
        店舗
  %li
    - reviews_menu = request_path_include?('reviews')
    .menu_item{style: reviews_menu ? "color: red;" : ""}
      = link_to_if_with_block !reviews_menu, shop_reviews_path(@shop) do
        口コミ
  %li
    - phtos_menu = request_path_include?('photos')
    .menu_item{style: phtos_menu ? "color: red;" : ""}
      = link_to_if_with_block !phtos_menu, shop_photos_path(@shop) do
        写真
```

```ruby:app/helpers/application_helper.rb
module ApplicationHelper

  def request_path_include?(string)
    request.path.include?(string.to_s)
  end

  def link_to_if_with_block(condition, options = nil, html_options = nil, &block)
    if condition
      link_to(options, html_options, &block)
    else
      capture(&block)
    end
  end

end
```

# まとめ？

contrller 名で判別してしまう方が、すっきりするかなあ？と思ったのですが、A/BテストなどでControllerを入れ替えてしまう、ということをすると適用できないと思います。

現在のURLから判別するのは、URLが変化しない、という前提があるからだけれど、かっこ悪さを感じるのは何故なんだろう？

どんな方法が他にあるか、聞いてみたい