rails4+bootstrapで作ったサイトを高速化するために行った事

rails4とbootstrapを使ったウェブアプリケーションを作って、そのなかで表示を速くするために行った事をざっと書いていきます。

## ActiveRecordのincludeやjoinに関する全般

これについては色々な所で書かれている通りです。ログ見ながらSQLを最適化させる形でActiveRecordの最適化を行いました。

## link_to

controllerとactionを指定した書き方の方がすっきりしますが、url_forを使ってroutingが発生すると遅いです。

ダサいですが、べた書きにしました。

```rb
link_to "link", "/action/#{m.id}"
```

参考URL:
http://railsguides.net/rails-url-for-helper-can-be-slow/

## render :partial

仕組み的にそういうものだよね、と考えることも出来ますが、例えばカレンダーのように同じviewをネストして何回も呼び出すとさすがに遅いです。

何回も呼び出す場合は、def_erb_methodを使うと速くなります。

```rb
def_erb_method('render_plan_summary(plan)', "#{Rails.root}/app/views/venue/_plan_summary.erb")
```

参考URL:
パフォーマンス・チューニング３：erbメソッド化を参照
http://www.ohmyenter.com/?p=105

##paperclip#url

画像のアップロードにpaperclip+S3を使っている場合に限定される話になります。標準のurlメソッドを使うとすっきり書けますが、

```rb
image_tag(product.pictures.first.data.url(:gallery))
```

遅いのでこれもダサいですがべた書きにしました。

```rb
image_tag("http://bucketname.s3.amazonaws.com/products/#{product.pictures.first.id}/gallery.jpg?1325844462"
```

## lazyな画像の読み込み

bootstrapを使うとPC/Tablet/Mobileで別々のサイズの画像を読み込むために、imgタグの量が多くなります。

さらにページを読み込んだ時点で全画像を読み込むとページの描画が遅くなるので、jQueryのlazyプラグインを使いました。

http://www.appelsiini.net/projects/lazyload

注意点として、ちゃんとサイズを指定してあげないと画像読み込み前と後でサイズが変更されてしまいます。ページ位置がずれるように感じてしまうので、ちゃんと画像サイズは指定してあげましょう。


## デバイス別にimage_tagの出力

imgタグのsrcに書かれたURLはブラウザが自動で読み込まれます。lazyを付けていても表示位置が来たら読み込まれます。display:noneしていても読み込みます。

良くある解決方法はbackground-imageにする事ですが、この方法は複雑に感じたのでデバイスの種類別に出力するimgタグを分ける方法にしました。

これに伴ってキャッシュもデバイスの種類別に分ける必要が出てきます。

image_tagを以下のようにデバイスによっては何も出力しない。

jpmobileを使っています。
https://github.com/jpmobile/jpmobile

```rb

  def devise_image_tag(path, options = {})

    if options[:devise].present?
      if request.mobile? || request.smart_phone?
        return "" if options[:devise].to_s != "mobile"
      else
        return "" if options[:devise].to_s != "full"
      end
    end

    if include_lazy?(options[:class])
      my_image_tag(empty_image_path, options.merge("data-original" => path)).html_safe
    else
      my_image_tag(path, options).html_safe
    end
  end
```

image_tagはそのまま使うと遅いので、自力でこんな感じに書き換えました。

```rb

  def my_image_tag(path, options)
    options[:src] = path if path.present?
    if options[:size].present?
      (options[:width], options[:height]) = options[:size].split("x")
      options.delete(:size)
      if options[:style].blank?
        options[:style] = "width:#{options[:width]}px; height:#{options[:height]}px;"
      end
    end
    tag(:img, options).html_safe
  end
```

imgタグの出力でlazyを使うことが多いので、classにlazyが指定されているかをこんな感じで確認。

```rb

  def include_lazy?(class_string)
    (class_string || "").split(/\s+/).any?{|v| v == "lazy"}
  end
```

## デバイス別にキャッシュを分ける

applicationコントローラーの中でデバイス種類を取得しておきます。

```rb
class ApplicationController < ActionController::Base
  before_action :fetch_devise
  def fetch_devise
    @devise_type = ((request.mobile? || request.smart_phone?) ? "mobile" : "full")
  end
end
```

cacheに使うprefixを準備して

```rb
  def cache_prefix
    "#{@devise_type}_"
  end
```

こんな感じでviewのcacheで使います。出力されるimgタグがデバイスの種類で異なってくるので、キャッシュされるHTMLも分ける必要があります。

```erb
<% cache(cache_prefix + "my_footer", :expires_in => 5.minutes) do %>
```


## fast_blank

railsのblank?を速くするgemです。

https://github.com/SamSaffron/fast_blank

## escape_utils

escape処理を速くするgemです。

https://github.com/brianmario/escape_utils

# インフラ面/リソース面で行った事

## リバースプロキシ

アプリケーションサーバーはpassenger、その手前にapacheでリバースプロキシを作ってリダイレクト等のアプリケーションサーバーにやらせるメリットが無いものを処理するようにしました。

passengerはforkして動くのでリソースファイルを捌くようなものは全てworkerスレッドで動作するリバースプロキシ側に任せました。

## CDN

Amazon CloudFrontにjsやcssやimageやfontを置きました。rails本体にリソースをCDNに配置する仕組みがなかったので、ここはデプロイするスクリプトをちくちくと自作です。

## 画像の圧縮

サーバーサイドでjpegを軽量化（jpeg-miniみたいな事）
http://qiita.com/tkosuga@github/items/a2dbe2f8dfb7904d5fc6

pngについてはpngquantを使って軽量化しました。
http://pngquant.org/

## cssスプライト

SpriteFactoryを使ってさくっとファイルまとめました。
https://github.com/jakesgordon/sprite-factory

## 一般的なパイプライン処理

jsやcssをそれぞれ1つのファイルにまとめてgzipにしてA3に転送してpermissionの設定をしたりと一般的なパイプライン処理を行っています。

CDNに配置したりする関係があって、rails標準のpipelineを使わず最小限に実現できるものを自作しました。標準でライブラリが全部そろっているので、Asset pipelineで悩んだり困ったりしているなら、これについてはもしかすると自作した方が良いかも知れません。

# やってもあんまり速くならなかったもの

## HTMLのスリム化

HTMLをスリムにすれば速くなると思ってhtmlcompressorを試したのですが、思った以上に処理に時間がかかるので止めました。生成したHTMLをキャッシュする前提ですが、それでも初回の生成が思った以上に遅かったです。

https://github.com/paolochiodi/htmlcompressor