txqz memo

CalendarHelperを使う

jvoorhisからcalendar_helper.rbをダウンロード。保存場所はapp/helpers。こないだの勉強会からずっと放置されていたcontrollers/test_controller.rbにhelper: calendarを追加。

class TestController < ApplicationController
    helper :calendar
    def index
        render :text => "Hello Rails!"
    end
end

vies/cal.rhtml に

<%= calendar(:year => 2006, :month => 5) %>

http://localhost:3000/test/cal にアクセスすると

            May
Sun Mon Tue Wed Thu Fri Sat
      1   2   3   4   5   6
  7   8   9  10  11  12  13
 14  15  16  17  18  19  20
 21  22  23  24  25  26  27
 28  29  30  31

成功。間違えて

helper :calendar
class TestController < ApplicationController
    def index
        render :text => "Hello Rails!
    end
end

と書く (helper :calendar がclassの外側にある) と

NoMethodError
undefined method `helper' for #<Object:0x294fa3c>
RAILS_ROOT: ./script/../config/..

Application Trace | Framework Trace | Full Trace 
#{RAILS_ROOT}/app/controllers/test_controller.rb:1
This error occured while loading the following files:
   ./script/../config/../app/controllers/test_controller.rb
Request
Parameters: None

Show session dump

Response
Headers: {"cookie"=>[], "Cache-Control"=>"no-cache"}

となる。Rubyの文法が怪しいとこうなる。注意。

以下教科書(ライド・オン・Rails)の81ページ以降に沿って「休日、祝日に色をつける」までやってみる。app/helpers/test_helper.rbに

module TestHelper
    HOLIDAYS = {5 => [3,4,5,7,14,21,28]}   # {}はハッシュ(連想配列)、[]は配列
    def holiday?(d)                        # ブール値を返すメソッドは後ろに"?"をつける習慣
        HOLIDAYS[d.month].include?(d.mday)  # returnがないからよくわからないが、d.mdayがHOLIDAYS[d.month]の配列にあればtrueを返す。calendar_helper.rbにmdayはちょろっとしか出てこないが、どうもRubyの文法構造はよくわからないところが多い。魔法のよう
    end
end

app/views/test/cal.rhtmlを

<%=
    calendar(:year => 2006, :month => 5) do |d|
        [ d.day, { :class => holiday?(d) ? 'holiday' : 'day' } ] # holiday?(d)がtrueを返したら日付カラムのclass属性をholidayに、falseならdayにする。あとはCSSで表示を調整してできあがり
    end
%>

土日は自動的に色が変わるようにしよう

本の例では2006年5月を表示すると決めているから、HOLIDAYS = {5 =>[3,4,5,7,14,21,28]}なんて決め打ちできるが、計算で簡単に求まるはずの日曜日をいちいち指定していたら汎用性がない。そこで日曜日と、あと土曜日を自動的に求めよう。

calendar_helper.rbの59行目と60行目に

first = Date.civil(options[:year], options[:month], 1)
last = Date.civil(options[:year], options[:month], -1)

という箇所がある。Dateクラスの定義はリファレンスマニュアルを参照。civilメソッドの定義は

([year[, mon[, mday[, start]]]])

mdayが-1のときは、最後の日をあらわす。

そこで first.upto(last) do |cur| の中に

This = Date.civil(options[:year], options[:month], cur.mday)

を入れてthis.wdayとすると、0~6の数字が返ってくる。日曜日の場合は0。土曜日が6。

配列を指定してやって

weekname = %w(sun mon tue wed thr fri sat)

として

cal << "      <td class='#{weekname[this.wday]} #{cell_attrs}'>#{cell_text}</td>"

とかすれば

        <tr>      <td class='sun day'>14</td>      <td class='mon day'>15</td>      <td class='tue day'>16</td>      <td class='wed day'>17</td>      <td class='thr day'>18</td>      <td class='fri day'>19</td>      <td class='sat day'>20</td>    </tr>

て感じにHTMLが出力される (てかなんでこんなにタブ文字があるのだろうか (ブログ掲載にあたりタブ文字は空白2つに置き換えました)) ので、CSSで

td.holiday{
    text-align: right;
    color: #c00;
}
td.day{
    text-align: right;
}
td.sat{
    color: #00c;
}
td.sun{
    color: #c00;
}
tr.dayName th{
    background-color: #ddd;
}

とやれば土曜日が青く、日曜日と祝日は赤く表示される。

振替休日も赤くしようとしたけど、月をまたぐ場合が面倒なのでやめた。月またぎを考慮しない場合は

def holiday?(d)
            HOLIDAYS[d.month].include?(d.mday) || (HOLIDAYS[d.month].include?(d.mday-1) &amp;&amp; d.wday==1)
end

かな? (今日が月曜日で、前日が祝日リストに含まれる場合)

その日の日記へのアンカーをつけよう

例によってcalendar_helper.rbをいじる。YYYY年MM月DD日の日記がhttp://example.jp/diary/YYYY/MM/DD にあるとすると、さきほど追加したweeknameの下あたりに

anchor = "/diary/#{this.year}/#{sprintf('%02d',this.month)}/#{sprintf('%02d',this.day)}"

なんて書いて

cal << "      <td class='#{weekname[this.wday]} #{cell_attrs}'><a href='#{anchor}'>#{cell_text}</a></td>    "

とかすればOK。ただし、アンカーによって先ほどの曜日によって色を変える指定が上書きされるので、a要素にclass属性をつけるなりして工夫する必要がある。

日記がない場合にはアンカーをつけないという処理をしたいなら、もう一工夫必要になる。

今回のプログラミングで出てきたrubyの文法、メソッド

rubyは記号が多いので、知っている人が書く分にはとても楽なのだろうが、初心者が読むのには結構骨が折れる。$とか@とか!とか?とか%d()とかカッコの省略とか……。とりあえず今回出てきた変な記号や見慣れるメソッドの確認。

a << b
文字列aに 文字列bを追加する。a += bと同じ
#{hoge}
リテラルの中に評価式を置きたいときは#{}を使う
sprintf()
printfの戻り値がString版
method(:attr=>var)
methodメソッドの引数として変数attrに値varを指定する? "()"の省略もできる? よくわからん
%w()
配列。[]と違うのは、ダブルクォートで文字列を囲まなくてもよいことと、空白区切りである点