jcalコマンドとして仕上げる
前回からの続き
数年分をリスト出力
- さらに、数年分を並べてリスト表示するカレンダーも作ってみた。
...中略... def list_cal(y, col) week_ja = %w(日 月 火 水 木 金 土) date366 = (Date.new(2004, 1, 1)..Date.new(2004, 12, 31)).to_a list366 = Array.new(366, '') (y...y + col).each do |y| holiday = JPHoliday.new(y) date366.each_with_index do |date, i| date = Date.new(y, date.month, date.day) rescue nil today_marker = (date == Date.today) ? "\e[7m" : '' rescue '' holiday_name = holiday.lookup(date).last.ljust_ja(12) rescue ' ' * 12 case when date == nil list366[i] += sprintf("\e[ 0m%s%s%s\e[0m", ' ' * 10, ' ' * 2, holiday_name) when holiday.lookup(date) list366[i] += sprintf("\e[31m%s%s%s\e[0;31m%s\e[0m",today_marker , date.to_s, week_ja[date.wday], holiday_name) when date.wday == 0 list366[i] += sprintf("\e[31m%s%s%s\e[0;31m%s\e[0m",today_marker , date.to_s, week_ja[date.wday], holiday_name) when date.wday == 6 list366[i] += sprintf("\e[36m%s%s%s\e[0;36m%s\e[0m",today_marker , date.to_s, week_ja[date.wday], holiday_name) else list366[i] += sprintf("\e[ 0m%s%s%s\e[0; 0m%s\e[0m",today_marker , date.to_s, week_ja[date.wday], holiday_name) end end end list366.each {|list| puts list} end matrix_cal(2014, 9) list_cal(2014, 5)
カレンダー出力機能をモジュールにまとめる
- ここまで二つのカレンダー出力機能をJcalモジュールとしてまとめてみた。
- refineのString拡張もJcalモジュールに含めたかったが、それはできない。
- モジュール名をJcalExとして、トップレベルのモジュールとしておいた。
...中略... module JcalEx refine String do def length_ja half_lenght = count(" -~") full_length = (length - half_lenght) * 2 half_lenght + full_length end def ljust_ja(width, padstr=' ') n = [0, width - length_ja].max self + padstr * n end def rjust_ja(width, padstr=' ') n = [0, width - length_ja].max padstr * n + self end def center_ja(width, padstr=' ') n = [0, width - length_ja].max padstr * (n/2) + self end end end using JcalEx module Jcal include JPCalendar module_function def matrix(y, m) start_date = Date.new(y, m) - Date.new(y, m).wday end_date = Date.new(y, m, -1) + (6 - Date.new(y, m, -1).wday) date_list = start_date..end_date holiday = JPHoliday.new(y) puts sprintf("%4d年 %2d月", y, m).center_ja(16 * 7) header = %w(日 月 火 水 木 金 土).map {|s| s.rjust_ja(16)} header[0] = "\e[31m" + header[0] + "\e[0m" header[6] = "\e[36m" + header[6] + "\e[0m" print header.join, "\n" date_list.each_slice(7) do |week| week.each do |date| today_marker = date == Date.today ? "\e[7m" : '' holiday_name = holiday.lookup(date).last.rjust_ja(14) rescue ' ' * 14 case when date.month != m printf "\e[37m%s%s%2d\e[0m", holiday_name, today_marker, date.day when holiday.lookup(date) printf "\e[31m%s%s%2d\e[0m", holiday_name, today_marker, date.day when date.wday == 0 printf "\e[31m%s%s%2d\e[0m", holiday_name, today_marker, date.day when date.wday == 6 printf "\e[36m%s%s%2d\e[0m", holiday_name, today_marker, date.day else printf "%s%s%2d\e[0m", holiday_name, today_marker, date.day end end puts end puts end def list(y, col) week_ja = %w(日 月 火 水 木 金 土) date366 = (Date.new(2004, 1, 1)..Date.new(2004, 12, 31)).to_a list366 = Array.new(366, '') (y...y + col).each do |y| holiday = JPHoliday.new(y) date366.each_with_index do |date, i| date = Date.new(y, date.month, date.day) rescue nil today_marker = (date == Date.today) ? "\e[7m" : '' rescue '' holiday_name = holiday.lookup(date).last.ljust_ja(12) rescue ' ' * 12 case when date == nil list366[i] += sprintf("\e[ 0m%s%s%s\e[0m", ' ' * 10, ' ' * 2, holiday_name) when holiday.lookup(date) list366[i] += sprintf("\e[31m%s%s%s\e[0;31m%s\e[0m",today_marker , date.to_s, week_ja[date.wday], holiday_name) when date.wday == 0 list366[i] += sprintf("\e[31m%s%s%s\e[0;31m%s\e[0m",today_marker , date.to_s, week_ja[date.wday], holiday_name) when date.wday == 6 list366[i] += sprintf("\e[36m%s%s%s\e[0;36m%s\e[0m",today_marker , date.to_s, week_ja[date.wday], holiday_name) else list366[i] += sprintf("\e[ 0m%s%s%s\e[0; 0m%s\e[0m",today_marker , date.to_s, week_ja[date.wday], holiday_name) end end end list366.each {|list| puts list} end end # module Jcal Jcal::matrix(2014, 9) Jcal::list(2014, 5)
オプションと引数を解析してコマンドにする
- 最後の仕上げに、オプションと引数の解析機能を追加するのだ。
- オプションと引数の指定から、できる限りユーザーの意図を読み取って、良きに計らい解釈してカレンダー出力するようにしておきたい。
...中略... require 'optparse' # オプション解析 options = {} OptionParser.new do |opt| opt.banner = 'Usage: jcal [options] [yyyy|mm] [yyyy|mm]' opt.separator('') opt.on('-y[NUM]', 'List NUM years.(0-10)', Integer) {|v| options[:years] = v} opt.on('-m[NUM]', 'Show NUM months.(0-12)', Integer) {|v| options[:months] = v} opt.separator('') opt.on('Example:', ' jcal # Show monthly calendar of this month.', ' jcal 8 # Show monthly calendar of Aug.', ' jcal 8 2 # Show monthly calendar from Aug. to Feb. of next year.', ' jcal 2010 # Show all monthly calendar of 2010.', ' jcal -y # Show all monthly calendar of this year.', ' jcal -y5 # List from this year to after 5 years.', ' jcal 2011 2012 # List from 2011 to 2012.', ' jcal -m # Show monthly calendar from last month to next month.', ' jcal -m6 2010 1 # Show monthly calendar from Jan.2010 to Jun.2010.', ) begin opt.parse!(ARGV) rescue => e puts e exit end end # ARGVから読み込み ARGV[0] && (ARGV[0].to_i > 12 ? y1 = ARGV[0].to_i : m1 = ARGV[0].to_i) ARGV[1] && (ARGV[1].to_i > 12 ? y2 = ARGV[1].to_i : m2 = ARGV[1].to_i) # '.'を今月に変換 m1 == 0 && m1 = Date.today.month m2 == 0 && m2 = Date.today.month # nilの処理 m1 ||= m2 m1 ||= Date.today.month if ARGV.empty? || options.key?(:months) y1 ||= y2 y1 ||= Date.today.year # mオプション引数なしの場合は、前月から翌月まで表示する準備 if options.key?(:months) && options[:months].to_i == 0 m2 = m1 + 1 m1 -= 1 m1 < 1 && (m1 = 12 ; y1 -= 1) m2 > 12 && (m2 = 1) end # mオプション引数ありの場合は、指定した月数分表示する準備 if options.key?(:months) && options[:months].to_i > 0 m2 = m1 + options[:months].to_i - 1 m2 > 12 && m2 %= 12 end # 西暦2つの場合は、リスト表示する準備 if y1 && y2 && (y2 - y1) > 0 options[:years] ||= y2 - y1 + 1 end # yオプションが西暦なら、西暦と解釈 # yオプションの最大値は、10 if options[:years].to_i >= 1900 y1 = options[:years] options[:years] = 0 elsif options[:years].to_i > 10 options[:years] = 10 end # カレンダー出力 case when options[:years].to_i > 0 Jcal::list(y1, options[:years]) when m1 && !m2 && !options.key?(:years) Jcal::matrix(y1, m1) when m1 && m2 && !options.key?(:years) if m1 <= m2 (m1..m2).each {|i| Jcal::matrix(y1, i)} else (m1..12).each {|i| Jcal::matrix(y1, i)} ( 1..m2).each {|i| Jcal::matrix(y1 + 1, i)} end else (1..12).each {|i| Jcal::matrix(y1, i)} end
実行例
- 年・月の指定がない場合は、今年・今月と解釈される。
- "jcal.rb 年 月"あるいは"jcal.rb 月 年"どちらでもOK。
- 月の指定として、"."を指定できる。"."=今月の意味。
- 通常、yオプションには出力する年数を指定する。
- 但し、y2014とした場合などは、西暦とみなして処理する。
- yオプションの引数なしは、表形式のカレンダーを1年分出力する。
- yオプションの引数がある場合は、リスト形式のカレンダーをその年数分出力する。
- "jcal.rb 2010 2015"は、2010年から2015年までリスト形式のカレンダーを出力する。
- "jcal.rb 8 2"のような年を跨ぐ期間指定は、来年の2月と解釈する。
- mオプションには出力する月数を指定する。
- mオプションの引数なしは、指定月の前後1カ月を含めた3カ月分を出力する。
$ jcal.rb -h
Usage: jcal [options] [yyyy|mm] [yyyy|mm]
-y[NUM] List NUM years.(0-10)
-m[NUM] Show NUM months.(0-12)
Example:
jcal # Show monthly calendar of this month.
jcal 8 # Show monthly calendar of Aug.
jcal 8 2 # Show monthly calendar from Aug. to Feb. of next year.
jcal 2010 # Show all monthly calendar of 2010.
jcal -y # Show all monthly calendar of this year.
jcal -y5 # List from this year to after 5 years.
jcal 2011 2012 # List from 2011 to 2012.
jcal -m # Show monthly calendar from last month to next month.
jcal -m6 2010 1 # Show monthly calendar from Jan.2010 to Jun.2010.
$ jcal.rb
- 今月のカレンダー
$ jcal.rb 8
- 8月のカレンダー
$ jcal.rb 8 2
- 8月から来年2月までのカレンダー(7カ月分)
$ jcal.rb 2010
- 2010年のカレンダー(12カ月分)
$ jcal.rb -y
- 今年のカレンダー(12カ月分)
$ jcal.rb -y5
- 今年から5年分のカレンダーをリスト表示
$ jcal.rb 2010 2011
- 2010年から2011年までのカレンダーをリスト表示
$ jcal.rb -m
- 今月とその前後1カ月のカレンダー(3カ月分)
$ jcal.rb -m6 2010 1
- 2010年1月から6カ月分のカレンダー(6カ月分)