Developmentモードのログを日毎にローテートする
RubyのLoggerは簡易的ながらもローテーションする事ができるのでそれを使えば簡単にローテーションを実現できる。
config/environments/development.rbに以下を追記すると日毎にローテーションするようになる。
config.logger = Logger.new(config.log_path, 'daily')
期間毎のローテーションのパラメータは以下の3種類。第二引数に指定する。
'daily' 'weekly' 'monthly'
参考
ActiveSupportに年月表示用のメソッドを追加する方法
以下をenvironment.rbに追加する。
ActiveSupport::CoreExtensions::Time::Conversions.class_eval do def to_yymm strftime("%y/%m") end end
MySQLの場合はDateTime型がRubyのTime型にマッピングされるからTime型を拡張している。
YYMM形式で表示したいシステムは結構多いと思うなぁ。
ついでにデフォルトの時刻表示フォーマットを変える方法も載せておく。
ActiveSupport::CoreExtensions::Time::Conversions::DATE_FORMATS.update :default => "%y/%m/%d %H:%M" ActiveSupport::CoreExtensions::Date::Conversions::DATE_FORMATS.update :default => "%y/%m/%d"
date_selectヘルパーの注意点
date_selectヘルパーは引数で渡したインスタンス名とメソッド名から年月日の選択状態を自動的に判断してくれて便利なのだけれど、インスタンスがローカル変数だと値を読み取ってくれず、常に最新の日付になってしまうと言う問題がある。
date_select(object_name, attribute_name)
上記のようなコードの場合、object_nameじゃなくて@object_nameに目的のインスタンスが入ってなきゃダメだった。
date_selectに限らず全般的にそうなのかも。スコープが原因なのかなぁ。
基本的に編集画面で表示されるインスタンスは一つだろうからそんなに多発する問題ではないと思うけれど、ちょっと不便と言うかドキュメントに明記しておいて欲しいなと言うか。
理由を詳しく知りたいと思ってactionpack\lib\action_view\helpers\date_helper.rbを調べるも、363行目付近の以下のコードで挫折。
def date_or_time_select(options) defaults = { :discard_type => true } options = defaults.merge(options) datetime = value(object) datetime ||= Time.now unless options[:include_blank]
datetimeオブジェクトが選択状態を決定するための変数なんだけど、datetime = value(object)っていうコードが何をしているのかが不明。valueが何なのか分からない。。。
同じように疑問に思ってる人もいるみたい。凄い昔だけど…。
3 日坊主日記 - date_select , actionpack-controller_name-untaint.patch
ちょっと便利なObject#blank?メソッド
ActiveSupportの拡張。
Stringでnilもしくは空文字列を判断する制御文がシンプルになる。
#before if str && !(str.empty?) #after unless str.blank?
実装はこちらを。
http://dev.rubyonrails.org/browser/trunk/activesupport/lib/active_support/core_ext/blank.rb
ActiveHeartはどのようにカラム名の日本語化を実現しているか
Base.set_field_namesメソッドでは定義したカラム名と日本語名の対照用ハッシュ(@field_names)を作成する。
そして、ActiveRecord::Base#human_attribute_name(column_name)と言うメソッドをaliasにより独自メソッドに置き換え、引数のカラム名が@field_namesに含まれていればハッシュから返す。なければオリジナルのメソッドを呼び出す。
と言うものだった。
以下のコードがすべてかな。
def set_field_names(field_names = {}) @field_names = HashWithIndifferentAccess.new unless @field_names @field_names.update(field_names) end
alias_method :_human_attribute_name, :human_attribute_name def human_attribute_name(attribute_key_name) if @field_names && @field_names[attribute_key_name] @field_names[attribute_key_name] else _human_attribute_name(attribute_key_name) end end
SpecialGenerationではlocalize/[table_name].ymlに同じような感じに書くのがDRYじゃないなぁ、なんて思ったり。
plugin-suiteみたいなのできないかなー。
Railsではアクション内にConst変数を宣言することができない?
以下のようなコードをアクションに書いたらシンタックスエラーになってしまった。
Item = Struct.new("Item", :value, :name) case type when 'select' item.new(0, "選択して下さい") when 'search' item.new(0, "すべて") else item.new(0, "すべて") end
エラーの出力内容は以下の通り。
SyntaxError (./script/../config/../app/controllers/hoge_controller.rb:55: dynamic constant assignment
定数は一度宣言してから代入しようとはしてないし…そもそも宣言でエラーだし。
グーグル先生に聞いてみるとRails Tracで同様の問題が報告されていた。
http://dev.rubyonrails.org/ticket/4287
回答の肝となる部分を引用。
Ruby doesn't allow assigning class constants within method bodies, hence you get the "dynamic constant assignment" exception. It's not exclusive to Struct nor to Rails:
http://dev.rubyonrails.org/ticket/4287
つまり、メソッドの中ではConst変数は宣言できないと言う事らしい。
def e Hoge = "aa" end #=> SyntaxError: compile error (irb):2: dynamic constant assignment Hoge = "aa" ^ from (irb):3
なるほど…。
と言うわけで、アクションメソッドの外にStructのサブクラスの宣言を移したら無事に動いた。
結論として、Railsではアクション内にConst変数を宣言することができない、のではなくRubyではメソッド内でConst変数を宣言することができない、と言う事なのね。
ARのWhere句を構築してくれるEz-Where pluginの次期版が良さげ
ARではWhere句を構築するのに結構手間がかかるので、いくつかpluginが公開されている。
http://d.hatena.ne.jp/yukiwata/20070524/1179977530
とは言うものの、どれも機能的には貧弱に見えたのでこれだったら我慢して直で書いちゃおうかな、と思っていたらez_where_twoと言うEz-Where pluginの次期版を発見した。
http://opensvn.csie.org/ezra/rails/ez_where_two
公式ブログで言及されているわけでもないので、チーム内や作者だけが使っている秘密のブツなのかも。公開すればいいのに。
READMEを読んでみるとかなり良さげだったので試してみる事に。
ruby script/plugin install http://opensvn.csie.org/ezra/rails/ez_where_two/
READMEのサンプルコードを若干修正したら動いた。
ANDとORの優先順位を認識し、括弧付けしてくれるのが素敵だ。
cond = Caboose::EZ::Condition.new cond += cc { name == 'fab' } + cc { login =~ 'loob%' } # AND - AND cond -= cc { age < 20 } | cc { login_count < 10 } # AND NOT - OR cond |= cc { login == 'admin' } # OR - cond += { :color => 'red' } cond.to_sql => ["(((((name = ?) AND (login LIKE ?))) AND NOT ((age < ?) OR (login_count < ?))) OR (login = ?)) AND (color = ?)", "fab", "loob%", 20, 10, "admin", "red"]
個人的に高評価だったは、Where Pluginと違ってnewしただけの状態でto_sqlを呼ぶとnilが返る点。
cond = Caboose::EZ::Condition.new cond.to_sql #=> nil
検索画面などで動的に検索項目を切り替える場合、何も入力しなかった場合は全件ヒットするのが一般的なUIだと思う。
:conditionsにnilを渡すと全件ヒットするのでこの方が都合が良い。
他にもREADMEに色々書いてあるので参考にしよう。
もうちょっと使い込んでみてまた再評価したい。