layoutを使い分ける
HTMLで表示をする場合、デフォルトのlayoutとしてapp/views/layouts/内のapplication.html.erbが使用されます。1つのRailsアプリケーション内で、複数のlayoutを使い分けたくなった場合のやり方についてのメモ。
renderする際にlayoutを指定
controllerの中でrenderする際にlayoutを指定できます。デフォルトのlayoutとは別のlayoutとしてapp/views/layouts/にanother_layout.html.erbを用意した場合、このように書きます。
respond_to do |format| format.html { render :layout => "another_layout"} end
controllerごとlayoutを指定
1つのcontrollerの中で(すべてのaction共通で)another_layoutを使うように指定することもできます。
class UserController < ApplicationController layout "another_layout" def index ... end
layoutを使いたくない場合
また、layoutを使いたくない場合もあります。例えば、ajaxリクエストを使用して部分的なHTMLを送出する場合などです。layoutを使わない場合は下のようにすればOKです。
respond_to do |format| format.html { render :layout => nil } end
Railsでajaxリクエストが二重に送信されてしまう
フォームからajax送信する際に、送信ボタンを2回押しているわけではないのですが、なぜか毎回2回リクエストが送信されてしまいます。ajax送信は以下のような感じ(一部抜粋)でやっています。
<%= form_tag(:action => :scan, :data-remote => true, :data-type => :html, :id => "my_form") do %> <%= text_field_tag 'name' %> <%= submit_tag "submit" %> <%- end -%>
def scan render :layout => false end
$(function(){ $("my_form").on('ajax:success', function(data, status, xhr) { $("#result_area").html(status); alert("Finish!"); //テスト用 }); });
こんな感じでやってますが、alertのウィンドウが2回表示されてしまいます。今回は参照のリクエストなのであまり影響はないですがどうも気持ち悪い。
原因は?
ウェブで同じような状況を探してみると、アセットパイプラインの取り扱いによって(?) 同じjavascriptファイルが二重に読み込まれてしまうケースがある、それによって1つの要素に対して.bind系(.live とか .on とか .delegate)が二重にセットされてしまう、ということが原因のようです。
自分のコードを見直してみて、application.jsの中の //= require_tree . を消して1つ1つ直接名前で指定したり、public/assetsを消したり(developmentモード)と色々やってみたのですが、ダメ…(x_x)
むりくり対策
よく分からないので、とりあえず二重にリクエストが飛んだときに2回目の処理をキャンセルするという、いささか強引な方法で対策してみました。
- 1回目のajax:beforeSendが送信された時に、form要素の属性として'ajax-processing'(名前はなんでもいいです)をくっつけます。
- 2回目のajax:beforeSendではform要素の'ajax-processing'属性をみてreturn false;して以降の処理をキャンセルします。
- 処理終了後にajax:completeが送信された際に'ajax-processing'属性を取り除きます。
$(function(){ $("form[data-remote=true]").on( 'ajax:success', function(data, status, xhr) { if ($(this).attr('ajax-processing')) { return false; } else { $(this).attr('ajax-loading', true); } } ); $("form[data-remote=true]").on('ajax:complete', function() { $(this).removeAttr('ajax-processing'); } ); });
あまり根本的な解決でないところがすっきりしませんが、1回しか処理されなくなったので一応よしとしておきます。
Railsでログローテーションする方法
Railsアプリケーションを動かしてるとログファイルがどんどん大きくなっていきます。ログファイルが巨大化しすぎると開くのに時間がかかっちゃいますし、何よりも大切なディスクスペースを圧迫してしまいます。
そこでログローテートです。定期的にログファイルを分割したり、古いのを捨てていったりするわけです。
ログファイルを指定サイズで分割
config/environments/production.rbファイル内で
config.logger = Logger.new("log/production.log", 5, 10 * 1024 * 1024)
とセットすると、ログファイルのサイズが10MBを超える毎に新しいログファイルを自動的に作って、古いログファイルをproduction.log.0とかproduction.log.1とかの名前にしていって、5ファイルを超える分を削除していってくれます。
ログファイルを日数で分割
config/environments/production.rbファイル内で
config.logger = Logger.new("log/production.log", 'daily')
config.logger = Logger.new("log/production.log", 'weekly')
config.logger = Logger.new("log/production.log", 'monthly')
などとします。production.log.20130120といったファイルが次々とつくられますが、こちらは自動で削除されていかないようです。
ログレベルの設定について
こちらの記事(ログに書き出すログレベルを変更する - Rails つまみぐい)で書いたようにログレベルはconfigファイルで変更ができますが、上記のように config.logger = Logger.new とした場合についてはRails.logger.levelで設定します。
Rails.logger.level = Logger::WARN # Rails.logger.level = 2 でもOK
config.log_levelでは設定できません(設定しても何も変わりません)。要注意です。
コードに日本語を書くとエラーになる件
Controller や Helper など ruby のコードの中に日本語を書くと
invalid multibyte char (US-ASCII)
というようなエラーが出ます。
変数名やメソッド名だけでなく、文字列として埋め込んでもダメです。うっかりデバッグ用に日本語を埋め込むとはじかれてしまいます。
このエラーを回避する方法は、各ファイルの先頭に
# encoding: utf-8
もしくは
# -*- encoding: UTF-8 -*-
というおまじないを書けばOKです。
ちなみに、コメント文であれば日本語問題なく通ります。View なファイルの中も大丈夫です。
ま、そもそもコードの中にがっつり固定文字列を埋め込むのはよろしくないので、 「i18n 化をきちんとやりなさい」というありがたい天のお告げだと思うことにします。(^^)
デバッグ用の文字列をログファイルに書き出す
ログファイルに書き出す
Railsの実行時に処理されるコードの中で
logger.debug("ログに吐き出す文字列")
と書いておけば、ログファイル log/development.logに書き出されます(実行モードがdevelopmentのとき限定)。
ログに書き出される情報は以下の5つのレベルのどれかに分類されていて、どのレベルでログを書き出すかで、使用するメソッドが異なります。
:debug | logger.debug | (デバッグ情報) |
---|---|---|
:info | logger.info | (一般的な情報) |
:warn | logger.warn | (警告情報) |
:error | logger.error | (エラー情報) |
:fatal | logger.fatal | (致命的なエラー情報) |
なお、productionモードではデフォルトの設定だとlogger.debugは記録されません。ログの内容にはレベルがありproductionモードでは実行に影響ないものは残さない、ということなのでしょう。
ですのでproductionモードでログに書き出したい場合はlogger.infoなぞを使って
logger.info("ログに吐き出す文字列")
と書けばよいでしょう。
オブジェクトの中身を記録する
デバッグしていると、このオブジェクトの中身はどうなっているんだろう?ってことがよくあります。
文字列や数字の変数ならそのまま var.to_s や "%s"%var で文字列化してしまえばいいですが、オブジェクトでそれをやると #<ClassName:0xXXXXXXX> みたいな訳分からん文字になってしまいます。
そんなときは .inspect を使うと便利。
logger.debug(var.inspect)
パスワードなどをログに残さないようにしよう
デフォルトの設定で、アクセスがあったときに与えられたパラメータがログファイルに書き出されます。
Started POST "/users" for 192.168.1.20 at 2012-01-01 00:00:00 +0900 Processing by UsersController#index as HTML Parameters: {"username"=>"Oratok", "password"=>"[FILTERED]"} Completed 200 OK in 149ms (Views: 148.3ms | ActiveRecord: 0.0ms)
3行目の"password"の値が[FILTERED]となって隠されています。この設定はデフォルトでconfig/application.rbファイル内で書かれています。
module RailsApp class Application < Rails::Application config.filter_parameters += [:password] end end
"password"というキーに反応して隠しているわけです。この行を消すとアクセスしたユーザーが入力したパスワードがログ上にもろにでてしまうわけです。
隠す対象のキーを追加したい場合はこんな感じで。
module RailsApp class Application < Rails::Application config.filter_parameters += [:password, :passwd] end end
参考サイト
ログに書き出すログレベルを変更する
ログ情報のレベル分け
ログに書き出される情報は以下の5つのレベルのどれかに分類されています。
:debug | デバッグ情報 | 0 |
---|---|---|
:info | 一般的な情報 | 1 |
:warn | 警告情報 | 2 |
:error | エラー情報 | 3 |
:fatal | 致命的なエラー情報 | 4 |
Railsが動作している時には、どのレベルの情報までログに書き出すかを決める”ログレベル”が設定されています。ログレベルが「:debug」であれば全ての情報がログに書かれ、「:warn」であれば:warn以上、つまり:warning, :error, :fatalの情報だけがログに書き出されます。
動作モードによってデフォルトのログレベルが決められています。
動作モード(RailsEnv) | デフォルトのログレベル |
---|---|
development | :debug |
test | :debug |
production | :info |
configファイルでのログレベルの変更
各動作モードでのログレベルのデフォルトは上記のようになっていますが、それを変更したい場合、例えばproductionモードでエラー情報以上しか書き出さないようにしたい場合はconfig/environments/production.rb内で
config.log_level = :error
とします。
ログレベルを動的に変更する
特定のメソッド内だけでログレベルを:debugに変更したりといったことも可能です。
class UsersController < ApplicationController def index Rails.logger.level = Logger::DEBUG # Rails.logger.level = 0 でもOK end end
URLに与えるパラメータで制御することもできますね。ApplicationController内にbefore_filterでセットするとよいでしょう。
class ApplicationController < ActionController::Base before_filter :set_debug_mode private def set_debug_mode if params[:debug] Rails.logger.level = Logger::DEBUG end end end
こうするとURLのパラメータにdebugをつけた場合
http://localhost/users/2?debug
ログレベルが:debugになります。
productionモードなのにブラウザにRouting Errorが表示される
PassengerでデプロイしたRailsアプリで、RailsEnvをproductionとしているのに、URLに存在しないパスを指定した際に、通常表示されるはずの404エラーページではなく、なぜかdevelopmentモードでおなじみのRouting Errorページが表示されていました。
このアプリ、もともとRails v3.1で作ったものをv3.2に更新したものなので、Railsのバージョンアップで何か変更があったのかもしれません。
で、いろいろ検索して、config/environments/production.rbファイルに下記の設定を追加したところ一応解決しました。
config.consider_all_requests_local = false
404エラーページがちゃんと表示されるようになりました (^^)