While I was looking through the Haml docs today, I found the ability to print out the date. My gears started spinning. Why not create a calendar?
It took a lot of research, but I made a functioning Haml Calendar. I say functioning, because it will display the correct date and tomorrow it will automatically display tomorrow’s date. You may also change the month, year, and day variables to display a specific date.
I realize this is not a practical calendar solution, but it was fun to figure out.
The first step: print out today’s date. Once I got that, I created variables for month, year, and day that default to today’s date unless otherwise specified. I also created an array
@days to hold the days of the week in the order I prefer.
I looped through
@days to create the headers for the calendar, but I decided to only display the first letter of each day. I used a list because I find that they are easier to style and they help keep my code organized.
- @days = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'] %ul.weekdays - @days.each do |i| %li.weekday = i
Next I created a new list for the ordinal days of the calendar using a new loop:
%ul.week - (1...32).each do |i| %li.day = i
I started by printing 31 days (with the start of the month at Sunday for now). I styled the list items so that only 7 can fit in each row. The list items naturally fell into calendar order.
At this point I had a calendar, but it wasn’t correct. August begins on Thursday, not Sunday. I found that if I adjusted the starting value of my loop, I could get the first of the month to start on Thursday
- (-3...32).each do |i|. That works, except now Sunday reads as -3 and so forth. I added a few constraints:
%ul.week - (-3...32).each do |i| %li.day - if i > 0 = i
The items that are
i <= 0 still exist, but they don’t print a number. Instead they create blank space allowing August to begin on Thursday.
The calendar works, but it’s not dynamic.
Making it Dynamic
I wrote a new variable called
monthStart. Using the date function along with my variables for month and year, I could get the day each month starts on. I wrote another variable to find the position of the first of the month in my
@days array. So in
@days Thursday’s position is 4, but my loop needs to start at -3 for the month to start on that day. I wrote down every day, its position in the array, and what number the loop needs to start on in order for the month to start on that day. From these findings, I created a formula. I take the negative of the hash and add 1.
- monthStart = Date.new(year, month, 1).strftime("%a") - @days = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'] - hashDays = Hash[@days.map.with_index.to_a] - monthStartNum = hashDays[monthStart] - adjustMonthStartNum = -monthStartNum + 1
So I updated my loop:
%ul.week - (adjustMonthStartNum...32).each do |i| %li.day - if i > 0 = i
To make sure my loop worked, I changed my
month variable to 9 to display September. It worked!
But, September only has 30 days and I’m still printing out 31.
I wrote another variable called
monthEnd. This new variable was slightly easier than monthStart as I automatically receive an integer.
- monthEnd = Integer(Date.new(year, month, -1).strftime("%d"))
I updated my loop once more (Here I add 1 to monthEnd to make sure it gets the last day and not up until the last day of the month):
%ul.week - (adjustMonthStartNum...monthEnd + 1).each do |i| %li.day - if i > 0 = i
I added a few more constraints when printing out the days to highlight today and to mute the days before today. I think I can write this more efficiently, but I’m ok with my progress so far.
If you edit in CodePen, try writing in a custom date. I commented out the space for you to give it a try.
I really enjoyed figuring this out. I’m new to Ruby, but I think I will do more experimenting!
p.s. I’m not sure about editing the timezone, so it might be a day ahead.