トップ «前の日記(2008-05-12) 最新 次の日記(2008-05-15)» 編集

Route 477



2008-05-14

[ruby] Rubyで「ナベアツカレンダー」を作ってみた (iCalendar gem)

前の日記で

■ [junk] 3の倍数と3の付く日だけ猛烈に開発して、5の倍数の日は一切PCに触りません

という開発スタイルはどうか。NDD(Nabeatsu Driven Development)。

メリハリのあるプログラミングライフをあなたに!

[20080512#p02より引用]

というネタを書いたのですが、実際に「3の日」と「5の日」ってどれくらいの割合になるんでしょうか?

例えば、Googleカレンダーで「3の日」「5の日」を表示できたら、とても分かりやすそうですよね。 そんなわけで、RubyからiCal形式のファイル(*.ics)を作る方法を調べてみました。

iCalendar gemの使い方

RubyGemsの icalendar というライブラリを使うと、iCal形式のファイルを簡単に作れるようです。

例えば、イベントが1つだけ登録されたカレンダーを作るならこんな感じ。

  require 'rubygems' 
  require 'icalendar'
  require 'date' # Rubyの標準添付ライブラリ。Dateクラス、DateTimeクラスを提供する。

  cal = Icalendar::Calendar.new
  cal.event do
    dtstart       Date.new(2005, 04, 29)
    dtend         Date.new(2005, 04, 28)
    summary     "Meeting with the man."
    description "Have a long lunch meeting and decide nothing..."
    klass       "PRIVATE"
  end
  puts cal.to_ical

とても簡単ですね。:-) インストールはいつもの「gem install icalendar」で。

ナベアツカレンダーを作ろう

というわけで、「3の日」と「5の日」が登録されたカレンダーはこんな感じになりました。 だいたい週2〜3の割合で「3の日」が、週1〜2の割合で「5の日」があるみたいですね。 この微妙なランダム性がいい感じです。

b288aa9cd026b842833473cb31714bf0.png

作成に使ったスクリプトは以下です。

require 'rubygems'
require 'icalendar'
require 'date' 

# 新しいカレンダーを作る
cal = Icalendar::Calendar.new

# 今年。(とりあえず年末まであれば良いだろう)
y = Time.now.year

# 各月の、
(1..12).each do |m|
  # 各日について、
  (1..31).each do |d|
    # 有効な日付なら (つまり、2月31日みたいな存在しない日でなければ)
    if Date.valid_date?(y, m, d)
      # 5の倍数の日は
      if d % 5 == 0
        # 「5」という名前の終日イベントを登録
        cal.event do
          dtstart Date.new(y, m, d)      # その日
          dtend   Date.new(y, m, d) + 1  # 次の日
          summary "5"                    # イベントのタイトル
          description "5"                # イベントの説明
        end
      # そうでなくて、3の倍数か「3」がつく日は
      elsif d % 3 == 0 or /3/ =~ d.to_s
        # 「3」という名前のイベント(0時〜1時)を登録
        cal.event do
          dtstart DateTime.new(y, m, d, 0, 0)
          dtend   DateTime.new(y, m, d, 1, 0) 
          summary "3"
          description "3"
        end
      end
    end
  end
end

# iCal形式で出力
puts cal.to_ical

Googleカレンダーにも「ナベアツカレンダー」という名前で登録してみましたので、どうぞご利用ください。 よいナベアツライフを!

[scheme] R6RSからテストケースを抜き出したい (1)

r6rs.tar.gzにはtexファイルが含まれてるので、それの\begin{scheme}...\end{scheme} を抜き出せばいいんじゃね?

途中経過。

(bytevector-sint-ref b 0 (endianness little) 16)=> -3

(bytevector->u8-list b)=> (253 255 255 255 255 255 255 255
               255 255 255 255 255 255 255 255)

(bytevector-uint-set! b 0 (- (expt 2 128) 3)
                 (endianness big) 16)
(bytevector-uint-ref b 0 (endianness big) 16) =>
    #xfffffffffffffffffffffffffffffffd

問題点:

  • &assertionとかどうしようかな
  • undefined と unspecified はどうしよう?
    • 単にそのテストケースを無視することにする?
    • 無視しないとすると、#<undef>をどう表すか? (define *the-undefined* (display)) とかか。
  • expectedに「0もしくはNaN」とか書いてあったりするんだがw

何にせよ「; two return values」とか「; approximately」とか「; prints: error opening file」とかあるので、 多少は手作業で修正しないといかんかも。

抜きだしスクリプトは以下。

;;; extract test data from r6rs/documents/*.tex
(use file.util)
(use gauche.collection)

(define *re-scheme-block* #/\\begin\{scheme\}(.*?)\\end\{scheme\}/)

(define (test-case? str)
  (and (not (#/\\hyper/ str))
       (not (#/\\vari?\{.*\}/ str))
       )) ;(#/\\[lx]?ev/ str)))

(define (tex->scheme-source str)
  (regexp-replace-all* str #/\\[lx]?ev/ "=>"
                           #/\%\s*$/ ""
                           #/\\exception\{\\&(.*?)\}/ "&\\1"
                           #/\\sharpsign/ "#"
                           #/\\backwhack\{\}/ "\\"
                           #/\\schtrue(\{\})?/ "#t"
                           #/\\schfalse(\{\})?/ "#f"
                           #/\\sharpfoo\{(.)\}/ "#\\1"
                           #/\\(ide|textrm)\{(.*?)\}/ "\\2"
                           #/\\#/ "#"
                           #/\\_/ "_"
                           #/\\&/ "&"
                           #/\\unspecified/ "#<unspecified>"
                           #/\\theunspecified/ "#<undef>"))

(define (tex->test-data-list str)
  (let1 match (*re-scheme-block* str)
    (if (and match (test-case? (match 1)))
      (cons (tex->scheme-source (match 1))
            (tex->test-data-list (rxmatch-after match)))
      '())))

(define (extract-file path)
  (print ";;; ---- " path)
  (for-each print (tex->test-data-list (file->string path))))

(define (main args)
  (case (length args)
    ((1)
     (print "usage: extract-r6rs-test foo.tex"))
    (else 
     (for-each extract-file (cdr args)))))