Join Kevin Skoglund for an in-depth discussion in this video Challenge: Multi-list maker, part of Ruby: Files, Formats, Templates.
(upbeat music) - [Instructor] It's time for another challenge assignment. This challenge is going to be very similar to the challenge we saw in the last chapter, where we created a List Maker. But this one's called Multi-List Maker and the idea is that we're going to allow the user to have multiple lists and that's because now we know about directories so we can have one directory and that directory can have a number of different files and each one of those files can be a separate list. So the user is going to be able to choose from among those known lists and the user can also add a list as well.
As in the last chapter I've also included a sample project in the exercise files to get you started. Let's take a look and I'll demonstrate. The project file is called multi_list_maker and I've given it a different name just to keep it separate but the basic framework is the same as what we were working with in the last chapter. Init.rb is exactly the same nothing has changed there. That's the file that we're going to run from the command line to get things started and it's going to launch this controller and the controller will then take care of controlling the process from there.
The controller is mostly the same, with a couple small additions. You can see that it's loading in a new file called list_dir we'll look at that in a moment, and you can also see that I have added a new action here load so in addition to viewing adding and editing a list. We can also tell it to load a list, you'll notice that initialize is a little different instead of initializing by setting it to a certain file instead it sets up a directory and then after we show the introduction it's then going to call that load action and that load action will pick the list for us.
So we don't set the list in initialize anymore, we set the directory and then we give the user the opportunity to pick which list they want right at the start. Everything else is pretty much the same, let's skip down to that load action and you'll see here it is in do action when the user tells us that they want to load something or when we've called it ourselves up here. Then what's going to happen is it's going to tell a instance of the list directory, the one we created up in initialize to call choose_list and that's going to pick the list once they have a list, we'll have a file name and we'll then tell list file to load that file.
That's a little different than before. Before we were just calling new and it loaded a default file. Now we have the ability to specify which file it should load. Once we have that list file then it also takes the extra step of just going ahead and viewing that file right from the start. So as soon as it loads it, it also shows it to us at the same time. Let's take a look at list file and see the changes that are there, the biggest change is that now we're passing in an argument to initialize which is the file name, if nothing is passed in then it will still use the default file name. But if we give it a file name, it'll then load that file name in instead and also it's no longer looking for that file in the app root it's now looking for it in the directory and that directory is defined by dirname you can see here ListDir dirname is giving us that value and that's going to be lm_lists you can see that I've already moved my other lm_list into this directory called lm_lists.
So that's where it's going to reside the rest of the ListFile class is going to be unchanged and that's because once we set the file path and we load in the file that we're looking for everything else is really independent of that. Editing a file, listing a file, all of that stuff happens once we have a file path, so if we change the file path to a new path it's working with the new file but the operations are the same. It's pretty convenient, okay so here's where your work really comes in for this project, and that is the new ListDir class. So you can see I have a module ListMaker, the class ListDir it has a class variable that sets the name of that directory lm_lists, the same name we see here and that's accessible here using self.dirname.
That's a class method to give me the directory name and you saw me use it back over here when I was trying to get the directory name for ListFile. Below that you can see I've got an attribute reader for files, the idea here is I want to cache the files that are in that directory, I don't want to have to keep going back and looking at he directory every time I want to find out what's in there. Instead I'm going to go one time, get a list of the files and store it in an array in an instance variable so that I can access it and then anytime I want I can refresh that list.
But I have a cached version that I can look at without going to the file system. So you'll remember that our controller back over here that the very first thing it did was initialized by calling ListDir.new so when we call that. That's going to call initialize we want to find this directory or create it if it doesn't exist, we want to also make sure that it's readable and it's writable and if you need to create the directory you do that using Dir.mkdir we learned how to do that already, and then we want to refresh those cached files essentially load the cache for the first time but we'll just call it refresh.
In fact I've got a method down here where we can do that, we'll come back to that in one second and then the last step would be to return itself. Because we're initializing we want to actually return this instance so that then that can be stored inside this instance variable. Alright so now let's look down at those cached files because refresh cached files is going to read the directory, so the directory exists. Now we just need to go to that directory and find out what entries are there and we can do that using either Dir.entries or Dir.glob, it's up to you which one you use but make sure you do not include any .files or any directories you might also want to look for a particular file ending like .txt files.
Also be careful that you only return the file names of what's in the directory, not absolute or relative paths, we don't care what the paths are. We know where this directory is located. We want to know what the contents of the directory are file names is enough for us to have that. Once we have this cached version of the files, then we need to write our code for choose_list and for add, so choose list is going to display a list of the files with numbers next to them. So then we'll ask the user to choose one of the numbers. The user can also type add if they want to add a new list instead, we'll default to the first file if the user data's invalid for any reason and we'll return the file name that they chose and that's because that's what we're going to load with ListFile, remember back over here when we provide a file name we're able to load up that file.
When we want to list the files, I've already given you a method that can do that it just simply goes through each one of those files, each with index and makes a nice list for you. On the add method if the user tells us they want to add we're going to call this instead, and we're going to ask the user to provide the file name they want. We're going to try and get a name that we like. Here I've given an example of something all lower case with underscores .txt. We want to make sure the file name ends in .txt you can do whatever kind of manipulation you want and error checking that's up to you.
Finally when we're happy with the file name, we're going to create a new file, we'll refresh those cache files because now we have a new file. You could also just add that file name to your array but calling for the new cache file also sorts them at the same time, and then finally return that file name so that it can be loaded by ListFile and that's because as soon as we add a new file we also should go ahead and open that file as well. We shouldn't ask the user to have to add it and then come back and choose it again. So again all of your work for this project is going to be in this list_dir.rb file and I would suggest working with initialize first then refresh cached file, then choose list and then add.
If you tackle 'em in that order and you take your time I think you'll be able to come up with a solution to this challenge. In the next movie I'll show you the solution that I came up with.
- File system basics
- Writing to and reading from files
- Renaming, deleting, and copying files
- Creating and editing directories
- Working with common data formats
- ERB templating