Calendar - for "REAL" programmers: Difference between revisions

Add Ruby.
m (fix typo in text)
(Add Ruby.)
Line 471:
31 Wed</pre>
 
=={{header|Ruby}}==
{{omit from|Tcl|Real Tcl programmers use lower case! To be exact, a preamble could be written which would transform a Tcl interpreter into using upper case, but that would have to be part of this task's solution and would itself be in lower case, at least initially.}}
UPPERCASE RUBY is normally impossible, because Ruby is sensitive to case, and most methods and keywords have lowercase letters. To solve this task, we wrote a new program loader for UPPERCASE RUBY programs.
{{omit from|Java|Java's keywords are lower case}}
 
Our program loader [[respond to an unknown method call#Ruby|responds to unknown method calls]] by calling their lowercase equivalents, so Kernel#PUTS acts like Kernel#puts. Our program loader also defines Object#RESCUE to replace the 'rescue' keyword. We can now write UPPERCASE RUBY programs that call uppercase methods and use no lowercase keywords.
 
* <code>loadup.rb</code> is the program loader.
<lang ruby># loadup.rb - run UPPERCASE RUBY program
 
class Object
alias lowercase_method_missing method_missing
 
# Allow UPPERCASE method calls.
def method_missing(sym, *args, &block)
str = sym.to_s
if str == (down = str.downcase)
lowercase_method_missing sym, *args, &block
else
send down, *args, &block
end
end
 
# RESCUE an exception without the 'rescue' keyword.
def RESCUE(_BEGIN, _CLASS, _RESCUE)
begin _BEGIN.CALL
rescue _CLASS
_RESCUE.CALL; end
end
end
 
_PROGRAM = ARGV.SHIFT
_PROGRAM || ABORT("USAGE: #{$0} PROGRAM.RB ARGS...")
LOAD ($0 = _PROGRAM)</lang>
 
* <code>CAL.RB</code> is an UPPERCASE RUBY translation of [[Calendar#Ruby]].
{{works with|Ruby|1.8.7}}
<lang 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)</lang>
 
* A local variable must start with a lowercase letter or an underscore, so we have many leading underscores (_YEAR, _COLUMN, _DATE and so on).
* We form blocks with curly braces <code>{ ... }</code>, never with lowercase keywords <code>do ... end</code>.
* <code>OBJECT = [].CLASS.SUPERCLASS</code> finds the superclass of the class of the empty array; this is the <code>Object</code> class! Class#CONST_GET finds some other classes.
* Class#DEFINE_METHOD defines a new method, Object#CAL, without a <code>def</code> keyword.
* Kernel#LOOP, Kernel#THROW, Kernel#CATCH and operator <code>||</code> act like a while loop without a <code>while</code> keyword.
 
<pre>$ ruby loadup.rb CAL.RB 1969
[SNOOPY]
1969
JANUARY FEBRUARY MARCH APRIL MAY JUNE
SU MO TU WE TH FR SA SU MO TU WE TH FR SA SU MO TU WE TH FR SA SU MO TU WE TH FR SA SU MO TU WE TH FR SA SU MO TU WE TH FR SA
1 2 3 4 1 1 1 2 3 4 5 1 2 3 1 2 3 4 5 6 7
5 6 7 8 9 10 11 2 3 4 5 6 7 8 2 3 4 5 6 7 8 6 7 8 9 10 11 12 4 5 6 7 8 9 10 8 9 10 11 12 13 14
12 13 14 15 16 17 18 9 10 11 12 13 14 15 9 10 11 12 13 14 15 13 14 15 16 17 18 19 11 12 13 14 15 16 17 15 16 17 18 19 20 21
19 20 21 22 23 24 25 16 17 18 19 20 21 22 16 17 18 19 20 21 22 20 21 22 23 24 25 26 18 19 20 21 22 23 24 22 23 24 25 26 27 28
26 27 28 29 30 31 23 24 25 26 27 28 23 24 25 26 27 28 29 27 28 29 30 25 26 27 28 29 30 31 29 30
30 31
JULY AUGUST SEPTEMBER OCTOBER NOVEMBER DECEMBER
SU MO TU WE TH FR SA SU MO TU WE TH FR SA SU MO TU WE TH FR SA SU MO TU WE TH FR SA SU MO TU WE TH FR SA SU MO TU WE TH FR SA
1 2 3 4 5 1 2 1 2 3 4 5 6 1 2 3 4 1 1 2 3 4 5 6
6 7 8 9 10 11 12 3 4 5 6 7 8 9 7 8 9 10 11 12 13 5 6 7 8 9 10 11 2 3 4 5 6 7 8 7 8 9 10 11 12 13
13 14 15 16 17 18 19 10 11 12 13 14 15 16 14 15 16 17 18 19 20 12 13 14 15 16 17 18 9 10 11 12 13 14 15 14 15 16 17 18 19 20
20 21 22 23 24 25 26 17 18 19 20 21 22 23 21 22 23 24 25 26 27 19 20 21 22 23 24 25 16 17 18 19 20 21 22 21 22 23 24 25 26 27
27 28 29 30 31 24 25 26 27 28 29 30 28 29 30 26 27 28 29 30 31 23 24 25 26 27 28 29 28 29 30 31
31 30 </pre>
 
{{omit from|D|D's keywords are lower case}}
{{omit from|Java|Java's keywords are lower case}}
{{omit from|Tcl|Real Tcl programmers use lower case! To be exact, a preamble could be written which would transform a Tcl interpreter into using upper case, but that would have to be part of this task's solution and would itself be in lower case, at least initially.}}
Anonymous user