Railsのpartial遅い問題

よくRailsのパフォーマンスの話になると槍玉に挙げられるpartial。
ちゃんと自分で確かめたことがなかったので、単純なlink_topartial化して比較してみた。

http://my-rails-bench.bornneet.com/partials
確かに遅い…。
(異常値とか考慮せず単純に時間測っただけだけど、何度かやっても変わらなかったので間違ってないはず)

500回繰り返してるけど、例えば100件のItemモデルを表示するviewで、items/_itemからさらに5つぐらいrenderしたら行くので、意識してないとやっちゃう数字だと思う。

自分が書く時はもちろん、コードレビューとかでもちゃんと意識して見るようにしよう。

参考

ActiveSupportのHash#sliceを知らなかった

http://api.rubyonrails.org/classes/Hash.html#method-i-slice

{ a: 1, b: 2, c: 3, d: 4 }.slice(:a, :b)
# => {:a=>1, :b=>2}

便利だ。
どんどんActiveSupport無しでは生きられなくなって行く。

apipie-rails

https://github.com/Apipie/apipie-rails

APIドキュメントを簡単に作成できるgem。
例えば、https://secure.freee.co.jp/developers/api/doc がこれで作られてると思われる。

この前はじめて使ったんだけど、Examples Recordingがめっちゃ便利だった。
https://github.com/Apipie/apipie-rails#examples-recording

describe "This is the correct path" do
  it "some test", :show_in_doc do
  end
end

RSpec.configure do |config|
  config.filter_run :show_in_doc => true if ENV['APIPIE_RECORD']
end

$ APIPIE_RECORD=examples bundle exec rspec

こんな感じでやれば、テスト結果をサンプルとしてドキュメント内に表示してくれる。
なお、jbuilderを使ってる場合は、render_viewsが必要。これもちゃんとREADMEに書いてある。

便利な世の中だ、、、


Unable to autoload constant Concerns::Foo

https://github.com/Apipie/apipie-rails/issues/347

config/initializers/apipie.rb
config.api_controllers_matcher = "#{Rails.root}/app/controllers/api/**/*.rb" # api以下に絞る

でひとまず逃げた。

Sucker Punchを知らなかった

https://github.com/brandonhilkert/sucker_punch

Railsの他にWorkerを立ち上げずに非同期処理ができる。

リトライとか、指定時間にとか、キューがいるようなのは無理だけど、「コントローラーからキックしたいが、ちょっと重いし、ユーザーへのレスポンスには関係ない」みたいな、ちょっとした処理はこれで十分っぽい。

早速実践投入したけど、超便利。

OmniAuthのSetup Phaseを使ってScopeを動的に変更する

ログインする時は、間口を広げるため緩い権限で、必要なときに追加で認証させたい、というニーズはわりとある。
でもOmniAuthはScopeをinitializerで指定しちゃうよなぁ、どうするのかな、と思ってたら、Setup Phase なるものがちゃんと用意されてた。知らなかった…。

例)Public Onlyでログインして、後からGistの権限を要求する

まじめにやる場合は、http://www.createdbypete.com/articles/dynamic-omniauth-provider-setup/ みたいに、Rackアプリを作るのが綺麗っぽい。
ここでは、手を抜いて、SessionsController#setupを使う。(参考:http://mikepackdev.com/blog_posts/2-dynamically-requesting-facebook-permissions-with-omniauth)

config/initializers/omniauth.rb
Rails.application.config.middleware.use OmniAuth::Builder do
  provider :github, ENV['GITHUB_CLIENT_ID'], ENV['GITHUB_CLIENT_SECRET'], setup: true # setup: trueが必要
end
cat config/routes.rb
get '/auth/:provider/callback', to: 'sessions#create'
get '/auth/:provider/setup', to: 'sessions#setup' # いつものに加えてこの行を追加
app/controllers/sessions_controller.rb
def create
    # 他の処理

    # TODO: もっと綺麗な条件で分岐したい
    if request.env['omniauth.params'].try(:[], 'scope') == 'gist'
      # gist向けの処理
    end

    # 他の処理
end

def setup
  if params[:scope] == 'gist'
    # 今回は元のscopeが無しなので単純に代入
    request.env['omniauth.strategy'].options[:scope] = 'gist'
  end
  render nothing: true, status: 404 # setupで404を返せばその後の処理はいつも通りやってくれる
end

これで、

  • /auth/githubはscope無しの認証
  • /auth/github?scope=gistはGistの権限を要求

が実現できた。