1. Open Source Vlog - Where Do We Even Start? 2. Open Source Vlog - Housekeeping and Merging Pull Requests 3. Open Source Vlog - Setting Up Rspec 4. Open Source Vlog - Setting Up For The Future 5. Open Source Vlog - Just Deleting Things 6. Open Source Vlog - Building A Calendar From Scratch 7. Open Source Vlog - Refactoring Our Calendar 8. Open Source Vlog - Filtering Events 9. Open Source Vlog - Finding a balance between Ruby and ERB 10. Open Source Vlog - Simple And Clean RubyGem With Rails Engines 11. Elegant Refactoring for Week and Month Calendars 12. Open Source Vlog - Refactoring Events And Uploading Version 2.0 13. Open Source Vlog - Rails Generators 14. Open Source Vlog - Sorting Calendar Events 15. Open Source Vlog - Writing Rspec Tests 16. Upgrading simple_calendar for Rails 5 17. Recurring events with the ice_cube gem 18. Adding Exceptions To Recurring Events
Episode 66 · June 28, 2015
Deleting our old code to make room for a refactoring
It's day five of the vlog! I can't believe it's day five already, this is so cool and I'm really having fun with this. Today, we're going to take the next step to writing tests for our gem, so we've been over the last few days kind of doing some housekeeping and setting up RSpec. Now we've got it set up, we've got some pending tests, it's time to start thinking more deeply about where we're going. Something that we should do is take a look at the calendar class in simple_calendar and think about all the concepts that it needs to have.
One of the big things that you'll notice if you open this class is that we have some initialized set up code, but then there's lot's and lot's of stuff around rendering. We have render, and I'm just going to delete this code.
def render(block) end def render_header end def render_table end def render_weeks end
I don't need comments because this is actually pretty obvious. The header and then the table underneath that, and then the header is the one where you've got the one with the "Next" and "Previous" links and the title of the calendar, then the table actually has it's own little header inside of it. Each week is a table row, each day inside of there is a
td, and so on, so we can just clear all those out and those should be obvious what they're doing, and then we get into all of these helper methods that I've written here at the bottom, and we can go through these and think about what's going on, this name, and I wanted it to be configurable so the param in the url is start date. This comes in or it has a default and the default star date is going to be the one that determines how the calendar should render. If you give a month calendar a start date of May 3rd, it's obvious that you should render May as the month calendar, so we want to use the start date or some type of parameter to figure out which display of the calendar we should use, so we need to make sure that we have a test for that.
describe SimpleCalendar::Calendar do it 'has a param that determines the start date of the calendar' it 'generates a default date if no start date is present' ... end
This is one of those things that regarding the date range, it has some work that it needs to do. If you have a calendar, if you have no start date, then today being June 28 should not render July, it should render June, and it should just have this, we're at the end of the June month, and maybe if you're doing a two week calendar maybe you want the beginning of the week to display, and then the next week in July to display. All of these calendars need to work differently but they are going to have some things in common, which is a start date and a range of dates. The parameter name in the url maybe doesn't need to be customizable, I actually don't know how many people are customizing the start param name, and it might be nobody, and maybe we don't need to allow that to be configured. I don't think it needs to be configurable, but I did it before and I'm not quite sure if people need that. If you are watching this, and you have an opinion on that, leave a note in the comments or in a GitHub issue and we can make sure that we can cover that because I think it's something important to think about, I definitely wrote it before with some intentions. This is another concept that I added as the gem grew was the idea of events, most people were rendering this calendar and they didn't have the understanding that if you have an array of events and you're displaying a calendar for each day of the calendar, you can just grab out the items from the array and just print them in there. So it was more useful for me to say: Give me all of the events, just tell me which column to sort them on and then I will pull them out and give them to you each day. It's really no magic, all I'm doing is grabbing the ones for the specific day based upon the start time of whichever field you're using to determine which day it goes in. So I normally call it start time and that was it. This does no magic but it saves you from having write that code inside the view or in a helper or something like that. So it saves you some time. We should also write a test for
describe SimpleCalendar::Calendar do it 'accepts an array of events' it 'sorts the events' it 'yields the events for each day' ... end
We'll have to come back to these tests and probably clear up the language a lot, but this is definitely a concept that we want to handle in the gem so we'll have to have this concept of events, so this code we can just get rid of for now, we'll probably make some very similar code to what we've written before. That's all saved nicely away in the master branch because we're working in our new branch.
Now we've got some previous links, the default title, which is empty, the default "Next" links which is also empty. All of these things are things that we should probably have. We have test for those, those three. Ideally, we don't want these to be called "default" whatever, because we're trying to make this much more friendly to use, and we're using options before, so we pass in the "Next link" option, you pass in the "Title" option, and that would handle it. I was already kind of using the name title, and then there's a default that it would fall back to, and because we're duplicating things, I ended up just naming the stuff that way, and really you should just be overriding this class and using that instead. You also have a default header for the table, which is a bunch of nested loops, or blocks. Kind of ugly looking, so maybe that's another thing too, is that if you've noticed here that I've done all of the rendering inside of ruby, it might make a lot more sense to render a partial and actually have a partial for these things, so maybe that is what we can do in refactoring and use this erb to render partial and save us from writing all of this view helper code inside of there. We'll see, we have a lot of work to do that, and maybe we can come up with a really great way of handling those, maybe month calendar actually have their own view and maybe we can just make these views that you can generate. I'm not sure, but we need to think about that.
Here's the start date that we've been talking about before, neither gets the option passed in or it looks for param or it sets the default to the current time. The date range takes the calculation of the number of days, and then it goes to the start date and then figures out how many number of days we want the calendar to display and then adds that to the original day to figure out what the end date is, so that could definitely be cleaner as well, and the last piece that we have here is the default
td classes, which are the classes that automatically get applied to the dates, so if you're trying to style a calendar which almost everybody wants to do, I tried to add a lot of helpful classes into the class attribute into the html tag so you'll get "today", "past", "future", "start-date", "previous-month", "next-month", all of these apply if they match the rule afterwards, so if the day matches the current calendar date, then it will display these things accordingly, so your CSS can just say: Cool, let's make all the previous and next months greyed out, and then we'll ignore those or whatever, that's really nifty and there's all these little helpers, and I think I'm going to make sure that we keep all of those as well, because I think they're really really useful.
get_option, last but not least, is a way for me to determine should the options get passed in, should they be called, should we execute those, whatever. When the option is being configured for the calendar we passed in, it could do some magic, and it's complicated and something you don't need. We've stripped out a lot of what we've got. We have some readers and we've delegated some methods to the view context, and that's how we can call
link_to and other view related things inside of this ruby class.
Ideally we're going to do this inside a template but we'll see. Calendars aren't really as complicated as people think, so I'm not sure if we're going to want to do that but it's very specific, what people want, so maybe we end up not sticking with ruby and not doing templates because that can be a little tough. Now it's one of those moments where we have kind of a big wide open space to explore it. I have all this code that I've written in the past, and I know how that generally works, and it produces decent code, it's not the most maintainable, and it's not the most fun to work with either, and because are very view centric, I'd love to be able to use the code that we're writing in the views, I'd love to be able to give you the views as the user and you can just override those things, maybe simple_calendar just generates a template and has helper methods for you to go through there. I'm not super sure there, and that's another thing that.
There's this older gem that would help you generate tables and a lot of people are using it for calendars, I can't seem to find it off the top of my head. TableBuilder, there it is! This was great, it looks like it moved to another owner, it was actually great, I used it all the time, and you would basically make a table for some objects, and then it would help you pull out the body and rows for all of these things, and actually that's what simple_calendar is doing behind the scenes, but I'm hiding all of this stuff inside a ruby class instead of inside of your template. I'm not sure this is the direction I want to go because, one of the pieces that I like, is that you can put the header columns in there, the days, the tasks, it would help you do all of that, and I believe it can do quite a bit, but I wasn't really happy with this, because it was like really complicating my views, and I don't know, I'd like to maybe explore this approach again and see if I can improve on their table generator in the views, but I'm actually very hesitant if that's the direction I want to go, so this is where test-driven development can kind oof break down, and if I would have to go and do this with normal test driven development we'd have to scrap all of that, probably spend a lot of time writing tests and dealing with views that I don't know if I want to do or not. So we're going to go spike this out, and spiking is just this concept of go write code, don't worry about testing or any of that stuff. Go see if that's an idea you want to continue with, and then come back and reevaluate once you've figured out if it's good or not. That's what we're going to do tomorrow, I'm going to generate a new rails application, we'll write a bunch of new code inside of that and see if it's anything that seems appealing or not, and it will probably come out similar to this calendar_for, and I'm a little afraid of that, I don't like where that's headed, but I also don't like the idea of having all of this code for rendering inside the ruby class, so maybe there's a balance we can strike. This one is pure ruby that I wrote, the table builders, pure view code, maybe there's a balance we can strike and maybe hit this thing in the sweet spot, I'm not sure. I don't want to use testing too much to slow me down, I'm not putting much time into this every day. Tests should help you be confident that your code is going to work, but it's still up to you to make design decisions. Don't let test-driven development decide everything you do, you're still a human being, you have to trust your gut on things. We will talk tomorrow, and that is what we're going to start doing, we're going to start spiking out some code and see if the ruby and the templating and all that jazz works out. We'll see, I have no idea where we're going, it may be a waste of time, and we might be right back wher we started two days from now. We'll find out. Peace
Transcript written by Miguel