Skip to main content

19 Open Source Vlog - Building A Calendar From Scratch

Episode 67 · June 29, 2015

Spiking out code to figure out what we want so that we can do a great refactoring of simple_calendar

Open Source


Transcripts

Hey, what's up guys, day six, and I just recorded this episode but the audio went bad ad I have to do it again, so maybe it will be a little quicker this time, but I apologize for that, I'm trying to get this all sorted out. Yesterday we decided that we're going to build a rails app to test and explore ideas before we dive really deep into rewriting this gem, so I've created a rails app called "Calendar", and I've generated a scaffold for a meeting model, and the meeting model has a name, has a start time and an end time. The end time, the end time is kind of optional, but it's one of those features I'd like to support as we go forward, so we're going to take a look at that and add that to our application and see what we can do.

Here's our calendar app, I've added one item into the calendar called "Holiday" for July 4th, called "Holiday in the US", and we've got a start time and an end time for that, and we're just going to start building a calendar from scratch. Inside this code here, I'm going to open up the meetings/index.html.erb, and I'm just going to build a table in here for the calendar. The table makes the most sense for the calendar because it doesn't require any CSS to make it look generally like a calendar. Obviously, almost for all cases for calendars you're going to want to use CSS for it, but in this case, the CSS isn't required because we're using a table, the days get kind of evenly distributed and all of that, whereas if you were to do this with CSS and flexbox and maybe divs, it wouldn't look nice without some CSS, and I don't want to enforce that, I want you to be able to completely flexibly do this, and I want it to be simple, simple is key here, so let's talk about building a calendar.

Now if you want to build a calendar you're going to need a table, you're going to need a header for the days of the week, like Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, and then you're going to need a body for the rest, so this is the days, the actual days in the calendar and the t head is going to need one row, and it's going to have a bunch of ths, which is like a header tag, and it's a header because it's going to have the days of the week in here. You'll close the tr, close this th, and then you'll have your <tbody> which will be a tr for every row of calendar, so in this case let's do a month calendar, and we'll have <td>s in here for evert single day of the month. We'll need to take this array and split it up for each week so we can generate the <tr> tags accordingly, but in order to build this dynamically we're going to make an idea of a date_range, and that's going to bean array of the first day of the month to the last day of the month.

<% date_range = (Date.today.beginning_of_month..Date.today.end_of_month).to_a %>

This will generate a range, which is lazy, so it's not actually an array, it will be able to generate an array for us, we have to force it to an array here at the end to actually make an array. But what it does is it says: Let's make an array of dates from this to this which will be a full month, which is awesome. And you might have noticed here that we're already duplicating this Date.today, and if we were to give that a name like

<% start_date = Date.today %>

We could see some interesting functionalities start to come into play, so we actually change this with start_date, then you have a starting date of the calendar, and actually if you make this "May 5th", well what's the beginning of that month for the day, for May 5th, well it's May 1rst, and then what's the end of the month from May 5th, would be the end of may, and actually we could make it dynamic so that we can use start_date to determine which view of the calendar to use, so we could actually make this a params like start date, and then default to Date.today at the end, and that would allow us to say: Anything in the url called start_date, if we parse that into a date, then we can use that as a start date, and that allows us to use those previous and next links at the top of the calendar, so we could go to the previous month or the next month, so we could just refactor this a little bit, so here we could do

<% start_date = params.fetch(:start_date, Date.today).to-date %>

We can make sure we always have a date. This allows us to have a date range, now we have a list of June 1rst to the end of June, which is the 30th, and we can start splitting that up into weeks, so this is cool. We can have a date range, let's print this out, we can do

<% date_range.each_slice(7) do |week| %> 
    <div><%= week %></div>
<% end %> 

If we do this each_slice, this is a method that takes an array, splits it up into many arrays of a certain length, so in this case we have each slice of seven so that it will be every seven days, so if we refresh our page we'll see that, so you have Monday through the 7th, 8th through the 14th, 15th through the 21st and so on. This is all of our month of June but we've got Monday as the beginning of the week. In the US, it's actually Sunday as the beginning of the week, and if we jump into our application.rb, we can set

config.beginning_of_week = :sunday

I've already set this and all we need to do is restart our rails server, but I could be wrong, because I've had some trouble with this code before, so let's take a look at this. Maybe we'll just kind of ignore this for now.

This wasn't working because we're not looking at the beginning of the week, we're actually just starting from the first day of the month. The first day of the month happens to be a Monday, and what you'll notice here is that this wouldn't generate an actual proper calendar, because this is a Monday, and it just happens that we lucked out there so if we change this to March, March 1rst is on a Sunday but if we do this with April, the beginning of the week is on a Wednesday, and we actually need to adjust our date range to start at the beginning of the week.

<% date_range = (start_date.beginning_of_month.beginning_fo_week..Date.today.end_of_month.end_of_week).to_a %>

This will always make a square calendar because it will always give you the beginning and the ends of the weeks on the first and last weeks, so this allows you to generate the full square calendar as you would expect. Before we weren't doing that and that was causing the days of that month to show up. We could pad those normally, but I like the idea of showing a couple days of the last month, and what I show you briefly there was that config.beginning_of_week, by default it's a Monday, but you can configure this globally like I've done here in application.rb or each user could have their own option and you could set the beginning_of_week variable before the page loads, you could put that in a before action to configure that if you wanted to . Now we have these arrays for every week, and if you've noticed this actually the start date parameter we've created based on the params.fetch is allowing us to go back and change which month we're looking at, which is super cool. We've already built a dynamic calendar, we just need to link it up, and we did that in just a few minutes which is so exciting. Here we can take this each_slice and move it into the <tbody> for every one of these

<tbody>
    <% date_range.each_slice(7) do |week| %>
    <tr>
        <% week.each do |day| %> 
        <td>
            <%= day %>  
        </td>
    <% end %>
    </tr>
</tbody> 

We're going to get a table now with all of the days of the month, so we get from March 1rst to April 4th. If we go to April we get March 29th all the way through May 2nd which is super cool, we can remove this parameter from the url, it will default the current month, and all of that jazz, which is really neat. The only thing left is to correct these headers so that we show the correct ones, and we need to just do that by saying: Let's take the first slice, let's grab the first slice of the range, and instead of doing each slice, we're going so say

<% date_range.slice (0, 7).each do |day| %> 
    <th><%= %></th>
<% end %> 

This is a piece of code that I'm going to steal from the existing one, but we're going to make this localized, so that if your user is in a different language, it will automatically default to using the correct localized title there. We can grab this piece of code and paste that in there

I18n.t("date.abbr_day_names")[date.wday]

What this is going to do is look up inside your config/locales directory, the translation for these items, so we're going to say that there's a date.abbr_day_names translation, and we're going to look it up and we're going to grab the correct one for the day of the week, if you were to look this up, you can just look up abbr_day_names, and you'll see a lot of people talking about this because it's pretty common, and we definitely want our gem out of the box to support translations, so if we were to do this with German for example, you could just say the date abbreviated day names and then list out those items and then the code that I've shown you there should help you with the translation, so that should automatically work as long as the user's got the translation set up appropriately. This already ships with rails by default for English, so you don't have to worry about it, so when we refresh the page, we get an error because we had the wrong variable name or change the name. Now we can refresh it, and you can also remove abbr there from the beginning and get the full day names which is super cool, and that's why you add the option in before because you might not always want the abbreviated ones, you might want something totally different, and it should be configurable, so we'll want to think about how to make this configurable in the future as well, but with all that said, we've created a functional calendar, a functional month calendar in a super short amount of time which is awesome, and that's it. We're basically done.

I'm going to leave it here for today and maybe tomorrow we'll make maybe a two week calendar, maybe a four day agenda calendar, I'm not sure, we'll try with different views and then we'll see how the code should be changed for each of those, at the heart of this is really all we're doing, this is all super cool. We've made a lot of progress, I hope that all made sense. It's my second time recording it, so I'm kind of jumbled and ask any questions that you have in the comments, be sure to like the episode so that other people can know you enjoyed it, and I can know you enjoyed it, and that you want to see more, and I will talk to you tomorrow. Peace v

Transcript written by Miguel

Discussion