RosettaCodeData/Task/Calendar---for-REAL-program.../Ruby/calendar---for-real-program...

85 lines
2.9 KiB
Ruby

# CAL.RB - CALENDAR
REQUIRE 'DATE'.DOWNCASE
# FIND CLASSES.
OBJECT = [].CLASS.SUPERCLASS
DATE = OBJECT.CONST_GET('DATE'.DOWNCASE.CAPITALIZE)
# CREATES A CALENDAR OF _YEAR_. RETURNS THIS CALENDAR AS A MULTI-LINE
# STRING FIT TO _COLUMNS_.
OBJECT.SEND(:DEFINE_METHOD, :CAL) {|_YEAR, _COLUMNS|
# START AT JANUARY 1.
#
# DATE::ENGLAND MARKS THE SWITCH FROM JULIAN CALENDAR TO GREGORIAN
# CALENDAR AT 1752 SEPTEMBER 14. THIS REMOVES SEPTEMBER 3 TO 13 FROM
# YEAR 1752. (BY FORTUNE, IT KEEPS JANUARY 1.)
#
_DATE = DATE.NEW(_YEAR, 1, 1, DATE::ENGLAND)
# COLLECT CALENDARS OF ALL 12 MONTHS.
_MONTHS = (1..12).COLLECT {|_MONTH|
_ROWS = [DATE::MONTHNAMES[_MONTH].UPCASE.CENTER(20),
"SU MO TU WE TH FR SA"]
# MAKE ARRAY OF 42 DAYS, STARTING WITH SUNDAY.
_DAYS = []
_DATE.WDAY.TIMES { _DAYS.PUSH " " }
CATCH(:BREAK) {
LOOP {
(_DATE.MONTH == _MONTH) || THROW(:BREAK)
_DAYS.PUSH("%2D".DOWNCASE % _DATE.MDAY)
_DATE += 1 }}
(42 - _DAYS.LENGTH).TIMES { _DAYS.PUSH " " }
_DAYS.EACH_SLICE(7) {|_WEEK| _ROWS.PUSH(_WEEK.JOIN " ") }
_ROWS }
# CALCULATE MONTHS PER ROW (MPR).
# 1. DIVIDE COLUMNS BY 22 COLUMNS PER MONTH, ROUNDED DOWN. (PRETEND
# TO HAVE 2 EXTRA COLUMNS; LAST MONTH USES ONLY 20 COLUMNS.)
# 2. DECREASE MPR IF 12 MONTHS WOULD FIT IN THE SAME MONTHS PER
# COLUMN (MPC). FOR EXAMPLE, IF WE CAN FIT 5 MPR AND 3 MPC, THEN
# WE USE 4 MPR AND 3 MPC.
_MPR = (_COLUMNS + 2).DIV 22
_MPR = 12.DIV((12 + _MPR - 1).DIV _MPR)
# USE 20 COLUMNS PER MONTH + 2 SPACES BETWEEN MONTHS.
_WIDTH = _MPR * 22 - 2
# JOIN MONTHS INTO CALENDAR.
_ROWS = ["[SNOOPY]".CENTER(_WIDTH), "#{_YEAR}".CENTER(_WIDTH)]
_MONTHS.EACH_SLICE(_MPR) {|_SLICE|
_SLICE[0].EACH_INDEX {|_I|
_ROWS.PUSH(_SLICE.MAP {|_A| _A[_I]}.JOIN " ") }}
_ROWS.JOIN("\012") }
(ARGV.LENGTH == 1) || ABORT("USAGE: #{$0} YEAR")
# GUESS WIDTH OF TERMINAL.
# 1. OBEY ENVIRONMENT VARIABLE COLUMNS.
# 2. TRY TO REQUIRE 'IO/CONSOLE' FROM RUBY 1.9.3.
# 3. TRY TO RUN `TPUT CO`.
# 4. ASSUME 80 COLUMNS.
LOADERROR = OBJECT.CONST_GET('LOAD'.DOWNCASE.CAPITALIZE +
'ERROR'.DOWNCASE.CAPITALIZE)
STANDARDERROR = OBJECT.CONST_GET('STANDARD'.DOWNCASE.CAPITALIZE +
'ERROR'.DOWNCASE.CAPITALIZE)
_INTEGER = 'INTEGER'.DOWNCASE.CAPITALIZE
_TPUT_CO = 'TPUT CO'.DOWNCASE
_COLUMNS = RESCUE(PROC {SEND(_INTEGER, ENV["COLUMNS"] || "")},
STANDARDERROR,
PROC {
RESCUE(PROC {
REQUIRE 'IO/CONSOLE'.DOWNCASE
IO.CONSOLE.WINSIZE[1]
}, LOADERROR,
PROC {
RESCUE(PROC {
SEND(_INTEGER, `#{_TPUT_CO}`)
}, STANDARDERROR,
PROC {80}) }) })
PUTS CAL(ARGV[0].TO_I, _COLUMNS)