Developmentモードのログを日毎にローテートする

RubyのLoggerは簡易的ながらもローテーションする事ができるのでそれを使えば簡単にローテーションを実現できる。
config/environments/development.rbに以下を追記すると日毎にローテーションするようになる。

config.logger = Logger.new(config.log_path, 'daily')

期間毎のローテーションのパラメータは以下の3種類。第二引数に指定する。

'daily'
'weekly'
'monthly'

参考

るびま

20070617追記

ローテーションしているとログファイルの所有者がmongrelの動作アカウントになる事に注意。
ユーザアカウントとmongrelのアカウントが違う場合、script/consoleで何か実行するとパーミッションエラーが出てしまう。

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に色々書いてあるので参考にしよう。
もうちょっと使い込んでみてまた再評価したい。