Thursday 13 December 2012

Ruby on Rails AntiPatterns (2)

Saleh, T.とPytel, C.著の"Rails AntiPattern"から幾つか私が参考になった箇所を少しずつ書いていきます。

AntiPattern: RESTfulでないアプリケーション

状況が許す限りscaffoldの状態に近いルーティングに保つのが良いプラクティスの様です。

デフォルトのCRUDにコントローラのメソッドを追加する際にも例えばconfig/routes.rbの中で

resources :orders do
  collection do
    post :shipping
    post :billing
  end
end

とすると下のようなパスが作成されます。

/orders/new
/orders/shipping
/orders/billing
/orders/1

これを書き直して

resources :orders do
  members do
    post :shipping
    post :billing
  end
end

とすると下のようにもっとRESTfulな下のようなパスをつくってくれます。

/orders/new
/orders/1/shipping
/orders/1/billing

私も以前に作ったショッピングカートのルートをリファクタリングしないと駄目です…。

AntiPattern: 何故か500ページが表示される

ユーザがボタンをクリックして何故か知らないけど500ページが表示されたり、トップページにリダイレクトされてエラーメッセージも表示されないなどのサイトが多いらしいです(耳が痛い…)。
ユーザとアプリケーションの信頼を確保するために正確なエラー表示が必要です。サーバサイドのエラーは管理者に報告して、ユーザサイドのエラー(例えばメールアドレスが違うなど)はユーザにその旨を伝えると同時に、New Relicなどのログシステムを使って管理者に随時報告するのが筋な様です。
エラーをキャッチする際にもメソッドにエラーの際はnilを返すようにして do_this rescue nilとするのは無責任なので、メソッドの中で的確なエラーをキャッチして報告する仕組みにする必要があります。
例えばメールを送信する場合はまず

config.action_mailer.raise_delivery_errors = true

にします。

Net::HTTPでは下のようにエラーが下のように定義されている様なので、

SMTP_SERVER_ERRORS = [TimeoutError,
  IOError,
  Net::SMTPUnknownError,
  Net::SMTPServerBusy,
  Net::SMTPAuthenticationError]

SMTP_CLIENT_ERRORS = [Net::SMTPFatalError,
  Net::SMTPSyntaxError]

管理者のログシステムとしてHoptoadを使用した場合、コントローラでは

と書く事ができるらしいです。

なんでもかんでもbegin/rescueしたら良い訳でも無く、例えばメールアドレスの誤入力など例外ではなく、通常の行動として予想できる内容はActiveRecordのバリデーション機能などを使用してレスポンスを返すのが的確な挙動です。

AntiPattern: 長いレスポンス待ち

問題の一つは、外部サービスを使っている場合(例えば外部サーバのSMTPを使ってメールを送信する場合)サーバからのレスポンスが遅い場合があります。デフォルトではNet::HTTPのタイムアウトは30秒に設定されているそうですが、これを3秒に設定するという対処方法があります。
もう一つの問題としては、大きなプロセスの仕事があります。この場合、対処方法の一つとしてはプロセスをキューに押し込んでしまい、バックグラウンドで仕事をしてもらい順々にこなしてもらうというものです。ライブラリとしてはdelayed_jobというのとRescueというgemがあるそうです。delayed_jobはSQLを、RescueはRedisを使ってキューを管理するそうです。
ちなみにeventmachineなどconcurrencyライブラリもありますが、大きな仕事の場合は同時にさばく意味が少ないので、外部APIから情報を引っ張ってくるなど待ち時間が発生して処理に時間がかかる仕事と、プロセスの処理自体に計算の時間がかかるなどの仕事はそれぞれ異なるストラテジーでさばく必要がある様ですね。


No comments:

Post a Comment