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の日」があるみたいですね。 この微妙なランダム性がいい感じです。
作成に使ったスクリプトは以下です。
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)))))