# Ruby Essential Training

## with Kevin Skoglund

In Ruby Essential Training, expert instructor Kevin Skoglund teaches the fundamentals of Ruby, the popular object-oriented open-source programming language. Kevin begins by walking through the basic data types, demonstrating Ruby's control structures (loops, iterators, conditionals, and code blocks) and showcasing the elegant syntax structure of the language. Kevin explains variable scope and shows how to use methods, arguments, and return values to write efficient code. After covering the fundamentals, Kevin focuses on Ruby's object-oriented features. He shows how to define classes and explores OOP concepts, including instances, attributes, access control, and inheritance. Exercise files accompany the course.
Topics include:
• Using Ruby in the Interactive Ruby Shell and in standalone scripts
• Learning to write custom code blocks to find, merge, and sort
• Using modules for namespacing or as mix-ins
• Reading from and writing to files
• Creating a full Ruby project from start to finish
author
Kevin Skoglund
subject
Developer, Web, Programming Languages, Web Development
software
Ruby
level
Beginner
duration
6h 54m
released
Oct 14, 2009

Keep up with news, tips, and latest courses.

• ### FAQs

Search the closed captioning text for this course by entering the keyword you’d like to search, or browse the closed captioning text by selecting the chapter name below and choosing the video title you’d like to review.

Introduction
Welcome
 00:00 (Music playing.) 00:03 Welcome to Ruby Essential Training. 00:05 My name is Kevin Skoglund. 00:07 I run a web development company called Nova Fabrica, where we develop websites 00:10 and web applications. 00:12 In this course, we are going to learn to program using Ruby, the popular 00:15 object-oriented programming language. 00:18 To begin with, we will examine the basic object types in Ruby, as well as the 00:21 fundamentals of the language syntax. 00:23 We will learn how object-oriented programming works and I'll walk you through 00:26 the steps to enable you to read and to write to files from your code. 00:30 Finally, we'll get practical hands- on experience using Ruby by building a 00:34 restaurant guide for managing your favorite restaurants. 00:37 Now, it doesn't matter if you're a beginner or if you have prior 00:39 experience. We'll cover all the fundamentals you need and many more advanced concepts as well. 00:44 If you're interested in website development, you will end with a solid 00:47 foundation in Ruby that will make learning Ruby on Rails much easier. 00:50 Let's get started learning how to program with Ruby. Collapse this transcript
Using the exercise files
1. Getting Started with Ruby
Introducing Ruby
Installing Ruby on a Mac
Installing Ruby on Windows
Using Ruby
Interactive Ruby Shell (IRB)
Documentation
2. Ruby Object Types
Objects
 00:00 In the introduction we discussed the fact that Ruby is an object-oriented 00:03 programming language, and I have pointed out several times that almost 00:06 everything in Ruby is an object, and that's one of its strengths. 00:10 Now, in other languages that's not always the case. 00:12 A lot of languages have something they call primitives, which are like the basic 00:15 object types that we are about to talk about, but primitives don't have a common 00:19 relationship to each other. 00:20 In the case of Ruby, all of these object types that we are going to discuss are 00:25 all related because all of them are objects. 00:27 That's the fundamental building block that everything else is built off of. 00:31 So keep that in mind in Ruby; everything is an object. 00:33 Everything you manipulate is an object and everything that's returned by your 00:37 manipulation is also an object. 00:39 So what is an object? 00:40 Well, we call it an object because it's rather analogous to an object that you 00:44 would have in the real world. 00:46 So for example, in the real world we would have an object that would be a 00:49 classroom, that's a thing, and that could be modeled in Ruby as being an object as well. 00:54 In the real world we would have students that would be in the classroom, and 00:57 those students would also be modeled as objects, and each of the desk in the 01:01 classrooms could be an object and so on. 01:02 Now, in the real world, a classroom can contain many desks and have many 01:06 students, who are sitting in a certain order, and we can move students around 01:10 between the desks, have certain students who are maybe absent on a certain day. 01:13 All those kind of complex behaviors we can use objects to talk about. 01:17 It's really analogous to what we are used to in the real world, which makes it nice and easy. 01:21 But objects can also be abstract. 01:24 We could have an object for the communication that occurs between students. 01:27 We could treat their conversation as if it was a physical thing. 01:30 So it's not always just going to be an actual physical object that we are 01:34 thinking of when we are modeling things in Ruby. 01:36 Now, in programming terms an object is actually an instance of a class. 01:40 We will talk a lot more about instances and classes in Ruby a little later. 01:44 But using our example, each unique student would be one example of the more 01:48 general classification student. 01:51 In programming we would say that each student is an object or an instance of the class student. 01:56 They are all unique but they have something in common too. 01:59 Now that we understand that everything in Ruby is an object, let's take a 02:02 look at these basic object types that exist in Ruby and begin learning to program with them. Collapse this transcript
Variables
 00:00 In this movie, we're going to talk about variables in Ruby and I'm sure that 00:03 you're going to think that I've already lied to you because I told you that 00:06 everything in Ruby is an object. 00:08 But here I am. I'm going to talk about variables and variables are not objects. 00:12 They are a rare exception. 00:13 It's kind of strange that we're going to start out talking about it, but we're 00:16 going to use variables a lot, so we need to go ahead and cover it. 00:18 Variables are just part of the Ruby language. 00:20 Variables are going to be used to keep track of our objects and to give us an easy 00:24 way to talk about those objects and reference them while we are programming. 00:27 We can treat variables just like objects because once a verbal is assigned an 00:31 object as a reference, it acts just like that object. 00:35 So, a variable always either be undefined or will act like an object. 00:39 So, variables will seem like objects even though they're not. 00:42 Variables work pretty much like the variables that you had back in algebra class, 00:45 except that we need to assign a value to them before we can begin using them. 00:50 Let's take a look. 00:50 I'm going to open up an IRB session and let's go ahead and use our first 00:55 variable. We will just call it x, just like we had in algebra. 00:58 But like I said, we want to assign a value to it. 01:00 Let's try x+2, just to see what that gives us. 01:02 x is undefined, it doesn't know what to do with it. 01:05 We need to assign a value for it. 01:06 So x=1. Now we can use our up arrow and go back to x+2 and now x has a value. 01:13 So that's how variables are going to work. 01:14 It just stores the object 1. 1 is an object. 01:18 We'll talk little more about that in the next movie. 01:20 But for now, just notice how x is a variable that is pointing to an object. 01:26 Now, 2 is also an object, and 3 that gets returned by x+2? 01:31 That's also an object. 01:33 In fact remember when we said puts x+2 that nil that we got back is also an object. 01:39 So, really and truly, everything is going to be an object in Ruby except for 01:43 these variables which are just used to allow us to point to objects. 01:47 Now, all programming language have a variable naming convention. 01:51 A lot of variables have a start with the dollar sign in a lot of languages. 01:54 In Ruby that's not true. 01:55 All we have to do is have a all lower case, underscored name. 01:59 So first variables equals 3, okay. 02:04 That's a well named variable in Ruby. 02:07 Now, we don't want to put mixed case. All right. 02:09 You don't want to do something like this, like you do in a lot of languages. 02:12 We don't want to put dashes in there. 02:14 It needs to be an underscore and typically you would not run words together like that. 02:19 We would go ahead and make it more readable like the English language by just 02:22 putting an underscore in there, and that way it's nice and legible. 02:25 We can quickly read our code and know what it's doing. 02:29 So in the same way since Ruby is readable, we want to try for readability too. 02:33 For example, we don't want to do aw_counter = 100. We want it to say 02:38 articles_written = 100. 02:41 So it's very clear right away what we're talking about. We don't have to sort of 02:46 decrypt the name of our variable. 02:48 So, give your variables good common sense names. 02:51 Now lot of times in these tutorials I'm going to be trying to show you something 02:54 quickly, so I'm going to use something like x or maybe I might just say var=1. 02:59 That's fine because we're just doing this for a demonstration purpose. 03:02 If I were writing an actual program, I would want something that gave me a 03:06 little bit more of a clue as to what was happening in the program. 03:09 Now, as I said, variables are references that can be assigned. 03:12 So for example, a=100 and then we could have b=a. Now, if we ask for the result 03:19 of b, which we can do just by typing a variable on a line, it will return the 03:23 value that variable, the object that it points to. 03:26 It's equal to 100 also. 03:27 All right, it's not equal to a, because a is just a pointer to an object. 03:31 So, b takes on that same reference and in fact if we do a = 50 now and we asked 03:37 for b again, be is still pointing at 100. 03:41 If you programmed in other languages before, this probably makes a lot of sense to you. 03:44 There is one additional aspect of variable naming that I want to touch on here, 03:48 even though we're going into a lot greater depth about it later on and that is the 03:52 variable scope indicators. 03:54 In addition to just having a lower case underscored word be the name of our 03:58 variable, we can also put some additional characters in front of it that will 04:02 let Ruby know what the scope or the variable ought to be. 04:05 In other words where it should be available? 04:07 Scope determines whether or not we have access to these variables from inside 04:11 classes, methods, and other coded structures and we'll talk more about it once 04:15 we introduce those structures, but it's kind of hard to show you examples when 04:18 right now we only have our global scope to look at. 04:21 So what we've been using so far are local variables, ones wherewe have nothing in 04:24 front of it, and all you really need to know for now is that variable names can 04:29 began with the $sign or the @ the front of it in order to give it different scopes. 04:33 So,$ sign in front of it would give it global scope. 04:36 Putting two @ signs in front of it makes it a class variable. 04:39 Putting one @ sign in front of it, it becomes an instance variable and we'll be 04:42 working with these later on. 04:44 For now just know that these are all legitimate ways that you can name 04:47 your variable in Ruby. Collapse this transcript
Integers
 00:00 In this movie we are going to take a look at the Ruby object type integers. 00:03 Now, integers are just simply numbers, and numbers are mostly commonsense. 00:08 But in Ruby, numbers are actually divided into two major categories. 00:12 We have integers and then we have floating-point numbers, which are called 00:15 floats for short, and they are better known outside of programming as 00:18 being decimal numbers. 00:20 Ruby is going to separate them out into two categories. 00:23 Let's take a look at integers now. 00:25 I am going to open up irb. 00:28 We were working with integers before when we had 1+1 or we had x=2. 00:33 Those are integers, and we are doing assignment with integers. 00:35 It's just a basic number. 00:36 That's what an integer is. 00:38 We saw that we can do other operations. 00:40 For example, we could have 4/2, we could have 4*2, we can have 4-2, 00:47 those will all work for us. 00:48 There's also an exponential operator, two asterisks together, and the result is 16. 00:54 That's four to the second power. 00:56 We also saw how we could do assignment, x=4. 01:00 We can do an operator with assignment, plus equals 2, and notice what 01:05 that gives us back. 01:06 It gives us the result of the operation, 4+2. which is 6, but it also changed x at the same time. 01:13 So that now x is going to be equal to the result of it. 01:17 So it both incremented it and made the assignment. 01:20 It's the same thing as if we had typed x=x+2. It does the exact same thing. 01:26 It's just a shorthand to do it this way. 01:29 We can do the same kind of assignment operator with all of these other things too, 01:32 the division, multiplication, minus, all of those work the exact same way. 01:36 Just put them in front of the equal sign and it will both perform the operation 01:40 and do the assignment at the same time. 01:42 It's kind of a nice feature. 01:43 We can also use parentheses in Ruby, (1+2) *3. 01:49 You see that it comes back with 9. 01:51 So integers work pretty much the way we would expect them to work. 01:54 Now, let's take a look at something else. 01:56 We said that integers are objects. 01:57 So let's do .class, and that will tell us what class it belongs to. 02:03 Before I explain this, let's try another one, 123456789123456789.class. 02:11 Now, notice that when I did one of them, its class was Fixnum and the other one 02:15 was Bignum. Neither one was class integer, which is what we might have expected. 02:19 It's because integers actually belong to one of two subclasses; they are either 02:23 put into Fixnum or Bignum, both are subclasses of integer. 02:28 The difference is only in the way that Ruby stores these values in memory. 02:31 It uses more memory to store these bigger numbers, so it's going to reserve a 02:36 bigger amount of space, but to keep it from taking up too much space all the time, 02:40 it also has the smaller more efficient structure called Fixnum that it can 02:44 use for smaller numbers. 02:46 So we have Fixnum and Bignum and Ruby will switch back and forth as needed and 02:50 so you will never need to worry about the difference. Just think of both of them 02:54 as being an integer. 02:55 You can try this out with a couple of things. 02:57 If you want to multiply together two Fixnums, you can see how long it takes for 03:01 you to get to be a Bignum. 03:02 So for example, let's say x=1234* 1234*1234, and than x.class, Bignum. 03:14 So you see it just switched back and forth between this and there wasn't a problem. 03:17 Integers can also be negative, so -200 is a perfectly valid integer. 03:22 200.abs is the absolute value. 03:25 That will return the absolute value by applying the absolute value method, which 03:29 we used in the dot notation we have seen before. 03:31 We can also have 200.next. 03:33 That's another nice method that we can apply to integers and it will return the 03:37 next integer that comes after it. 03:39 It's the same as if we would have said 200+1. 03:42 That's really all there is to integers. 03:43 Just don't be thrown by the Fixnum, Bignum distinction. 03:46 If you look at the class, they are both considered integers. 03:49 But if you keep that in mind, I think integers will be a breeze. 03:51 But in order to really understand the way that Ruby handles numbers, we need to 03:56 not just look at the integer class, we also need to look at the float class, and 03:59 we will do that in the next movie. Collapse this transcript
Floats
Strings
Arrays
Array methods
Hashes
Symbols
 00:00 In this movie we are going to talk about the Ruby data type for symbols. 00:03 And symbols are one of the most misunderstood types by most beginning Ruby programmers. 00:07 So I want to make sure that we have an understanding of it. 00:09 On one hand symbols are going to seem a lot like strings, but they are not. 00:13 And sometimes they are going to seem a lot like variables, but they are not. 00:16 What asymbol is, is a label that's going to be used to identify a piece of data. 00:21 Now a string can also be a label too. 00:23 We just saw that when we were working with hashes, right? 00:25 So, why do we need symbols? 00:26 Well the reason is because a symbol is going to be stored in memory one time. 00:31 Whereas a string is going to get stored each time. Let's take a look. 00:35 I am going to open up a irb session. 00:38 And the way that we type a symbol is that we simply use the colon and than the name. 00:43 And the name works the same way the variable names would. 00:46 So we can have for example test. 00:48 That is a symbol, or this_test. 00:52 So, that's a symbol that we have just declared. 00:54 It's an simple object. 00:56 Now to show you how the symbol test is different from the string test. 01:01 Let's say this_test and then let's ask for it's object ID. 01:05 This is a very rarely used Ruby function. 01:08 But we can ask for the object ID of something, and you see it gives me back a number. 01:12 And that's why it's not used very often. 01:13 It's a little bit meaningless to have this number. 01:15 But notice that when I do test. object_id, it has a different object ID. 01:22 Well, let's do the test. object_id again for a string. 01:26 I have a different one. 01:27 Third time, three different ones. 01:28 And it's because it created a new string called test, right? 01:31 It didn't reuse this old one. 01:33 It's a whole new object. 01:34 This is one object, and this is the second object. 01:37 Now, let's go up and let's do the symbol again. 01:40 Notice that this time it's the same one, because the symbol is stored one time 01:44 in memory, and then it goes back and reuses that same symbol, because it's just a label. 01:49 Now for that reason symbols are going to work really well inside hashes. 01:53 So, let's say hash = first_name Kevin and last_name Skoglund. 02:05 Now that's a great way to label our people, because then if we have another 02:09 person, who will use that same label, first name, again. 02:12 We don't have to create a new bit of memory to store it in. 02:15 So we conserve the amount of memory our computer has to use to run our program. 02:20 Where if we use a string for the first time, like we did back in the hash movie, 02:23 every time we created a new person, it would create a new Ruby object and 02:28 store that in memory. 02:29 So when we really want something to be a label, which is definitely what we want 02:33 with hash, we are going to want to use that symbol most times. 02:37 Unless we really need to use the string, go ahead and use a symbol. 02:40 Now there is an important point though. 02:41 Watch if I now ask for hash and ask it to give me back it's first name. 02:46 It goes back and says no, sorry, don't find that label. 02:50 That's because it doesn't have anything that has a string object called 02:55 first name as a key. 02:57 What it has is something with a symbol. 03:01 So it can tell the difference between them. 03:04 And some of you may have said, wait a minute. 03:06 Let's say that I have four or five people and I have four or five first names. 03:11 Each one of those has a different object as the label. 03:14 And it's a different object every time. 03:16 How does hash then still return the same thing to me? 03:19 This is because when hashes have a string as the key, it does say well, it's good enough. 03:25 It doesn't have to be the exact same object. 03:27 It has to have the same value. 03:29 It has to be of the same class and have the same value. 03:31 Not necessarily be the exact same object. 03:34 The other thing that I don't want you to be confused about is that symbols are not variables. 03:39 So if we have something like test = 1, it comes back and says nope, sorry, I don't 03:44 know how to do that. That's a label. 03:46 So up here hash is the variable. 03:49 It's a hash that has the label first name. 03:52 So you may feel like this is a variable, but it's actually hash that's the variable. 03:57 First name is just the label that's in the hash. 04:00 So the rule of thumb is if what we are talking about really is a word and if 04:04 the sequence of a character is important, or if it is going to be for output, 04:08 then we are going to want to use a string. 04:09 But if it's a label that's being used to identify a piece of data, or as we will 04:15 see later on, to pass a message around between different parts of our program, 04:19 we will want to use a symbol. Collapse this transcript
Booleans
Ranges
 00:00 In this movie we are going to talk about the Ruby object type for ranges. 00:04 Now a range is going to typically be a range of numbers. 00:07 Right, so let's say numbers from 1-10. 00:10 Well, we could have an array that would contain all of those numbers or we 00:13 could simply have a range which will say well, here is the starting point and 00:16 here is the end point. It's from 1-10. 00:17 Specially if we have something like 1- 1000, it makes a lot more sense to have 00:21 something that just tells us the start and end point instead of trying to 00:24 construct an array or something that has all 1000 numbers in it. 00:28 We can use a range instead. 00:30 And there is two kinds of ranges. 00:31 There is the inclusive range, and the exclusive range. 00:35 And the notation is right there. 00:36 That's how it works. 00:37 It's just the first number and then either 2 or 3 dots depending on which 00:41 one you want to use. 00:42 Inclusive would be the numbers 1, 2, 3, 4, 5, 6, 7, 8, 9, 10. 00:47 Exclusive range would be 1, 2, 3, 4, 5, 6, 7, 8, 9. 00:52 It would exclude the last value. 00:54 So it would be one up to but not including 10. 00:57 Now when you're programming in Ruby, you'll find that the first one, 01:00 the inclusive range, is much more common. Why? 01:03 Because you can see both of the values that are included so that makes it nice 01:07 and clear what's included in it. 01:09 And it's one less character to type. 01:11 So that saves us a little bit of typing as well. 01:13 But if you need to use the exclusive range, it's there and you can use it. 01:17 You could also just do 1..9, which I would say is probably more common and what 01:21 I would recommend to you. 01:22 Let's try them out, see how they work. 01:23 So I am going to open up irb and let's start by just trying out that simple 01:29 inclusive range, 1-10. 01:30 You see that it returned 1..10 to me. 01:33 It didn't return all the numbers. 01:35 It didn't expand it. 01:36 It kept it as a range. 01:37 That's actually a range object. 01:39 x =1..10 x.class is a range. 01:45 Now I want to show you something here. 1..10.class is gong to give me some problems. 01:50 I set the value x equal to that and then ask for its class, 01:53 that's not a problem. The problem is with the dots. 01:56 The dots get a little bit confusing to Ruby and so it says, "oops, I don't 01:59 know what you mean." 02:00 If you put simply parentheses around it then it clears it all up. 02:05 And then it knows exactly what you mean. 02:06 So just be careful with that, with ranges. 02:08 If you try to apply one of these methods directly to the range, it will give you some problems. 02:13 If you assign it to a value or if you put it in those parentheses then 02:17 it will be all cleared up. 02:19 We have a couple of methods that we can use here. We have x.begin that will 02:24 return the beginning of the range, and end that will return the end of the range. 02:30 x.first is another one, and x.last, and those essentially do the same thing. 02:37 Now let's try another one. 02:40 Let's say y = 1...10, another range but this one is now exclusive range. 02:45 Let's ask it for its y.begin and y.end. 02:50 Notice that it still reports the same beginning and end, even though it is 02:56 exclusive and inclusive. 02:57 So be careful with using begin and end on your ranges because it may not be what 03:02 you expect it to return. 03:04 To show you that they are actually different though I can say, for 03:06 example, x.include?(1). Yes, it does. 03:12 Does it include 10? Yes, it does. 03:15 But if I now try that on y. Does y include 10? No, it does not. 03:20 Now what if we actually did want to expand this? 03:24 What if we wanted to expand it out so we had all of those numbers 1 through 10 03:28 in an array that we could work with. 03:29 Well, there is a nice way that we can do that, if we use the array notation, 03:34 which is square brackets. 03:35 We will put our x inside of it and in front of the x we will put the asterisk. 03:40 We call that the splat operator. 03:42 So the splat operator in front of it and we will just say z is going to be equal to that. 03:48 And there is what it returns. 03:49 It breaks it all out. 03:50 So x is still equal to just the range but z is now equal to all of those 03:54 numbers. They have just spread it out and expanded it into all of its values. 03:59 Now numbers are not the only thing that we could have as ranges. 04:01 That's the most common thing we will use. 04:02 But let's say that we have 'a'..'m'. 04:10 So a to m. Actually let me do that again and we will just set alpha equal to it, 04:16 alpha.include. Does it include g? 04:21 Yes, it does include g because g is in that range between those letters. 04:25 And we can do the same thing if we have that splat operator, alpha, here we go. 04:30 And that returns all the letters a through m. 04:34 So it's a good shorthand to be able to use especially if we're working with 04:37 numbers like 1-5000. 04:38 but that's really all there is to using ranges. You don't use them that much but 04:43 when you do use them, they are really a critical tool to help you out. Collapse this transcript
Constants
3. Control Structures
Conditionals: if, else, elsif
 00:00 In this chapter, we are going to be talking about the control structures inside Ruby. 00:04 In the previous chapter, we were talking about a lot of the basic object types, 00:07 but we really don't get that far if we are just assigning different 00:10 objects to variables. 00:11 Control structures are really going to provide the action that happens inside 00:15 of our Ruby programs. 00:17 It's what's going to decide what takes place under certain circumstances 00:21 and the first way that we are going to determine those circumstances is 00:24 using conditionals. 00:25 So this is, if something is true, we are going to do one thing. 00:29 If it's not true, do something else. 00:31 Right, that's the basic thing. 00:32 We will run one set of code under one condition, another under 00:35 different conditions. 00:36 That's why they are called conditionals. 00:38 To do that, we are going to use the syntax of conditionals, which is going to be 00:42 if and then a boolean expression and then end at the end and then in between 00:47 will be the code that we want to take place if that Boolean value evaluates to true. 00:51 So if x is equal to 1 then do this bit of code. 00:54 Otherwise don't bother, just skip right past it. 00:57 That's how conditionals will work. 00:59 Now, we could write two conditionals. 01:01 We could have if x is equal to 1, then do this. 01:04 If x is not equal to 1 then do that. 01:07 But because that happens so common we have if and else that we can use. 01:12 So if x is equal to 1, do something, else do the alternative. 01:17 Right, it allows us just to branch it in two simple directions based on the 01:20 true/false value of that first conditional. 01:22 But what if we want in branch in more than one direction. 01:25 We don't want to just have a yes or no action. 01:27 We want to have more complicated choices. 01:30 Well, we have another option for that which is elsif and that's not a typo. 01:34 It is e-l-s-i-f and that allows us to do another test before we get to else. 01:39 So if x is equal to 1, do something, elsif and let's say x equals to 2, then do something else. 01:47 But if neither of those is true, then do this default else action. 01:51 That will be the fallback choice 01:52 So be careful that you spell elsif correctly. 01:54 That does trip up a lot of beginners, especially because other programming 01:57 languages do it differently. 01:59 So an actual conditional statement might look something like this: if x < 10 02:03 puts "Below 10"elsif x > 20 puts "Over 20"else 02:09 puts "10-20" and then end. 02:12 Now notice that we don't have any parenthesis or curly braces or anything like that, 02:17 letting it know where each of these sections is. 02:19 Different languages handle that differently and a lot of them require you to put 02:22 either parenthesis or curly braces. Ruby does not. 02:25 It's one of the nice things we can just leave them out. The Ruby interpreter 02:28 knows that when we start a new line, we are ready to start the code in that 02:32 if statement and it will just keep parsing away until it gets to either 02:36 elsif, else, or end. 02:38 And when it gets to one of those, it knows that those are magic keywords 02:41 that ought to tell it to stop that block of code and now do another part of the conditional. 02:46 Now the tabs that are in there, where I have it intended for each put statement, 02:50 do not signify anything to Ruby. 02:52 It doesn't use those at all. 02:53 They are only there for our readability, so that we easily see what's being 02:57 executed between each of these parts of the conditional. Let's try one out. 03:01 So we are just going to irb and instead of using just x, let's say name = 03:07 "Kevin". That will be my assignment. 03:08 So now the variable name, so we say if name == to "Bob" and again the 03:17 indentation doesn't make a difference, but we go ahead and do it, puts and let's 03:21 say "Found Bob" else puts "Not Bob...". There we go. 03:29 So it was Not Bob. 03:31 Now if we did the same thing, let's hit the Up arrow and ask it, if name equals 03:36 Kevin then let's put "Found Kevin!" else puts "Not Kevin...". 03:47 And let's type the end. 03:50 So there it is, Found Kevin! 03:51 So you see it does match. 03:53 And elsif works the same way. 03:55 You can say if name equals to Bob again, puts "Found him" and then elsif without 04:05 the e, name equals Mary puts "Found her", else puts "Not Bob or Mary". 04:18 Now I know that this seems like a lot of typing,. We have never used the Up arrows 04:21 to back and reenter old lines. 04:23 It a little bit cumbersome to do it this way. 04:25 You can put all of this into a separate text file and then just copy and paste 04:30 into irb each time and then when you paste in, it will execute the code. 04:34 So that's a nice way to do it. 04:35 It save yourselves typing if you're trying to work out a particular problem. 04:39 Now notice that throughout this I did not use parenthesis or curly braces to 04:43 tell Ruby where the blocks of code are. 04:46 Other languages do that. 04:47 They require you to use curly braces and parenthesis in different places. 04:51 You don't have to do that here. 04:52 The Ruby interpreter knows that when you go to a new line after an if 04:55 statement, that we are now in that if block of code and it will keep going until 04:59 it gets to elsif, else or end. 05:01 That's a conditional keyword, so now I know that I'm ready to do whatever that 05:05 next conditional instruction is. 05:07 So it's nice that we don't have to type any of those or make sure that we've 05:10 closed each and everyone that we've opened and so on. 05:12 Now Ruby also has a very nice feature where we can put conditionals in line. 05:18 What I mean by that is I can say puts "This is Kevin" if name == "Kevin", all right. 05:27 I have the statement I want and then a space if the Boolean. 05:32 So we can put it at the end and it knows because it was preceded by a statement, 05:36 it's the statement that is only conditionally executed and that has the same effect. 05:40 Now, let's just try if this name is equal to something it's not and now you see 05:45 that it doesn't do the put statement. 05:47 So that's a nice handy thing you have to do, so we don't always have to write 05:50 three lines of code, if name = "Kevin" puts "This is Kevin" end, right. 05:55 That's different lines. 05:57 Instead, we can just put it all in one line nice and quick. 06:00 There is a few more things I want to show you about conditionals. 06:02 We will do that in the next. Collapse this transcript
Conditionals: unless, case
 00:00 We have gone over all the basics that you will need in order to work with 00:03 Conditionals, if, else and else if will do everything that you need. 00:07 However, there are a couple of convenience conditionals that we can also use 00:11 that work kind of like a shorthand. 00:12 We are going to look at four of them. 00:13 There is unless, case, the ternary operator and or/or-equals. 00:19 So let's start out by looking at unless. 00:21 It looks like this, exactly like the if statement, but instead of if it's unless. 00:25 So unless a Boolean is true, which means the same as if not true, it's the reverse. 00:32 So instead of being if not x > 3, it's unless x > 3. 00:39 So unless it's true, do this block of code. 00:42 Now unless can sometimes tie you in logical knots trying to work through exactly 00:46 when it's going to be used. 00:47 So only use it if it makes a lot of sense. 00:50 If it feels right and it's convenient use unless, but don't let it drive you crazy. 00:54 You can always just switch back to if and then the exclamation point for not and 00:59 it's the same thing. 01:00 Now you can use else and elseif still with unless, but there is no such thing as unlessif. 01:05 That doesn't exist. 01:07 Unless often works best in really simple cases. 01:09 When you start getting really complex, if is the better way to go. 01:12 Now we saw how we could have multi- layered conditionals right, if something 01:17 is true elseif something else is true, elseif something else is true and we could keep going. 01:21 When we start to have a lot of these the thing that ends up being better to use 01:24 as a shorthand is the case operator. 01:27 So we basically say let's look at a set of cases when something is true, do this. 01:32 When something else is true, do that. 01:34 It's the exact same thing as the if statements. We have just rewritten it as the 01:39 case operator instead. 01:40 Now this might not seem like it's any shorter than what we had before. 01:43 It's actually more lines of code to do the same thing, where it really starts to 01:48 pay dividends though is when there is a real parallel sort of assignment where 01:51 we can say okay, when x = 1 do this, when x = 2 do that, when x = 3 do that and 01:57 so on, and it's just right down the line in each of these specific cases. 02:01 Now because that's when case works best is when we are comparing a test value 02:07 against a given value. 02:09 There is also another way to use case, which I think is probably even more often 02:13 used, which is to put the test value right after case. 02:17 So case and then x, let's say. When 1, do a block of code. 02:23 When 2, do a block of code. 02:25 So this really is more of a shorthand and really saves you from having to 02:28 write x == 1, x == 2 each time, we just say x when it's 1, when it's 2, when it's 3 and so on. 02:36 And we can have the else statement there at the end or we could leave that off, if we want. 02:39 Now in some other programming language this kind of structure is called switch. 02:43 So if you've been using that, you will want to switch over to start using case 02:47 instead, because that's what it's going to be in Ruby. 02:49 The next one I want to show you is the Ternary operator and what this is it's a 02:53 shortcut for simple if and else statement. 02:56 So if something is true, do one thing, else do something else. 03:01 Well, in the case when those are really short, especially the blocks of code, 03:04 it's just one very quick little bit of code that we want to write. 03:07 Writing those five lines takes up a lot of space and we can do a lot faster if 03:12 we just do it with the ternary operator, Boolean space question mark. 03:17 Make sure you have the space there and then a question mark, and then whatever 03:20 little tiny bit of code we want to run. 03:22 You don't want to run a whole lot of stuff in here. 03:24 That's not what this is for. 03:25 This is only when we are just going to do something real quick and we want a 03:28 very quick conditional. 03:30 If that's not true, the else is the colon. 03:32 So space colon space and then the second bit of code we want to do. 03:36 So as an example, we can have puts x == 1 and then it will either return 1 or 03:43 not 1 and that's what we will get output, depending on the Boolean x = 1. 03:46 Now this is not highly readable code like a lot of Ruby is. 03:51 So you really want be careful about using it and use it only in those cases when 03:55 we are just going to do something very small. 03:57 But in those kind cases it can be a real lifesaver and save you a lot of typing. 04:01 The last one that I want us to look at is the or and or/or-equals operator. 04:06 Imagine that we have the simple case where we want to say if y has a 04:09 value, then set x to it, but if not, go to z, which is like a default value, a fallback. 04:16 So if you have y set it to y. If not, fall back to something else. 04:20 Well, we can use the Or operator for that and we could use it like this. x = y or z. 04:24 So if y doesn't exist, then it will be z. 04:29 Now we use that in our Booleans remember as an or operator and you will remember 04:33 that it evaluated the first part to see if it was true or not, and if not, then 04:37 it went to see whether the second part was true or not. 04:39 Well, that's essentially what this is doing. 04:41 It's checking that first one. If it doesn't exist and doesn't have a value, 04:46 then it's not true. 04:47 So it goes to the next part and it does have a value the value z, so that's 04:51 what it sets to x. So it's worked exactly like our Booleans do, but we can use it for assignment. 04:56 So this is a real nice shorthand. 04:58 It's a lot shorter than writing something like this. 05:01 The other one that comes up a lot is we want to say unless x already has a value, 05:06 set x = y. So if x has a value already, just leave it alone, don't set anything. 05:11 But if not, we want to set it to y. Y is like a default value. 05:14 Well, the notation for that is going to be x and then the or operator with the 05:19 equals after it, the same way we had plus equals, minus equals and all those kinds 05:23 of assignment operators. This is or equals. 05:26 So if x has a value, we will leave it alone, but if not then we will set x = y, 05:32 and I think that also is a lot shorter than writing something like this. 05:36 So all four of these are really just shorthand methods that allow you to do 05:40 things a little bit more efficiently. 05:42 You could use if, else and elseif to accomplish the exact same thing in every case. 05:47 Go back over it one more time to make sure you understand it. 05:50 Go into irb, try all four of them out, get used to how they work, because they 05:54 really are going to be big timesavers for you. Collapse this transcript
Loops
 00:00 In this chapter we are going to take a closer look at Ruby's code blocks. 00:03 We briefly touched on code blocks back when we working with loops and the iterators. 00:07 But let's start out by looking at what is a code block and reminding ourselves. 00:11 When we had an iterator like 5 times, we had a code block and the code block was 00:15 a block of code that we wanted to be executed each time through the loop, 00:19 each iteration and that block of code is defined between the do and the end. 00:24 So everything between do and end is our code block. In this case it just simply puts Hello. 00:28 Now do end is what we use for multiline code blocks. 00:31 That's where it really is the most benefit because we can just keep putting lots 00:34 and lots of lines in there. 00:35 It could be a hundred lines of code that would be executed five times. 00:39 Now if we have something that's so simple like puts Hello, then we saw that we 00:43 can also use the shorthand notation which is just the curly braces. 00:46 So 5.times puts Hello. 00:49 If we have something that is that short this can be much more efficient, 00:51 cleaner and easier to read. 00:53 We also saw when working with iterators that during each iteration we can have 00:57 the iterator yield the value up to a variable. 01:01 In this case a variable that is declared between those upright pipes. 01:04 So the variable i would be a block variable. 01:07 And so each item in the dataset 1 to 5 would be yielded up to that variable so 01:12 that that would be its value each time through and we would have access to that 01:15 value then while we were inside our code block. 01:18 So in our case we just puts Hello and then we used i after converting it to 01:22 string just as output. 01:23 But we could do anything with that value we wanted to and this block variable 01:27 declaration that we did is not unique just for the do end syntax. 01:31 We can also use it with that curly brace syntax just as easily. 01:34 Let's switch over to irb and try it out. 01:36 Let's open up another irb session with simple prompt so we get that out of the way 01:41 and let's start by just creating an array and our array will be 1, 2, 3, 4 01:45 and 5, nice and easy. 01:48 So we saw we can use an iterator like each and then we can have a code block and 01:52 I will use num as the variable that will be yielded up instead of i. We can name 01:56 it anything we want, puts num * 20 and then we will close our code block. 02:02 So it will iterate through the array each time the value 1 will go up to num, 02:07 then number 2 and then 3, assigning it to that block variable which we can then 02:11 use in our code block to multiply it by 20. 02:14 Now before we knew about blocks we were using local variables, right? 02:17 We were just simply typing a variable like x=1. 02:19 That's a local variable. 02:21 Local variables and our block variables are going to look very similar. 02:24 Do you remember back to when we introduced variables in Chapter 2, 02:27 remember that I gave you the naming conventions and local variables and block 02:31 variables look the same. 02:32 They don't have a $sign or an @ sign in front to distinguish them. 02:36 But the reason there are two different kinds of variables is because they have 02:38 different variable scope. 02:40 We touched on scope briefly but you will remember I said we didn't really have a 02:43 way to talk about scope yet. 02:44 Well now we have blocks we do have a way to start discovering what variable 02:48 scope means and how it applies. 02:49 So I have now gone through my iteration. 02:51 I have declared a local variable array. I have declared a block variable num and 02:56 I have declared a local variable x. 02:58 Let's ask for the value of our block variable, puts num. 03:02 You will notice that it comes back and it says that the variable num is 03:06 undefined because it is looking for a local variable. 03:09 That block variable exists only inside the block. 03:12 So we only have access to it inside the block. 03:14 And that's what we mean by variable scope. Because we have access to it inside the block, 03:19 the block is its scope, right. 03:21 We don't have access to it outside and what it really is doing is it is using 03:24 num in the temporary sort of a way and then discarding it at the end because the 03:28 block is done, so there is no reason for it to keep it around. 03:30 Now that is variable scope at its simplest, but there's three important points 03:34 that I need to make for you about scope. 03:36 The first is that even though we don't have access to our block variables 03:39 back in our local scope, we do have access to local variables inside the block's scope. 03:45 So I am going to hit the up arrow until I go back to my iterator and I am just 03:49 going to make a small change here. 03:50 I am just going to put in a +x at the end. 03:53 So we are calling now this x=1 that we have defined in the local scope. 03:58 It doesn't exists in the block variables at all. 04:00 So let's go ahead and hit Return and try it and you will see that it does still 04:03 pull in the local variables into the block. 04:06 So that's the first point. 04:07 The second important point is sure to confuse some of you. 04:11 Let's try doing a puts num again. num still is undefined. I have never 04:15 defined in the local scope. 04:17 So let's try it now. 04:18 Let's say num=1 and let's try our example again. 04:22 I will just hit the up arrow and we will run it. 04:24 Now let's say puts num. 04:27 Now some of you will have gotten the result of 5, just like I did. 04:30 But I suspect that there are also some of you who have gotten the number 1 and 04:35 if you did, just bear with me for a minute and we will get to explaining why. 04:39 If you got the number 5 like I did what's happening is that Ruby is bringing in 04:44 this local value for us to use inside the block. The same way that it brought in 04:48 the local variable x, it brought in num. 04:50 The fact that we declared it as a block variable Ruby didn't worry about. Ruby said, Oh num! 04:55 Well I know there is already a num. 04:57 I am going to bring that variable into scope. 05:00 It is almost as if we had not declared it as a block at all. 05:03 It is just making use of it the same way we can use x. That's what happened for me. 05:07 Now for those of you who didn't get that result, who got the number 1 back, 05:11 which means that it did not change its value, it was still num=1. 05:15 That's because you are using a newer version of Ruby. 05:18 With Ruby version 1.9, I think actually starting with 1.9.1, it behaves 05:23 differently and if we declare this value here, it keeps it from bringing it into scope. 05:29 It says no I really mean num ought to be a block variable. 05:34 The fact that we have used x here still works just the same. It is bringing in 05:37 the local scope variable. 05:39 But the fact that we have declared it as a block variable makes it only be a 05:43 block variable and that makes a little more sense. 05:45 So I think that's going to be an improvement for the future, but it is a big 05:48 change and it is a big change that's happening over the next couple of months. 05:51 So this is coming soon. 05:52 So you want to be aware of both cases so that if you are working with a version 05:56 of Ruby that's before 1.9 that it will have the old behavior, if you have a 06:01 version that's newer than 1.9 it will have the new behavior. 06:04 And this difference in Ruby versions is the third point that I wanted to make 06:07 you about scope inside blocks. 06:09 Now if all of that was confusing to you and you just want to avoid the issue 06:12 altogether, there is a very easy way to do it, which is just to name them 06:15 different things, to name what's your local variable and your block variables 06:19 different things and then you won't have that kind of conflict. 06:22 And one good way to do that is to give your local variables long 06:24 descriptive names like count=0, 06:28 then inside your array use variables like num, i, x, a, b, c those kinds 06:35 of things that are really short and sort of indicate that it is temporary in 06:39 nature. We are just going to be using it inside the block. 06:41 Now there are times when you are going to want to use things that are 06:43 descriptive inside the block because you are going to be using it a lot. 06:46 So it might be better to use something instead of just as a or b. But a lot of 06:49 times having a real difference between the local variable names and the block 06:53 variable names can really help make a good distinction for you. 06:56 Now that you understand the basis of code blocks and we have gone over how 06:59 block variable scope works, we are ready to take a look at some of the common 07:03 Ruby methods that are going to make use of blocks, specifically find, merge, 07:08 collect, sort and inject. 07:11 Now there are by no means all of the Ruby methods that make use of blocks. There are a lot of them. 07:15 But these of the most common ones and the ones that you will get the most use out of. 07:18 And I think they also provide a good illustration of the different ways that 07:22 blocks can come into play. Collapse this transcript Find  00:00 In this movie we are going to see how we can use code blocks effectively with 00:03 various Ruby Find methods that allow us to find objects inside a dataset. 00:08 There are five main Find methods we will be looking at: 00:11 find, which also is a synonym of detect, find all, which also has a synonym of 00:16 select and then any?, all? and delete_if. 00:22 Now delete_if may not technically fall under the umbrella of Find, but what 00:25 we are really doing is finding something and then deleting it, all in one step. 00:29 So I am going to go ahead and include it here. 00:31 The thing that all of these methods will have in common is that they are going 00:33 to take a code block and that code block for each and every one of them is going 00:37 to be a Boolean expression. 00:40 So code block does not necessarily have to be made up of sort of action 00:43 statements, things like puts or setting variables and all sorts of processing. 00:47 It can be a Boolean just as easily. 00:49 I think it will make a lot of sense once we try it. 00:51 Let's start by open up irb with a simple prompt and let's start out by using a range, 00:57 remember we talked about ranges before. 00:59 A range 1..10 inclusive and let's apply the find method to it. 01:04 Now we are going to need to have a code block after that. 01:05 We know a code block is going to be the curly braces for a simple one line code block 01:09 and I am also going to put the upright pipes and declare a block variable of i. 01:13 So each number in the range will get put into i as it iterates through the set. 01:19 Now it's not actually an iterator but it does work according to the same principles. 01:23 It is going to take each value and present it up to that block variable. 01:26 So now we need to write a Boolean expression here. 01:29 We could just write something simple like i =5 and then when we return it, 01:35 you'll see that it comes back and it says ah, 5. 01:38 It went through the set and it went until it found the item where i was equal to 5. 01:42 Now that's kind of an obvious thing but we could have been checking for 01:45 something much more complex and we might not have been using just numbers in a range. 01:48 We could have be using something like an array of complex objects, where 01:52 we were checking various things as we went through to see if they match. 01:55 But let's give another example which I think will illustrate it a little better. 01:58 Let's erase this and instead let's use the modulo operator. 02:02 Now if you are not familiar with this from other programming languages-- 02:04 we haven't talked it about yet. 02:05 What it does is it divides i by 3 and returns the remainder, whatever is left over. 02:11 And so if it divides evenly the remainder should be 0. 02:14 So what we are asking for is what numbers are evenly divisible by 3. 02:19 So let's run that and you will see that it comes back with the first one, which is three. 02:24 Now there are other ones that it could have returned but it doesn't do that. 02:27 It just returns the first one and that's why find has this synonym of being detect. 02:34 So start looking at this set and detect the first one, so that works the exact same way. 02:38 Let's try detect again, but instead this time let's find out what numbers when 02:42 multiplied by 3 are still between 1 and 10. 02:46 So I will have another range, 1 to 10, and we can use include, remember we worked 02:51 with that earlier, is include in that set i*3. 02:54 So if we multiply 5 by 3 is it still included in this set? 02:58 It came back and reported 1, because 1 times 3 is still in that set. 03:03 It found the first one where that applied. 03:06 Now what if we don't want to just find the first one? What if we want to find all? 03:09 So find and detect will always either return a single object or they will return 03:14 nil if nothing is there. 03:15 We can try that real fast. 03:17 Let's say detect the number 20 and there and it comes back and says no. 03:20 So it is always either an object or nil. 03:23 Now what if we want to find all? Let's go up here. 03:26 Let's try our modulo again and this time let's just say find all for everything 03:32 that's divisible by 3. 03:32 You will that it comes back this time with an array, an array of the numbers 03:37 which are divisible by 3. 03:38 And if there had been none there, let's say numbers which are divisible by 30 03:43 and there is none of them so it gives us back an empty array. 03:46 So as I mention when we are listing them out, find all also has a synonym which is select. 03:51 Let's try a select. 03:53 Let's try it with this one here. 03:54 So let's find everything where this is true and that's going to be select. 03:59 So now 1, 2 and 3, all can be multiplied by 3 and still will not exceed the number 10. 04:06 The number 4 when multiplied by 3 will be 12 so it I outside that range. 04:09 We can also use Any and All in a very similar way. 04:12 Let me just go back up here where we have the divisible by 3 and instead of 04:16 using the Find All method we can use any? 04:19 So are there any in this set where this is true? 04:22 Don't return the value to us. 04:24 Return a Boolean, True or False, right. 04:27 So we are using a Boolean inside the block and we are going to return a Boolean 04:31 as well and it returns the Boolean True. 04:33 So it is true. There are numbers divisible evenly by 3 in our set. 04:38 We can do the same thing with all and of course that will tell us well, are all 04:42 of them meeting this requirement? 04:44 And the answer is no, they don't. 04:46 Now delete_if is not going to work with a range. Let's try it real quick. 04:49 Let's delete_if, if it is evenly divisible, delete_if, there we go. 04:55 So it comes back and it says, "sorry, a range you can't apply delete_if to." 04:59 But if you remember, know how to turn our range into an array, you just put 05:04 the square brackets around and instead and the asterisk, the splat operator, to expand it. 05:10 All right so and now look what it gave us back. 05:13 It is 1, 2, 4, 5, 7, 8 and 10. 05:17 It left out 3, 6 and 9. 05:18 So it is the exact same thing as doing a find all for 3, 6 and 9, but then it 05:24 also does the extra step of removing them from the original array. 05:28 So it is not technically a find, but because it works on the same principles 05:31 I think it is useful to see it here. 05:32 Now just because I am using ranges and arrays of very simple numbers to 05:37 illustrate the point, doesn't mean you can't use more complex things. 05:40 You can detect whether a string matches or not; you can see whether a hash has a 05:45 certain value in it. 05:46 Really any Boolean or almost any set of objects that you have where you can go 05:50 through the set looking for something, you can apply these same methods to it. 05:54 So just remember when you are using them that Find and Detect will return an 05:58 object or they will return nil. 06:00 Find All and Select will return an array of things that it finds that match. 06:04 Any will be a Boolean. Either there are some in there or they are not. 06:08 All would be like any except they all must meet that requirement of our Boolean test 06:13 and they will return a Boolean True or False and delete_if will return an 06:17 array with the items removed. Collapse this transcript Merge  00:00 In this movie, we are going to take a look at another Ruby method that's going 00:03 to make good use of code blocks, and that's Merge. 00:06 And Merge is going to be used for merging hashes together, and it's for hashes only. 00:10 This is not for arrays, or for ranges or strings or anything else. 00:14 This is just a method that we are going to use with hashes. 00:16 So let's open up irb with a simple prompt, and let's start out by just declaring a hash. 00:22 I am just going to call it h1, real simple. h1 is going to be equal to a, which 00:28 will point to the number 111, and let's have b, which will point to the number 00:34 222 and that's our first hash. 00:37 Now, let's make a second hash, h2, and in that hash, we are going to have b as a 00:42 key for the value 333. 00:46 And let's have c as a value for 444. 00:48 Now, you can put in just about anything you want here. 00:51 The important thing about the two is that you'll notice that b is a key in both 00:55 of the hashes, and that's because I want to show you what happens when we merge 00:58 them together in that case. 01:00 So all we need to do to merge the two hashes together is h1.merge(h2) and it 01:07 returns a merge hash. 01:09 It takes the two and smashes them together. 01:11 And if they are conflicting keys, the values that are in h2 are the values that 01:16 are going to win out. 01:17 That's what we are going to have. 01:17 So you see we end up with 333, not 222. 01:20 If we'd merged it the other way around, you'll see that we would have ended up with 222. 01:24 All I did was switch which one was being merged into the other one. 01:28 So this is just a real simple merge between hashes. 01:31 It doesn't make use of a code block and that's what I really want to illustrate here. 01:35 Now, this is just a very simple Ruby merge. 01:38 We are not using a code block yet, and that's what we really want to see is how 01:41 to make use of a code block. 01:42 Well, we can also supply code block optionally if we want. 01:46 So I am just going to hit the Up Arrow to get back to this h1.merge(h2). 01:50 Now, the block in the case of merge is only going to be called when there's a 01:54 conflict between two keys. 01:56 So when it's merging together, it has no problems merging the A and the C keys together. 02:01 You can easily just put those in there and there is no conflict. 02:03 But what we are going to use is this block, it's going to be for 02:05 conflict resolution. 02:07 So we'll supply the block, and we are going to need to declare some values here. 02:11 Now, we can call these block variables anything we want, but there are three 02:15 values that are going to be yielded up to us by Merge. 02:18 So it makes a lot of sense to name them something that indicates what their purpose is. 02:23 The first one is the key, the conflicting key. 02:25 The second one is going to be the old value, the one that's in the h1 in 02:30 our case, and the third one will be the new value, the one that's about to be overwritten. 02:35 Now if I simply put a New here as the way to resolve this, I'll hit Return and 02:42 you'll see that we get the same behavior that we got up above. 02:45 If I go back, and I change it to now be Old, you'll see that now we get 02:50 the other behavior. 02:51 It kept the old value. 02:53 The final value that we return out of the block ought to be whatever we want 02:57 the new value to be. 02:59 So the block essentially says hey, instead of me automatically deciding which 03:02 one wins out, let me pass you those variables and you decide. 03:07 And at the end, what I need you to give me back is the correct value. 03:11 Now, we can put something very complicated in here, but ultimately what we need 03:14 to come up with is the value that ought to went out between these two. 03:18 Now, if we did something like old times 2, you'll see that it did that for us. old times 5. 03:25 Essentially, all we are saying is Merge, here's what I want you to do when 03:29 you find a conflict. 03:31 So as another example let's have h1.merge do and let's go ahead and do our key, 03:39 old, and new, just so we can keep those straight, and now let's do if old is 03:45 less than new, then keep the old one, else, return the new one. 03:53 So do you see what we are doing? 03:54 We are saying when there's a conflict, pick the one that has the lower value. 03:57 I don't care which one is older or newer necessarily or which one it came from. 04:02 I want to make sure that I end up with the lowest value, and you'll see that it picked 222. 04:06 Now this is a little bit of a clunky way to write it. 04:08 Actually, you probably would want to write it using something more 04:11 efficient like merge(h2). 04:13 We'll use our in-line notation, and I can just say K, O, N. 04:19 And that's going to indicate to me which one is the key, the old value, and 04:21 the new, old, less than N, then return O, otherwise return N, and that's using 04:28 that ternary operator notation that we learned back when we were doing our conditionals. 04:32 So that's a nice shorthand for us. 04:33 It does the exact same thing, all in one line instead of being spread 04:36 across several lines. 04:38 The last thing that I want to show you about Merge is you'll notice that merge 04:40 did not change my original values that I started with. 04:43 Let's go back to one of our merges that doesn't have a block to it. 04:47 Here we are, h1.merge(h2). 04:49 If I put an exclamation point after merge. 04:52 Now, h1 has been changed. h2 is still the same, but h1 has the bang at the end 04:59 with the exclamation point. 05:00 So it has said permanently change this one. 05:03 This is a common theme we'll see in Ruby where this exclamation point gives us 05:07 sort of a different version of something or maybe a supercharged version, in this 05:11 case something that makes the change permanent. 05:13 And just like the regular version of Merge, we can also supply a block to this 05:17 version that has the exclamation point. Collapse this transcript Collect  00:00 In this movie we are going to be taking a look at Ruby's Collect method, which 00:03 also makes use of a code block. 00:05 Collect has a synonym which is Map. 00:07 It's really up to you as to which one you use. 00:09 Sometimes I use map just when I am trying to conserve space, maybe keep it from 00:13 wrapping around the line or something like that, or maybe I am feeling lazy. 00:16 The Collect and Map are completely synonymous. 00:18 We can use either one, so use the one that feels most comfortable to you. 00:21 The Collect or Map method is going to really work the best with arrays, 00:25 hashes, and ranges. 00:27 An array is really the object that you will use it most often with, but hashes 00:31 and ranges also work. 00:32 So I'll open up a new irb session, simple prompt. 00:37 Now, let's create ourselves an array to start with. 00:39 So array = 1, 2, 3, 4, 5, nice, simple array. array.collect and then our code block. 00:48 We are going to have a block variable declared. 00:50 Let's call it i for now. 00:52 So what Collect does is it iterates through the array, taking each item and 00:58 passing it up to i, then does whatever we tell it to do in our code block, 01:04 applies that processing to it, and puts that value into a new array. 01:09 Let's try it just to see. Let's say i+1. 01:12 That will be nice and easy to see. 01:14 You'll see what it does. 01:16 It took every item, and it applied this processing to it, and returned a new 01:21 array with that processing applied. 01:23 I think that's a really good way to think about it, is to think of that block as 01:26 being whatever processing you want done to each element. 01:30 So let's try it again. 01:32 Instead of +1, let's do times 40. 01:35 You'll see that we get 40, 80, 120, 160, and 200 out of that. 01:39 We could do the same thing if we had something like apple, banana, and orange. 01:45 So it's just another simple one. 01:47 Let's use Map this time, and we'll take each fruit as it comes up and that's 01:52 what we'll call it, fruit. capitalize. So there we are. 01:59 We capitalized each one as it went through and it made a new array for us out of it. 02:03 Now let's try something else. 02:05 Let's say fruit capitalize if fruit = banana. 02:12 That wraps around there, but you see what I did. 02:14 I am saying capitalize it, if the fruit equals banana. 02:16 Now notice when I get back here. 02:18 I did not get apple, and orange back. 02:22 I just got nil banana and nil. 02:24 This is a very important point that throws off a lot of people about 02:28 using Collect and Map. 02:29 What happens here is if fruit is equal to banana, then it capitalizes it. 02:33 Here it's returning the value of fruit capitalized, but if not, 02:38 it's returning no value at all. 02:40 Now, we can have something that works that way. 02:42 We can say let's just erase this real quick. 02:45 So I have do and then fruit, and then we'll start a new line, and now we'll 02:50 write if fruit = banana, then return the value fruit.capitalize, else, return fruit. 03:04 Now it will do what we expect. 03:06 The important thing is that we do need to return some value out of it. 03:10 So an if statement like this is not going to say apply this change only if it's true, 03:15 and otherwise return the regular value. 03:17 We need to be explicit about that and say return that value. 03:21 So whatever comes out of our processing, whatever value results from it, is 03:24 what's going to get put in our array. 03:26 So the value of processing fruit. capitalize if fruit equals banana, when it was 03:30 apple, well it was not equal to banana, so nil was the result. 03:36 So it put nil in there. 03:37 Now why didn't it just return an array of banana only? 03:40 That's a very important point that I want to make that I think will really help 03:43 you out when working with collected map. 03:45 You want to always keep in mind that the number of items that go into this 03:50 Collect is going to be equal to the same number of items that come out. 03:54 So if we take 10 things, we do processing on them, it's going to result in 10 more things. 03:59 Now there may be some nils in some of those spots, but there are always be 10 items back out. 04:04 So number of items in equals the number of items out. 04:07 The other thing that you need to remember about Collect is that even though it 04:11 works with arrays, hashes, and ranges, it always returns an array. So let's try that. 04:17 1 to 20 has arrange. 04:19 Let's do Collect and for each one we'll call it num and num times 20. So there it goes. 04:27 It returns each one multiplied by 20, but it turned it into an array at the same time. 04:32 So that's the second thing that you don't want to throw you off. 04:34 You might think oh, I have a range, I am expecting to get a range back or something. 04:38 No, it doesn't work that way, and the same thing with the hash. 04:40 Hash equals let's make a real simple hash here, A 111, and B is going to be 222 04:50 and the key C will point to 333. 04:55 Now, if we say hash.map, now with hash, we get two things rendered up to us. 05:01 We get both the key and the value. K, V is a common way to write that instead of 05:06 writing out key value. 05:07 Then we can decide what we want to do with that key and value. 05:10 So I can say well, take each one and return the key. 05:14 I get an array back of all the keys. 05:16 It's the same thing as if we ask the hash for its keys, hash.keys. 05:20 We could do the same thing with the value, or we could say something like value 05:24 times 20, and it returns an array again, taking each of those values and 05:28 multiplying it by 20, or we could do something crazy like this. 05:31 Let's say a string with the key in it, followed by colon, and then let's go 05:39 ahead and put the value right after it. There we go. 05:43 And then close our string. 05:44 So there we go, we get a set of strings with the key value pairs with 05:48 colons between them. 05:49 It's still always an array that returns back, three items in equals three items out. 05:54 If we try and put any kind conditional in there, we really need to make sure 05:58 that we always get some kind of useful value unless we are okay with having 06:03 nil values returned. 06:04 There is one final pitfall that I want to show you with Collect. 06:07 Let's go back up to where we had our apples and bananas example. 06:10 So here we are with the simple version of it. 06:13 We are just capitalizing all the fruit, just to remind ourselves that's what it does. 06:17 Let's put Puts in here. 06:19 So puts fruit.capitalize. 06:21 So we did do the Puts, apple, banana, and orange, but look what the return value was? 06:27 Nil, nil, nil, and that's because whenever we do a puts, right? 06:31 Puts 1,2,3, the return value is nil. 06:35 So if you put a Put statement inside there, you're going to end up with an array 06:39 that is filled with nils. 06:41 So if you are trying to for example take that whole thing, and you try to say ah! 06:45 Well let's set that equal to something. 06:47 Let's say new array equals. 06:51 Now, if I say well what is the value of new array, because like I said it's 06:55 equal to nil, nil, nil. 06:56 It did not put those values in there. 06:58 The right way to get them in there would be not to put the Puts in there, 07:02 but just report the value back, and now a new array is equal to what we would expect. 07:06 Just like we saw when we were working with Merge, Collect and Map had 07:10 exclamation point versions that we can also use. 07:12 Just put an exclamation point at the end and it will transform the array that 07:15 we are working with 07:16 so that the processing is applied in place, and it actually replaces the values, 07:21 instead of giving you a new array. 07:23 Collect and Map are really a powerful way to take an array and apply a 07:26 transformation to all the elements in the array. 07:28 That can be a really useful thing for you, when you are trying to do data processing. 07:32 We want to take every item and just apply that same transformation for it. 07:35 Just make sure that you keep in mind all the pitfalls I have shown you here, and 07:38 I think you'll find it to be really useful. Collapse this transcript Sort  00:00 In this movie we are going to look at Ruby's Sort method which also makes use of a code block. 00:04 Now the code blocks we have been looking at before we've put Booleans in our 00:08 code block or we've put conditional statements that determined whether or not a 00:12 certain value should be returned, or we've returned a value as the result of 00:16 some sort of transformation or processing that we have done. 00:18 Well, the Sort, we are going to provide a comparison. 00:21 We haven't really talked about comparisons yet, so let's look for a moment at 00:24 the Comparison operator. 00:25 Now, I also call this the spaceship operator, because that's what it looks like, 00:28 a little flying saucer. 00:30 So we have the less than sign, the equal than sign, and then the greater than sign, 00:34 and altogether that makes up the Comparison operator. 00:38 The way we use it is to compare two values. 00:40 Value one on one side, value two on the other. 00:43 When it compares those, the same way that a boolean compares two values and 00:46 returns true or false, Comparison operator is going to do more than just 00:50 check true or false. 00:51 It's going to return -1 if one thing is less than the other, 0 if 00:56 they're the same, or return the value of 1 if value one is more than value two. 01:02 So Comparison operators are going to be really useful in sorting, but we can 01:05 even use them just directly in irb. 01:07 Let's open up a new irb session with simple prompt and let's just try 1 and then 01:12 our space ship operator or comparison operator with 2. It returns -1. 01:16 Let's try it again, just flipping around. 01:19 It returns 1 to us, and then let's try it 2 spaceship operator 2. So now it's 0. 01:25 That illustrates very clearly what's going on here with the spaceship operator. 01:30 Now how would we apply it to sorting? 01:32 When we use Sort, it's going to use the Comparison operator to decide which 01:36 direction the two values ought to go in the sort. 01:39 So if it's -1, in other words value one is less than value two, value one 01:44 should be on the left. It will move left. 01:47 Now obviously we are not talking about literal left here. 01:50 We are talking about being less than when we have an ordered set. 01:54 It's easy sometimes to visualize it as being left. 01:57 If they're the same, well then they are the same. 01:58 It doesn't matter which one comes first. They're identical. 02:01 So we can just leave them wherever they are. 02:03 If it returns 1 though, meaning that value one is more than value two, 02:07 value one will move right to swap places essentially. 02:10 Value one is a bigger number, so it will move off to the right. 02:13 I think it's going to make a lot of sense when we try it. 02:16 Let's start by giving ourselves an array that's out of order. 02:18 So array equals, and I am just going to use 3, 1, 5, 2, 4. 02:25 That's all jumbled up. 02:26 That's our array, and an array is always going to be maintained in order. 02:30 That's the nature of an array, is that it's an ordered set. 02:32 So it will always be in that order, until we perform our sort on it. 02:35 So array.sort and then we are going to need our code block. 02:40 And the code block is going to have a couple of things declared as block variables. 02:44 The first is going to be value 1, and the second is value 2. 02:48 That's what we will use to compare. 02:50 The Sort method will send each of these values up to us in turn for the comparison. 02:56 So we can say V1 and then our comparison operator V2, you could write up value1, 03:02 value2 or item1, item2, but the numbers 1 and 2 are very helpful to let you know 03:08 that we are comparing the two. 03:09 So what's happening in essence is that it's yielding up the first two values, 3 and 1. 03:14 When those values come up to the sort block, it says oh! 03:17 when I do my comparison, 3 is greater than 1. 03:21 It returns the value of 1 from the comparison. 03:24 Therefore 3 should move to the right. 03:27 1 will move to the left, but 3 is going to move to the right, and that will put 03:31 them closer to the correct sort order. 03:33 Now Ruby is not actually progressing through each and every one of those pairs. 03:37 It has a more efficient algorithm that it can take advantage of to do it faster. 03:40 But this describes the behavior of what's happening. 03:43 So let's go ahead and try it and you'll see that sure enough it does sort them 03:47 into 1,2,3,4,5. You may have already realized that we could do array.sort by 03:52 itself, with no block and get the exact same result. 03:55 That's the default behavior of sorting. 03:57 The reason that we use the block is so that we can put more complexity to it and 04:00 change the behavior. 04:02 Let's say for example we wanted to reverse sort them. 04:04 We could do that just by changing them and saying well, now if value 2 is 04:09 greater than 1, or value 2 is less than 1, do that comparison instead, and that 04:13 will reverse sort them. 04:14 We also have a convenience method for that. 04:16 I don't want you to feel like you have to go to that trouble all the time. 04:20 array.sort.reverse will return the exact same thing. 04:23 So what do I mean by having more complexity then? 04:25 Well numbers aren't maybe the best way to see that. 04:28 Let's try some strings. 04:30 Let's go back to our fruits that we were working with earlier, fruits. 04:33 Let's make banana and an apple, and an orange and a pear. 04:40 So those all have different length names. 04:42 We will go ahead and make that our array. 04:44 Now if we just say fruits.sort, it's going to sort them in alphabetical order. 04:49 That's the default behavior of Sort when it's applied to strings. 04:53 It's going to compare them, and say ah, I am comparing two strings. 04:55 I am going to sort them just simply based on this same V1, V2 comparison that we did up here. 05:01 Well, what if we want something more complex? 05:03 So for example let's say that we wanted to sort them by the length of the string. 05:07 Not by alphabetical order but how long the string is. 05:10 So fruits.sort, let's call the first one fruit1, and fruit2. 05:18 So each pair of fruits will be passed up to it and we'll compare them saying 05:22 fruit1.length when compared to fruit2's length. 05:27 So you can see that it puts pear at the beginning because it's the shortest 05:31 string, and banana at the end because it's the longest string. 05:34 We could reverse the order of that if we wanted to, just by switching fruit1 and 05:38 fruit2 or we can sort it the first way we did it, and then just apply that 05:42 .reverse at the end. 05:43 Now there is an easier way. 05:45 If we know that we are just going to sort it according to one simple property 05:48 like that, we can say fruits.sort by, and then we would have just one value that 05:55 is going to be yielded up to our block variables, and we just need one fruit, 05:59 and then we can say fruit.length. There we go. 06:02 So now we get the same effect, just by using sort_by allows us to do it a little faster. 06:08 Now essentially what sort_by is doing is it's saying go to the blocking get the 06:12 value I should use for a comparison. 06:14 The comparison is actually internal to sort_by. 06:17 So it's making use of the same thing. 06:19 It's just little more convenient to do it using sort_by. 06:23 Finally, just to further illustrate the point, let's change it from length. 06:27 Let's sort it by the reverse order. 06:31 So what that does is it says okay, go to each one, reverse the characters, and 06:36 sort it based on the word backwards. 06:38 Now it didn't actually output the word backwards, but banana went first, because 06:42 it ends an A, orange went second because it ends an E, and then G. If we look at 06:47 apple, it's E and then L, which comes after G, and pear is at the end, it's R. 06:51 So it's sorted it based on the alphabetical order if we reversed it. 06:55 So I think you get the idea now. 06:57 The last thing I want to show you is just that fruits for example hasn't changed. 07:01 It's in its original order still, just like when I first defined it. 07:04 But there is a exclamation point version just like there was for merge and collect. 07:08 So we can have the same thing. 07:09 Let's go up here to our long example. Here we are. 07:13 We can just use our cursor and go over, and right after Sort, put an 07:17 exclamation point, and now when we ask for fruits, it is permanently sorted 07:22 instead of just being temporarily sorted, in which case we would have to catch 07:25 it with a variable. 07:26 The last thing I want to show you is that while arrays are typically what we 07:29 would sort you can sort hashes as well. 07:32 The thing is that Ruby turns a hash into an array to do the sorting. 07:36 So let's take a look real quick at how that would work. 07:38 I am going to put in a real simple hash here, C is going to be 222, A is going 07:42 to be 555, D is 111, and B is 333. 07:45 So if you want a sort of hash, what Ruby is going to first do is say hash to 07:50 array and that's what it's going to turn it into. 07:52 It's not going to be a hash anymore. 07:54 This is very important. 07:55 So when we talk to the code block, the first one will be this item compared with this item. 08:03 So if we just compare the items themselves, well unless both the key and the 08:07 value are the same, which is not possible in the hash, because the key can't be 08:11 the same, they would replace each other, unless that's true, we have to do 08:15 something else in order to make this a productive sort. 08:18 Let me just show you that the hash still is the same. 08:20 So we'll have hash.sort and then we are going to have item1. 08:25 That will be that first one, item2, oops, I forgot my upright pipe, and the other pipe. 08:32 And then, we'll do our comparison. 08:34 Item 1 versus item 2, but we need to look at something inside item1, and this is 08:40 the item at index 0. 08:42 We know how to work with arrays. 08:45 So the item at index 0, when compared with the item2 at index 0, and that will 08:53 sort them by their keys A,B,C,D, or if we asked for item1, now it sorts them 09:01 according to their values 111, 222, 333, 555. 09:05 That's one important thing you need to remember about hashes is that they 09:08 get converted into arrays, and so in that code block, you can't talk to it 09:11 like a hash anymore. 09:12 It doesn't have its key value pairs anymore. Instead, it is now an array that 09:17 has two items in it and so you'll either be talking to the key or the value in your sorting. Collapse this transcript Inject  00:00 In this movie, we're going to take a look at inject, which is another Ruby 00:03 method that makes good use of code blocks. 00:05 Well, it may help to know that inject is an accumulator. 00:08 So that's what it's going to do. 00:09 It's going to accumulate for us. 00:11 So when you see the word inject immediately think accumulate, and if we're 00:15 going to accumulate values then we're going to need a place to accumulate 00:18 them, right, a variable. 00:19 And so we'll have a block variable that will do our accumulating for us. 00:23 And by convention, we call that block variable, memo. 00:26 You can call it something else if you want, but most programmers are going to 00:29 use memo and I suggest you do too, just to remind yourself that's what it is. 00:33 That this is the accumulator where we're accumulating our values. 00:36 I think it's also helpful before we look at inject to look back at collect for a second. 00:41 We saw how we could have a range like 1 through 10 and use the collect method 00:44 with the block and we would apply that block as a transformation to each one of the elements. 00:49 Well, with Inject, we're going to be doing the same kind of thing but we're 00:53 going to be taking that value and accumulating it each time and we're going to 00:58 be storing it in memo. 01:00 So that's going to be the difference. 01:01 So now let's take a look at inject. 01:03 So here we once again have 1 through 10, we're going to use the inject method 01:06 and then we have our code block. 01:07 There is memo right off the bat, we've declared memo as being the accumulator 01:12 and then the invariable is where each one of the elements will be yielded up so 01:17 that we can use it in our block, just like we had with collect. 01:19 So now, our block needs to tell it how should I do this accumulation and the end 01:24 result of whatever we put in there is what's going to be stored in memo. 01:29 So, if we just put the number 1 there, then it doesn't matter how many elements 01:33 at the end, memo is equal to 1, because every time it loops through, we just say oh! 01:38 1, 1, 1, 1. So memo is equal to 1. 01:41 Now, what I've done here in this example is shown you a simple sum of all the numbers. 01:46 So each time it's going to go through, it's going to take whatever value it had 01:49 previously and add the next number to it and then store that in memo, then take 01:54 the next value and so on. 01:55 Let's walk through it just to make sure it's really clear. 01:57 When it starts out, memo is going to be equal to 1. 01:59 Now that's just because it takes the first value as its initialization values. 02:03 So it will take 1 as memo and then it will go to the second value. 02:07 When it goes to the block the first time, memo is already 1 so then we'll say 02:12 memo equals memo plus 2, which is 3. 02:15 It'll store that in memo and will go to the next time through. 02:18 The next element is 3. 02:19 So memo will be equal to memo plus 3 and so on, until we accumulate all of those 02:25 values which is a sum of all the numbers 1 through 10. 02:27 Let's start by launching an irb session with simple-prompt and let's 02:33 create ourselves an array. 02:34 Let's just say array equals, and let's use that splat operator, *1...10, and 02:41 we'll make a full array, 1 through 10. 02:44 Now, let's do the sum that we just saw. 02:45 Sum equals and we'll take our array and we'll inject, declare memo and n and 02:55 then we'll say memo+n is what should you do each time, storing the resulting 03:00 value in memo automatically . So, the fact that the word memo is there, 03:04 this is the process that we're doing. 03:06 We are telling it use your old value memo. 03:09 The results of this will be stored in the new memo value that will be passed to the next loop. 03:13 So here it is, the answer is 55, and sure enough, if you take all the numbers 1 03:17 through 10 and you add them up, you come up with 55. 03:19 Now, you may have thought it was little peculiar that the first time through 03:23 that memo gets set to 1 automatically and it doesn't actually apply the block to it. 03:28 It only really applies the block the first time when it gets to 2. 03:31 That's the behavior if we don't give it another value, but we can give it another value. 03:35 We can say array.inject and then in parentheses here, we'll provide the starting values. 03:41 So let's say 100. 03:42 That's the initialization value of memo. 03:44 So now the first time through, memo will be equal to 100 and the first number 03:49 it adds to will be 1. 03:50 Okay, so that will be the first iteration that will be starting with the number 1. 03:54 Whereas before, it used the first number as an initialization value and 03:58 the first iteration was actually with 2. 04:00 So let's just try it and you'll see now we get 155, it starts with 100 and then 04:05 it adds them all up. 04:06 Of course, we're not limited to just addition. 04:08 Otherwise we could just call it sum. 04:10 But instead, we can do a lot more complex things. 04:12 For example, let's say product equals array.inject and memo n and memo times n. 04:22 So now, it will give us the product of all of those numbers multiplied together, 04:26 1 through 10 and just like the one above it, we could give it a starting value. 04:30 For example, if I said, if the starting value is 2 then now, it will be that 04:35 same number times 2 because it starts out with the value of 2 and then does all 04:39 of its multiplication. 04:40 Now just like we're working with the collect method, you want to be careful 04:43 about the value that gets returned here. 04:45 You want to make sure that it always is the value that you really do want to store in memo. 04:51 Okay, so that whatever comes out of this, the resulting return value is what you want 04:56 and if I instead said, well let's go through it but this time, let's do the 05:00 plus n if n is not equal to 3. 05:05 So we add it but only if n is not equal to 3. 05:07 If we run that, we'll see we get an error and the error is essentially 05:11 saying that it tried to use the plus, addition, when we had nil and you can't 05:16 add something to nil. 05:18 Nil is not the same thing as zero. 05:20 What happened here is that when it looped through and it got to the number 3, it said, oh! 05:25 well n is equal to 3, so I'm not going to run this at all. 05:29 So none of this statement takes place. 05:31 And the resulting value is nil and nil is what gets stored in memo. 05:36 That's the end result. 05:37 So, nil gets put aside and in the next time through, it tries to add nil and the 05:42 number 4 together and it gives an error. 05:43 So just be careful about that and make sure that the value you are returning 05:47 really is the value you intend to store at the end of that iteration. 05:52 Let me show you another example that frequently trips people up. 05:56 It is something that we also saw earlier with collect which is if you do some 05:59 kind of an output in here. 06:01 So we're going to puts memo plus n. Well we get the same kind of error because 06:06 the return value from this is nil. 06:09 Now, one way to work around this, if we really do want to do that, we could 06:13 also use an each method. 06:14 That's probably the better way to do it, is to do each and do a puts that way. 06:18 But if we're really trying to accumulate, what we could do is say well that 06:21 final value ought to be memo, for example. 06:24 A semicolon, let's just put it on the same line. 06:27 So that's the one time we can use semicolons in Ruby. 06:29 So now it does output results every time without giving us an error, but what 06:34 it's actually doing is setting memo equal to 1 every time because it started out being 1. 06:38 So, it puts 1 plus 2 which is 3, but then it says, "and your return value is 1" 06:44 and so it just sets memo equal to 1 every single time. 06:47 So, again, the overall point is just to be conscious that the result of all of 06:51 this is what's going to be stored in memo for the next iteration. 06:55 Hence, you want to make sure that is what you intend. 06:57 Now, let's look at some more exotic examples that we could do. 07:01 Let's say fruits, we got apple and we'll have a pear and let's have a banana and a plum. 07:09 So there's our fruits. 07:11 Now, right off the bat, you might think that you can't use inject with an array 07:15 that's made of the strings like this, but there's no reason you can't. 07:18 All we're doing is storing values for the next round. That's all it is. 07:22 We're storing a value each time we go through that we'll then use in the future. 07:25 So for example, let's try longest_word equals and we'll take fruits, we'll use 07:32 inject and we'll use the do form this time because we're going to use it on 07:36 multiple lines, fruit. 07:39 And then let's put if memo length. 07:43 So the length of whatever was stored before is greater than the length of 07:48 whatever we are looking at now. 07:51 So if memo is greater, we want to keep memo, but if not that means that fruit 07:57 was longer and so what we want to save this time around is fruit. 08:02 So each time we're just saving the string and in the end, it will tell us what 08:05 the longest word was. 08:06 So, it goes through with each one and does a comparison and says, "ah! 08:10 This one is the best one I've got right now" and then the next time through, 08:13 it says, "let's compare what we had last time that was our winner with what we've got now." 08:18 Ah! That old one was actually still better. 08:20 So let's keep apple around one more time because it beat out pear. 08:24 So then we go to the next one and this is ah! 08:26 But now, banana is longer. 08:28 Banana is longer than apple so we're now going to store banana into memo. 08:32 And then in the final round, it compares banana, which was stored, with the next 08:36 one which is plum and banana is longer so it returns memo. 08:40 So, that's its final result at the end and that's what it sets equal to the longest word. 08:44 Real quick, I'm just going to give you another example of a place that you could 08:47 use this kind of thing potentially in the real world. 08:50 Let's imagine that you have a website and you have a menu bar that goes across 08:54 the top that tells all about the company, okay. 08:56 So let's say that menu bar looks something like this, a Home, History, Products, 09:01 Services and Contact Us. 09:02 That's the string that's going to be across there. 09:04 We could use menu.inject and let's say that we have five characters of 09:10 margin on either side. 09:11 So there's five on the left, five on the right. 09:13 We want to find out our total width. 09:15 We could do that with memo in each item and then we could say memo plus the item length. 09:21 And that will come back and tell us, your total width of characters is 47 and 09:28 then we could scale that up to however many pixels and know how wide our website 09:31 should be or something like that. 09:33 So, I think the first couple of examples give the easy and common way that we 09:37 would use memo but I think then these last ones show the more complex ways 09:42 that we can use it. 09:43 We don't just have to do math with it. 09:45 We can do different kinds of comparisons and checking. 09:48 There could be all sorts of stuff going on, but the end of the day, we just say 09:52 okay, and for the next iteration, this is what you should keep around. 09:55 That's the important part. 09:56 That's what it does. 09:57 It's an accumulator that accumulates values in memo and we can accumulate 10:01 whatever we want in it. Collapse this transcript 5. Methods Defining and calling methods  00:00 In this chapter we're going to talk about Ruby's methods. 00:02 We are going to start out by talking about how we can define and call methods. 00:05 Now we've been working with methods already, as early as when we were talking 00:08 about the string class, we had Hello. 00:11 and we put the Reverse method on it, and that reversed all the characters. 00:14 And then we took the result to that, and we applied another method to it, 00:17 the Capitalize method. 00:18 And we did similar things, and we were looking at iterators and we were 00:20 looking at code blocks. 00:22 Now these methods are object methods. 00:23 They're meant to be applied to an object. 00:26 That's a little bit different than what we'll start out with, because we haven't 00:29 learned about object classes yet, and we'll get to that in the next chapter. 00:32 This is going be basically standalone methods that we're talking about. 00:35 In a lot of other programming languages, they just call it Functions, but in 00:38 Ruby we call them methods. 00:40 The way you create a method is very simple, we have def, which is short for 00:44 define, and then the name of our method, then we have end, at the end, and 00:48 in between def and end is the code that we want the method to execute, when it's called. 00:53 Defining method is that easy. 00:54 Let's go to irb and create one. 00:56 So I am going to have simple-prompt after irb, and let's try def, and we'll make 01:02 a simple welcome method, puts " Hello World!" So there we are. 01:09 We've defined our method now. 01:11 Notice the return value of defining the method is nil, and it didn't actually do 01:15 the code inside, right? 01:16 The puts "Hello World!" didn't happen. 01:18 That's because we've defined the method, but its code that's sitting there 01:21 waiting for us to call it. 01:23 And the way that we call it is just simply by giving the method name. 01:26 There is no dot notation here, dot notation is when we are talking about object 01:29 methods, which we'll get to, and we are just talking about standalone methods 01:32 you just call the name, and you will see now it executed the code. 01:35 It did the puts "Hello World!". 01:37 The reason why methods are useful, the reason we're going to want to use them is 01:40 because we can put large blocks of complex code inside them, and then call all 01:45 of that complexity with one single name. 01:47 Now, because we are talking about putting large blocks of complex code, it means 01:51 that irb is going to stop being quite as useful for us as it has been, when we 01:55 are using it for the simple calculations. 01:57 So instead, we are going to move over to the file system, and start creating 02:00 Ruby files that we can run from the command line instead. 02:03 So I'll just quit out of that. 02:05 Let me hide irb, and now you'll notice that on my desktop, I have got a folder 02:09 called ruby_sandbox, and that's where I am going to be putting all this work. 02:12 So I open that up, you'll see that there is already a file and they are called methods.rb. 02:16 We'll open it up and take a look at in a moment. 02:18 If you don't have the exercise files, you can pause the movie and copy down what 02:22 I've got, but if you do have them, you can find this file there and make a copy 02:26 into the ruby_sandbox. Let's take a look. 02:29 I've just put some simple method definitions in there, I've got my 02:31 welcome definition. 02:33 I've also got add, which just adds one and one. 02:36 I've got one called longest_word, notice the underscore between them, just 02:40 like a variable names. 02:41 This finds the longest word out of this array of words, and it uses the inject 02:46 method that we saw from the last chapter. 02:47 Finally, I've got a method called over_five? 02:50 with the question mark at the end. 02:52 Method names can have question marks and that can be really useful to indicate 02:56 to yourself when you're using it that it is used for a test, that it's going to 03:00 test some value, and either return the results of this test or maybe a boolean. 03:04 Now in this case I am not returning just true or false, I am actually returning text. 03:08 And now I am actually going to put that test, either over_five? or Not over 5. 03:12 Now we have our methods. 03:14 So let's save this file, and let's go back to our command line, and what we want 03:18 to do here is navigate to wherever that file is. 03:21 So cd and for me that's going to be inside desktop, inside ruby_sandbox, and 03:26 that's where I am going to find that methods file. 03:28 And then just ruby methods. rb will execute that file. 03:33 It may not look like it did anything, but it did. 03:36 It read in all four of those methods, it defined them, but then they didn't do anything. 03:40 The same way up here we just did our definition of welcome, but then nothing 03:43 happened until we actually called it. 03:45 Now we need to call those methods. 03:48 Well, there's a couple of ways we can do that, and I want to show you both of them. 03:51 The first is that we can go back into irb, and we can use require, and require, 03:56 and then what the name of the file is that we want to require in quotes and you 04:00 can also provide more of a path information if you need to help if find where 04:04 that file is, but if you are already in the same folder, you should just find it right there. 04:08 So responded with true, which means that it was able to find the file, and it 04:12 did read it in, and process it, right? 04:14 So all the Ruby code that was in there was executed. 04:17 So in this case, it made four definitions for us. 04:19 So now we can just say welcome, and now we can call it. 04:23 So we have access to all of our methods, over_five? 04:27 and Not over 5 is what it returns. 04:29 That's the first way. 04:30 The second way is that we can actually open up that file, and in the file we 04:35 can now say welcome. 04:37 Now when we run the file, again, I am just from the regular command line not in 04:40 irb, ruby methods.rb now it outputs those results to our command line, not to 04:47 irb to our command line. 04:48 It will output to either place. 04:50 Now of course, if we go back in to irb, when we do this require now, it's going 04:55 to do that output, we'll still be able to have some interactivity afterwards, 04:59 but it will do the same thing just inside irb first. 05:04 So I want you to feel comfortable being able to switch back and forth if we need to. 05:07 The technique of going into irb is useful, if you want to continue to interact 05:11 with it, after it's done. 05:13 If we just want to run the script and then be done with it, then we can just 05:16 simply run it from the command line. 05:18 So let's switch back to methods now, and let's go ahead and call our other ones. 05:21 We have add, we have longest_word, and we over_five? There we are. 05:28 So once again, I'll just run Ruby methods, and you'll see that it does all four 05:32 of those successfully. 05:33 One last point I want to make is that we do have to define our methods before we call them. 05:37 So I have just added a little note here that's probably commonsense, but it does 05:41 read this page in order, and so things get processed in order. 05:45 For example, if we were to put welcome up here at the top before it's been 05:49 defined, we'll Save it. 05:50 We'll go back and run it. 05:52 You'll see it comes back and says, "oops! 05:53 I don't recognize that, I don't know what to do with that." 05:56 So we do have to do the definition first. Collapse this transcript Variable scope in methods  00:00 Now that we have seen how to define Ruby methods, I want to take this 00:02 opportunity to take a look at variable scope. 00:05 We first talked about variable scope back when we introduced variables, and at 00:08 the time I told you that we really wouldn't be able to go into it in any kind of 00:11 depth until we had some more complex Ruby structures that we could look at and 00:15 see how variables interacted in those context. 00:18 We revisited the subject again when we got to blocks and now that we have 00:22 methods, we can see how variable scope applies to methods as well. 00:26 In my ruby_sandbox, I have now got another file called method_scopes. 00:29 Its basically the same file that we were working with, I just saved a different 00:32 copy of it, so that I could keep the work separate. 00:34 I have also removed all the comments that we had made. 00:36 You will notice in our method definitions we are using local variables in a few 00:40 places, words, longest_word, and value. 00:44 We have also got block variables, word and memo that we are using. 00:48 We have already seen how block variables work. 00:50 I want to focus on the local variables. 00:52 Now, the reason why they are called local variables is because their scope 00:56 is local to the structure that they are in, and methods are exactly that kind of structure. 01:02 So value = 3 only has scope inside the method over_five. 01:08 Now, when I say it only has scope, that means that it is assigned inside there. 01:12 It can be used inside there, and when the method is over, that variable will be 01:16 discarded and can't be used outside of the method. 01:19 We can see that if we go down here below our method calls and we just do puts 01:23 value, and then we will Save our file. 01:26 Let's go back into our command line, notice that I am already inside my 01:30 ruby_sandbox, and from there I will run my new file method_scopes.rb. 01:35 So it ran our code successfully. 01:37 We got all the results from the methods that we would expect. 01:40 But then when we got to outputting the value, you will notice that we got 01:43 undefined local variable. 01:45 That's because value has not been set inside this scope, the global scope of 01:51 this file, it was only set inside the scope of the method. 01:55 The same thing is true if we were to set a value, let's say, at the very top of 01:58 the file, value = 7, so now we will Save that. 02:04 If we run it again, now this time we didn't get the error because value was set, value is 7. 02:11 It didn't change it when we ran our method. 02:14 Either defining it or calling it did not change it. 02:16 Completely separate, and it still says it's not over_five. 02:20 It didn't use 7 as a value, and it didn't change what we had. 02:23 They are two separate variables, value and value, because they have two separate scopes. 02:29 The same thing would be true if we tried to output the value of words. 02:32 However, longest_word is going to be a little bit different. Can you guess why? 02:36 Let's try just doing puts longest_word. 02:41 02:41 We will save that file. 02:42 We will go back to our command line and let's run it one more time, and notice 02:47 what we got this time. 02:48 We got banana and then we got banana again. 02:52 Now, you might be thinking that it actually output banana from here. It didn't. 02:57 What it's doing is it's putting the method, longest_word, that's what it's putting. 03:02 That's why we get this sort of strange nil down here, at the very bottom, is 03:06 because that's actually the result of this value that comes back. 03:10 It's putting the longest_word here. 03:13 That's when we see the word banana the second time, because we have called the 03:16 method, and the return value from this puts is nil. 03:20 We saw that earlier in irb. 03:22 So therefore it's now outputting nil, and that's why we get that other nil there. 03:25 So our variable name and our method name are the same, and that's where we are 03:31 getting this confusion. 03:32 So that brings up the point that you want to be careful when you name your 03:35 methods to make sure that it's not confusing and that Ruby can always tell 03:39 which one you mean. 03:40 Now in reality, once we start programming with Ruby, it's really not that often a problem. 03:44 I just want you to be aware of it, that your method names and your local 03:48 variables are going to look the same, and so you want to make sure that it's 03:51 always clear which one is which. 03:53 Now, what about the case where we do want to be able to bring in something 03:57 from outside the scope? 03:58 I am going to go ahead and just comment this puts longest_word out for now. 04:02 So let's say, for example, we wanted to have a word list that was defined before 04:07 we ran it, and we wanted to make use of it inside there. 04:10 There are a couple of ways that we can do it. 04:12 One is to have a variable whose scope does stretch inside there, and an instance 04:17 variable is that kind of variable. 04:19 So we put the at symbol in front of it, now it does run successfully. 04:23 Let's go ahead and run it one more time to see everything worked out just 04:26 like we would expect. 04:28 That's because this array is available inside longest_word. 04:33 In fact, we don't even have to have it available before we define it. 04:36 It just has to be available before we run it, right? 04:39 It has to be available down here. 04:40 Let's take a look at the variable types again. 04:43 You recall that we had five variable types: 04:45 Global, Class, Instance, Local, and Block. 04:48 When we are working with methods, the things that will have scope inside the 04:51 method are going to be Global, Class, and Instance. 04:54 We can use any one of those, and it will still be available to us inside that method. 04:59 Local and Block are going to have scope that's limited and does not go into the method. 05:04 We don't have it available to us once we are inside the method scope. 05:08 Now, there are times when it's perfectly legitimate for us to do it this way, 05:12 to have a variable that's set outside of a method and then use it inside the method. 05:18 We will especially be using that when we start looking at classes, but most 05:21 of the time you want to use this very sparingly, because let's say that words had not been set. 05:26 Well now, longest_word is expecting the we have set that, and if we forget to do 05:30 it, then our method no longer works. 05:32 It becomes fragile and it breaks. 05:35 A much better way would be for us to pass in the variable that we want to use at 05:40 the time that we call the method, and we do that with arguments. 05:43 We can see how to do that next. Collapse this transcript Arguments  00:00 In this movie I want to talk about arguments. 00:02 When I say arguments, I don't mean the disagreement between two people. 00:06 In programming, arguments are going to be a comma-separated list of values that 00:10 we pass into our methods. 00:12 It's a way that we can get around the problem that we saw in the last movie of 00:15 variable scope, because now it doesn't matter what the scope is, when we call 00:19 the method, we pass in the values that it's going need. 00:22 We don't have to make sure that they have all already been set. 00:24 We pass them in at the time that we call them, and that's going to be arguments. 00:27 You will also sometimes see arguments abbreviated as just args, so if you see 00:33 that, that's what it means. 00:34 It's just short for arguments. 00:37 In my ruby_sandbox, you will see that I have a file already called arguments. 00:40 It's basically the same code that we have been working with, but I have added 00:43 a comment to the top, noting that methods with arguments typically use parentheses. 00:48 Methods without arguments typically do not. 00:51 But it's completely optional as to whether we use them in both cases. 00:54 So that's just sort of the standard. 00:56 If we have something that's just welcome and we don't need arguments, we don't 00:58 put the parenthesis, but we could, we would just put parenthesis after it. 01:02 If it was going to have arguments, then we probably would put them, but we don't have to. 01:07 In order to make arguments work, we are going to need to have a local variable 01:10 that's going to catch those values as they are passed in to our method. 01:15 So for example, name. 01:17 Now we can make use of that local variable inside our method, and it has the 01:22 same scope as we had down here when we defined value. 01:25 Just to show you what I was talking about with parenthesis, we could just leave 01:29 off the parenthesis as well and that would work. 01:32 Typically you don't see that. 01:33 You usually do see the parenthesis. 01:35 Now our welcome method is expecting to receive one argument which it will assign to name. 01:42 Let's try it out. 01:42 We will go over to our command line, notice that I am already inside my ruby_sandbox. 01:48 We will type ruby arguments.rb. 01:52 Notice that I get an error. 01:53 Welcome gave me an error, wrong number of arguments (0 for 1). 01:58 That's the important part here. 02:00 I sent it zero arguments and it was expecting one, and that's the important 02:05 point about arguments. 02:06 Once we have defined an argument here, it's expecting to receive it. 02:09 We are essentially saying hey, I need this before I can keep going. 02:13 So when we call welcome down here, we now have to put in a value, like Mary. 02:19 Now it will pass in the string Mary as a value that will be received by the 02:24 local variable name and then used inside. Let's try it. 02:29 ruby arguments, Hello Mary! 02:31 And the nice thing about that is now it's reusable, we could have Hello Mary! and Hello Larry! 02:37 We can call it twice, and the only thing that we are doing different is we are 02:43 sending a different value inside for it to use. 02:46 Before we go on, let me also just show you that when you call these values as 02:49 well, we don't have to put the parenthesis. 02:51 We could have Hello Mary! Hello Fred! 02:55 There we are. 02:56 I left off the parenthesis around it. 02:57 It's still an argument that's being passed in, ruby arguments, Hello Fred! 03:02 Now up here, typically we do put the parenthesis with arguments, down here when 03:07 we are calling it, it's really a matter of personal style. 03:10 Some Ruby developers do it one way, some do it the other, there's no right or wrong answer. 03:14 It's really a matter of your personal preference. 03:16 Throughout the tutorial, I am going to put them in there, because I think it 03:18 makes it clearer for beginners especially to see where the argument list starts and stops. 03:23 We can also add arguments to our add method up here. 03:26 We will put our parenthesis and we can put multiple arguments, n1, n2. 03:31 Remember it's a comma-separated list of values. 03:33 So the commas will know where to break each of those values, and then we can 03:37 make use of those, n1 and n2, and we will need to pass those in down here. 03:42 So let's say 2, 2, not 2+2, 2, 2, those are each of the arguments that are 03:49 being passed into it. 03:50 Let's take a look at longest_word. 03:52 We could have, for example, the word list being sent in. 03:55 This is a lot better than the other solution we were looking at earlier. 03:58 Remember we were doing something like this, where we had the instance variable 04:01 that was being used. 04:02 Well now, we can just take our words, we will just come down here before we call 04:07 it, and let's actually rename them to fruits. 04:11 We can pass in fruits as an array. 04:14 It will then become the local variable words once we are inside. 04:20 It might have been fruits. 04:21 It might have been cars. 04:23 It might have been dogs. It doesn't matter. 04:25 Once we pass it in, it gets assigned to words, and we work with it then as the 04:29 local variable word. 04:31 Let's change over_five to just be value, and now we just take this out and 04:36 over_five, we can pass in a value. 04:38 Let's pass in 8 this time. 04:39 I will just put some spaces in here so we can separate these out and see all 04:43 the different ones. Save it. 04:45 Let's go back and run it one more time, and you will see that everything works, 04:49 all my arguments get passed in successfully. 04:52 Now our methods are much more usable because now they are flexible and we can 04:55 use them in lots of different contexts, and we don't have to go to the trouble 04:59 of setting instance variables or anything like that. 05:01 We can just, at the time we call it, we can pass in the values that we want it to use. 05:05 So, for example, over_five becomes a lot more useful now that we can do 05:09 something like 112/18. 05:12 I don't know off the top of my head whether that's going to be over_five or 05:15 not, but we can run our method and find out that, yes, it is in fact going to be over_five. 05:20 As you can see, arguments are what give the real power and flexibility to our methods. Collapse this transcript Argument default values  00:00 In the last movie we saw how if we didn't send the same number of arguments to 00:03 our method as it was expecting, that we got an error. 00:06 In this movie we are going to see how we can use argument default values 00:10 to avoid those errors. 00:12 You will see that I have a new file on my ruby_sandbox, its argument_defaults, 00:15 and I will open it up. 00:16 It's basically the same file that we have been working with, just a little bit cleaned up. 00:19 The case that I want us to look at is how we can set a default value so that, 00:22 for example, if we called welcome, and we just called it by itself and we did 00:27 not send anything, that there would be a default behavior that would happen 00:30 instead of giving us the error. 00:33 The way we do that is we just go ahead and make an assignment to this variable 00:36 inside the argument list. 00:38 So Hello World, for example. 00:40 Now when we call it, we don't get an error anymore, because even though we 00:44 didn't send an argument, it was satisfied because it said, ah, my argument did 00:48 get a value in the end, not one you sent to me, but one that I set myself. 00:53 So it's the equal sign right after it. 00:56 If we have multiple arguments, you just assign them each, like =0 and then for 00:59 this one, =0.Let's go ahead and just try that out. 01:02 Let's just say add, and for add, let's go ahead and submit 3. 01:06 We will just put in one argument. 01:08 So we are going to add 3. 01:09 It doesn't really make sense, but that's what we are going to do. 01:11 We will go into our Terminal. 01:13 Notice that I am already inside ruby_sandbox, and I will just run 01:17 ruby argument_defaults. 01:20 So there it wrote Hello Mary! 01:22 Then we get Hello World!, the default behavior. 01:25 We added 2 and 2 and we got 4. We added 3. 01:28 It just came back with 3. 01:29 Because we sent 1 and it got used here as n1, and then n2 got assigned the value of 0. 01:38 Obviously, the order of our arguments is going to be important. 01:41 It has to know which one of our values to assign to which one of these slots up here. 01:45 So, for example, 3 would never end up being n2. 01:49 Its always going to get assigned to n1, because it's the first value. 01:53 If I put something else in front of it, nil, let's say, then nil would get 01:56 assigned to n1, and 3 would get assigned to n2. 01:59 So the order does matter. 02:01 You want to be careful about that. 02:02 Therefore, you will want to make sure that you put all of your required 02:05 arguments up at the front. 02:07 You could do n1 and then n2. 02:09 So n2 is optional now, n1 is required. 02:12 If I did it the other way around, this isn't really that useful. 02:16 It's going to give me an error unless I provide both. 02:19 There's no circumstance where I can just provide 1 to it. 02:22 If I just give this, add 3, it would say oh, n1 is equal to add 3, but n2 gives me an error. 02:28 It's not really that flexible. 02:29 So you just want to make sure that if you have some that are required and some 02:33 that are not, the required ones are the first ones there, so that then as you go 02:37 down the list, the optional things come later on. 02:40 Now, we have already seen how we can put a string and an integer as being the variables. 02:45 We can also put in arrays as a default value. 02:47 So there's just an empty array, two square brackets side-by-side. 02:51 That's going to say look, if you don't get an array, then set yourself to an 02:54 empty array, which a lot of times will keep your method from blowing up on you, 02:58 because it will then have a default value. 02:59 So if you are doing like each, for example, doing each on an empty array will 03:04 just not do anything. 03:06 But having nothing assigned to it will give you an error saying, "oops, you 03:10 didn't send in the value, I am not very happy with you," kind of thing. 03:14 So let's try that down here. 03:15 Let's just try longest_ word and see what we get back. 03:19 Then same thing with over_five. 03:21 Let's try setting it to nil. 03:22 We are going to say if you don't get a value, then don't object, don't raise an 03:27 error, just set value equal to nothing. 03:30 Don't pass an error, just keep on going, because I am okay with the fact that I 03:33 might not have gotten value. 03:36 Let's just do over_five by itself and see what we get now. 03:40 So let's try all this out. 03:42 We will come back over here, and defaults. 03:44 Now, notice that over_five still raised an error, but the error was 03:47 different this time. 03:49 The error this time was, undefined method > for the NilClass. 03:54 So it basically said, well, I don't know how to handle that with nil, if we were 03:58 to instead just put a to_i on it to make sure that it changes it into an 04:02 integer, then we can get it to pass. 04:05 So now it goes through and it says, "ah, I got nil." 04:08 I am going to convert nil to an integer, which is going to be 0, and therefore 04:13 it will say, "ah, 0 is not greater than 5." 04:16 So we got an error, but the error was not an argument error. 04:19 When you are programming and you are defining your methods, it's always a good 04:22 idea to stop and take a second when you are making your argument list, and think 04:26 about what really is required for this method to do its job. 04:30 For example, over_five, having nil really doesn't make a lot of sense. 04:33 So we might say you know what, if you are going to use over_five, you have 04:36 to send me a value. 04:38 Now, we still might want to do this to_ i in case we were to send a string or 04:40 something like that. 04:41 It's not a bad idea. 04:42 But if we leave it out, then we are basically saying you must send me a value. 04:46 If we put in something like 0 or nil, then what we are saying essentially is 04:51 it's okay if you don't, I will handle it, I will live. 04:55 So as you are making your argument list, always stop and think about where your 04:58 default values ought to be. Collapse this transcript Return value  00:00 In last movie, we saw how we could use arguments to get values into our methods 00:04 so that we could use them. 00:05 What about getting values back out of the methods? 00:08 For that, we need to take a look at the method Return Value. 00:11 In my ruby_sandbox, you'll see I have a new file now called return_value.rb. 00:15 If we open it up, you'll see this is basically the same file we've been working 00:17 with, just pared down a little bit. 00:19 Now we've already been working with return values without really knowing it. 00:22 Now, we have already seen return values when we were working in irb. 00:25 Let's go back and just open up an irb session to remind ourselves. 00:29 I'll just open one without simple-prompt. 00:31 If we have 1+1, we're not doing a puts, right? 00:35 We're getting the return value of 2. 00:37 If I do puts 1 +1, then we are outputting 2, and our return value is nil. 00:43 And we talked about that puts always has return value of nil. 00:46 So that was true in irb. 00:48 It's also true back here in our method definitions. 00:51 So when we call welcome, it runs puts, which outputs what we asked it to, and 00:58 then there's a return value for it. 01:00 Now, if there is another statement then that return value just gets ignored, and 01:04 we keep going with everything else. 01:06 But what happens at the end of the method is that the return value for the whole 01:10 method is whatever the last thing was. 01:13 So in this case, when we were doing a puts, the return value is nil, we can see that. 01:17 Let's go down here and let's say returned_value = welcome("John") and then 01:26 puts returned_value. 01:29 So now we can actually see that value. Let's save it. 01:31 Notice that I'm already in my ruby_ sandbox here, and I'll just run ruby and 01:35 then return_value.rb. So here we are. 01:38 It runs the Hello John! 01:40 that's when it calls the method, because it's still calling it here, still calls the method. 01:45 But then we are catching the value, and we are putting it again, and that's the 01:49 nil that we're seeing here. 01:51 So they all have a return value automatically. 01:54 If we put 1+1 at the end, and save that, now let's run it again. 01:59 Now, instead of nil we get 2. 02:02 So whatever the last value is inside the function is what gets returned by 02:06 default, and is the exact same thing as if we said return, return this value. 02:11 So that is the way we're very explicit about it and say this is what I want you to return. 02:16 And return not only returns a value, it returns from the method. 02:21 So it exits the method at same time. 02:24 So if, for example, we did 2+2, our code will never get there. 02:28 It will never do the 2+2, because as it's executing, it will do our output, then 02:34 it will come and do 1+1 and figure out it's 2. 02:36 It will return that value to us so that we can catch it and work with it and it 02:41 will just stop right there. 02:43 Let's try it, and you will see that we get the exact same thing back, we did not 02:47 get 4 as our return value. 02:49 So that's the second point. 02:50 So the first point that I made is that all methods have a return value, even the default one. 02:55 So there is always one, even if we don't catch it. 02:57 So in the case of welcome ("Mary ") here, we just won't catching and 03:00 doing anything with it. 03:01 The second point is if we're explicit about it and we say return, and then we 03:05 return from the method and we are done. 03:07 Now most of the time what we want to return is the result of the processing 03:11 that the method does. 03:12 So for example, I'll take out the 1+1, and now we'll just do return "Hello #{name}!. 03:16 So it has the same effect, 2+2 will never get executed, but the return value 03:21 will be the result of whatever welcome method did. 03:24 It's processing and it did. 03:26 In this case it just made a string for us. 03:28 Now, let's just save this, run it again, notice that we don't get Hello Mary! anymore. 03:34 We just get Hello John!, because calling it here did nothing. 03:37 All it did was to generate the string, return the value, and then it got thrown 03:41 away, because we didn't catch that returned value. 03:44 In the case of welcome John, we did. 03:46 Now it's a little bit up to you as to whether you put the puts statement 03:49 inside your method or outside the method, and it really depends on what the 03:53 method is meant to do. 03:54 If the purpose of the method is to do some output, then it makes sense to put it in there. 04:00 But otherwise it might make more sense to put it outside, and that way we can 04:04 put it when we want to put it, like we did here when we did puts 04:07 returned_value, or we could not, like we just assigned it to returned_value, we 04:12 could to do something with it. 04:13 Let's say for example here with longest_word, return the longest_word at the end of it. 04:18 So now instead of having the longest_word, let's puts the 04:23 longest_word (fruits).length. 04:25 We won't get banana anymore. 04:27 Instead now, we'll get the length of that word. 04:31 We don't care what the word was, we want to know how long it was. 04:33 It's 6, and maybe we're going to use that for some purpose. 04:36 So not having the puts inside the method a lot of times gives us 04:40 greater flexibility. 04:41 Doesn't mean you have to. 04:42 It's perfectly valid to have a method that it does output. 04:46 Now so far, the return that we've been doing has been at the end, where we were 04:49 basically saying well, return this after we are done all the processing. 04:53 But we don't have to, in fact that's the real power of return, is that it works 04:56 well with if statements. 04:58 So for example, here I have said return "Exactly 5", if the value is 5. 05:03 Don't go any further. 05:04 We're just going to call it done, and if not then do this additional process, in 05:08 which case I have another if else statement. 05:10 Now obviously you could have written this all as one statement. 05:12 But I wanted to show you the way that we could do return if, because this is a 05:16 common Ruby idiom that will do return something if this is true. 05:20 Exit the method right away if certain conditions are met. 05:23 And then if we want this to work, of course, we'll need to do, puts over_five or 05:28 else catch that string and do something else with it. 05:30 So frequently return is used not just at the end of the method, but at various 05:34 points within the method. 05:36 Now, the last and perhaps most important point I want to make about a return 05:40 value is that it's singular. 05:42 There is only one return value that comes back. 05:46 So let's rewrite our add method so that we would like to return two values from it. 05:50 Let's say add_and_subtract, and we need a method that will do both for us. 05:54 It's going to take n1, and figure out what their addition is, but it's also 05:59 going to do subtraction, say n1-n2. 06:03 How can we return both of those values back? 06:06 When we have multiple values to return, the way to do it is to put them in a single object. 06:12 In this case an array. 06:13 So add and subtract. 06:15 Now that is one object, its one return value, but inside there are other 06:21 multiple values, and that's what you get around it. 06:23 So we can only return one object from a method. 06:27 But that object could have multiple objects inside of it. 06:31 It could be an array. 06:31 It could be a hash. 06:32 It could be some other more complex object that contains lots of other stuff. 06:37 Frequently, you'll see the Ruby developers just leave off these are square braces. 06:41 So this will just be return add, sub. 06:43 It's still returning an array. That's what it is. 06:46 Those square brackets are just optional in this case, so we don't actually have to have them. 06:49 But it is still an array and when we get the value back, we'll need to treat it as an array. 06:54 For example let's do now add_and_ subtract, and let's say result = so we'll need 07:02 to puts result and the 1 item and puts result and the 2 item. 07:11 So those are going to be the two items in our array. 07:13 Let's just try that out. 07:15 Ruby return_value.rb, and here we are, we get our value 4 and 0. 07:19 So, 2+2 is 4, 2-2 is 0. 07:22 Another neat trick that you can use is you can do double assignment. 07:26 So we could say for example, add, sub = and then let's do, add_and_subtract and 07:33 we could do something like 8 and 3. 07:37 Add, sub is actually an array as well, just with those square braces missing. 07:42 But it does the same thing. 07:43 It does double assignment now. 07:44 So now when an array comes back, the first value of the array goes into the 07:48 first one, and the second one goes into the second one. 07:51 And that's a nice, convenient shorthand to know about. 07:53 Otherwise you get little bit confused when you say wait a minute add, sub =, 07:57 how is that possible? 07:58 Well that's what it means. 07:59 It means that this is returning two values back and we are catching both of them 08:03 into this array of variable assignments. 08:05 So just to recap those points we've seen about returned values, methods already 08:10 have a default return value, and it will be the last operation's return value. 08:15 We can explicitly use return, and it will both return the value and exit the method. 08:19 I also suggested to you that returning a value and using puts outside of a 08:24 method can be a lot more flexible than if you were to just simply do the output 08:27 from inside the method. 08:29 And we saw how return is especially useful with conditional statements, and that 08:32 methods could only return one object. 08:35 You will want to use an array if you need to return more than that. Collapse this transcript Operators are also methods  00:00 You'll be very surprised and quite possibly amused to discover how prevalent 00:05 methods are in Ruby, and you didn't even know it. 00:08 Common operators in Ruby are methods too. 00:11 When I'm talking about operators, I'm talking about things like Plus. Plus is a method. 00:16 Now in most other languages, you would just say it's an operator. 00:18 It's just very simple, 8+2, just like on a calculator. 00:21 Now in Ruby, it's actually a method. 00:24 It's the plus method that's being applied to the integer 8 and the 00:28 first argument is 2. 00:30 So the name of our method is that + there. 00:33 Instead of having it be a word in English that we'd recognize, it's just the +. 00:37 Both of these will work if you go in the IRB and try it out. 00:40 Now we've already seen how the parenthesis around the argument list is optional. 00:45 We saw that back when we were talking about arguments. 00:47 But there is another change that's happening where Ruby is allowing us to 00:51 just put + instead of having to put dot plus, and that's something that's 00:56 happening internally in Ruby to make it easier for us to use, so we don't 00:59 have to do it the cumbersome way. 01:01 Instead, we can do it a slightly sweeter way, and that's why we call it 01:04 syntactic sugar, because it's made the language a little sweeter for us. 01:09 The syntax of the language is a little sweeter, because it's much easier for 01:13 us to write expressions like 8-2, 8*2, 8/2 and 82, then it is to write the 01:20 method names for them, eight dot minus and then parenthesis, whatever argument is, and so on. 01:27 But the column on the right is absolutely what's going on in Ruby when you type 01:31 what's in the column on the left, and both of these will work. 01:33 We'll try it out in a moment to actually prove to you that it does work. 01:37 But let me show you a few more. 01:38 Let's say that we have a simple array, and to that array we're going to append 4 to the array. 01:43 Then we're going to ask for what's at index number 2 to be brought back out of 01:46 the array, and then we'll set the value of what's at index 2 with the string 01:52 X. These are also all methods that look like operators, but they're actually methods. 01:58 The first one is pretty similar to what we saw before. 02:00 It's just the .<<, and then the argument that's being passed in. 02:05 Again, syntactic sugar makes it nicer and easier. 02:08 The second one might not be as obvious how it's working. 02:11 The name of the method is actually square bracket, square bracket. 02:15 That's the name, and then the argument is what's inside those square brackets. 02:19 Ruby just uses syntactic sugar to rearrange things a bit. 02:22 The last one works in a similar way, but what's surprising about it is that the 02:26 = actually becomes part of the method name. 02:28 So the method name being applied is square bracket, square bracket =, and then the first argument is the 02:33 index and the second argument is the value that should be put into that index. 02:37 Thankfully, we don't have to ever write it the way it is on the right-hand 02:41 side of the screen. 02:42 We get to use syntactic sugar and write it the way it is on the left side of the screen. 02:45 But here's the reason why it's important to understand this. 02:49 You can name your methods with these kinds of symbols. 02:53 So if you have something like you have a classroom and you want to put a student 02:57 in the classroom, you can write a <<, append method of your own that will put 03:03 the student in the classroom, or if you have a long string, let's say it's a 03:06 page of text, and you want to break it up into thirds, you could use the divisor 03:10 method, and use the slash that we'd use for dividing. 03:13 It wouldn't mean to divide in the normal sense of dividing, it would mean, take 03:17 this page and divide it up. 03:19 So keep that in mind when you're naming your methods that you can make use of 03:23 the syntactic sugar for yourself. 03:25 Now that we understand that these are methods being applied to objects, we can 03:29 revisit something we saw earlier, which is that hello, the string, multiplied by 03:34 5 gives a different result than 5 multiplied by the string, hello. 03:39 The first time we'll just get hello five times, the second time we'll get an error. 03:44 The reason why is because the first is taking a string and using its multiply 03:48 method on it with the argument 5, which does have a valid result. 03:52 The second is something different. 03:54 It's an integer that is using the multiplication method, which integers when 03:58 multiplied by something, and we know what they'd do, right, 5x2 is 10. 04:03 Well, when you pass it a string, it says, "wait a minute. 04:06 I was expecting it an integer. 04:08 That's how multiplication works." 04:10 So a string has a different behavior, because it's a method on the string 04:13 class, whereas it works differently when we have the multiplication as a method 04:18 on the integer class. 04:20 So as promised, let's try all these craziness out and prove that it still works. 04:24 So here I am in my ruby_sandbox, and I've got a file called syntactic_sugar, I'm 04:28 just going to open that up. 04:29 It's already pre-populated with everything. 04:31 I've just got those same things that I had in my slides. 04:34 So first, I've got the nice syntactic_ sugar version, and then right below it, 04:37 I've got the not so nice version, which we could also call syntactic_vinegar, 04:41 where it's not so tasty. 04:43 So we'll have one after another, and they should give us the same values all the way down. 04:47 Then when we get to the arrays, I went ahead and created a simple array. 04:50 I just called array1, [1, 2, 3], and I've got a second one, array2. 04:54 Now this is actually an actual equal sign. 04:56 It's an actual operator where we're doing a variable assignment. 04:59 It's not a method on an object, because remember our variable is not an object. 05:04 That's one of the few exceptions to things that are not objects, is a variable. 05:07 So then I'm going to go through and just perform each of these operations to 05:11 either array1 or array2, using either syntactic_sugar or syntactic_vinegar, and 05:16 then I'll just inspect it each time to see what it looks like. 05:18 So let's just try it. 05:20 I'm going to go into my command line and I'm already inside my ruby_sandbox, so 05:24 I just need to do ruby syntactic_sugar.rb. 05:28 There you go, they give the exact same results all the way down the line, works 05:32 exactly like you would expect it to. 05:34 Thankfully though, we're able to use the syntactic_sugar version most of the 05:38 time we're programming, but it's still useful to know what's actually going 05:41 on behind the scenes. Collapse this transcript 6. Classes Defining and using classes  00:00 In this movie, we're going to be looking at classes. 00:02 Classes are the backbone of object-oriented programming, because classes define objects. 00:07 They tell what an object is and they define what an object can do. 00:11 Now we've been working with objects already. 00:12 We've seen that pretty much everything in Ruby is an object. 00:15 But those are Ruby's objects, the ones that are predefined for us. 00:18 This is going to be where object- oriented programming really begins for us, 00:22 because now, we're going be able to write our own classes that will define our own objects. 00:27 In order to get started with that, we first need to see how to define a class, 00:30 and the syntax is very simple. 00:32 It's just class and end with the definition of our class in the middle, very 00:36 similar to what we had when we had methods, we had def and end, now it's class. 00:40 The one big difference though is the naming of the class. 00:44 Methods were all lowercase with underscores between the words. 00:47 Class names are going to be what we called CamelCase. 00:50 We call it CamelCase because it has the humps in the middle like that capital N. 00:54 Well, the idea here is that each word is capitalized, but they're all smushed together. 00:59 We don't have underscores at all, so we still have readability, because each 01:03 word is broken up with a capital letter, but it's all one word, and most 01:06 importantly, it begins with a capital letter. 01:09 That's very important, class names have to begin with a capital letter. 01:12 So it wouldn't matter here really if we had SomeName and it was a lowercase n, 01:16 or if we wanted to capitalized the n. So it's up to you when you define it, 01:20 whether you want to call it Somename with a lowercase n or SomeName with a 01:23 capital n, but the s has to always be capitalized for a class name. 01:27 Now classes are going to do a number of things for us. 01:30 But one of things they're going to do is they're going to group our code into 01:33 discrete well-categorized areas that make it easier to work with. 01:37 So at the very beginning, what we're going to learn to do is wrap up methods 01:40 inside there, and see how we can take the methods that we just learned in the 01:43 last chapter, and put them inside our classes. 01:46 Let's try creating our class with a method in it. 01:49 So to start out with, let's just open up IRB, irb --simple-prompt. 01:55 Let's try creating a class. 01:57 So let's say just class Animal, right, there's my capital letter A in front. 02:02 That's very important. 02:03 Then I'll start a new line and then typically you'll indent everything after 02:07 that, so that it's very clear where the class begins and ends. 02:10 So let's put a method in here. 02:12 Let's call it make_noise, and it's just going to make the noise of an 02:17 animal Moo, let's say. 02:19 You'll remember that there is a implied return there, right. 02:22 The return value is the default return value of just the string, Moo, so that's 02:26 what will be returned, and then end. Now it's set. 02:30 It defined it for us. 02:31 The return value was nil from doing that, just like when we defined the method. 02:35 It didn't actually do anything besides the definition. 02:37 We have to actually activate it, right, actually call upon it to do something. 02:42 The way we do that with classes is that we create a new object from the class. 02:47 So let's say Animal.new to start with. 02:51 That creates a new animal. 02:52 That's all there is to it. 02:53 But we need to hold onto that once we've created it, so we're going to assign 02:56 that to a variable, animal = Animal.new. 03:01 Now, I created a new object, and now that object is being held by my variables. 03:06 So I can always just say Animal, and there it is, my variable Animal 03:11 always points to it. 03:12 Now I just want you to see, if we just call make_noise on its own, it says, 03:18 "oop, I don't know what that is." make_noise, no idea. 03:21 make_noise is inside the class Animal. 03:24 So we need to tell Animal to do the making of the noise, and we do that with the 03:28 dot notation, animal.make_noise. There we are. 03:32 It returns Moo. 03:34 So it did what we asked it to. 03:35 It called the method make_ noise inside that object. 03:39 So notice what we've done here, we took an object, assigned it to a variable, 03:44 then we told that object, call your method make_noise. 03:48 Now what it returned to us is a string which is an object and we can do things 03:52 like make_noise upcase. 03:54 We can daisy-chain them together just like we can any other method anywhere else. 03:58 All we've done now is write our own method on our own custom object. 04:03 Notice also that we can't do this animal.make_noise, but that comes back and 04:08 says, "oh, sorry, you can't do that either." 04:11 And we'll talk about that in a little more depth later on. 04:14 But I just want you to see for now that make_noise, we have to have that object 04:18 created from the class, then we can tell that object to do things. 04:23 So why is defining our own objects so important? 04:26 Why is it a good thing? 04:27 Well, it's because objects let us organize our code into well-categorized areas. 04:32 Then it's easy to find things. 04:34 So the instance of our Animal class, any code having to do with an animal could go there. 04:40 Then if we said, oh, you know what, we need to make a change to our code, having 04:43 to do with animals, we know right where to go. 04:45 We'd go to the Animal class. 04:47 The second benefit is that objects carry around their classes code. 04:51 So all those definitions that we put inside of a class, they're always available 04:54 to us, if we have that object. 04:56 So that variable now, animal, that contains an object. 04:59 We can pass it around, we can pass it into methods. 05:02 It's an object, just like any other object. 05:04 We can pass it in, like with a hash, we can put it inside a hash, we can put it 05:08 inside an array, and it will always still have all those behaviors like 05:12 make_noise available. 05:14 The third thing is that it allows complex behaviors using very simple statements. 05:19 Imagine for a second that we have two classes. 05:21 One is a classroom, like in a school, and the second is a student. 05:26 We want to put the student in the classroom. 05:28 Now we can just simply say classroom.add student, and it will add the student to the classroom. 05:33 But a lot of stuff can be going on behind the scenes there, we can be assigning 05:37 the student to a default seat, we can be generating a name tag for them, we can 05:41 be notifying the head office that they've been assigned. 05:44 A lot of stuff can be going on behind the scenes, just by using very simple statements. 05:49 Last of all, our object are going to correspond to real-world objects. 05:53 That makes it easy for us to take abstract code, and think of it in a concrete way. 05:58 So in the example of our classroom and student, we're not thinking about all 06:02 of the 0s and 1s that have to get moved around to get those things to happen, 06:05 we can just think about it in very real-world terms, classroom.add student, 06:11 and so the student gets added to the classroom, student sit at desk, and then 06:15 we can use common sense and regular English phrases to describe what we want our code to do. Collapse this transcript Instances  00:00 Now that we know how to define a class and how to create an object from it, we 00:04 need to talk about instances, and have an understanding of what an instance is, 00:07 because an instance is a fundamental concept in object-oriented programming, and 00:11 it's going to be central to working with classes. 00:13 Outside of programming, the typical definition for instance might be a case or 00:18 an occurrence, an example put forth. 00:20 So what we are talking about when we talk about programming is a very similar thing. 00:24 We are talking about an occurrence of our class. That's what it is. 00:28 It is an object that's being created from a class. 00:32 So when we created Animal.new, we created an instance of the class animal, and 00:37 then we assigned it to a variable. 00:38 Now, before I was just saying it's an object that we created, but it's an instance. 00:42 You want to start using that word. 00:44 You can use object too when you are talking about it. 00:45 That's perfectly fine. It is an object. 00:48 But it's an instance of a class, and that's very important for us. 00:51 I am going to go into my ruby_sandbox, where I have got a file called classes.rb. 00:57 You can either copy the code down or you can pull it from the Exercise Files, 00:59 but it's basically just the simple animal class definition that we did earlier 01:03 in IRB, but now I have put it into its own file. 01:06 Right here is where we are creating the instance. 01:09 What gets returned to us from Animal. new as a result of the operation is an 01:13 instance, and then we are assigning that instance to animal, and then we tell 01:17 that instance to make_noise. 01:19 Now, we can do that again. 01:20 Let's say down here. 01:22 Now we are creating a new instance. 01:23 We will call this one animal2 and this one animal1, just so we can tell the difference. 01:27 Now we can ask animal2 to do the same thing, animal2.make_noise. 01:32 Now, these two instances are going to do the same thing. 01:36 Both of them are going to return Moo! 01:37 But the important point about instances is that they are not the same, they are 01:41 two separate objects, and we will see how to differentiate them a little later 01:44 on, but it's important to realize that every time we call Animal.new, we get 01:50 back a new and different instance, a different object. 01:54 It's a lot like one of those memo pads, like a while you were out pad, and you 01:58 can pull off a separate page every time you need to take a new message. 02:02 Each one is different, but they all came from the same place. 02:05 The memo pad is like the class. 02:07 It has all the definitions for it. 02:09 It has all those blank spaces filled in. 02:11 It predefines how you can take a message. 02:14 But then each and every time we pull it off, we make a new instance of it, and 02:19 we fill it out differently, with different information for each call. 02:22 So how can we fill in the blanks on these forms? 02:25 How can we tell our animals apart so that different animals make different 02:28 noises, or have different names, or different numbers of legs? 02:31 Well, in order to do that we are going to need to put variables inside of our 02:34 class definitions to hold those attributes. 02:37 Let's look at attributes in the next movie to see how, and then I think it will 02:40 be a lot clearer when we are dealing with different instances. Collapse this transcript Attributes  00:00 Now that we understand about instances, we need to learn about attributes, 00:03 because attributes are how we can differentiate between instances. 00:07 For example, if we have an instance of our Animal class that we want to be a 00:09 pig, and then another one that we want to be a cow, we have to have some 00:13 attribute that will give us a way to tell the difference, and tell which one is 00:16 the pig and which one is the cow. 00:17 If you think back to our Memo Pad example that I gave you earlier, an 00:20 attribute would be the blanks on the form that we're going to fill out with different values. 00:24 So what we're really talking about when we say attributes, are values that will 00:28 persist inside of an instance, and for that we're going to need a variable, and 00:33 a variable that can store those values and keep them around inside the instance. 00:37 It turns out that Ruby has a variable that's especially designed for just that purpose. 00:41 We've seen it before back when we were looking at variable scope. 00:44 It's the instance variable, and it's prefixed with the @ sign, just a single @ 00:47 sign and that's how we know right away that it's an instance variable. 00:51 Instance variables are what we're going to use to store the attributes inside of our instances. 00:57 You may notice that there's also a class variable right above that. 01:00 You may think, well, why don't we use a class variable inside of this class. 01:03 We'll get to that a little later on. 01:05 Right now, just focus on the instance variable, because that's really what we 01:08 want to use inside of the instance of the class, not the class itself, but the 01:12 instance of the class, and instance variables are going to allow us to keep 01:16 track of attributes. 01:17 So let's try adding a noise attribute to our Animal class that's inside 01:22 classes.rb, the file we've been working with. 01:25 Calling an attribute is just as easy as saying @noise. 01:29 That's all there is to. 01:29 We're just asking for the value of this variable. 01:32 Now the question is how do we set that variable to begin with, so that we can 01:35 then return it and make a noise. 01:38 Well, we could just simply do Moo, but that doesn't really give us a lot of 01:41 flexibility, then all our instances are the same. 01:44 It's sort of like having a preprinted memo pad, and every time you pull of a 01:47 page, it says that the call was from John Smith. 01:49 The call needs to be from different people. 01:51 So, we're going to take that away. 01:53 Let's try experimenting with setting in a different way. 01:55 Let's say animal1.noise = "Moo !", there we go. I'll save that. 02:02 Let's try running that. 02:03 I'll open up my command line. 02:05 Notice that I'm already inside my ruby_ sandbox, and I'll run ruby classes.rb. 02:10 You'll see that it returned an error to me. 02:12 This error indicates a very important point about Ruby that I cannot stress 02:16 enough, which is that you do not -- never, never, never, do you have access to 02:22 instance variables from outside of the instance. 02:25 They're locked away inside of the instance, on their own, where we can't get to them. 02:30 We can't set them, and we can't retrieve them. 02:33 Now wait a minute, you're thinking, we can't retrieve it. 02:36 That's exactly what we're doing when we say make_noise. 02:38 Well, that's because we can access the methods that are inside the class, and 02:43 the methods have access to it. 02:45 So the scope of an instance variable is inside all of the methods of the 02:50 instance, but not outside the instance. 02:52 So, if we want to get to those values of the instance variable, we have to do 02:57 it by using methods. 02:58 It's a very important point. 03:00 So instead of doing this line that won't work, instead let's set an instance 03:05 that will set it for us. 03:06 So let's call it set_noise. 03:09 For now, let's just say @noise = "Moo!". 03:13 Now this doesn't give us the flexibility that we're looking for just yet, but 03:18 we'll get there in one second. 03:19 Let's go ahead and just try running this first. 03:21 Of course, we'll need to call that method. 03:23 So let's say animal1.set_noise. 03:26 Let's save it and let's try running that. 03:29 So once again here I am. 03:30 I'm going to run ruby classes.rb again, and you'll see that it did call it. 03:34 Now, notice a couple of things here, first of all, notice that now my two 03:38 instances are different. 03:40 The first one has a noise of moo, the second one has a noise of nil, because 03:43 nothing was ever set, we didn't call set_noise down here. 03:46 We just didn't make noise. 03:48 So the first point is we can see now we have two separate instances that are different. 03:53 The second point is notice that the instance variable persisted inside the 03:59 instance, and we still have access to it when we called a different method. 04:04 So we set it here, we've retrieved it here. It didn't matter. 04:08 It was still around. 04:09 We still had access to it, even though they're in two different methods. 04:12 Now as I notice, we're still missing that flexibility to make our instances 04:16 sort of be whatever we want, but we know how to do that with regular method 04:19 calls, set_noise equals to noise, and now @noise can just equal noise, and 04:25 there's no conflict. 04:26 This is a instance variable, this is a local variable. 04:29 They are two different types. 04:30 So as far as Ruby is concerned, they are completely different. 04:33 They point to two different locations. 04:35 There's no conflict there. 04:36 So now when we say set_noise, we need to specify, Moo, and then let's go 04:41 ahead and do the same thing down here animal2. set_noise, and let's make it equal to Quack. 04:48 There we go. 04:48 Let's try it saving it. 04:51 We'll go back to the Terminal, and we'll run them, and I get back Moo and Quack. 04:55 So now we have two different instances with an attribute that we have access to. 05:00 In the next couple of movies, I want to explore the idea of how we can get and 05:03 set these attributes using methods a little bit further, and show you some 05:06 shortcuts that I think will help a lot. Collapse this transcript Reader/writer methods  00:00 At the end of the last movie, we saw how we could access our instance 00:03 variables using methods. 00:05 I want to explore that idea a little further in this movie by looking at 00:08 reader and writer methods. 00:10 Now this is a very common feature of object-oriented programming languages, 00:13 sometimes in other languages, they're called getter and setter methods. 00:16 Either one is fine. 00:17 You can use either name, typically in Ruby, we're going to call them reader 00:20 and writer methods. 00:22 So, here is the code where we left off inside classes.rb, what we're looking 00:26 for is a pairing of something that will set a value for us and something that 00:30 will get a value for us, and that's exactly what we already have with our two methods. 00:34 The first one is a setter method or a writer method. 00:38 So we're setting the value of noise equal to a value, and this is the classic 00:43 format for what a setter method would look like, set_noise equal to a value. It sets it for us. 00:48 Then we have make_noise. 00:50 Typically, we would call this get_noise. 00:52 It's a getter method. 00:53 We need to change it here when we call it as well. 00:57 In either one what do you say, make the noise or get the noise is up to you, but 01:00 the idea is that we are getting that value back. 01:03 One of these methods sets it, one of them gets it. 01:06 Now the important thing about these two methods is that they give us access 01:09 control over these instance variables. 01:12 So for example, if we were to take away get_noise, now we have no way to 01:16 get that value back. 01:18 Of course, we could write another method that would do it for us, but our 01:21 getter method is gone. 01:23 Same thing here, if we were to take away set_noise, now we have no way to set it 01:27 by calling a method. 01:29 There might be another convoluted way, maybe a back end way that we could 01:32 get that value set, but we need a setter method if we want to be able to do it directly. 01:37 Now set_ with whatever the attribute name is and get_ is very common in a lot of 01:42 programming languages. 01:44 In Ruby though, we can make use of that syntactic sugar that we looked at 01:47 earlier, and so we can take get _out and simply ask for noise. 01:52 So it asks Animal to do the method noise, which returns the instance variable, noise. 01:58 It's a very commonsense approach. 02:00 So now animal1.noise and animal2. noise just returns that to us, where the 02:05 syntactic sugar comes in is that we can do the same thing with set, we can take 02:09 away set and just say noise=(noise). 02:14 Remember how we did that earlier? 02:15 So noise =, and we saw that the parenthesis are optional, we can take away 02:20 those, and now it feels just like we're assigning it to a regular variable. 02:26 So it feels like we're assigning it to that attribute, but we're not. 02:30 We're actually passing it through a method to get to that attribute. 02:34 That's a very important difference. 02:36 So we get the usage, as if we're just assigning it, but something more complex 02:40 is going on behind the scenes. 02:41 Now if we didn't have this method, then as we saw earlier, noise= "Moo!" 02:45 won't work anymore. 02:47 It'll say oops, sorry, I don't have a method called noise =, we have to write that method. 02:52 That's our setter method. 02:53 So now that we have our class rewritten in the sort of standard way that Ruby 02:57 does these reader and writer methods or getter setter methods, let's save it 03:01 and just try it out. 03:02 I'll go into the Terminal, notice I'm inside my ruby_sandbox, ruby classes.rb, 03:07 and it still works exactly as we would expect. 03:09 Now some of you are probably thinking, well, that's cool, and I'm glad that I'm 03:12 able to just use this = and everything, but are you telling me that every time I 03:16 have an attribute, I have to write this pair out? 03:20 Well, fortunately not, there is a shortcut, and I'll show you that in the next movie. Collapse this transcript Attribute methods  00:00 We've seen how we can use reader, writer methods in order to access our attributes. 00:04 We saw how we could even use Ruby's syntactic sugar to make the process a little easier. 00:09 But one thing that's striking is you may be thinking, well, what if I have a 00:11 large class that has lots of attributes? 00:15 Are you telling me that I have to go through and write a reader and a writer 00:19 attribute for each and every one of those attributes. 00:22 Well fortunately, the answer is no, you don't have to do that over and over again. 00:26 Ruby provides a very nice shortcut, because this is done so often that we can 00:30 just simply use an attribute method. 00:33 These are also frequently called attr methods or attr_* methods, and that's 00:40 because they all begin with attr_ and that * serves as a wild card for 00:45 everything that comes after it. 00:46 The three methods that we're going to be looking at are, attribute reader, 00:49 attribute writer, and attribute accessor. 00:52 Each one of them is actually a method that we're putting into our class. 00:57 When that method gets called, it takes a symbol that we provide after it and 01:02 turns it into a method for us that we can then use. 01:06 So it's exactly the same thing as if we had typed it out the long way. 01:10 You can guess what each of them does. 01:12 Reader creates a reader method, writer creates a writer method, and accessor 01:16 creates both a reader and a writer method. 01:18 So for example, attr_reader with the symbol name passed to it as an argument, 01:24 will tell your class, hey! 01:26 class while you're booting up and getting started, create this definition for 01:30 me, create this method that I can then call. 01:32 It's exactly the same as if we had typed it out ourselves, exactly the same. 01:37 Same thing for attribute writer. 01:39 It's the same as if we had typed that same thing we did for noise, if we 01:42 had attribute_writer : noise, then it would give us def noise = (value) @noise = value. 01:51 And that's exactly what it would create inside the class for us, just by typing 01:55 this one simple line, and where that really pays off, of course, is when we want 01:57 to be able to read and write. 01:59 So attr_accessor creates two methods for us, and saves us a whole lot of typing. 02:05 If we want to create more attribute accessor methods, we just put a comma after 02:09 the first one and put another symbol and then a comma and so on for all of the 02:13 different methods that we wanted to create. 02:15 It really is that easy, but it was important that I showed you the long way of 02:19 doing it before I showed you the shortcut, so that you would really have an 02:22 understanding of what was happening under the hood. 02:24 So let's try using these attribute methods. 02:27 So in our classes.rb file that we've been working with, I'm not going to 02:31 change the class Animal's noise, we already have those methods, I'm not going to replace them. 02:36 Let's create some new ones just to see how easy it is. 02:38 attr_accessor and then let's make the animal's name, so we'll name our animals, 02:45 Tom, Fred,something like that. 02:47 Then we could have attribute writer, and let's set the attribute writer equal to 02:53 the color of the animal. 02:55 Let's say the attribute reader will set equal to the number of legs the 02:59 animal has and then arms. 03:01 We can put commas so that we could indicate that we have more than one. 03:04 So let's stop and realize what it did for us here. 03:06 When we first load our class, right, when it first reads in this file, it will 03:11 start processing the class. 03:12 It will come here and will say, "Ah! 03:14 The attr_accessor method. 03:16 I need to execute this command." 03:18 What it does is creates attribute accessor for a name, which looks pretty much 03:23 like what we have here for noise, except with name in place of noise. 03:27 So now, we'll have an instance variable called @name that we can access. 03:31 We can access it in the same syntactic sugar way that we were accessing noise. 03:35 Then once it's done with that, it will come to the attribute writer line and 03:39 it'll say, "okay, let's create a writer for color." 03:41 So that will create only this one for color, there will not be a method like 03:46 this that will allow us to ask for the colored by just saying .color. 03:50 Then last of all, it will come to the reader line and say, "Ah! 03:52 Let me create a reader for legs and a reader for arms." Not a setter. 03:57 There'll be no way to set it. 03:58 So let's just try making use of those. 04:00 Let's say here with animal1, let's say, animal1.name = Steve, and then let's ask 04:08 it to puts animal1.name so that it will output it. 04:13 So we should have been able to write to it, and to read from it with those two steps. 04:17 Let's try doing animal1.color = "black ", and then let's do puts animal1.color. 04:29 Now we should be able to write to it, but not read from this, so we're expecting 04:32 an error there already. 04:35 Then let's try last of all animal1., and we'll just do legs = 4, and 04:42 animal1.legs, we'll need a puts in front of that. There we are. 04:48 So that should return these values to us, but we're going to get a couple of 04:50 errors, because we're trying to read and write when we shouldn't be allowed to. 04:53 So let's just see how far we get. 04:55 Let's go into the command line, notice that I'm already inside my Ruby sandbox 04:59 and I'll run ruby classes.rb, and sure enough, undefined method color. 05:04 So the method color doesn't exist. 05:06 It did, notice, come back and report Steve, first. 05:11 But then it got to here and it said, no, sorry, I do not have this method. 05:15 It didn't complain here. 05:16 It has that method. 05:17 It didn't have the reader method. 05:19 Now we could put our own in. 05:21 Let's just do that, def color, and let's return @color. 05:26 Maybe we want to do something a different though, maybe we want to say return, 05:31 The color is, something like that. 05:37 Now let's run it, we'll still get an error later on, but we'll get a little further. 05:41 Now we get, The color is black, so now we get undefined method legs=, because we 05:47 did not allow it to be a writer. 05:48 It's just a reader. 05:50 So we need another way to set the legs. 05:52 Let's say that we have something here. 05:54 Let's create another method at the beginning called def, and setup_limbs, we'll call it. 06:00 When we set up limbs, we're going to say, well, legs should be equal to 4, and 06:06 arms is going to be equal to 0. 06:08 So let's just try running that real quick, we'll need to run that setup. 06:12 Let's do it right here, animal1. setup_limbs, and let's run it. 06:21 Now we get -- oops, sorry, we still have that legs=, we've got to take that out 06:24 of there, because that won't work. 06:26 We can't set it anymore, but now we can still read it back. Let's try that. 06:31 Undefined local, puts animal1, that's my mistake, little typo. There we go. 06:36 So now everything returns to us. 06:40 So take a moment and look at what we did there. 06:43 We used these attribute accessors, writers and readers, to make these 06:47 definitions for us much easier. 06:49 We can still define it ourselves, we can do something different when we define 06:53 it, or we can still have access to those through other methods, but it's just 06:57 the simple access that we'd lose, right, we don't have that direct access to 07:01 them unless we write something for ourselves or use these attribute methods. 07:07 Because creating these kinds of attributes that have access control is done so 07:11 commonly, Ruby provides this very handy shortcut for us. Collapse this transcript Initialize method  00:00 Very often in Ruby when we create an instance of a class, we want that instance 00:04 to do some initialization, settings some default values or calling some default 00:08 methods that will happen right at the beginning. 00:11 We saw a little bit of this in our last movie, when we wrote the set_limbs 00:15 method, and that was a method that we called that set the values of the legs and 00:20 arms, instance variables on our animals so that they had a default value there. 00:24 Well, instead of calling a separate method that will set those up, let's instead 00:27 use the initialize method. 00:28 So here I'm in the Animal class that I've been working with. 00:32 Creating an initialize method is just as easy as calling initialize. 00:36 That's all there is to it. 00:37 Now I no longer have to call setup_limbs, boom! Done! 00:41 It's just like that. That simple. 00:42 Now initialize will set any values that I want here. 00:46 It will also allow me to execute some other Ruby code, I can call other things in here. 00:51 Maybe I want to output something. 00:53 Let's try puts "A new animal has been instantiated". 00:59 That's a fancy word for saying we created an instance of it that has been 01:03 instantiated, a new animal has been instantiated. 01:06 Let's go into the command line, you'll notice that I'm already inside my Ruby 01:10 sandbox, and I'll just run ruby classes on it. 01:14 There we are, A new animal has been instantiated, right? 01:18 So it both sets values, but also calls any Ruby code that we want to put in there. 01:22 So that can be any number of things, but every time an instance of this class is 01:26 created, do these initialization routines. 01:29 Now notice that we never call initialize explicitly, initialize is sort of a 01:33 magic method name that happens automatically on every object. 01:38 So when we call Animal.new, the new method creates it, but the first thing that 01:44 does upon creating it is run this initialize method. 01:47 Now initialize, like all methods also allows us to pass in arguments to it. 01:52 So let's say, for example, legs, arms, and actually let's go ahead while we're 01:57 at it, and let's put noise at the beginning. 02:00 Well now, we have access to legs and arms, and while we're at it, let's go ahead 02:05 and set @noise = noise. 02:08 So it works exactly like a regular method, there is nothing special there, but 02:11 how do we pass these values into initialize? 02:15 Well, it turns out that the new method takes values and passes them right 02:21 on into initialize. 02:22 As soon as it creates the new object, it passes initialize, whatever values it has. 02:26 So we could for example, say, Moo is the noise, and legs, and arms, 4, and 0. 02:32 So now we no longer have to set noise, and we never set legs that was being set 02:38 before in the methods. So let's try that. 02:40 Let's go back and run ruby classes. 02:43 You'll notice that now, I'm getting a wrong number of arguments error. 02:47 That's on my second animal, because once we start adding arguments to new, it 02:52 always expects them, unless we set some default values. 02:55 We saw that before with methods, there is nothing really new there. 02:57 But let's just do Quack, there we are, so we don't have to set this anymore. 03:02 Now for legs, let's set the default equal to 4, and the arms, the default, equal to 0. 03:07 So now we don't have to pass those values if we don't want to. 03:11 When we run it, everything works exactly like we would expect. 03:14 So that's all there really is to using initialize. 03:17 It works just like a normal method, the only difference is that it gets called 03:20 automatically, and that values we pass into new, get passed on to initialize. Collapse this transcript Class methods  00:00 So far we've been creating instances of our classes, and then we've been calling 00:04 methods on that instance. 00:06 We can also call methods directly on the class itself, and we call those class methods. 00:11 The other ones of course are called instance methods. 00:13 So in this movie we're going to look at how we can create our own class methods. 00:17 We've already seen how to call a class method, even though we didn't know it, 00:20 and that was Animal.new. 00:22 New is a class method. 00:24 It's a built-in class method to every Ruby object. 00:28 So, we can call new, and it returns a new instance to us. 00:31 But it's still a method. 00:32 It's a method that exists on the class even when we don't have an instance, and 00:38 that's what you are going to use class methods for. 00:40 We are going to use a class method when we want the class, to do something for us. 00:44 Even though there may not be an instance in sight. 00:46 It doesn't matter, maybe there are instances, maybe there aren't.Defining class 00:49 methods is very simple. 00:51 We do it just the same way we do instance methods except that we put the self 00:55 keyword in front of it. 00:56 So, self.method_name let's us know that that is a class method. 01:01 It's something that our class itself will execute. 01:04 Now the self keyword in Ruby applies to whatever object we're currently in. 01:09 So, in this case, where we're in the definition of the class, self is the class itself. 01:15 If that still sounds very abstract and hard to get your head around, I think 01:18 it'll be easier once we actually try a couple of examples. 01:21 So, let's start by adding a class method to our Animal class, in the classes.rb 01:27 file we've been working with. 01:28 I am going to do that above initialize, this is mostly by convention. 01:31 You can put it anywhere in the file you want, but typically you'll put the class 01:35 methods above the instance methods. 01:37 So, def self and let's create one called all_species, just like we were talking 01:42 about, and this will return a list of all possible species that this class knows 01:47 about, obviously not all species that exist. 01:50 And I am just going to paste in an array there, 01:54 [cat, cow, dog, duck, horse, and pig]. 01:54 So, when we ask the class, hey! 01:57 Tell me about all the species that are there. 01:59 It doesn't have to have an instance, it already, the class itself can tell 02:03 us that information. 02:04 We don't have to create an instance in order to get to it. 02:06 So, let's just save it. Let's try it. 02:08 Before we even get to an instance down here, let's just do puts, and we will ask 02:12 the Animal class for all_species, right? 02:17 This will put out an array. 02:19 Let's go ahead and do an inspect on the end of the array too, just so that it 02:22 outputs it in a little nicer format for us to read. 02:24 So, let's switch in our command line, notice I am already inside my 02:28 ruby_sandbox, ruby classes.rb, and there we are. 02:33 Just see that it outputs the array. 02:35 So, there are three important things to notice there. 02:38 The first is that we didn't have an instance when we called it, the second is 02:43 that the way we called it, which is to call it directly on the class, and the 02:46 third, was that the kind of information that we returned was general information 02:51 about the class itself. 02:53 Now as I noted earlier, new is an example of another class method. 02:57 We can create our own class methods that do things for us instead of just 03:01 returning information. 03:02 So, let's add another method, this one will actually do something instead of 03:06 just returning information, let's say a def self create_with_attributes, and 03:14 the attributes we are going to pass are going to be the noise that it should 03:17 make and the color. 03:20 And then all we are going to do is create a new class, right? 03:24 Create an instance of the class, and then make sure that it has these attributes 03:29 before we return it to the user. 03:31 So, we'll still need to create one Animal.new, and you'll remember when we 03:36 created new, now we need to provide a noise. 03:39 That's a required attribute. 03:41 So we'll pass in noise, and let's go ahead and capture that as a local variable. 03:46 Animal =, and then we could set, animal.color, because we set attr_writer to color up here. 03:53 So, we can write to color and we will set it equal to color, and then last of 03:57 all, we'll return the Animal object itself. 04:00 So, we are going to return the actual animal instance, so we are calling a 04:03 class method, just like new, but what happens in the end is it returns an 04:08 instance of it to us. 04:10 So, we'll try this out, before we do it, I just want to point out one more thing 04:13 which is that I am calling Animal.new here, because I am inside the class, I can 04:18 also make use of self. 04:19 Once again, the keyword refers to the object that we are in. 04:22 It's not only for use in defining our methods, we can make use of it 04:26 throughout the method. 04:28 Let's drop-down, and let's try this create with attributes. 04:31 Let's just do it to animal2 here. 04:33 Let's try create_with_attributes, and now in addition to Quack! 04:39 I just need to tell it white, and then puts animal to noise, and the very last 04:44 thing we puts animal color, right? 04:48 And I have a method here that will report the color to me. 04:51 So, let's try that out. 04:53 ruby_classes.rb, and there we go. 04:55 We are getting the Quack! 04:56 and we are getting the color as white. 04:58 So, both are being set by our new create with attributes method. 05:02 So, hopefully now you see not only the mechanics of how to work with class 05:06 methods, because I think that's pretty easy, but also conceptually, when you 05:10 would use it, I think that might be a little bit harder. 05:12 It's things that are at the class level that do not have to have an instance 05:16 around, things that are more general than a specific case. Collapse this transcript Class attributes  00:00 So far we've seen how we can create methods on our instances, and those are 00:04 called instance methods. 00:05 We've seen how we can create methods on our classes, and those are called class methods. 00:09 We also saw how we could put attributes on our instances. 00:12 It would store values that were particular to the instance. 00:16 In this movie, we are going to take a look at class attributes, which are the 00:19 same thing, but attached to our classes. 00:22 That is, they are for storing values that apply to the class generally. 00:26 The same way that the class methods apply to the class generally. 00:30 And the way that we'll store these values is going to be in a class variable. 00:34 We took a peek at this before, when we were looking at instance variables. 00:37 It's in the line right above it with two @ signs in front it. 00:40 So, if you see the two @ signs, it's a class variable and it will persist 00:44 anytime we have the class, even if we don't have an instance. 00:48 Instance variables are going to be only inside of the instance. 00:52 That's the only time we'll have those. 00:53 Let's see how we can use class variables. 00:56 Finding a class variable is very simple, we just simply use @@ and let's say species =. 01:02 That is now the class variable. 01:05 It will persist anytime we have the class. 01:08 Now the one problem with this is that this doesn't get defined unless we call 01:13 this method all_species, right? 01:14 We don't have an initialize method, like we do with a class or we could do 01:18 this when it boots up. 01:20 Instead what we do, if we want it to happen automatically, as we put it in the 01:24 body of the class, then it happens when the class is defined. 01:28 So, the initialization of the class is when it gets read in. 01:32 So, the Processor starts reading in the file and it says, "Ah! 01:35 I am going to create a class, I am going to call it animal, I am going to create 01:38 an attr_accessor, a writer, and a reader, and I am going to set the class 01:42 variable species equal to this array. 01:45 Then I am going to set up these definitions for what I'll do inside the class 01:49 and inside my instances" and so on. 01:51 So this is the way that we define class methods that we want to exist from the 01:54 beginning, all right? 01:55 It's the same as putting them in the initialize method for an instance. 01:59 So now if we want to retrieve that value with all_species, we just say 02:03 @@species, and that will now pull back that value that was already set, we don't 02:08 have to even have an instance. Let's try it out. 02:11 Notice down here, I've already got the call to all_species, right? 02:14 puts Animal.all_species, I don't have an Instance at this point, but first 02:18 Instance gets created here. 02:20 So, we're looking up here where we don't even have an Instance. 02:23 Let's open up the command line, notice that I am already inside ruby_sandbox, 02:27 ruby_classes.rb, there we go. 02:30 It still pulls up those values. 02:32 That is now being stored in a class variable which is also known as a class attribute. 02:37 Now this is certainly a perfectly appropriate time to use a class variable, 02:42 because we are using it for information which is general for the whole class, so 02:46 then we have the ability to say oh! 02:48 When we are creating a new Instance maybe, maybe we want to make sure that it's 02:52 one of these species. 02:53 That's a possibility. 02:55 We can now check that against our @@species variable. 02:58 Another very common use case is if we want to keep track of things. 03:02 Let's say we have @@total_animals, very common usage, total_animals = 0. 03:08 And then whenever we initialize a new animal, all we have to do is say oh! 03:12 By the way, while you are initializing it, let's take @animals and do += 1. 03:17 Now the number of animals increases every time we create one, so we can keep 03:21 track of how many animals have been created. 03:23 That's one way to do it. 03:25 Another great use of class methods is not just to keep track of the number of 03:28 animals but to actually keep track of the instances themselves. 03:31 So, let's say here we make this instead current_animals. 03:35 So, if the animals that have been instantiated, the ones that have been created 03:38 and instead of being 0, it's going to be an empty array. 03:42 So, now down here we'll say well, current animals should instead be appended the value self. 03:50 In this case self is an Instance, it's the instance that's just been created 03:55 and that keyword will plug that object not the class, remember self is a magic 03:59 word for the object. 04:00 It's inside at that moment. 04:01 But this time, it's going to be the Instance. 04:04 Up here, when we used it, it was the class. 04:05 So, we are using it for an Instance and saying put this Instance inside the 04:09 current animals array, so we can keep track of it. 04:12 So, let's drop down here to the bottom and we'll put, puts Animal to class, and 04:18 now we want to ask it for its current_animals. 04:22 That's what we want to ask it to return is the current_animals. 04:25 Let's go ahead and put inspect on it. 04:27 So, we can see what that array is going to look like in a nice friendly way. 04:29 So, let's try that out and let me give you a hint this is not going to work. 04:33 We've done ruby_classes, at the end undefined method, current_animals. 04:37 Now that's because the same thing is true with our class attributes and class 04:42 variables as was true with our Instances. 04:45 We cannot access those values, those attributes from outside the class. 04:50 In order to do that, we need Reader and Writer methods. 04:54 Let's see those in the next movie. Collapse this transcript Class reader/writer methods  00:00 We saw how instance attributes used Reader Writer methods to give access to them 00:04 from outside of the instance. 00:06 Now in this movie we are going to see how we can use Class Reader/Writer methods 00:10 in order to get access to our class attributes from outside of the class. 00:15 So I am still inside the classes. rb document we've been working with 00:18 throughout this chapter, and in the last movie we tried to call the current 00:21 animals method on animal, right? 00:24 And we can't do that because we don't have a current animals method. 00:27 We have a current animals attribute, right? 00:30 A class variable that we set, but we don't have a method to be able to read it. 00:34 Well writing that method is trivial, right? 00:36 We know how to do that, self.current animals. 00:40 And then what should it return? 00:42 It should return current animals. 00:45 That's all there is to being able to create a Reader method for this that will 00:49 let us return that value. 00:50 In fact we had already done that here with all species. 00:53 We could just simply shorten this to species and then it kind of fits more with 00:57 the typical Ruby style of giving us a little bit of syntactic sugar. 01:01 In fact we could have a Writer method, def self.species=, and then let's ask it 01:08 to provide an array and we will give it a default value of an empty array. 01:13 And then, of course that's going to take @species and it's going to set it equal 01:18 to the array that we've asked it to send out. 01:21 So now just like we had for the Reader/Writer methods we created for our 01:25 instance methods, we have the same thing here for our classes. 01:29 Species is the Reader, species= is the Writer. 01:32 The only difference is that we are saying it's a class method and it's a class 01:37 variable being returned. 01:38 Otherwise it's exactly the same thing as what we had before. 01:41 Let's go ahead and just prove to ourselves that worked. 01:43 I am down here before we asked for species, we'll need to change this from all 01:47 species to just species, right? 01:48 or that will break. 01:50 Let's set that value, let's say Animal, tuck in the class directly, species=, 01:56 and let's throw in a couple of species here. 01:57 Let's say frog and fish. 02:01 So it's just something that's very different than what we had before. 02:03 Now we'll just go back into our command line. 02:06 Notice that I am already in my ruby_ sandbox, ruby classes.rb, there we go. 02:11 Now the value of species has been set. 02:14 So I will set it to something different and I read it back. 02:17 I use my Reader/Writer attribute on it. 02:18 You'll also notice that the other request you made down here at the end, for 02:22 current animals, one we were trying to do in the last movie that also works now 02:26 as well because I created a Reader method for it. 02:29 So now it returned to objects for us. 02:32 What we are seeing here is a summary of the way that it tries to represent an 02:36 object as text, right an object is not text. 02:39 But it tries to give us something that sort of looks like it. 02:41 It shows us what all those instance variables inside there are. 02:44 So we've got the first animal which has color black name Steve, noise is Moo, 02:49 the second animal color white, noise equals Quack! and so on. 02:53 So we've got those two objects inside of our array. 02:55 We can also apply the Each method to it instead of inspecting it. 02:59 We saw how to use the Each iterator to loop through each of those items one at a 03:03 time and then we could ask it for its color, or its sound, or something like 03:06 that, and so that will tell us then all the current animals. 03:10 It will keep track of the inventory for us. 03:12 So that's really all there is to creating these class methods that are 03:15 Readers and Writers. 03:16 The one thing though is that we had before this attr_writer and we had 03:22 attr_reader and attr_accessor. 03:25 We don't have anything like that at the moment in Ruby. 03:28 That may be something that comes in a future version, some kind of a 03:31 convenience method for that. 03:32 Right now you have to write it yourself, which isn't as big of a problem, 03:36 because these happen a lot less often than the other kinds of attributes. 03:39 The other ones happen very frequently so we need the shortcuts. 03:42 For the class ones you'll have to write them out yourself or you could create 03:46 your own method that would do that for you. 03:49 In Ruby on Rails, for example they've created one called cattr_accessor. 03:54 So you can make use of it if you are using Ruby on Rails you can get access 03:58 using the cattr_accessor. 04:01 But that's something that doesn't exist in the core Ruby currently. 04:04 It's something that was added on. Collapse this transcript Inheritance  00:00 In this movie we're going to talk about class inheritance. 00:03 Now this is not the same as inheritance in the real world where you inherit 00:07 money and objects and things like that from your ancestors, instead in 00:11 object-oriented programming inheritance refers to inheriting the methods and the 00:15 attributes of another class. 00:17 It's a very common feature in almost all aspects of object-oriented programming 00:21 and Ruby is no different. 00:23 Let's say for example that we have our Animal class that we have been working with. 00:26 Up until now we've been creating instances of that Animal class. 00:29 We can create several instances and then we can give attributes to 00:33 those instances, right? 00:34 Cow, pig, and duck, we can give them sounds. 00:36 Well, instead of doing that we could also have the ability to create 00:40 subclasses, classes that would inherit information from animal which would be 00:45 their own unique class. 00:47 So instead we can have the animal class and create a new class called cow, and 00:52 that cow class would inherit all of the behaviors of the animal class, but it 00:57 would be its own class at the same time. 00:59 We could also create another one called pig and another class called duck. 01:02 All of these would inherit from animal but each would be its own class. 01:05 They could have its own unique behaviors separate from the animal class, even 01:09 though they sort of come from the same route. 01:11 We call the parent class animal, the Superclass or you can just call it the 01:16 Parent informally, but technically it's the Superclass, and these other ones are 01:19 the Subclasses and we call those the Children informally. 01:23 So we talk about the parent and child relationship a lot of time between two 01:26 different classes, what we are really talking about is the superclass and the subclass. 01:30 And then from these classes we could create our instances, we could have a Cow 01:33 instance and give it the attribute Moo for its noise. 01:37 We could do the same thing for pig, make it Oink and Duck would be Quack. 01:40 So we would end up with in the end are still three instances. 01:43 But there are three instances that might have very different behaviors. 01:48 For example, a duck might have a whole bunch of methods related to being able to 01:52 swim, but a cow doesn't need to have those methods. 01:55 A cow might have a lot of methods about how to graze for grass, but the duck 01:59 doesn't necessarily need those methods. 02:01 But at the same time all of them could inherit some common attributes, from 02:06 animal, like the fact that they have legs, like the fact that they make a noise, 02:10 those would be inherited. 02:11 I think I have illustrated it enough. 02:13 Let's try to put into practice. 02:14 I am still in my classes.rb file that I have been working with. 02:18 I am going to use a feature of Textmate here to just fold up some of these 02:21 methods that we've been working with. 02:23 It's kind of a nice feature that Textmate just kind of let you get things out of your way. 02:26 So that's what I am doing here. 02:27 If you are not using Textmate that's fine. 02:29 It just clears up my screen a little bit and gives me a little more room. 02:31 I am going to just create a new class and we are just going to call this class Cow. 02:36 The way we indicate that it is a subclass of the animal parent class is with 02:41 the less than sign, and then the name of the parent class, and you'll notice 02:45 that it gives me a nice italicized thing there in Textmate to let me know that 02:49 that's what's happening. 02:50 So, the cow class inheriting from animal. 02:54 That's all we've got so far. 02:55 Let's just try with only that. 02:58 Let's try to drop down here at the very bottom after we've done all this stuff 03:01 that we've already been working with. 03:02 Let's do a new one. 03:03 Let's say maisie, the cow, is going to be Cow.new. 03:08 Let's just try that out, see what happens, if we just try and create a new 03:12 instance of our class Cow. 03:14 Let's go to our command line. 03:16 Notice I am already in my ruby_sandbox, ruby classes.rb. 03:21 So it goes through and it does everything as we would expect. 03:23 And then right here it says, "woop, wait a minute. 03:25 Initialize wrong number of arguments. 03:28 You sent 0 and I was expecting 1." 03:30 Well, that's because it inherited this initialize method. 03:35 Initialize is expecting us to pass in a noise. So let's try that. 03:39 Let's try passing in a noise when we create it. 03:41 So the noise will be Moo! 03:43 And then let's go ahead while we are down here, and let's say well, let's puts maisie.noise. 03:49 Let's ask what Maisie's noise is? 03:50 So let's go back ruby classes, we'll run one more time. 03:54 This time it works. 03:55 You'll see a new animal has been instantiated. 03:58 That's happening in that Animal parent class, but we inherited that into the Cow 04:03 class, and then it comes back with Moo! 04:05 So, it did successfully created. 04:07 It created it using this initialize method and we were able to use this noise 04:12 method to return a value all just by sub-classing Cow from Animal. 04:18 Now there is a difference here, right, if we were to say do say puts 04:22 animal1.class and puts maisie.class. 04:28 Then we'll actually see those class names. 04:30 Let's run that one more time. Oops! 04:31 Sorry I mis-spelled maisie, and let's try that again, maisie, there we are. 04:38 Now it comes back and it tells us, aw! 04:39 one is an animal and another one is a cow. 04:41 Now we can actually tell the difference. 04:43 Now we have the ability to know is this thing I am working with a cow, we don't 04:47 have to give it an attribute of cow it can be the class cow and have different 04:52 behaviors that we can add on. 04:53 We could just as easily go in and start adding new features to our cow class 04:58 that would exist only in cow. 05:00 They would not be in animal. 05:01 So we get everything that was in animal, plus anything we define inside cow. 05:06 And that's really all there is to the fundamentals of creating Inheritance. 05:09 It's a good way to help organize your code, but there are also some very 05:12 powerful things that we can do when we start borrowing features from other classes. 05:17 The one last note I want to make on this is that unlike a lot of object-oriented 05:20 languages, we cannot have multiple inheritance. 05:24 In Ruby we can inherit from one and only one superclass, right, not 05:28 multiple, only one each time. 05:31 If you want to have different behaviors we'll use modules for that, we'll talk 05:34 about that in the next chapter. 05:35 So I just want to make a footnote, if you are used to working with other 05:37 languages that use multiple inheritance, here you can only inherit from one class. Collapse this transcript Subclass overriding  00:00 In the last movie we saw how we could inherit all of the methods and attributes 00:05 from a parent superclass into the child subclass. 00:10 But what about, when you don't want to take all of the methods? 00:12 Let's say you want everything that's in the parent class except there are a 00:16 couple of things you want to do differently than the parent did them. 00:19 Well, we can do that by overriding methods in the subclass. 00:22 So here I am in the same classes.rb file we've been working with throughout the 00:26 chapter and I've got class Animal and then I've created a subclass of Cow, which 00:31 doesn't have any attributes or methods in it. 00:33 It's inheriting all of its methods and all of its attributes from the Animal class. 00:37 Now, let's take a look here at color. 00:40 Right now we have a method inside our Animal class called Color. 00:43 The color is blank. 00:45 Let's try and access that. 00:46 Down here we'd already created an instance of the Cow called maisie. 00:50 Let's drop down one more line here and let's say puts maisie.color, all right. 00:55 So we'll save that. 00:57 Let's just run it and see what we get to start with. 00:59 So I am inside my ruby_sandbox already, we'll run ruby classes.rb. 01:04 The color is nothing, so it hasn't been set. 01:06 We need to set Maisie's color first. 01:09 We can see that one of the ways we can set it is using create with attributes 01:13 when we create it, right, that allow us to set the color. 01:15 So let's try that instead. 01:17 Let's try now using Cow create with attributes. 01:20 We still have this method that's available to us because it came from the 01:23 parent class, and now let's create the color, and let's say that Maisie is a yellow cow. 01:28 All right, so yellow cow will save it. 01:31 Now let's just try running it one more time, ruby classes, the color is yellow. 01:35 So now Maisie has the color yellow. 01:38 So now let's see how we could override this behavior. 01:40 Now that we see what it does by default, it's real easy. 01:43 We just simply write def color, and then whatever we want our new thing to be. 01:48 So let's say The cow's color is. 01:52 We'll do the exact same thing, but now it's going to tell us that it's a cow. 01:56 So that's something that would be in the Cow class, and the animal class would 01:59 just say the color is whatever it is, but the cow knows that it's a cow. 02:03 Let's try it one more time. There it is. 02:06 The cow's color is yellow. 02:07 So now we've overridden it. 02:09 Now it didn't change up here for these other animals we created where we created 02:12 something from the Animal class directly, we just have the colors white. 02:16 All we did was say inherit all the behaviors of animal, but after you do that 02:22 overwrite the Cow class with this one method, it really is that easy. 02:26 The reason why this works is because Ruby reads in the class 02:29 definition sequentially. 02:30 So the first thing it does is it makes the inheritance, then it redefines color. 02:35 Now we can redefine things as often as we want, right, redefine color. 02:40 We could say my color is and then do the color, right? 02:43 Let's just save that, go back to our command line. 02:49 My color is yellow. 02:50 It didn't object to the fact that we had more than one method this one got overwritten. 02:54 This might as well not exist at all because we not only overwrote the parent 03:00 superclass, we overwrote our own method a second time. 03:04 So it's no problem, Ruby doesn't mind us having more than one but whatever the 03:07 last definition is that's the one that wins. 03:10 That's the one they got assigned and kept inside the class. 03:13 The fact that Ruby lets you override these methods is actually one of the 03:16 really cool features of Ruby because we could override any class method in exactly this way. 03:21 Let me show you what I mean. 03:21 Let's go back to our command line. 03:23 I am going to open up a new irb session with simple prompt. 03:26 So we've gone back in irb. 03:28 Let's just create a simple array, x=1, 2, 3. 03:33 Now if we ask it to turn that array into a string, x. to s, we get back 1, 2, 3. 03:40 That's what the two-string method does for an array. 03:42 It just smashes everything together. 03:44 Well, let's just say we don't like that behavior, we'd like to do something 03:47 a little different. 03:48 All we have to do is open up class array. 03:51 Now this is the Ruby class array, we are changing the behavior of a fundamental 03:55 Ruby class, we have the ability to do that. 03:58 And then let's redefine it, def to_ s, and now it can be whatever new 04:03 definition we want. 04:04 I am going to say self.join and then, space and then we'll need to end and end. 04:12 Now we've changed the definition of this one method, everything else about class 04:17 array is still the same. 04:18 It was already defined. 04:19 What we did essentially was reopen the class. 04:22 Just for a second, make a new definition, and it went ahead and put that into 04:26 that previous class definition that was there. 04:28 It's not the same thing as a superclass and a subclass, but we are overriding 04:32 the existing method. 04:33 And now let's just try it again. 04:35 We'll just go up to the array x.to string, and now I get this new behavior. 04:40 I think that's a very powerful feature of Ruby, the fact that we have the 04:43 ability to override methods, not just in parent classes and subclasses, but any 04:48 class method we want. 04:49 At any time, we can stop and override it including the Ruby Core Library, and 04:54 that's a really powerful feature. Collapse this transcript Accessing the superclass  00:00 In the last movie we saw how we could override some of the methods that are 00:03 subclass inherited from its superclass. 00:06 But sometimes we don't want to override the method entirely. 00:09 We want to still take in most of that behavior from the parent class, but we 00:13 just want to tinker around the edges a little bit. 00:15 Well, we can do that, thanks to the keyword Super, and that's how we'll access 00:19 the superclass from the subclass. 00:22 So I've already got my class Animal that we've been working with and I created a 00:25 subclass called Cow. 00:27 Let's create another subclass now. 00:29 Let's call it subclass Pig. 00:31 That will be subclass of Animal so it will inherit all those behaviors from 00:35 Animal, but let's override something inside of it. 00:37 Let's say we'll take the noise method that we've been working with. 00:40 So it normally just returns noise. 00:42 Let's override that. 00:43 def noise, and we've seen how to do basic overriding. 00:46 Let's say Hello, right, so that's just going to return Hello instead of whatever 00:51 the animal's noise is. 00:52 Let's go ahead and instantiate it down here. 00:54 Let's say wilbur is going to be equal to Pig.new, and we know that we need to 01:00 provide something to the initialize method, so that's going to be just Oink! 01:04 So now it will have a noise but then when we say puts wilbur.noise, it will 01:12 return to us, Hello. 01:13 Let's go to our Terminal and try it, ruby classes, notice I am already in my ruby_sandbox. 01:19 So there we are, we just get back Hello, we didn't get back Oink! 01:22 we overrode it successfully. 01:23 We've seen how to do that. 01:25 Now though if we want to call what was in the parent method, all we have to do is put super. 01:30 So let's go ahead and just comment out Hello for now, and we will just have that 01:34 super method in there so we can see what it does. 01:36 So let's run it again, Oink! 01:40 So effectively in this case I did not override it. 01:43 I started to override it, and then I said, oh -- and by the way Super means do 01:48 whatever the parent was going to do, call the noise instance on the parent. 01:53 Go find that and do that original version. 01:56 That's what super does. 01:57 Now let's swap this around for a second and let's just say we call super 02:01 and then we call Hello. 02:03 Now think about what you think this is going to do before we do it. 02:06 I'll save it and let's go back and let's run it one more time and we just get 02:11 Hello, we don't get the Oink! 02:13 And I want to make sure that you understand why. 02:15 Super calls this parent method, right, I am just going to fold up Cow here so 02:19 it's out of our way. 02:20 It calls this which returns the value at noise. 02:23 So this returns at noise to this function, okay, doesn't get returned back to 02:30 where we called this noise method. 02:33 It just returns it here. 02:34 Then the next thing we do is Hello, which is the same thing as return Hello. 02:39 Now if we were instead to do something like parent_noise = super, and then we 02:47 said Hello and also, and then we did parent_noise. 02:53 Now it will return both of them to us. 02:56 So super is just a method. 02:58 That's all it is, and it can return a value, but it doesn't automatically return 03:03 it back as this return value here. 03:05 We still have to return at the end of it. 03:06 So I just want to make sure that's clear. 03:08 Let's try that out, Hello and also Oink! 03:12 That's all there is to being able to access these parent methods is this keyword 03:16 super, but just make sure that you understand what it's doing. 03:19 It's actually calling that method, and then it's just as if we called any other 03:23 method, we still have to work it in the same way here. 03:25 So it doesn't matter if we call it before we do something or after we do something. 03:28 It's just simply a method call. Collapse this transcript 7. Modules Namespacing  00:00 In this chapter we will be taking a look at Ruby's modules. 00:03 Put it simply, modules are wrappers around our Ruby code. 00:07 Now, you may be thinking, wait a minute, isn't that what a class was? 00:10 A class was a way for us to roll up different methods and attributes into a wrapper. 00:14 Well, modules work in a very similar way, but modules have one important 00:18 difference from classes. 00:20 They can't be instantiated. 00:21 We can never have an instance of a module the way that we have an instance of the class. 00:26 Instead, we are going to use our modules in conjunction with our classes. 00:29 That will make more sense as we examine the two reasons why we would want to use modules. 00:33 We will look at the first one in this movie, which is namespaces. 00:37 If you come from another programming language, you may already have 00:39 familiarity with namespacing. 00:41 It's a very common concept. 00:43 If not, imagine that you have a classroom that has two students in it named Sophia. 00:47 The teacher would end up calling one of them Sophia M. and the other one Sophia 00:51 S., so that she could tell them apart, and that way she could address questions 00:54 to the right Sophia and not just simply say Sophia and have both of them try and 00:58 answer at the same time. 00:59 Well in Ruby, in the same way, namespaces allow us to have class names which 01:05 don't conflict, so that we can tell the class names apart. 01:07 Let me give you a concrete real-world illustration. 01:11 Let's suppose for a moment that we have developed a website for online dating. 01:14 So our users are going to login, they are going to fill out personal 01:16 information, and then they will get matched with people that they might want to date. 01:20 Well, in the process of building that site, we might need a class called Date, 01:23 which would set up the meetings between the two people. 01:25 But our usage of Date is very different from the standard Ruby definition, which 01:29 is a calendar date, a year, a month, and a day. 01:32 Now, if they have the same name, we will have a conflict, because Ruby won't be 01:35 able to tell them apart. 01:36 For example, we would have dinner equals date new and we would be talking about 01:40 this meeting that should happen, but then when we try and say on what calendar 01:43 date it ought to happen, we would be saying dinner.date = Date.new. 01:47 Ruby can't tell those two things apart. 01:49 To avoid this name conflict, we can use a module to namespace our date. 01:52 All we have to do is wrap our class in a module name. 01:56 So here you can see I have put the module Romantic around the class. 02:00 Romantic is still capitalized and everything is exactly the way a class name 02:03 would be, but it's a wrapper around our class. 02:06 Now when we want to get a new instance of our date for our dating site, we use a new notation. 02:12 Now we say dinner = Romantic:: 02:16 Date, and that tells it that we are talking about this namespaced date. 02:20 The one that belongs inside the Romantic module, that is kept completely 02:24 separate from the dinner.date = Date.new. 02:29 Now it knows that's Ruby's internal date. 02:32 Modules are named exactly the same way that classes are. 02:34 It's just a wrapper that goes around it. 02:36 So module Romantic is the example I have given. 02:39 We can make that anything we want. 02:40 That double colon lets you know that it's been namespaced. 02:43 So we are not just talking about the standard date, we are talking about date 02:46 within the namespace Romantic. 02:49 Namespacing really is that easy. 02:51 Now, I don't want you to think that namespacing is only to keep you from 02:55 conflicting your classes with Ruby's classes. 02:58 It could be to keep it from conflicting with another class that you have written 03:01 inside the same site. 03:02 There might be two different contexts where we need to use a certain class name. 03:06 One very common use of namespacing is if we were going to try and release 03:10 our classes out as open source, so that other people could use them in their projects. 03:15 We can't anticipate ahead of time what their class names will be, so to make 03:19 sure that our classes don't conflict with whatever they have got, we would 03:22 namespace them and we might wrap them in a module that had some kind of unique name. 03:26 Maybe our name or our project's name or something like that, so that we could 03:30 make sure that it always stayed separate and didn't conflict with whatever they already had. 03:33 I think that's enough on namespacing to make the point. 03:36 Let's take a look at the second more powerful use of modules, which is as Mix-ins. Collapse this transcript Mixins  00:00 In the last movie we saw how to use modules for namespacing. 00:04 In this movie we are going to look at the second more powerful use of modules, 00:06 which is to use them as mix-ins. 00:08 Let me explain what the term mix-ins means. 00:10 Unlike some other programming languages, Ruby doesn't let us have 00:14 multiple inheritance. 00:15 If we have a class, that class can inherit its behaviors from only one parent or superclass. 00:21 That's it, just one. 00:22 If we need additional functionality to be brought in that's shared among 00:26 classes, we will need to put it in a module, and then we can take that module 00:29 and all its functionality and mix it in to any class that needs it. 00:33 It's a very powerful way for us to keep our code organized. 00:36 In my ruby_sandbox I have now got a file called person.rb that I created. 00:41 It's a very simple class for person. 00:43 It has some attributes. 00:44 It has a couple of methods that work with those attributes, nothing that we 00:48 haven't seen before. 00:49 Now, if you are like me, you frequently end up with a case like this, where 00:52 you have class Person. 00:53 But then you want to say, oh, well, I also need a class Teacher, and maybe 00:58 let's also have a class Student, and we end up with all these different classes 01:02 which are very similar. 01:03 We want the full name method to be in all three of them. 01:05 Now, we could just copy it three times, and with something as simple as the full 01:09 name method, that's not a terrible choice, but if it was a really complex 01:12 method, that would be a really bad choice. 01:15 So instead what we want to be able to do is use modules as mix-ins for this behavior. 01:20 So let's create a new module up here. 01:22 Now, this is not going to wrap around our class. 01:24 It's going to be a standalone module. 01:26 module ContactInfo. 01:28 Notice I did CamelCase here, just like a class name. 01:31 Now, as I said at the beginning of this chapter, this module is just a wrapper for code. 01:36 So let's take all of this functionality, every single bit of it, and let's just 01:40 cut it out of our Person class and put it into our module class. 01:43 So now our class is no longer wrapping all that functionality, the module is. 01:48 Well, that's great, except now we need a way to get the module and all its 01:52 functionality into our class, and we do that with the Include method. 01:56 So include ContactInfo. 01:57 We have to write the module name. 02:00 We will include that module and all of its functionality. 02:03 It's exactly the same as if we typed all of those lines in right in place of 02:09 where the word include is. 02:10 So let's try that out. 02:11 I am going to save that document. 02:12 I am going to go into my command line. 02:14 Notice that I am already inside my ruby_sandbox. 02:17 I am going to open up irb with simple-prompt. 02:18 I am going to load in that file, person.rb. 02:23 So load just simply reads the contents of the file. 02:26 That's exactly like if we had typed Ruby and the name of the file from the command line. 02:30 Now, you may need to provide a full path to this file if you are not inside the 02:34 directory where it already exists, but if it succeeds, it will come back and say true. 02:38 So now that file is loaded. 02:39 The difference between running from the command line is now we can interact with it. 02:42 So let's say person = Person.new, person. first_name = "Joe", person.last_name = "Smith". 02:52 Then we should say puts person.full_name. There we go. 02:59 We do have our attributes there and we do have that full name method. 03:02 All of that functionality from contact information was just dropped in to person. 03:06 Of course the real benefit to that is that not only can it be in Person, but now 03:10 we can also put it inside Teacher and inside Student, and now, if we decide to 03:14 make a change to one of these methods and we say, oh, wait a minute, you know 03:17 what, I actually want two spaces before the zip code. 03:20 We make that change there and it happens everywhere that module is included. 03:24 Now, module functionality is also inherited, so I just want to point out that we 03:28 could also do something like this. 03:29 Student, let's say, it will inherit from Person. 03:32 Now I don't need to even declare this anymore, we can have different attributes here. 03:36 Let's say that the Student has many books and grades, and our Teacher up here 03:43 can have some different attributes, accessor, lesson_plans. 03:49 So each of our classes is now different, but all three of them have 03:53 contact information. 03:54 These two have it because they explicitly included it, the Student inherited the 03:58 behavior of Person and just added a few attributes to it, and inheriting from 04:01 Person inherited that module's behaviors as well. Collapse this transcript Load, require, and include  00:00 In the last movie, where we were looking at modules as mix-ins, we defined our 00:04 module and the classes all in the same file, and we just made sure that the 00:08 module came before the class, so that Ruby would have a definition for the 00:12 module, so that it could include it when the class definition came up. 00:16 Well, more typically, we would not put those in the same file. 00:19 We would break them into separate files, and our modules would start to exist 00:23 in their own files, like code libraries, that we could just mix-in that library 00:27 whenever we needed. 00:28 So frequently you will end up with these small little module files that you can 00:31 draw upon whenever you need. 00:33 Well, if we break it off into a separate file, what that means is that we have 00:36 to have a way of making sure that Ruby has loaded in that module and knows 00:41 that definition before we try and mix it in to one of our classes, like our 00:45 Person class, and that gives us the perfect opportunity to take a look at 00:48 three Ruby commands: 00:49 Load, Require, and Include. 00:52 Because the difference between these three methods frequently trips beginning 00:55 programmers up, let's take a look at them. 00:57 So in the last movie I created my Person class and then I also have a 01:00 module, ContactInfo. 01:01 What I want to do now is break that up into its own file. 01:04 So I am going to start by just doing a Save As, and I will save this file 01:08 as contact_info.rb. 01:11 Now its in its own file, notice I am in contact_info.rb. 01:14 I am going to erase everything that's not in that module. 01:17 So now I have just a module definition in there. 01:19 Let's move that out of the way. 01:21 Let's go into the sandbox, and let's open up person.rb again, and now let's 01:25 do the opposite here. 01:26 Let's take away everything that's not our Person class definition. 01:30 Now, I have still got Teacher and Student here. That's fine. 01:32 I am just going to leave them for now. 01:33 Typically you would probably break them off into their own files, but I am not 01:36 going to worry about them while I go ahead and make the main point that I want 01:38 to make, which is that, before we can include contact_info in this file, Ruby 01:44 needs to know about contact_info. Let me show you. 01:47 Let's go into the command line. Let's open up irb. 01:50 Notice I am already in my ruby_sandbox, simple_prompt. 01:54 Let's go ahead and just load in that person file, just like we did in the last movie. 01:59 So person.rb, load that in. Wait a minute. 02:01 It says uninitialized constant Person::ContactInfo. 02:05 It's trying to find contact_info, and it can't. 02:08 That's because we haven't loaded it in. 02:09 Ruby doesn't know what it is, doesn't know how to find it, so it can't include it. 02:13 So what we need to do is load, and then contact_info.rb, true. 02:20 Now I am going to hit the up arrow twice and let's load in person.rb now. 02:24 No problem this time. 02:25 So as long as contact_info is loaded in first, we are okay to load in person.rb. 02:31 So we could say that person.rb depends on contact_info, it's a dependency. 02:37 So what we want to do is take this line, load, and put it at the top of the file. 02:42 So that way the first thing that person. rb does is it loads in contact_info, and 02:48 then it can go ahead and execute. Let's Save it. 02:50 Let's try it. 02:51 I will quit out of irb. 02:53 Let's go back into irb, so we get a fresh copy. 02:56 There is nothing stored in memory yet, and now let's just try load person.rb. True. 03:01 It worked, because it first loaded in contact_info and then loaded in this file. 03:06 So we have solved the problem. 03:08 Now incidentally, this is a path here. 03:10 It's not just a file name. 03:11 It's the path to a file. 03:12 We will be talking more about paths when we get to the section on files. 03:16 But it works the same way that paths do. 03:18 If you are used to working with Linux or DOS or anything like that. 03:22 We can provide an absolute path, or we can provide a relative path to get to that file. 03:26 So this solves the problem that we started with and it also illustrates how Load 03:30 works and how Include works. 03:32 So Include is used exclusively for bringing modules in as mix-ins. 03:37 That's what its for. 03:38 Include has nothing to do with loading in files. 03:41 We use Load for that. 03:42 Now, let me show you something about Load that's very important, which is that 03:45 let's say load person.rb again, it loaded it again, loaded it again, and so on. 03:51 Every time it returns true, because it loaded in those files again. 03:55 If we have already loaded it in, if Ruby has already seen that definition, 03:58 there's no reason to do it a second time. 04:00 Each and every time that it loads in person.rb. 04:03 It's also executing this load of contact_info again. 04:06 But we don't need it to load those every single time. 04:10 Let's say that we had Teacher and Student in their own class. 04:13 We would want then to also have a statement at the top that would say 04:16 load contact_info.rb, because each one of those would have the same kind of dependency. 04:22 Well, we don't want to load contact_info three times. 04:24 That's waste of time and energy, and it might actually have a negative impact 04:27 on our program if somewhere along the way we have made changes to what's in contact_info. 04:32 So a better way to do it, and which we will typically see instead is Require. 04:37 Require works exactly the same as Load, the only difference is that Require 04:41 keeps track of the fact that it's already included the file and only includes it 04:45 if it hasn't been loaded before. 04:47 Now, it's possible to trick Require by having some different paths to get to 04:51 files, but for the most part it's pretty good. 04:53 Now, let's also put it here. 04:54 Let's say require.person.rb. 04:58 Now, the first time I executed, it should return true to me, because it is 05:01 requiring it for the first time. 05:03 But now Require has added that file to its list of files that it knows it 05:07 required, and now when I say require person.rb, it comes back and says false. 05:12 It did not bring the file in again. 05:14 It is still there, but require didn't do anything. 05:17 So if you need to actually refresh the code and bring something in a second 05:21 time, you will want to use Load, but more often than not what you want is just 05:25 to bring this library in, so that Ruby knows about it, and then once you have 05:29 required it, you don't need to read it in again. 05:30 Ruby already knows about it. 05:32 Now, you will want to pay careful attention if you have been programming 05:34 in other languages, because these may have different meanings than they 05:37 had in those languages. 05:38 For example, if you are working in PHP, Include, it means load in a file, not 05:43 include a module, so just be careful about that. 05:46 So just to make sure that you are clear on the differences. 05:48 Load loads in a source file every single time its called. 05:52 It will always return true if the file is successfully loaded in. 05:56 Require loads a source file only once, after that it keeps track of the 06:01 fact that its loaded in, the fact that it knows about it, and it doesn't 06:03 bother loading it again. 06:04 It says, "oh yes, I have already seen that." 06:07 Then last of all, Include is what we use for including modules. 06:11 It has nothing to do with files and reading files in. 06:14 It's only for bringing in that mix-in behavior of modules. Collapse this transcript Enumerable as a mixin  00:00 Before we leave modules and move on, I want to give you a little bit of insight 00:04 into way that Ruby uses modules inside its standard library of classes. 00:08 Now, we've seen a number of methods so far, like sort, detect, select, reject, 00:13 collect, and inject and even more that are shared between a number of different classes. 00:17 We saw how we could use these on arrays and hashes and ranges and strings, and 00:22 that they are used in more classes as well. 00:23 And they might have slightly different behaviors on each one, but the same 00:27 functionality essentially is part of all these classes. 00:30 When you have shared functionality like that, it's a good indication that a 00:33 module may be at work, and that's exactly what's happening with these classes. 00:36 So here I am taking a look at the Ruby documentation at ruby-doc.org in the core 00:41 library, and I've got a definition for array. 00:44 Now we see all the methods that are in array, but notice here, it says 00:47 Included Modules>Enumerable. 00:50 Enumerable is an included module in array. 00:52 Let's take a look at Hash, scroll down Included Modules>Enumerable and Range, 00:59 Included Modules>Enumerable. 01:01 String, same thing. 01:02 It has Comparable and Enumerable in there. 01:05 So the module that's being included in every case is Enumerable, and enumerable 01:09 is just something that can be counted, right. 01:11 If we have a set of things that can be counted, then we ought to be able to do 01:13 something with them. 01:14 We got to be able to sort them, we got to be able to collect them to search 01:18 through them, and that functionality is mixed in to all of those sets, right. 01:23 Arrange is a set, an array is a set, a hash is a set. 01:26 So all of them have this enumerable behavior. 01:28 Now if you look at the definition here for Enumerable, it tells you that it's a 01:31 mixin and it provides collection classes, things which have a collection, with 01:36 several traversal and searching methods and with the ability to sort. 01:39 The one caveat is that class must provide a method each, which is going to 01:43 yield successive members of the collection, and we saw how to do that when 01:47 working with code blocks. 01:48 We saw how to yield up a value. 01:50 And then it goes on to say that if we want to use the max, min, or sort 01:53 functionality, then we'll also need to provide the comparison operator so that 01:57 it will know how to compare two different values. 01:59 So we can see how Ruby goes about sharing this functionality between 02:04 the different classes. 02:05 And you could override any of those methods just like we saw you could do 02:08 earlier if you want different functionality, but if not, we can include the 02:12 module and get all of its functionality for free. 02:14 All we have to do is provide this each method. 02:17 Now here's where the advanced extra credit part comes in. 02:20 We can mix in that Enumerable module for ourselves. 02:24 Now while you can often get the same functionality just by putting an array 02:28 inside your object, and using Enumerable off that array, it's really not that hard to do. 02:33 So let's go ahead and just see how to do it even though most of the time, it's 02:36 really an unnecessary step that you won't want to do. 02:39 I have got a simple class here in to_ do_list.rb and I just have a simple 02:43 attribute called items that I'm initializing as an empty array and then I have 02:47 given some sample usage down here in the comments. 02:49 We'd just initialize it, assign a list of to-do list items to the items array, 02:54 and then we could call select on this array. It's an array. 02:58 It already has enumerable mixed into it. 03:01 So that's why I say this is not strictly necessary. 03:04 Let's make the to-do list itself enumerable. 03:06 All we have to do is say include Enumerable. 03:09 We don't need to do any kind of require or anything, because it's already in Ruby. 03:13 If Ruby is loaded then we have Enumerable. 03:15 So include Enumerable. 03:17 That will then give us all those features. 03:18 But if you remember, the documentation told us that we need to have an 03:21 each method declared. 03:23 So it knows what is going to be rendered up, what are the enumerable things. 03:27 We could have a lot of different things inside this to-do list, right. 03:30 We could can have several attribute accessors. 03:31 So we could have items, we could have completed items, so on. 03:36 So it wants to know what exactly are you enumerating. 03:39 We'll tell it, well, it's going to be items, items.each and then we'll just put 03:43 in a code block, we know how to do this earlier and yield that value for items. 03:48 So it will just yield up this value each and every time to Enumerable, so 03:52 Enumerable can make use of it. 03:53 That's all we have to do to get most of that Enumerable functionality. 03:57 Let's save it and try it out. 03:59 Let's go into command line. 04:01 Notice I am already in my ruby_sandbox. 04:03 I'll open up irb with simple prompt and now, let's do our require and the file 04:08 is to_do_list.rb true. 04:12 So it loaded it in. 04:14 Now let's say list equals ToDoList. new and let's give our items a value. 04:21 items equals and we'll just use the same ones, laundry and dishes and vacuum. 04:28 Okay, so there is our array of items. 04:31 Now we already know that we could do lists.items.select. 04:36 That's the one we are using before select each item, if the item length is greater than 6. 04:42 So it comes back with laundry. 04:44 But what we've now done is we've put that enumerable on the object itself. 04:48 So now list.select returns the same thing. 04:52 We're essentially just sending the same array to it, but we don't have to be. 04:56 We could be putting in something much more complex here. 04:59 So what we were yielding up is some complicated calculated value maybe, maybe we 05:03 are mashing together a couple of different objects. 05:06 The important part is just that we've told Enumerable what collection should be enumerated. 05:10 It can be a manufactured collection or a simple collection like an array, but we 05:14 just have to tell it here is each item. 05:16 That you will iterate through by using the each method, and from there, we get 05:20 all of this other behavior built in. 05:22 This is really isn't the most practical thing to use in the world. 05:25 So don't go start using Enumerable all over the place. 05:28 It really is for a sort of special complex case where you need to create an 05:32 Enumerable class that doesn't already exist. 05:35 Otherwise, most times, you would just use an array like we did here. 05:39 We don't need to go that extra step. 05:40 I did it just to make the point so that you could see how easy it is to mix in 05:45 modules that give your classes lots of extra functionality. Collapse this transcript 8. Working with Files Input/output basics  00:00 In this chapter, we are going to learn to work with files and directories in 00:03 Ruby, and I think it will be helpful if we start up that discussion by first 00:06 taking a look at the basics of input and output. 00:09 Frequently, you'll see input and output abbreviated as simply I/O. So if you 00:13 hear me say I/O, that's what I am referring to it as input and output. 00:16 And what we were talking about is input into a Ruby program and output from that program. 00:21 We've been using some of the basic Ruby output for some time now. 00:25 We've been using both the puts and print, and we have seen how both of those 00:29 work that's basic output, and so that our program can put something out to 00:34 either the command line or to irb. 00:36 Now input, we haven't really seen yet. 00:39 We've been running a file, running our program, but that's not really input 00:42 that's the program itself. 00:44 Even in irb where we were typing things into the command line, that still is 00:47 sort of writing the Ruby code itself. 00:50 With input, what we really mean is getting information into the program. 00:54 So instead of putting information out of the program, we'll use gets to 00:59 get information back. 01:01 Let's try it on the command line and see how it works. 01:02 So I am in my command line, I am going to open up irb, I'll put simple prompt, 01:09 and let's try out, gets. 01:11 So if we just simply say gets, by itself, just gets. 01:16 We don't get anything back. It just waits. 01:19 It's waiting for us to give us some input. 01:21 So let's type in Hello, and it returns the return value of gets is whatever I 01:29 typed and then a liner turned at the end, because I did type a return at the end 01:32 but it knew that was the ending of gets that's how it knew we were done getting 01:36 input, and it also put that in that string there. 01:39 So what we want to do is catch that value. 01:42 So the easiest way to do that is just to assign into a variable. 01:45 So let's say input equals gets. 01:48 All right, it waits, Hello, and now, the value of input is equal to that user input. 01:55 And then most of the time, we don't want this line ending there. 01:58 It just gets in our way. 02:00 So there is a really easy way to get rid of it. 02:02 Ruby has something called the chomp method, and we can apply it to any string. 02:06 So let's say that I have a string and say this is a test of chomp. 02:11 I am going to put a line return at the end inside double quotes. 02:17 So it is an actual line return there and then I am going to apply 02:20 chomp, c-h-o-m-p to it. 02:23 And it returns the string without the line ending. 02:26 Now if the line ending had been an r instead or if it had been \r\in, those are 02:33 the variations on line endings that different operating systems might use. 02:37 They all still get chopped off at the end. 02:39 That's what chomp does. 02:40 chomp will remove the line ending if it's the last character. 02:44 If it's somewhere in this string, it leaves it alone, only the last character. 02:49 It also has another version which is chop. 02:52 Let me just show you chop. 02:54 It does the exact same thing, but the difference was chop. 02:57 Let me show you this, is if I say Hello.chop, chop always removes the last 03:03 character, whether it's a line return or not. 03:06 So chomp is a little bit safer. 03:08 So typically, we are going to want to use chomp and the way we would do it is 03:11 just to say input equals gets, and just right away. 03:14 As soon as that value comes back, chop off the end of it. 03:17 So there it is, the input comes back. 03:19 Let's say This is my input. This is my input. 03:23 So I get a clean string that's ready to use. 03:27 Now that demonstrates it in irb. 03:29 Let's just quit out of irb and I'll clear that screen. 03:32 Let's look at it inside a file. 03:35 So I have got a file here in my ruby_sandbox called input_output. 03:38 It will be in your exercise files or you can pause the movie and copy it down, 03:42 but it's very simple. 03:43 All I am doing is that same input equals gets chomp and I have got some 03:47 reminders up here as to what those do. 03:49 We've already seen print and puts but I have reminded you, what each of those does. 03:53 Print doesn't put a line return, puts does put a line return at the end. 03:57 So we are going to get some input and then we are going to output it back. 04:00 And then the last example I have got it is just that we can use gets inside a 04:04 loop, if we want to get multiple feedback. 04:07 And so I have got a loop here that basically waits for me to say quit and when I 04:11 say quit, then it access the loop, but until then, it just keeps getting user 04:15 input, and then turning around and outputting that same value. 04:19 Let's try running this program. 04:21 So I'll just switch back to my command line. 04:23 Notice that I am already in my ruby_ sandbox, ruby input_output.rb. So there it is. 04:30 I ran the program and the first thing it does is it does the gets. 04:33 So it's waiting for me to type something. Hello there. 04:37 You just told me: Hello there.. 04:39 Notice I have extra period there that's not great programming, I added a period 04:42 in my code and since I had a period here already, it doubled it. 04:46 But now I have got this prompt, so that was something I did here with print. 04:50 So we are getting that prompt now, and now it's waiting for me to type things. 04:53 So I can say Hello, Again, And again and it just keeps repeating to me until 05:02 finally, I type the magic word, quit. 05:05 And then it gets that input, it outputs quit, because that's still part of that loop. 05:11 After it gets the result and outputs it, but then when it goes through the loop 05:14 the next time, result is equal to quit. 05:17 So it access the loop and just puts the goodbye. 05:19 So that's really all there is to user input, but it turns out to be a very 05:22 powerful tool even though it's a very simple, because now we have the ability to 05:26 run a program and our user can interact with it. 05:29 It can be an interactive program. 05:30 It can ask questions of the user, the user can input their choices, they can 05:34 select from menu items, all sorts of things and we can run Ruby code based on that input. 05:39 So now that we understand the basics of input and output, why is that important 05:44 for learning to work with files and directories? 05:46 Well, the reason why, because files are just a type of input and output. 05:50 We are inputting things when we read from the file or outputting things when 05:54 we write to a file. 05:56 So it's just input and output still. 05:57 In fact, there's a Ruby class that's called IO and it's an input/output class 06:03 that handles the basics of input and output. 06:05 And it handles various forms of input and output. 06:07 We can send things across Internet connection and things like, but its main 06:11 star, its primary subclass is file. 06:15 And that's what we will be working with next is the file class, and we'll see 06:18 how we can apply these I/O basics that we just learned. 06:21 puts, print, gets, chomps, those will all be applicable to working with files. Collapse this transcript File system basics  00:00 In this movie, we are going to take a look at some of the basic things you need 00:02 to know about working with the file system. 00:04 Now the real basics I am assuming that everyone knows. 00:07 We have files on our hard drive, they're grouped into different folders, and we 00:11 can navigate through those folders to find the files we want, and in the same 00:15 way, we can do that from the command line, we can also do that inside our Ruby 00:20 code in order for Ruby to be able to find those files. 00:23 But there are couple of pitfalls that we need to watch out for, and that's what 00:26 I want really cover in this movie. 00:28 And they are especially tricky, because they're different on 00:31 different platforms. 00:32 Its cross-platform issues that we have, and the first pitfall is with the file 00:36 path separators, and the second is with file permissions. 00:40 With file path separators, it's a pitfall, because each of the platforms, 00:44 Windows on one hand and Unix-based platforms like Linux and Mac OS X, on 00:49 the other hand, use different separators between the folder names that lead to a file. 00:55 So the separators of the path, the path to the file are different. 01:00 On Unix, it's going to be a forward slash, on Windows it's a backward slash. 01:05 And you may already know this from working on the command line. 01:07 So which one of these do we use in our Ruby code, because we want our Ruby code 01:11 to be cross-platform. 01:12 We want it to be able to run in both places. 01:14 The answer is that Ruby does a really good job of allowing you to always just 01:19 use the forward slash. 01:21 So you can use the forward slash, and even if you're on a Windows machine, 01:24 everything should work fine. 01:26 You don't want to ever use the backslash, because that probably will not work on 01:31 a Unix-based machine. 01:33 So forward slash is the better of the two, but there's an even better more 01:36 professional way to handle it. 01:38 And that's by using the file class. 01:40 The file class has a class method called join and all that join does is take a 01:45 series of strings and join them together with the appropriate separator. 01:49 So on Unix, we get the forward slash and on Windows, we get the back slash. 01:54 Now if you are on a Windows machine, and File.join gives you forward 01:57 slashes, don't panic. 01:59 File.join will take care of it. 02:00 It will make sure that it gives you something that your machine can handle. 02:03 So if you got forward slashes that means your machine was able to handle 02:07 the forward slashes. 02:08 Now the second issue is file permissions, and file permissions is just a general 02:12 problem that sometimes jumps up and bites you that you need to know about. 02:17 It's made even trickier by the fact that the way we handle it is different 02:20 depending on whether we are on Unix or Windows. 02:22 Every file on your computer has an owner of that file. 02:26 Chances are that owner is you if it's a file you created. 02:29 And it also has permissions that say who can read the file, who can write to the 02:33 file, and if it's a program who can execute the file as well. 02:37 The default behavior is going to be the most files on your local computer are 02:42 owned by you and accessible by you. 02:44 So it's not something you have to worry about a whole lot. 02:46 Now I know there are exceptions, but most text files that you might have on your 02:50 hard drive or something that you could work with. 02:52 Now if you are on a network, then there's probably a lot of files that you 02:55 are not allowed to touch, a lot of things that you are not allowed to have access to. 02:59 So if we are going to run a program in a network environment, it would be 03:02 something we would have to look into further. 03:04 So I am going to give you some jumping off points, if you did want to look into it further. 03:08 If you're using Unix, Linux, or Mac OS X, then you would want to look at chmod 03:14 as the command to allow you to change the permissions for a file, and chown 03:19 would allow you to change the owner. 03:21 Those are just some very basics Unix commands, they also exist in Ruby, and 03:25 there are some of Ruby methods that allow us to do chmod and chown as well. 03:30 Now if you're on a Windows machine though, chmod and chown aren't going to 03:33 be much use to you, because Windows has a different permissions model than Unix does. 03:38 Instead, you are going to manage those files through the Properties/ Security tabs. 03:43 So you click on Properties, then on the Security tab and you can change who the 03:47 owner and the permissions are for the file. 03:49 So I know that's not concrete solutions for how you would actually go about 03:53 solving permissions problems, but the main thing is just to know that they 03:56 exist, that there are permissions that would keep you from being able to write 03:59 to a file, or keep you from being able to read from a file. 04:03 But again, with most files that are easily accessible to you on your computer, 04:06 you're going to have access to and you're going to have permissions for. Collapse this transcript File paths  00:00 Now that we've seen how we can use Ruby to get the correct file path separators, 00:05 we need to understand how we can get meaningful Files Paths. 00:07 How we can locate the files that we either want to read from or write to. 00:11 There are two ways that we can specify the path. 00:15 The first is using an Absolute path. 00:17 The Absolute path is the path from the root of the hard drive, from the very 00:21 beginning, how do we locate that file? 00:23 It's a path through the folders that would take, that we would have to open up 00:27 till we got to that file. 00:29 And we know that it's an Absolute path, because it begins with a forward slash 00:33 and that lets us know starting at the beginning. 00:36 The second type of path is a Relative path and that's a path from where we are 00:40 right now, how do we get there? 00:42 We don't care about going back to the beginning. 00:45 We say from the current position what folders do we need to either open up or 00:50 what parent directories do we need to jump backwards into? 00:53 So you will see there in the Relative path example I have got, I have got .. 00:57 that's when we want to move to a parent directory, move back one directory into 01:01 the directory above us, and then a single . is the current directory. 01:05 That's the directory we are in right now. 01:07 So if we are going to use a Relative path, we need to know where we are, when we 01:11 are running one of our Ruby programs. 01:14 And Ruby gives us a variable that lets us know that, and it's _File_ and there's 01:18 actually two underscores on either side of capitalized word File. 01:22 And it's a special variable name that refers to the file that were in right now. 01:26 So that's actually the File where these characters are typed. 01:30 Now the File will just return the name of that file to us. 01:34 If we want to actually find out where that file is, we will need to use a method 01:39 on the file class, which is expand_ path and that says tell me what the full 01:43 path, the Absolute path is to get to this file. 01:47 Now while this works great for finding the file that we are in right now, if we 01:50 want to find a different file, it's going to be much more useful if we start by 01:55 using this Files directory and we do that with the dirname method. 01:59 So dirname will tell us what directory is this file in and we can then use that 02:04 as a starting point in our navigation, either to go deeper into other 02:08 directories or back into a parent directory. 02:10 Let's take a look at some code to see how it all works. 02:14 Inside my ruby_sandbox and in the Exercise Files, I have a file 02:17 called file_location.rb. 02:20 You can pause the movie if you want to copy this down. 02:22 At the top I have Absolute paths. 02:25 To begin with, I have just got a simple string. 02:27 That's all this is. 02:28 It's a string using forward slashes. 02:30 Even on Windows this will probably work as a file path. 02:34 But the much better way to do it and what I am going to recommend to you is that 02:37 you use File.join instead. 02:39 That's really going to ensure that we have that platform independence. 02:42 And what we are asking File.join to do is to build up that same string for us by 02:47 appending those together. 02:48 If we want it to be an Absolute path, the first item just needs to be an empty string. 02:53 So with an empty string at the beginning, it will say okay, this is from the 02:55 root Users, Kevin, Desktop, ruby_sandbox. 02:59 Now you may have noticed the problem with Absolute paths which is that you 03:03 probably don't have this same directory structure on your computer. 03:07 For example, you probably don't have a folder called Kevin, unless your name 03:10 happens to be Kevin too. 03:12 So this doesn't make for great code. 03:13 If I am going to write something you can't bring it on your computer and run it 03:17 if it's going to ask for this Absolute path. 03:20 Instead it's much better to use Relative paths. 03:24 The first thing we'll see is what the value of that File variable actually is. 03:28 That's this current file File_location.rb. 03:32 That's what it's going to return to you is the name of that file. 03:34 If we want to find out where that file is, we use expand_path. 03:38 Now this is an Absolute path, but this is an Absolute path it's generated at the 03:43 time it's being run, okay? 03:45 We are asking Ruby to calculate that for us. 03:47 So if you've got it on your hard drive, you will have whatever your hard drive 03:51 structure is, whatever path it takes to get to it. 03:54 It will not depend on you having something like what I have. 03:56 So it's a much better way to do it. 03:58 This is the way to do Absolute paths, to take a File or Relative path and then 04:02 get the Absolute path from there. 04:04 It's going to be much easier to navigate, if we start out by using dirname, 04:07 so we will just take a look at what that value is, and then finally let's put it all together. 04:12 We have got File.join, we have got the current file, we are taking it's 04:15 directory and then I am backing up a directory into the parent directory, which 04:19 in my case is going to be the Desktop. 04:21 And then I am going into another folder which I have another folder here called 04:24 Exercise Files, so that's what I am going into. 04:27 I used it partly so that I could show you what happens when you have a space 04:31 in the name of a file. 04:32 I've escaped it with the slash and very important I've put it inside double quotes. 04:39 So if you have a file with a space in the name, you want to take those two extra 04:42 steps, escape the space with the backslash and put it in double quotes. 04:46 Putting it in single quotes won't let this escape do its job, right? 04:50 Remember that about double quotes. 04:51 So when we put all of this together that will allow us to navigate in that 04:54 Exercise Files folder and then from there we presumably would want to load up 04:59 some file which I'm not doing it. 05:01 Right now we are just going to output the string that's generated by this. 05:03 So let's save that and let's just go to our command line and let's try running it. 05:08 I am in my ruby_sandbox already, so I can just type ruby file_ location.rb. 05:13 First thing we get back is just simply a string. 05:16 Now we get the File.join that's building that string up for us. 05:20 If you are on Windows, you may have gotten back slashes. 05:22 If you got forward slashes, don't worry. 05:24 It means Ruby thought that you could handle the forward slashes. 05:27 And then we have got the name of the file. 05:29 That's just the results of that file variable. 05:32 Then we asked it to expand path on that, so that's the full path to get to the file. 05:37 When we asked it to give that file's directory name, it said, well it's this 05:41 directory, because it was able to say you are in the ruby_sandbox so it's this directory. 05:45 That's just a single dot. 05:48 If we want to get to Exercise Files from here, we would go . which is the 05:52 current directory, back up a directory and then go into Exercise Files. 05:56 Now let me just show you if we back up a directory into the Desktop, I can now 06:02 run ruby ruby_ sandbox/file_location.rb that's the full path to get to the Ruby file. 06:08 Now when I run it, it says the directory is ruby_sandbox. 06:12 That's how we would get there from the Desktop, you would go into ruby_sandbox 06:16 and then we would be in the right place. 06:18 Now for getting the Exercise Files it says well you go into the ruby_sandbox, 06:23 then you back up one, then you go into the Exercise Files. 06:25 It's not very efficient sometimes, but it has been the net effect that we are looking for. 06:29 And we could of course apply expand_ path on that and it would give us the 06:34 Absolute path to those Exercise Files. 06:36 It would stop being Relative. 06:38 Now having these tools on how to work with File Paths, it's going to be 06:41 essential for being able to find the files we want, so that we can be able to 06:45 read and write from them, and that's what we are going to start doing next. Collapse this transcript Accessing files  00:00 Now that we have talked about the basics of working with the file system, and 00:03 we've seen how we can create paths to the files that we want to work with, it's 00:06 time to see how we actually go about accessing those files. 00:10 How do you open them up for reading and writing? 00:13 There are two different ways that we can do it, and we'll take a look at both of them. 00:16 The first is with File.new and the second one is File.open. 00:20 Let's look at File.new in irb. 00:23 So I will open up a new irb session with simple-prompt, and notice that I am 00:27 inside my ruby_sandbox. 00:29 That's important, because that's where things will be saved. 00:32 That's where my files will go. 00:34 The first approach, File.new, is instantiating a new file object, like you would think. 00:39 So we want to assign that to a variable, just like we normally do when we 00:42 instantiate an object. 00:44 File.new wants two arguments. 00:46 The first is the filename that we either want to read from or write to. 00:50 We are going to create a new file and write to it. 00:52 I am going to call it irb_testfile.txt. 00:58 That will be the name of the file that it will create. 01:00 The second argument is going to be the mode that we want to open it in. 01:04 We are going to use write mode to start with, which is just to say w. We will 01:09 talk more about modes in a minute, w is for write, r is for read. That's it. 01:14 Now we've created this new file object. 01:16 Now, take a look, all we did was instantiate it, but look here. 01:21 It actually created the file for us. 01:23 It's in my ruby_sandbox. 01:25 So the file exists at the same time as this object exists, because that 01:30 object is the file. That's what it is. 01:32 It's in the file system. 01:34 It's not like Microsoft Word, where you write a document and it doesn't actually 01:38 exist in the file system until you hit Save. 01:41 At this point we've instantiated a file, an actual file. 01:46 Now, that file is open for us to write to, in the same way we might have a 01:51 document open in a word processor. 01:52 So we're now able to write to it, and when we are done, what we want to say is 01:56 file.close, and you want to make sure that you always close your files. 02:00 There are two main reasons. 02:01 One is it makes sure that you don't accidentally write to the file when you don't mean to. 02:05 And the second reason is because it allows Ruby to free up those resources that 02:09 it was using to hold that file open. 02:11 So you want to be a good citizen and always close up those files. 02:14 Now, we didn't write anything to this file, we just opened it for writing and 02:18 then closed it again. 02:19 We will see how to write to it in a minute. 02:20 Now let's look at the second way we could do it with open, but let's do that 02:23 from an actual file instead of from irb. 02:26 I am just going to type quit, and let's jump back over to the sandbox. 02:30 I have a file here in the Exercise Files called accessing_files.rb. 02:35 If you open that up, you will see its got the same thing we just did here for 02:38 instantiating a file object. 02:40 It has a different name, so this will create a new file. 02:42 Then the second way we can access a file is using open. 02:47 It works very much the same way, but instead of instantiating a file object, we 02:52 just open a file and then pass it a block of instructions. 02:56 That's just a code block. 02:58 We do whatever instructions are in there, and at the end it closes it for us. 03:02 So we never have to instantiate it. 03:03 We don't have to hang on to this variable. 03:06 We don't have to remember to close it. It does it for us. 03:08 For that reason I find this works best most of the time. 03:12 Use open instead of new, because it does close it for you, you don't have 03:16 to remember to do it. 03:17 And then it's also nice and clear, all the things you want to put in that file 03:21 are just statements that are right here, or everything you want to do when you 03:24 read back in from a file is a statement right here. 03:26 Notice that we are using read this time. 03:29 So we can go ahead and run it. 03:30 Let's go ahead and just do that. 03:32 I will open up the command line and we will just type ruby 03:36 accessing_files.rb, and it runs it. 03:39 Now, I don't get anything back, but it did what I asked it to. 03:44 It did create file1.txt. 03:46 It opened it for writing and then it closed it, then it opened it up for reading 03:50 and then it closed it again. 03:51 Before we move on to see how we can actually write information and read 03:55 information from it, let's take a look at those file modes. 03:59 There are six main file access modes that you are going to want to consider. 04:02 There is r, which is read from the start, and for that the file must already exist. 04:07 There is w, which writes from the start. 04:10 If the file doesn't exist, it will create it. 04:13 But if it does exist, it will wipe it clean. 04:16 It will truncate it, so that there is nothing in it, and then start writing at the beginning. 04:20 So you have to be careful, w is a little bit destructive if the file already exists. 04:24 The third is append, and that lets us append to the end of a file. 04:28 That's really useful if let's say we have a log file and we want to keep a 04:32 log of the latest action that happened, maybe it was an order that came in or something. 04:36 We can just simply open up a file, append the new information to the end, and 04:40 then close the file again. 04:42 Now, I noted that w is destructive, and that it really wipes the file clean. 04:46 What if we want to read and write from a file but not erase everything that was there? 04:50 Well, for that we need to use r+, which you will see over in the right column. 04:54 That lets us both read and write. 04:56 It does not destructively wipe out the document first. 05:00 It just lets us both read and write and it puts us at the beginning of the 05:04 document, ready to start. 05:05 W+ lets us both read and write, but it does that same destructive truncating, so 05:11 we don't use it that often. 05:13 And then append+ does the same thing. 05:16 It puts us at the end of the document and will let us both read and write, and 05:20 we'll see how we can move around in the document a little later on. 05:22 Now, most of the time what you are going to want are r, w, a, and r+. 05:29 a is for the special case where you want to just append something at the end. 05:31 W is most useful when you want to start a brand new document. 05:36 And then r and r+ are the things you will use if you're just reading or you're going to read and write. Collapse this transcript Writing to files  00:00 Now that we have seen how we can access files, and how we can open them up 00:04 to enable us to write to them, let's see how we can actually write data to the file. 00:08 Fortunately, because in the very first movie of this chapter we saw how to do 00:10 the basics of input and output, it's going to be super simple. 00:13 I want to start out by opening up the irb_ testfile.txt that we created in the last movie. 00:19 It should just be a blank file. 00:21 But let's type something in there, This is a test. 00:24 You can type anything you want. 00:26 Save it, close it up, now it has some text in there. 00:29 Now I am going to go into irb. 00:31 Notice I am already in my ruby_ sandbox where that file is located, 00:34 simple-prompt, and I am going to just instantiate a new file, File.new, and it 00:43 will be that file name, irb_testfile.txt. 00:47 You want to make sure you type the name correctly otherwise it will create a new file. 00:52 What we want to do instead is open that file and actually overwrite what's there. 00:56 That's what I want you to see. 00:57 So I've instantiated it now. 00:59 Let's go take a look. 01:01 Open up irb_testfile.txt and you'll find that its empty. 01:06 So at the moment that we instantiate it, it wipes it clean. 01:10 Now let's see how we can actually write data to this file. 01:13 Before when we wanted to output data, we used puts. 01:16 Well, we can use the same thing here, we just do it as a instance method, 01:20 file.puts, and then whatever we want it to put, let's say abcd. 01:26 Now let's take a look. 01:27 It returned nil to us, just like normal. 01:29 Let's go back over and look at the file again. It's not there. 01:32 So whenever we write something to a file, it waits for us to actually close the 01:36 file before it writes all of that data. 01:39 That makes sense, I mean every time that it's going to write a little tiny bit 01:43 of data, it would have to access the hard drive again. 01:47 So if we were going to output let's say a thousand rows that we were composing, 01:51 then it would have to access the hard drive a thousand times. 01:54 It's much better to assemble all of that and then write to the hard drive just one time. 01:59 So let's go ahead and say file.close, just so we can see that. Let's close it. 02:04 Open up irb_testfile.txt, and there we go, you see it's there. 02:06 So now let's just go back and open the file again. 02:10 We are going to overwrite what was there, so we are going to need to 02:13 do file.puts "abcd". 02:16 Now, as you might guess, we also have file.print available to us. 02:20 So we have efgh that we can print, and the difference is that print doesn't put 02:25 a line return like puts does. 02:27 There are also two others that are basically the same as print, with just subtle 02:30 differences, file.write "ijkl", and that will do the same thing as print. 02:37 It will write to the file, but notice that it returned something besides nil. 02:42 Instead, it actually returns the number of characters that it wrote. 02:47 So if you feel more comfortable using write instead of print, that's fine. 02:51 Either one works the exact same way. 02:53 Then last of all, we can append to the file, to the file object, we can use the 02:57 append method and append mnop to it. 03:00 The only difference is that it will return the file object to us. 03:04 Otherwise it's essentially the same as print and write. 03:07 So last of all, let's just close this up. 03:09 We can go take a look to see what's there, irb_testfile.txt, and it's exactly 03:15 what you would expect. 03:17 We had also created this file1.txt. 03:20 I am just going to open up accessing_ files.rb, because I've written something in 03:23 here that we can use to populate that. 03:25 So again, we are just going to open up that file for writing, and then I am 03:29 going to put in Ruby programming with a line return. 03:33 I don't have to use puts to get a line return, I can always just put my own as 03:36 long as I put it in double quotes. 03:39 So use this line return in double quotes, and then it will return the line that 03:44 I am doing the write and the fun and close it, exact same thing. 03:46 Let's just open this up, we will quit out of irb, and instead we will run the 03:52 file, accessing_ files.rb. 03:55 It doesn't return anything to us. 03:57 But if we go and we take a look over here, we will see that file1.txt does have 04:03 those three lines in it. 04:04 That's all there is to being able to write to files. Collapse this transcript Reading from files  00:00 Now that we know how to write data to files, it's time to see how to read data 00:04 back from files, and fortunately it's just as easy. 00:08 In the last movie, I wrote the irbtestfile.txt and I just put the alphabet from 00:13 a to p with a line break after the d. So that's what we are looking at. 00:16 That's the text we're going to be working with. I'll close that up. 00:19 Let's go back into irb, notice I'm in my ruby_sandbox, simple-prompt. 00:26 So let's go ahead and just instantiate a new file. 00:28 I am going to use the instantiation method because I want to step through the process. 00:32 I don't want to execute the whole block of code at one time. 00:35 irbttestfile.txt and then read. 00:42 We are going to read back from this time, not write. 00:45 Write would overwrite what we have there; instead we want to read. 00:48 So now, if we used puts to put information into the file, then guess what? 00:53 We use gets to get a line back out of the file and notice it includes the line return. 00:59 It is in fact the mirror image of puts. 01:01 Puts a line plus a line return; gets gets a line plus the line return. 01:07 And we've seen how we could use file. gets.chomp, and that will return the next 01:12 line without the line return. 01:13 Let's try running file.gets. chomp one last time now. Oh! 01:19 We get back an error, because this time there was nothing left to get. 01:23 So we need to be careful when we use chomp, we need to make sure we can 01:27 get something first. 01:28 When we get to the end of the file, there is nothing there. returns nil. 01:31 Watch file.gets returns nil. 01:34 Nil can't handle chomp. 01:36 It won't chomp anything off the end of nil. 01:39 So unlike where we are getting user input where we can just use chomp 01:43 automatically on top of the gets, we're going to have to be a little more 01:46 careful here when we are reading from a file and first make sure that we did 01:49 get something back. 01:50 So I am going to go ahead and just do file. close and then let's reopen that file again. 01:55 This time though, instead of reading a full-line, I am going to show you how we 01:59 can do to read to read back part of a line. 02:03 So we used write earlier and that wrote something without a line return, it just 02:07 wrote a few characters and returned the number of characters. 02:11 Well now with read we just tell it, well how many characters do we want to read? Read 4 characters. 02:16 So there is 4. Let's tell it to read 3. 02:20 It gave me the line return and nef. 02:24 Line returns still counts as a character. 02:26 Let's asked it to read us another 3, ghi and so on. 02:31 So those are the two primary ways that we can read from the file. 02:33 We can either get back a whole line or we can get back just a certain number of characters. 02:39 Now that reads data but we have a lot of data, if we've a lot of lines, we don't 02:43 want to do this one at a time, right? 02:45 We want to actually loop through it. 02:47 Let me show you how we can make that kind of loop. 02:48 Let's do a file.close, be good citizens and close up our file and then I'll 02:53 go ahead and hit Quit. 02:55 Let's switch over to accessing_files.rb. This is the file we're working with. 03:00 We had it output 'Ruby programming is fun' to file1.txt, we wrote that. 03:05 This will overwrite that with the same thing again when I run it, but now I am 03:10 actually read back in using a loop. 03:12 Now notice what we've done here. while line = file.gets, so while there is a 03:18 new line, if it returns nil, the loop exits. 03:21 So that's a good way to loop through and get each line one after another. 03:26 Then I have applied chomp down here, not up here. Remember that gives me a problem. 03:31 So instead we want to make sure it exists. 03:32 And then if it exists, chomp off the end of it, reverse it and put it 03:37 inside these asterisks. 03:40 Now if you know that you're going to loop through each and every line and that's 03:44 really what you're after, then an easier way to do is with each line. 03:48 So we just open up the file and then we say, all right, file, go through 03:51 each one of your lines and taking that line in my code block, what do I want to do with it? 03:57 And here I am just going to upcase it. 03:59 So let's save it, let's see how this works. 04:01 We're going to the Terminal, I am in my ruby_sandbox, so I just have to do ruby 04:05 accessing_files.rb and there we go. 04:08 The first one uses a gets loop to go through each one. 04:12 The second one just uses each line to go through and process it. 04:15 Essentially, they both do the same thing. 04:17 Each line just might be a little easier for you to write. Collapse this transcript File pointer  00:00 In this movie, I want to explain what the file pointer is and show you how 00:03 you can manipulate it. 00:04 The file pointer works a lot like the cursor does inside your word 00:07 processing program. 00:09 You can use the arrows to move around the text until you find the position that 00:12 you want and then you can start typing and it'll start typing at that spot. 00:16 Well the file pointer works the same way with a couple of differences. 00:19 First of all, if the file pointer is at a certain spot, it doesn't insert text. 00:24 It overwrites text. 00:25 So be careful about that. 00:27 The second difference is that we use the file pointer not just for writing to 00:30 files but also for reading from files. 00:33 And you may have noticed that in the last movie, when we're reading back data 00:36 from a file, if we asked it to give us 5 characters, then when we asked it for 00:40 another five, it started after those first five. It kept track. 00:44 It moved the pointer along to keep track of the fact that it had already given 00:48 us 5 characters and it didn't give them to us again. 00:50 And if we did a gets then it advanced the file pointer go to the beginning of 00:54 the next line, so that the next gets would return the next line. 00:57 Let's see how we can find out the location of the file pointer and also move it 01:01 to different locations. 01:03 I am going to be using irb_ testfile.txt that we created earlier. 01:06 It's a real simple text file, you can use any bit of text that you want. 01:09 Mine is just letters a through p with a line return after the d. I am going to open 01:14 up my command line, I will do irb simple- prompt and notice I am already in my ruby_sandbox. 01:21 From there I'll instantiate a new file, File.new irb_testfile.txt, and 01:29 we'll open it in r+ mode. 01:31 Remember we are going to read from it and also write from it and r+ puts us at 01:35 the beginning of the file. 01:37 So we can find out that we are at the beginning of the file by finding its position. 01:40 file.pos and that's how we find out the current position. 01:44 Just like an array it starts counting with zero. 01:45 And if we now say file.read(3), that will return three characters or three bytes to us, abc. 01:54 Now if we go up, file.pos, we are at 3 now. 01:57 It's moved the pointer along to 3 and if we do another read, 02:00 the same thing would happen. 02:02 Let's do a file.gets now. 02:05 Notice that while we've been casually saying that gets returns a line to us, 02:10 it doesn't actually. 02:12 What it does is it returns everything from the current file pointer position all 02:16 the way till it gets to the end of the line. 02:18 So if we change the position to move over one at the beginning and then do the gets, 02:22 every single line would be missing its first character. 02:26 Now if we ask for a file.pos, you'll notice that position is not 0 because 02:31 we hit the new line. 02:32 It just keeps on counting. 02:33 It goes to the entire document as one long series of numbers. 02:37 It doesn't wrap around it at all. 02:39 Inside the file, it doesn't care about the difference between the d and the line return. 02:43 Those are both considered one character. 02:46 And we can move around inside the file too. 02:47 For example, let's say file.pos= and we can give it a position, let's say 13, 02:55 file. and let's read back 3 again, m, n, and o. So at position 13, that's the text that's there. 03:02 Now the position moved when I did that. 03:05 Now I am at 16 because it advanced it when I did the read operation, so just 03:09 be careful about that. 03:11 So there should be one more letter there, file.read(10). 03:16 Let's see that file.pos is now equal to 17. 03:20 So when I did to read, it stopped at the end. 03:22 Just stopped right there at the end. 03:24 It said okay now you are at the end and we can actually say file.eof? true. 03:30 It is at the end of the file and if we weren't at the end of the file, it would 03:33 return false of course. 03:35 Now we can write something if we want here. 03:37 Let's do file.write and let's do qrst and we can also jump back to the beginning 03:45 of the file, we could use file.pos=0. 03:48 We know how to do that but we also can use file.rewind and that takes us back to 03:53 the beginning as well. 03:54 I want to show you that you can use file.pos+= if you want to go from the 03:59 current position, if you want to move forward 6 from wherever we are. 04:04 Now we could a file.read(1). That's what I get. 04:08 I can do the same thing file.pos-=. 04:11 Let's just go back 2, file.read(2). I should get back e and f. 04:17 So that's really all you need to know about being able to move the file pointer 04:21 around inside the file so that you can read and write at various spots. 04:25 Now there's too big pitfalls that I need to caution you about though because 04:29 they can trip you up. 04:30 The first is let's do a file.rewind. 04:33 Okay now I am back at the beginning of my file. 04:35 There is something called file.lineno and that, you may think, oh, that's the line number. 04:42 Let's do file.gets and then let's ask for the next line number. Okay, now it's 1. 04:47 It seems to be it's returning the line number but it's not. 04:52 Don't get tripped up by this. 04:53 It is an internal counter that gets uses, gets and also another method called 04:59 read line and they used it to keep track of what line they're on at the time. Let me show you. 05:04 We have done a gets now. 05:05 Let's change our position now, file.pos=0. 05:08 We'll go back to the beginning. file.lineno, still on 1. 05:14 It has not changed. 05:16 And if I say file.gets, now file line number is equal to 2. 05:21 Line number is just an internal counter that keeps track of how many times 05:24 gets has been called. 05:26 It has no relationship whatsoever to the file pointer. It has nothing to do with 05:31 the file pointer, so do not get fooled into thinking that. 05:35 If we were to do file.rewind, there's one slight difference between setting the 05:39 position equal to rewind, which is position equal, 05:42 it does nothing to line number. rewind also sets line number back to 0 at the same time. 05:47 So file.lineno is back at 0. 05:49 So what is this line number thing good for then, if doesn't actually tell us the lines? 05:53 Well it's useful if you're just going through and using gets over and over and 05:57 over again to know what line you're on. Let me show you. 06:00 Let's write a real simple while line = file.gets and let me just paste in a line 06:08 of code here so I don't have to retype it, puts and then we'll output the line 06:13 number and then the line and then end. So there we go. 06:17 Now we have been able to sort of prefix every one of our lines with the line number. 06:21 That's the kind of thing that it's good for. 06:23 Now the second pitfall that you need to watch out for is that you can set the 06:26 position beyond the end of the file. 06:29 When we were doing a read, remember it stopped at the p. It didn't keep going 06:33 but we can set the position. 06:34 Let's say file.pos+=, and let's say 100. 06:40 So now I'm position 121 of my document and you know that I only have the letters 06:46 a through t now, right? 06:47 You can see that in the gets that we just did. 06:50 So I definitely don't have 121. 06:53 If I do a file.read(1), it returns nil. 06:57 That's what it reads there. 06:58 There's nothing, but I can do a file.write. Let's do xyz. 07:03 So it did successfully write it. 07:05 Let's go ahead and do this loop that we just did up here, the gets loop. 07:08 Let's do that again and see what we get back this time. 07:11 So let's do file.rewind, get back to the beginning and then while line = 07:16 file.gets and I'll paste in that same line again. 07:21 Now it looks like it worked there. 07:22 It looks like it did what we would want. 07:24 It just started writing at the end of the file. 07:27 Let's say file.close now and let's just reopen our file one more time, file = 07:35 File.new "irb_testfile.txt" and we'll open it in both reading-writing mode again. 07:44 file.gets, file.gets. Now look at that. 07:52 So when we did it this way, it seemed to all work just fine. 07:58 But when I did it this way, look what we got back. 08:01 When it wrote, it wrote a bunch of invisible characters, almost 100 of them 08:06 because that's how far we jumped ahead. 08:08 This is the octal notation for nil. 08:11 So what it's doing is it's taking all of those nils and converting them into 08:14 invisible characters. 08:16 In fact, let's just look here. 08:17 If we open up irb_testfile, they look empty, right? 08:21 It looks like there's nothing there but in fact they are. 08:24 It's being written with these real strange characters, which can throw 08:27 things off for you. 08:28 So I want you to be really careful about writing beyond the end of your document. 08:33 You can always rewind to the beginning of it and then do gets until you get to 08:37 the bottom and when gets returns nil, you'll know you're at the bottom, then you 08:40 can start writing but don't just sort of use position to jump off into outer space 08:45 and start writing or you'll get this kind of unpredictable behavior. 08:48 If you keep those two pitfalls in mind though, moving around inside the 08:51 document will be really easy. Collapse this transcript Renaming and deleting files  00:00 Now that we know how to read and write from files, let's see how we can rename 00:04 our files and delete them. 00:06 So I am going to do the renaming and deleting from irb rather than inside a script. 00:10 Let's do simple-prompt, open up irb, and I'll create a new file to start 00:16 out with that we can rename and then delete when we are done. 00:19 So file equals File.new and let's call this file_to_rename.txt and we'll go 00:27 ahead and just say we are going to write. 00:29 And while we are at it, we'll say puts "This is a file for renaming and then deleting". 00:40 file.close. 00:41 All right, so now we've got our file there. 00:43 Let's just double-check. 00:44 Look at our sandbox, file_to_rename, there it is in my ruby_sandbox. 00:48 You want to make sure that where you are located is where it'll be. 00:52 So now let's see how to rename it. 00:53 It's nice and simple. We just use the File class and call the rename class method. 00:58 So File.rename, and we give it the old name as the first argument, 01:01 'file_to_rename.txt', and then the second argument will be the new name of the file. 01:09 Since we are going to delete it next, let's call it 'file_to_delete.txt' and that's it. 01:15 That's all there is to being able to rename a file. 01:17 If we look here now, it's called file_to_delete. It works really well. 01:22 Deleting works just as easily File.delete and then now it's 01:27 called file_to_delete.txt. Here it is. 01:31 Let me take a look. 01:33 Sure enough now the file is gone. 01:35 So that's really all there is to the syntax of being able to rename and delete files. 01:39 One thing you need to really watch out for though is that with read and write, 01:42 we had to make sure that we had read and write access to the file, but if we are 01:46 going to rename and delete, in addition to having write access to the file, 01:50 we also need write permissions on the directory that contains the files. 01:54 So you want to watch out for that. 01:56 The other question that I want to address that sometimes comes up is what about copy, 01:59 what if we want to copy a file? 02:00 Let's create a new file again. 02:03 Let's go back up here and make a new one, but this time we are going to call it 02:06 file_to_copy and then we'll just go ahead and close it. 02:10 We don't need to put anything inside. 02:11 That will create the file for us. 02:13 Take a look and confirm that it's there, file_to_copy. 02:15 So now, let's take that file and let's say File.copy and let's try using the 02:21 same syntax as rename. 02:22 Let's do file_to_copy.txt and let's give it a new name for the new copy. 02:29 We'll just call it copied.txt. Let's close it up. 02:34 Oops! there is no copy method on the File class. Instead the way that we copy files is 02:41 by including one of the standard libraries that comes with Ruby. 02:45 It ships with Ruby. 02:46 It's already there but it's not loaded up, but we know how to load things up. 02:49 We just use, require, require and it's going to be called fileutiles. 02:57 That's all we've to type is require 'fileutiles'. 02:59 It comes up true, means that it loaded in all that Ruby code. 03:02 It's just sitting there waiting to be loaded and now we can say FileUtils.cp 03:11 for copy or copy and then let's do file to copy.txt, and this time, let's call 03:20 it copied_file.txt. 03:25 Take a look, and sure enough, here's my copied_file and my file_to_copy. 03:29 So that fileutils is the way to get the copy functionality. 03:33 So in fact, let me break it down for you a little more. 03:36 The file class gives us rename and delete among all the other options of 03:40 course that we've seen, all the other methods we've been looking at throughout this chapter. 03:43 But we have rename and delete there, and unlink is a synonym for delete. 03:46 So you can use that if you would rather. 03:47 Then we have this file utilities class that we can load in. 03:50 It's part of the Ruby standard library. 03:52 require 'fileutils' brings it in so that we can use it. 03:55 And we have copy, move, and remove all in there and you'll notice that the two 04:00 letter abbreviations cp, mv and rm. If you're familiar with working with Linux in 04:04 the command line, you'll recognize those as being the kinds of command lines you 04:08 would enter in Unix. 04:10 So that's really what File Utility is doing. 04:12 It's giving us a Ruby interface to all those things that we normally would do 04:15 from the command line. 04:16 So we also have cd, chmod, chown, pwd, ln, touch, make directory, remove 04:22 directory, all of those are available to us in file utilities as well. 04:25 So it's very useful. 04:26 I wanted to caution you against using something else that's called FTools. 04:30 That's also a part of the Ruby 1.8 standard library. You probably have it. 04:35 You can use require ftools, and what it does is it's a module that adds a lot of 04:39 these fileutils features to file, especially copy. 04:43 So we have copy then inside our file class and that File.copy would work, but 04:48 it's not recommended that you use FTools. 04:50 It's better to use FileUtils for a lot of reasons. 04:53 The biggest reason though is that it's going to be removed from the standard 04:56 library in Ruby 1.9. 04:59 So pretend that it doesn't exist, just ignore it. As soon as you see it, think 05:02 oh, that's not something I want to use. File and FileUtils are going to handle 05:06 all of your file renaming, deleting, and copying needs. Collapse this transcript Examining file details  00:00 In addition to reading, writing and deleting files, a lot of times we want to 00:04 actually just find out some basic information about the file and that's what 00:07 we're going to see how to do in this movie. 00:08 To demonstrate, I am going to go into irb. Notice I am already in my 00:11 ruby_sandbox, simple-prompt. 00:15 Before we start, I am going to just assign a variable file equal to a string and 00:20 that will be the name of the file, and let's make this that irb_testfile.txt or 00:25 you can put any filename you want. 00:27 Any file that you know already exists. 00:29 We're just going to be checking out information about that file. 00:31 So that's my file, I can then use the string instead of having to type that 00:35 all out from now on. 00:36 I can just pass the variable file in. 00:37 There is a number of class methods on the file class that let us check 00:41 out information, File.exist, for example, very handy for finding out if a file exists. 00:47 If we want to find out whether it exist before we try to read to it, write it, 00:51 rename it, delete it, that's very useful and we'll use that a lot. 00:54 We also can check to see whether it's actually a file. file, true. 00:59 Well, we say, well what's the alternative? 01:01 Well, it could be a directory. 01:02 A directory will also return true that it exists, but then we can tell the 01:06 differences whether it's a file or not by using file or directory. 01:10 We can also check whether it's readable, true. 01:15 Is our file writable? File. 01:19 How about executable? 01:21 Now, this would be for scripts and for applications that have the executable 01:26 permission set, my file doesn't by default. 01:28 You can also checkout file size, File. size(file) and that's going to return 01:34 in bytes the answer. 01:36 124, that's the how large my file is. 01:39 Now that's the number of characters that are in there, if you are using a 01:42 single-byte language. 01:43 So that's also really useful, and it's especially useful for making sure that 01:47 you don't write beyond the end of the file. 01:49 Remember, we talked about that how we could set the position beyond the end of 01:52 the file, and there were some pitfalls that came with that. 01:54 Well, size helps us make sure we don't go too far in the file. 01:58 We've also seen a couple already that we'll just review, we had File.dirname 02:01 that will show the directory of the file. 02:04 So the directory is the directory I am in now, which is ruby_sandbox while I am running irb. 02:10 We also saw File.expand_path. 02:15 So that expands to show us the full path of where the file is located. 02:18 We have something that's sort of the opposite of that File.basename which I've 02:23 just putted on the string. 02:25 It just returns the string to me. 02:26 That's no magic there, right? 02:28 But if I do expand the path, let's do a File.expand_path, and let's set 02:34 that equal to full_path. 02:36 So we'll just set that equal to variable. 02:38 Now if we do File.basename on full_path, we'll get back just the name of the file. 02:46 So it really is sort of the reverse of expand_path. 02:48 If you want to expand it to get the full thing, basename says just give 02:51 me what's at the end. 02:52 It's also File.extname, the extension name, and we can call that, and it gives 02:58 us back.txt in this case. 03:00 That's useful, especially if you are working with images when you know if it's a 03:03 JPEG or something, you can use that. 03:05 The three more that I want to show you, and then I want to explain a little further. 03:08 We have File.atime(file), File. mtime(file) and File.ctime(file). 03:19 So it just all three of those give you back a time related to the file. 03:22 Let's talk about what each one is, because it can be confusing. 03:25 File.mtime is the last modified time and that's pretty simple 03:29 and straightforward. 03:30 It's the time the file was last written to. 03:32 File.atime is the last time it was accessed, even when it's been read. 03:37 It lets us know whenever it's been read from or written to. 03:40 We don't know which one happened, unless we also look at mtime and then we 03:43 might be able to tell. 03:44 But mtime is write, atime is read or write. 03:48 ctime is the last status change time. 03:52 It's not the created time. 03:53 It confuses a lot of people. 03:55 It is not created time and people think well, I've modified, I've accessed, 03:59 what about created? 04:00 That's because it's not possible to find out the created time of a file. 04:04 I mean, after all what is the created time? 04:06 If I retrieve a file from a backup copy and put it on my hard drive, was it 04:10 created at the time that it was backed up or created on my hard drive, right? 04:13 It was the time that it was modified. 04:16 So ctime, let's talk about what it is, is the last time that the status changed. 04:20 That means that it was read to or written from, or any changes to owner, 04:25 group and permissions. 04:26 In general, try not to use it. Unless what you are really trying to find out is 04:30 when the owner, group or permission is changed, there is no reason to use it. 04:34 So just avoid it and stay away from it, but it's definitely not created time. 04:38 That covers most of these class methods on the file class. 04:40 But what about when we're working on a file, right? 04:43 What if we have myfile = File.new(file), and let's open it up for reading. 04:50 All right, so I am going to open that file up. 04:52 I am now inside the file. 04:54 Inside, I have an instance. The instance is called myfile. 04:57 Notice that I called it myfile so it wouldn't conflict with file that we 05:00 had already created. 05:01 You want to be careful about using this word file too often or you'll get confused. 05:04 So I've called it myfile. 05:06 It's now an instance. Can we called instance methods on it? 05:09 Let's try myfile.size, no. 05:11 myfile.readable, no. 05:16 Those methods don't exist as instance methods on the file class. 05:20 However, we have something called stat, myfile.stat, and that returns 05:27 a File::Stat object. 05:30 Class is File::Stat and you can look inside there and you can see a lot of the 05:34 attributes that it has. 05:36 So a lot of these things are there, like size. 05:38 So we can say myfile.stat.size and find out the size, or myfile.stat.readable. 05:47 And we can find out whether it's readable or not. 05:49 So that's the way you do it. 05:50 From inside, you just have to make this one extra step to call for that stat 05:54 object, and once that stat object appears, and you have it ready for you to 05:58 work with, then you can call all those methods as instance methods on that stat object. 06:03 Again, I find that that's especially useful, if we need to stop and check 06:06 the size of the document to make sure that we aren't writing past the end of the file. 06:10 And that's really all there is to it. 06:11 Let's go ahead and close up our file, just for good measure. 06:13 Now that we know how to find out information about our files. 06:17 It really concludes a full understanding of how we can work with files, 06:21 but we still haven't talked about directories, the folders that house our files. 06:25 Let's look at that next. Collapse this transcript Working with directories  00:00 Now that we understand how to work with files in Ruby, it's important that we 00:03 also understand how to work with directories. 00:05 Because we're going to want to navigate around directories to find our files, 00:08 and we'll want to create and delete directories in order to organize our files. 00:11 Fortunately for us, working with directories is very similar to working with files. 00:16 Before we go into IRB, I want to take a look at my ruby_sandbox. 00:19 That's the directory I'll be using. 00:20 You could use any directory you want, doesn't matter, but take note of 00:23 the contents of it. 00:25 I've got accessing_files, all the way down to, to_do_list. 00:29 Those are the files that are in that directory that I can see. 00:31 Now, let's switch over to the command line. 00:34 Notice that I am already inside my ruby_sandbox. If you are on a Windows 00:38 machine, once you are in the directory you want, you'll type dir to get a list 00:41 of the contents of the directory. 00:43 If you are on a Linux, Unix or Mac machine, you'll type ls dash la. 00:49 Now, we can view the full listing of these files. 00:51 I wanted to use those extra options because I wanted to show you that there are 00:55 some files that appear above accessing_files. 00:58 Now if you don't have the same thing as me, don't worry. 01:00 I just want you to be aware that it's possible that you would. 01:04 .DS_Store is an invisible file. 01:07 That's what the dot tells us. It's invisible. 01:09 It means that it's basically a configuration file. 01:11 It's not a real file that we normally want to worry about, in the sense that we 01:14 usually work with files. 01:16 So the operating system hides it from us. 01:18 Now DS_Store happens to be a configuration file that the Mac operating system 01:22 uses to keep track of things like the size of the window and the position on 01:26 the screen, and that's how it keeps track of it. 01:28 It's in this little file. 01:29 Now there is a couple of others up here though, we've got dot dot, which is the 01:33 parent of this directory, and dot, which is this directory. 01:36 It seems kind of strange to have this directory as an item inside this directory. 01:42 But what we're actually looking at is the directory listing, right. It's sort of 01:45 index of items that the directory knows about. 01:48 So it knows about all the items that are inside of it, but it also knows where 01:51 its parent directory is. 01:52 It knows what parent it belongs to, and so it has an entry for that. 01:56 It also has an entry for itself. Why? 01:59 Because that way it can keep track of the permissions that are on it. 02:02 So it can use that entry about itself to keep track of information about itself. 02:07 So these items that begin with the dot at the beginning, we'll just want to be 02:10 aware of as we are working. 02:11 We'll either want to filter them out or skip over them or something, and get to the real files. 02:16 But those are considered entries in the directory. 02:19 So let me just clear my screen here. 02:21 I am going to open up an irb session and notice that I am already inside my ruby_sandbox. 02:26 That's important. 02:28 We've seen already how we can use File.dirname and that special variable file to 02:32 find out the directory of the file that we're in. 02:34 Well, we are not in a file, we are inside irb, but irb still returns the 02:38 directory that we're in, which is ruby_sandbox, and we can check that 02:42 with expand_path. 02:43 We saw that earlier, sure enough that's where we are. 02:46 We are in the ruby_sandbox. 02:48 Now, this is what you want to use inside your Ruby files, inside your code, your RB files. 02:53 You want to use File.dirname file, because that gives you your starting point 02:56 relative to that file. 02:58 We also have the directory class, Dir, and it has some methods, one of which is pwd. 03:03 That stands for path to working directory. 03:07 That tells us where we are right now. 03:10 Now it return to the same thing that this whole expand path did, when I gave it the dirname. 03:15 You still in your code want to use, File. dirname(__File__), because PWD can change. 03:20 It keeps track of where we are right at this moment. 03:23 This is saying relative to this file. 03:25 That will always be the same. 03:27 It will never change. 03:28 PWD can change as we move around, and we have the ability to move around. 03:32 We can tell the directory class, you know what? Move around. Let's go to chdir 03:37 for change directory and let's go back one. 03:40 Let's go into my parent directory, with dot dot. 03:43 Let's now try Dir.pwd. Sure enough, I am now on my desktop. 03:48 Dir.chdir will also take a absolute path as well as a relative path. 03:54 I am just going to paste something in here, so you don't have to watch me type it. 03:57 But we saw how to do that effectively with file join earlier. 03:59 So we make sure we get the right line separators, the empty string at the 04:03 beginning says start at root and then we string all of these together to come up 04:07 with a string, with the right separators, and we can change directory into there, 04:11 and they are sure enough we are back in our ruby_sandbox. 04:14 So that let's us move around. 04:16 What about the contents of the directory though. 04:18 How do we actually work with those contents? 04:20 Well, we call the entries class method on the directory class. 04:25 So entries and then tell it what directory, right. It doesn't know what directories. 04:29 It's not an instance. 04:29 So I am going to say this directory. 04:32 So there is the list of entries. 04:34 It's just an array. 04:35 It's an array of strings. 04:36 Now we can turn on and pass those strings to the file class, we could open each 04:40 one up, do something, or we could filter through. 04:42 We can look for only files that begin with the letter A for example, and then we 04:46 could do something to those, delete them perhaps. 04:48 The important thing is that all it is, is an array. It can be sorted. 04:51 It can be collected. 04:52 It just simply an array. 04:54 Most often what we want to do with that though is we want to call each on it, 04:59 and that will let us go through each entry. 05:01 We'll just make it code block. 05:03 Let's start out by saying print entry plus colon space, so that will print 05:10 the name of this file. 05:11 That string will get printed with the colon after it. 05:14 Then let's use some of those methods we worked on earlier. Let's say If File. 05:19 is a file, entry and File.readable, check and see if it's readable or not, 05:28 entry, then that's File.open up the file and we're going to open the file name, 05:36 we'll read from it. 05:38 We'll be in read mode, we'll pass another code block, which will return the file 05:42 that we can work with, and then puts file.gets, so that will just get back 05:47 the first line of the file, and it will output it to irb. 05:51 That's what it's going to do, and in that code block, 05:53 let's go ahead and put an else statement in here, just because if it's not a 05:59 readable file, I did a print up here, and I just want to do a line ending. 06:04 So I'll just do a simple puts. 06:06 So we'll just put a line ending and then return from there. 06:09 So puts and then end, and then finally last of all, let's do a final end, here we go. 06:15 So there it went, an output. 06:18 So the first two, dot and dot dot, are directories. 06:21 This is a directory and the parent directory is a directory. 06:24 So it didn't try an output anything there. Instead this puts happened and 06:27 it just did a simple return. 06:30 This though was a file. 06:31 It's an invisible file, but it's actually a file. 06:33 So we did read from it, we pulled its first line back, then accessing_files, 06:37 we got its first line, and so on. 06:39 So every single one of those files now we pulled back the first line. 06:42 So it shows how you can loop through and work with all these directory entries. 06:46 Now it's also possible to open up a directory object to instantiate it, or to do 06:51 open the same way that we did file open here, but doesn't really not much point 06:56 to doing it that way, because once you get inside, really all you can do is work 06:59 with the entries anyway. 07:00 So you might as well just go ahead and use this class method on the directory class, 07:04 and because so often we want to loop through all of those, because that 07:08 such a common procedure, 07:10 we also have Dir.foreach, and that's a shortcut to just say take all of the 07:14 entries and loop through each one. 07:17 We'll say entry then we'll just say puts entry. There it is. 07:21 It just output the list of entries. 07:24 Making and deleting directories is just as easy. 07:26 Let's say Dir.mkdir and then let's say temp_directory, here we are. 07:37 So it created a directory. Let's take a look. 07:39 Here it is, temp_directory, and then we can just go back when we're ready and 07:43 delete it with delete. 07:49 Delete temp_directory. 07:50 Sure enough, there it is. It's gone. 07:52 Now in order for that to delete to succeed, not only do we have to have write 07:56 permissions on the directory that contains it, just like we did when we were 07:59 deleting files, also the directory has to be empty. 08:02 All right, the directory can't have anything in i, if we are going to delete it, 08:05 or it will raise an error. 08:07 If you want to get rid of something that has contents, you either need to loop 08:10 through and get rid of all those contents first, and then delete the directory, 08:14 or that file utilities class that we looked at earlier. 08:17 The one that we looked at in the renaming and deleting files movie. 08:21 You can use file utilities and it has some removal features that will let you 08:25 remove the directory, and force it to remove even if it has contents. 08:30 So if you really need that functionality, you can go there. 08:32 By now you should have a good understanding of both files and directories and 08:36 feel very comfortable working with the file system from Ruby. Collapse this transcript 9. Ruby Project: Creating the Food Finder Project overview  00:01 Now that you've mastered the essentials of the Ruby programming language, I want 00:04 us to apply those techniques to a real world project. 00:07 Our goal will be to successfully use the right Ruby techniques to write some smart code, 00:11 and not only will you be able to get experience that will reinforce the 00:14 lessons that we've covered so far, it will also give me an opportunity to make 00:17 some points about the best practices you should use in your own Ruby projects. 00:21 Now the project we'll be creating is something I call the Food Finder. 00:24 Let's take a moment to get an overview of the project. 00:26 Simply put the Food Finder is going to be a little Ruby program that will allow 00:30 us to find the food that we're in the mood for. 00:33 So we'll be able to consult a list of restaurants and find the restaurant that 00:37 would be right for us, either to go out to tonight or to order in from, 00:41 something like that. 00:42 So if we were to make a list of the requirements for the project, it would be 00:45 that it's a Ruby program launch from the command line. 00:47 It's going to serve as a Rolodex of our favorite restaurants and it's going to 00:50 do that by saving those restaurants into a text file. 00:54 The program will allow user interaction. 00:56 It's not going to just simply output a list to us. 00:59 We are going to be able to actually interact with it, ask it questions, look for 01:03 one kind of restaurant, then look for another kind of restaurant perhaps. 01:05 We'll be able to list out restaurants. 01:07 It will give us the name, the cuisine, and the price. 01:10 You can certainly add more attributes to it, but we are just going to be 01:13 sticking with those three to keep it simple. 01:14 It will also be sortable by those three attributes as well. 01:17 And we'll of course need to be able to add restaurants to the list, and finally 01:21 be able to find restaurants by keyword. 01:24 So those are the goals we're going to hope to accomplish for the next several chapters. 01:28 Now we won't be creating this project inside our ruby_sandbox anymore. 01:31 I am going to create a new folder for it that we'll be working in. 01:33 You can put that folder anywhere you like, just somewhere where you can find it easily. 01:37 I am going to have mine on my desktop, and I have just called it food_finder and 01:41 to start with I've just put a folder in there and they are called lib. 01:44 Lib is short for library. 01:45 It's a common practice inside Ruby programs to call all of the files that will 01:49 be in your library, lib. 01:51 I think the best way for you to have an understanding of where we're headed with 01:54 this project is for me to show you a demonstration of the final result. 01:58 So from the command line I am just going to launch the program and you'll see 02:02 that it comes up with some introductory text and it tells me here are the 02:05 actions that you're able to perform. 02:07 So let's try the first one. 02:08 Let's try list. It lists out the restaurants. 02:11 So it has three columns, Name, Cuisine, and Price, all nicely formatted. 02:15 It also gives me a note that says I can sort using list cuisine or list by cuisine. 02:21 Let's try a list by cuisine and sure enough. 02:25 It sorted those by making cuisine go in alphabetical order. 02:28 Let's try a list by price. 02:30 Now it sorted the prices, and it put the prices into ascending order. 02:35 Let's try the second action there. We have got find. 02:37 So let's try find and let's see what we get back, and it says find a restaurant. 02:40 Find using a key phrase to search the restaurant list. 02:43 So find tamale, find Mexican or just find mex. Let's try that. 02:47 Find tamale, I got back Hot Tamale. 02:51 Let's try find pita. 02:52 I got back fast food. 02:54 Let's try find mex. I got back to the Mexican again, Hot Tamale. 02:59 Let's try find 20. I got like all restaurants that are under$20. 03:05 That's what they gave me. 03:06 Let's try the add now, so we should be able to add a new restaurant to 03:09 our restaurant finder. 03:10 We've just gone to some fabulous place that we loved and let's say that this 03:14 is called the Paradise Cafe and the Cuisine type we'll just say Snack, and 03:20 Average price is $5. 03:23 So there it is, Restaurant Added. 03:24 Let's see if it's added. List. 03:25 Sure enough there it is. 03:26 Paradise Cafe pops up in the middle of the list. 03:28 It's being alphabetized by name by default. 03:31 We could also do list by price again, and we'll see it at the top or we can 03:35 do our find for paradise and we'll see just the Paradise Cafe, if we want to find it again. 03:40 Last of all quit, and quit says Goodbye and Bon Appetit. 03:44 So that's a quick tour of what the finished product is going to look like. 03:48 Now we're going to see how to actually go about creating that using the skills 03:51 that you have already learned in Ruby. Collapse this transcript Application paths  00:00 Most times when you are starting a new Ruby application, one of the first things 00:04 you want to do is establish your application paths. 00:07 Essentially you are telling your application, figure out where you were located 00:11 inside the file system at this point in time, and then we will be able to use 00:15 that path as a basis for any relative paths that need to load in other files, 00:19 either for requiring them or for reading and writing to them. 00:22 Inside my food_finder project folder I've gone ahead and added a couple of things. 00:26 I have got my lib folder, 00:27 which is my library for different classes of objects. 00:30 and I've got a starting class here, guide.rb, which is empty. 00:33 There is absolutely nothing in there. 00:35 It's just a placeholder file for now. 00:36 And then I have got in init.rb. init is short for initialize and init.rb is 00:41 sort of a Ruby convention that this is the starting point of our application. 00:45 This is what we will use to launch it. 00:47 Now you don't have to follow that convention if you don't want. You can call 00:50 this food_finder.rb, or you can call it launch.rb. 00:53 But the convention is init.rb and it makes it easy for other people to know, 00:56 hey, this is the file that really kicks things off and get things started. 01:00 So we are going to use that convention for our program as well. 01:02 So we have got some comments at the top reminding you that this is how you get 01:06 started, that you launch it from this file, and then I'll define my application path. 01:10 And this should look familiar to you. All I am doing is taking a constant called 01:13 APP_ROOT, and I'm setting the current directory of this file, init.rb. 01:18 That's where my application resides. 01:21 That's the folder I'm in. 01:22 That's food_finder, wherever that happens to be. 01:25 Now I have the ability to locate other files. 01:28 For example, if we want to require the guide file, we can say 01:30 require, APP_ROOT/lib/guide. 01:32 Now that is a full absolute path now, not a relative path, right? 01:38 What we were able to do is get an absolute path here for APP_ROOT, so now, 01:43 everything we say after that relative to the APP_ROOT is an absolute path as well. 01:47 So this will work, we can launch our init.rb, we will get no response back, 01:52 because we aren't actually doing anything besides setting these values and 01:55 requiring that empty file. 01:57 But before we go on, I do want to show you a couple of other techniques here, 02:00 including one that we haven't talked about before. 02:03 So we have seen that we can require it this way. 02:05 Let's comment that line out, and let's do a different require. 02:09 We saw that we could use file join, and we saw that that could be a little 02:12 bit of a better way to do it, because it will use the separators based on the operating system. 02:17 And we can use guide.rb or we can just do the same thing and leave the rb off, 02:22 and it will still know where we are. 02:23 So either of those will work for us. 02:25 I am just going to comment that one out. 02:27 and I will go ahead and comment that one out as well. 02:29 Those would be slightly superior to the first way that we did it. 02:32 An even better way uses a technique that we haven't seen yet. 02:35 It's a little bit advanced, so I don't want to go into great depth about it. 02:38 But we can use the dollar sign colon variable that's a very strange looking 02:43 variable name, but it is a special Ruby variable. 02:45 It works a lot like this File does, File with the two underscores on either side. 02:50 It's a special magic variable inside Ruby. 02:52 And what it does is it contains an array of all the folders that Ruby will look 02:59 in to find the files that we've asked for. 03:01 Up here we were asking for a full absolute path, but if we could tell Ruby about 03:07 different folders by putting them in this array, we wouldn't have to give it an 03:11 absolute path anymore. 03:12 It would check different places for us. 03:15 So the most common way that you do this is you use unshift. 03:18 That is what's going to append whatever we want at the beginning, and then we 03:23 would put something like this in there. 03:25 Let's go ahead and copy this for now. 03:27 So File.join but we don't want to tell it a specific file. 03:30 What we are telling it is lib. 03:32 That's the folder I want you to look in for stuff. 03:35 And now that we've added to that list of standard Ruby paths, now we are able to 03:38 say require and just guide, require 'guide'. 03:42 So let's go ahead just to make sure that this is all working. 03:45 Let's go to our command line. I am already inside my food finder. You want to 03:48 navigate to that folder or else you want to provide a longer path to get there. 03:52 If I just say Ruby and then init.rb, and of course we didn't get any results back, 03:58 because all we did was that initialization, and then the file was empty. 04:02 Once it actually required the file, it didn't do anything beyond that. 04:04 We are going to need to actually define that guide class, and then instantiate it, 04:09 and have it do some work for us. 04:10 Let's see how to do that next. Collapse this transcript Guide class  00:00 In this movie we are going to see how to create the guide class for our 00:03 Food Finder project. 00:04 And the guide class really is going to be the heart of the food finder, because 00:08 in that init.rb file, we are going to instantiate the guide class. 00:12 We are going to create a new guide, right, a new instance of the guidebook. 00:15 And then we will tell that guidebook, hey, guidebook take over, and at that point 00:18 the guide class will take over, and it will handle all of the user input, 00:22 and do all the processing of the actions that have been asked for, and handle 00:26 the user output as well. 00:27 So really the guide class is going to be the controller class. It is going to 00:30 control what's going on. 00:32 To start out with, we will just outline the features that we think it's going to have, 00:35 and then over time we can come back and fill those in. 00:38 So I have already got a placeholder for my guide.rb inside the lib folder and 00:42 I have gone ahead and just given it the class declaration of guides, just to get us started. 00:46 So the first thing we are going to want to do is initialize this object. 00:49 What do we need to do to initialize it and get things off to the right start? 00:53 Well, it occurs to me that the guide needs to know where the restaurant file is 00:57 that we are going to store all these restaurants. 00:59 We could simply hard code that value in at some point, say well, you know what. 01:02 It's always going to be in the same location relative to APP_ROOT or 01:06 relative to the guide. 01:08 Instead of doing that though, I think because we will make our classes more 01:11 general and a little bit reusable, I think it's better if we actually pass in 01:15 the path to the file as an argument. That will then allow us to have 01:19 different files potentially. 01:20 We could have a guide that's for Dallas and a guide that's for Seattle, let's say. 01:24 So, I think that amount of flexibility could potentially be very useful for us. 01:27 Now once we actually pass in that path, there are going to be couple of things 01:30 we will just comment for now. 01:32 We want to locate the restaurant text file that's at that path, or if we 01:35 can't locate it, we want to create a new file, and if the create fails then 01:39 we'll just exit, maybe there was a permissions problem, maybe we weren't able to create it. 01:43 So we want to keep that step in mind. 01:45 So once we have initialized our guide object, there might be some other 01:48 configuration and things that we want to do, but ultimately what we want to do 01:51 is launch it and say, "all right, guide, here you go, take over and run." 01:56 So I've given it the launch method and I've given it an exclamation point at the end, 02:00 just to sort of make it clear that this is a real strong and powerful 02:03 method that does a lot of stuff. That's our exclamation point, just sort of says 02:06 it's sort of turbocharged. 02:07 Now the launch method will attempt a couple of steps. It will give an 02:10 introduction that just says welcome to the food finder, here's how you get 02:14 started, something like that. 02:16 And after that it will go into a action loop and it's going to ask for the user 02:20 input, what do you want to do? 02:21 and you will have the choice to input list, find, add or quit. 02:25 And then we'll do that action, and then as soon as we have done it, 02:28 then we are ready to accept the new response, right? 02:30 So we listed all the restaurants and then we say, "right, now what you want to do?" 02:32 And the user says, "I want to add something." 02:34 Okay, well, once we added them we say okay, and now what do you want to do? 02:37 We want to list it again. 02:38 So it will just be an action loop that will keep going until the user quits and 02:42 then when the user finally quits, then we will have some kind of a conclusion 02:45 where we will actually just say goodbye. 02:47 Now the introduction and the conclusion are just text. 02:50 So let's go ahead and just paste those in for now. 02:52 You can put in whatever you like. I've just got something that says 'Welcome to 02:55 the Food Finder, this is an interactive guide to help you find the food you crave' 02:58 and then when they finally leave, it just says 'Goodbye and Bon Appetit!' 03:01 So we can now uncomment introduction and conclusion, and those are actually our 03:04 method names, right? 03:05 So it's going to perform this method and then call this method, right, on this class. 03:11 So let's go ahead and try all of this. 03:12 Obviously we are still missing a lot of this functionality for finding the file, 03:15 and for the action loop. 03:17 So the next step of course after we define the class is to go into init.rb, 03:20 and get things started. 03:22 So let's go ahead and say guide = Guide.new, and we will tell it where the files 03:28 is going to be, even though we don't have that functionality built in there, 03:30 let's go ahead and put this in. 03:32 So the file is going to be called restaurants.txt, create a new guide, and 03:36 once you do, guide.launch!, take it away. 03:39 Now at this point our init.rb file is done. 03:43 That's just going to bring up a new guide instance and get it started and 03:46 the guide is going to do the rest. 03:48 So we can save that and let's go to our command line and just try it and I am 03:51 already inside my food_finder folder, you want to navigate there, ruby init.rb. 03:57 So there we go, we get the introduction, and then we get the conclusion. 04:00 We don't get anything between. 04:01 It didn't do any kind of file checking for us, or anything like that. 04:04 We will still have to write that but now we know that our Ruby program is at least started. 04:09 All right, we have a class defined. 04:10 It doesn't do much, but it does bring it up and launch it for us. Collapse this transcript Restaurant class  00:00 So far we sketched out our guide class, and we have been able to instantiate it 00:04 and launch it, but it doesn't actually do much. 00:06 So we need to start going back into those methods and fleshing them out. 00:09 And I want to start with the initialize method. 00:11 if you all remember, the initialize method in the guide takes a path that we 00:14 sent them into it, and tries to find a file located at that path. 00:18 If it can't find it, it tries to create it. 00:20 Now we could just put that functionality right there in the initialize method. 00:24 But that's not the best option. 00:25 It's not as object-oriented as it could be. 00:28 Instead, it's going to be much better to have a restaurant class and put all of 00:32 the functionality of dealing with that data file in the restaurant class. 00:35 So I have gone ahead and started the restaurant class. 00:38 Let's open that up. 00:39 It's in restaurant.rb in the lib folder, and it is just a real simple class definition. 00:44 Notice I have got a class variable here for file path, so we don't need an 00:48 instance to have the file path, and I'll start it out equal to nil. 00:51 Then I have got three class methods and we know their class methods, because 00:54 they have self at the beginning of them. 00:56 The first is file_exists. 00:58 So the restaurant class should know whether it's file exists or not. If not, 01:02 they will know how to create the file. 01:04 And then the last class method will be, that it will be able to look in the file 01:07 and read the file, and after it's done reading, it will return instances of the 01:12 restaurant, making each name, cuisine and price into an actual instance. 01:17 So those are the class methods we are going to be dealing with. 01:19 Let's just push that aside for a second and let's open up the guide class, and 01:23 let's take a look at this initialize method here. 01:25 So locate the restaurant text file at the path. 01:27 Well, we are going to be talking to the restaurant class now, so we need to say 01:31 well, restaurant class, restaurant, set your file path equal to path. 01:36 Restaurant.file path=path. 01:37 There's two important things here. 01:39 The first is whenever we call another class its a good idea to require that class, 01:45 because it's now considered a dependency, we want to make sure that 01:48 class has been loaded in before this class. Not just does it exist, but will it be loaded in? 01:55 And it's be loaded in early enough that we can start talking about it. 01:58 Now notice that I'm not providing a full absolute path here. 02:01 That's because remember earlier, I told that init.rb file where it could find 02:06 everything that it was looking for. 02:07 I would gave it that lib folder as a path. 02:09 So the first place it will check for restaurant.rb is in that lib folder. 02:13 Okay, so back to our initialize method. Do you remember what the problem with 02:16 file path equals is? Hopefully you do. 02:19 File path, the class variable, is not accessible from outside of the class. 02:24 Instead we need to have a setter method, self.filepath= and then whatever path 02:31 will make it nil by default, and this will now be a setter method that will 02:35 allow us to call it from outside the class. 02:37 All right, we don't have the ability to call the attr things for class methods, 02:42 right, just for instance methods. 02:44 Instead we have to write it out in full. 02:46 That's okay, because then we will say filepath=and we could just say its equal 02:49 to path, but I am going to actually say File.join (APP_ROOT) with the path. 02:56 Now that does assume that the path will always have to be relative to the app root, 03:02 but that's fine. 03:03 That's a restriction I am comfortable making. 03:05 Otherwise, if we wanted to allow it to be a file somewhere else, we could 03:08 require them to send in an absolute path instead, or we could check and see does 03:12 the path start with a slash or something like that. 03:14 But for now I am just going to assume that whatever has been passed in is 03:17 relative to the APP_ROOT. 03:18 So that will take care for setting file path. 03:20 So now let's jump back over to initialize, and let's finish taking care of this. 03:23 So, we have now set it. We have would told it where the path ought to be. 03:26 Now we have the ability to say hey Restaurant, does that file_exists? 03:32 And if it does, then let's just say puts "Found restaurant file" or if not elsif 03:40 hey Restaurant, try and create_file. 03:42 So see how I am controlling it form the guide class, but the actual work is 03:47 being done by the restaurant class. 03:49 So if we're able to do that, then let's copy that line and let's say 03:53 "Created restaurant file." 03:55 Or if neither of those happens, it will just exit, and so we will say puts 04:00 "Exiting.\n\n" and finally exit! 04:05 Now we haven't talked about that yet. 04:06 It's a Ruby method that says okay, no matter where you are, no matter where 04:10 you've wandered to inside different classes, abort the script. 04:14 Just get out, go back, and just finish up what you're doing, take me back to the command line. 04:19 And so that's what it will do. It will just stop. 04:21 It doesn't take us back to init.rb to finish up anything there. 04:24 It just stops cold, right there in its tracks. 04:27 So I think that's going to do a pretty good job on our initialize method. 04:29 We can go ahead and try running this. 04:30 It's not going to do much for us, but let's go ahead and see what happens. 04:33 Ruby init.rb and it comes back and says oops! Exiting. 04:37 And the reason why it exited, was because, file_exists was not true and 04:41 create_file was not true. 04:43 So therefore, it fell to this condition, Exiting. 04:45 So we need to write something for file_exists and create_file. 04:47 That's what we will do in the next movie. Collapse this transcript Accessing the restaurant file  00:00 Now that we have our Guide class successfully interacting with our Restaurant class, 00:03 we need to give our Restaurant class the methods it needs to interact 00:06 with the file system. 00:07 We haven't written those methods yet. 00:09 That's what we will do in this movie. 00:10 Now just as a quick review. Remember in the Guide class we are calling a couple 00:13 different of methods on the Restaurant class. 00:15 The first is filepath equals, which is no problem, we have already created that. 00:19 We also have file_exists with a question mark after it. 00:22 We haven't written that method yet, and Restaurant create file. 00:26 And notice that I am using them as part of a conditional statement, if then. 00:29 So we will also want to be mindful of what return value we give for each of these. 00:32 We want to make sure we return True or False to let it know whether it 00:35 succeeded or failed. 00:36 So let's jump over to the Restaurant class. 00:39 Here is our file_exists method, and let's just make a if statement, if something-- 00:44 we'll come back to it, return true else return false. 00:48 So now let's figure what that condition would be. 00:50 Well, the first thing is if file path hasn't been set, well then we can't 00:54 determine if the file exists. 00:55 So might as well just return false at that point. 00:57 So first we need if filepath. 00:59 Now we couldn't say if filepath is not nil, but there is no reason to do that. 01:03 If this value is nil, then it hasn't been set, and that will be equal to the Boolean false. 01:08 But we don't just need to have is set; we also need to check and make sure 01:11 that the file_exists. 01:14 Does the file_exists at filepath? 01:17 So this will check and see has it been set and when you go to the file system, 01:21 is the file actually there. 01:22 That's a good check for file_exists. 01:24 Now we could also check for whether the file is readable, writable, right? 01:28 Those aren't bad things to test for. 01:30 If we did, we probably would want to rename the method something else though, 01:32 because file_exists, really is just does it exist? 01:35 I mean, it doesn't say anything more than that. Whether the file is actually usable. 01:39 So let's actually create a new method. 01:41 We won't end up using this for now, but just to illustrate the point, file 01:45 usable, and I want to show you a different technique for writing these 01:48 if-then statements, because this is a very common sort of paradigm in 01:52 programming, especially in Ruby. 01:54 So we can say return false unless filepath, return false unless file.exists. 02:04 And we can just do a series of these, all the reasons that it might fail, and 02:08 let's do return false, unless file.readable at filepath. 02:15 And I'll just do one more. I'll copy it. If the file is writable. 02:19 So those are all the reasons it might fail, is the file useable or not, and 02:22 finally if it passes all of those, return true. 02:26 So the main reason I wanted to create this method was to show you that a) it's 02:29 not a bad idea to check if the file is readable and writeable, but b) to show 02:33 you this structure, return false unless, return false unless. These could be If 02:37 statements, it doesn't matter. 02:38 The main point is they are all these steps along the way where it might fail. 02:42 But if it passes each and every one of those, then the last thing is that 02:45 it will return true. 02:47 So this is a very common way to write these kinds of Boolean tests, instead of 02:51 trying to write If-then statements. 02:53 Now that we have written this new better method, file usable, let's actually use that instead. 02:57 Let's switch back over here from file _exists to just make it file_usable. 03:01 We will use that from now on. 03:04 So I'll leave file_exists in here, just so we have it for reference, but we are 03:07 going to be using file_usable. 03:08 Let's look at create_file. 03:09 This is something very basic from where we talked about working with the 03:12 operating system. file.open and then filepath, that tells us where it is. 03:17 We are going to open it in write mode, and we could provide a code block here, right? 03:21 That's how we have seen open in the past, but we don't have to. 03:23 If we do that, it will just create the file if it doesn't exist. 03:26 So we can say create the file, and let's actually say unless file_exists, then create it. 03:33 Now if it does exists, it's no big deal to open it up with write access and then 03:37 close it again right away. 03:38 That's what happens here, there's no problem. 03:40 The one thing we want to be sure though is that we return whether or not 03:44 the file is usable. 03:46 So at the end of all that, is the file usable? 03:49 That's what we are going to return as a Boolean, to make sure that the file we 03:52 just created is going to be usable, and we'll pass our Boolean test here. 03:56 So let's try all this now. 03:57 Let's just go to our command line. 03:59 I am already inside my food_finder, so ruby init.rb, and there it is. 04:04 It says created restaurant file. 04:05 It said, "there wasn't a restaurant file, let me create one," and sure enough 04:08 here is that file, restaurants.txt. 04:10 You'll find that there's nothing inside of it. 04:12 It just opened it and closed there right away, but it did create the file for us. 04:15 And if we come back and run it a second time, it says found restaurant file. 04:19 So that works as well. We were able to both create it at the first time and 04:22 then find it on subsequent attempts. Collapse this transcript Handling input in the action loop  00:00 So we are off to a good start with our guide at this point and our 00:02 initialization routine is basically done. 00:04 But what we don't have is any kind of interactivity in our Food Finder and 00:08 that's what we are going to work on next. 00:09 So I will just open up the guide.rb file and the initialize method is what we 00:14 have been working on so far. 00:15 We can now call that done. We have really written it. 00:17 It is interacting with the Restaurant class and the Restaurant class is handling 00:20 everything appropriately. 00:21 I am going to use a feature of TextMate to just fold up that initialize method 00:25 and I can do the same thing for the introduction and conclusion, because we are 00:27 basically done with those two. 00:29 But this launch method here has this action loop and we need to tackle that now. 00:34 So what do we want to do? 00:35 That's what we want to ask the user. 00:37 We want to ask the user to give us some feedback. 00:40 Well, we know how to do that. 00:41 We have learned how to take user input before. 00:43 I am going to give the user a prompt first of all and we will put a space 00:46 after it and then right after we print the prompt, then we want to do 00:49 user_response = gets.chomp. 00:53 Remember how to do that? 00:55 So it is going to chop off the line return or whatever they give us, but it will 00:58 then wait for them to give us a response. 01:01 Then we want to do whatever action they give us. 01:04 So we will say do_action(user_response) and we will have a method then called do action. 01:11 Let's put that right here with def do_action. 01:16 So we will need to pass in the action as a parameter to that. 01:20 We will come back to that method in a moment. 01:22 And then after we do that action we want to loop. We want to do it again. 01:26 So let's write a loop, loop do and then let's indent all of these all the way 01:33 down here and finally end. 01:37 So that will just loop through and it will just keep going, keep going and just 01:40 looping asking for the user response every time. 01:42 It doesn't get us out of the loop though. 01:43 There is no way for us to quit. 01:45 Now, we could put something right here that would say break if user_response == quit. 01:54 That would work. 01:56 And even better way though I think is to go ahead and keep all of our actions 01:59 including quit grouped together, so that we will do the action quit and we will 02:03 get back to the result of that action. 02:06 So if the result that comes back from passing in that string quit to do action, 02:12 if that result tells us to quit, then we quit. 02:15 And so instead of passing the word quit, I am actually going to just pass the symbol quit. 02:18 So if the result that comes back is the symbol quit, then we know we need to quit. 02:23 So let's jump down here to do action and write that. 02:26 The action that we pass in will be different. 02:28 So we want to have different response based on what those actions are. 02:31 But we know that they will be ahead of time. 02:32 It's either list, find, add, or quit. 02:34 Those are the four actions we are going to let them do. 02:36 So let's have the case statement. 02:39 So case action and then we can say when they pass us list, then puts Listing... for now. 02:48 We will come back and actually do the action a little bit later. 02:51 For now we are just going to have some kind of response. So Finding... 02:58 and when they have told us to add, we will do puts Adding... 03:07 and finally when quit and we know what quit needs to do. 03:12 Quit is going to return the symbol quit. 03:15 Then last of all let's do else puts and a line return. 03:20 I don't understand that command. 03:24 So there we go, so that will-- and then end at the end. 03:28 So now we have nice case statement that just handles the action. 03:31 It says okay, take that action, if they gave us list, then do the list. 03:35 If they gave us find, then we are going to do the finding. 03:37 If they gave us add, we will do the adding. 03:38 If they gave us quit, just return the symbol quit, which gets caught by result 03:43 and says hey, let's break out of the loop now. 03:46 So let's go ahead and try this. 03:47 I will just save it. 03:48 Let's jump over here to the Terminal and let's try ruby init.rb. 03:54 Welcome to the Food Finder. 03:55 So now it's waiting for me. List, Listing..., 03:58 find, Finding..., add, Adding.... 04:01 Let's try something garbage. I don't understand that command. 04:05 And let's try then finally quit, which sends the symbol quit and breaks us out 04:09 of the loop and once it breaks us out of the loop, 04:12 that gives us the conclusion and we then wrap things up. 04:15 So this works, this is great. 04:18 I think that loop is not used that often in Ruby. 04:21 Usually, there is a better way. 04:22 If you find yourself just doing a simple loop, there is probably a better way to 04:26 write it in most cases. 04:28 So in this case what we actually could do instead, what I think is a better 04:31 way to do it is until result is equal to quit and then we just have to 04:37 initialize result up here. 04:38 So result equals let's just say nil to start with. 04:42 So result starts out being nil. 04:43 Until it is equal to quit, which it's not the first time, then we do the loop. 04:47 We don't need this break anymore. 04:49 Here we can just take that out. 04:52 So we go through the loop. 04:53 Is result now equal to quit? 04:55 If so, then we break out of the loop, because the until statement told us to. 05:00 So it is little bit cleaner, a little bit more succinct and a little bit more Ruby-like. 05:04 I think that most Ruby programmers would tend to program it this way instead 05:07 of using that loop. 05:08 Loops are just a little bit clunky. 05:11 The one last footnote that I want to give you about this 05:13 now that we have a loop of user input is that if you need to break out of it, if 05:16 you wrote something incorrectly and you are stuck in an infinite loop, from the 05:19 command line you should be able to use Ctrl+C. That's hold down the Ctrl key and 05:24 press C and that should exit out of the program. 05:26 It should force it to quit. 05:28 So that's a very useful keyboard command to know. Collapse this transcript Limiting input  00:00 In the last movie we enabled our Guide class to be able to handle user input. 00:04 In this movie, I'd like to improve that a little further by restricting the data 00:07 that will allow the user to give us. 00:09 The thing that I am mostly concerned about is that we are accepting any response 00:12 from the user here and passing it right away off to the action. 00:16 And the action is handling it appropriately. 00:18 The action, if it doesn't fit one of these categories, comes up and says, 00:20 "oh! I don't understand that command." 00:23 But I think it's better software design if we actually trap it before we try and do the action. 00:28 Let's make sure we got valid input before we run off and try and do it. 00:32 The other one works. 00:33 There's no reason that that's wrong. 00:35 You can do it that way, but I think it's a slight improvement and it will also 00:38 give us a place to make some more improvements a little later on. 00:40 So, let's go ahead and make that change. What I want to do is make a new 00:44 method called get action, and I am just going to right now move these two 00:49 lines down into get_action and that means that these I'll just take away, and 00:56 instead of being the user_response, now we will say, well the action is going 00:59 to be equal to get_action. 01:02 And then we'll just go ahead and do action. 01:05 We'll pass in the action here. 01:06 So, at the end of this, what we want to do is return the action, which means 01:13 that of course, my action has to be equal to my user_response. 01:18 Now, that probably seems a little bit redundant, but that's because what I have 01:23 in mind here is that we can actually make a quick improvement to the 01:25 user_response by saying well let's actually take the response and downcase it, 01:29 and strip any spaces out of it. 01:31 So, already we've improved what the user gave us. 01:33 We'll make sure that if they gave us all capital, they have their Caps Lock key on, 01:36 and they type list, 01:37 it will still work, because it will be downcase before we would return it. 01:40 But the big improvement that I want to make is that I want this to now be inside a loop. 01:45 So that it checks to see whether or not this is a valid action before it 01:51 actually returns it and says let's get started. 01:53 So, we need a way that keeps track of what the valid actions are. 01:56 So, I am going to come up here at the top of the Guide, and I'm going to create 01:59 a class inside the class. 02:01 Now this is not strictly necessary to have a full class in here, but I am 02:05 going to call it Config, and that's where I am going to put configuration 02:07 information about the Guide. 02:09 Having a separate class can be a nice way to be able to group together some of 02:12 these attributes and methods, but it also just illustrates the fact that you can 02:16 have a class inside of class and that's mainly what I want to show you. 02:19 So, it's a little bit overkill in this case, but it does make the point. 02:22 So, let's say actions =, and the actions we are going to allow are list, and 02:27 find, and add, and quit. 02:31 Those are the things that they are allowed to do. 02:33 Now, we'll need a way to be able to read that, self.actions. 02:37 Remember it's not accessible from outside the class. 02:40 And I'll use my semicolon, so I'll just put it all on one line, just return the action. 02:45 So, notice that I have just got semicolons here that let me, instead of breaking 02:49 it up onto several lines, I can just a one quick line, here is a reader method 02:54 that will read back those. 02:55 So, now down here I can actually ask for that. 02:57 I can say Guide::Config.actions and that will return that value. 03:05 So, does it include whatever action we have been given? Well until it does, 03:12 we are going to loop. 03:17 So, see what I have done. 03:18 It's a loop that's says all right, let's keep going through, every time 03:21 we'll loop through and see does the action that we've been given, is it one of these actions? 03:25 If so, we can return it. If not then, we have got to go through the loop again. 03:30 So, let's start out with action = nil, so that it has a value the first time 03:35 through and that first time it will be equal to nil. 03:37 So, the nil is obviously not in that list, so then it will go through again and 03:41 again and again, until finally, they give us something that matches and at that 03:45 point it will already be down cased and stripped. We'll return it. 03:48 I'll just put in a comment here to remind us that that's what it's doing. 03:52 One nice thing about doing it this way is that now we have the opportunity 03:54 to not just say hey! user, we didn't get good feedback. Instead we can say here are your available actions. 04:01 If that list changes and we add a new feature, we add a new action, boom! 04:04 It gets added to the list automatically. 04:07 So, we'll take that list of actions, we'll just join them together with a comma 04:10 and a space between them and so we will have a list, saying what they are. 04:13 Now it's a little weird the first time you come there to say it, so we might 04:15 some have something like if action, which means the first time when it's nil, 04:19 it won't output this list. 04:21 It's only if we get a failure. 04:22 So, let's just try running all this. 04:24 Let's see how all this goes together. 04:26 Let's go to the command line and let's run ruby init.rb. 04:31 So, here we are, food we crave. 04:33 So list, Listing. LIST, Listing. Space space list space space, still works. Listing. 04:40 If I now type something that's garbage, it comes back and says oops actions and 04:45 it gives me a list of the available actions. 04:46 It's a little bit friendly and then finally quit when we are done. 04:50 So again, while it's not strictly necessary. 04:52 I think that it is better to pull all this off into its own get_action, because 04:56 that gives us a place to add some enhancements in there, and they don't clutter 05:00 up this launch method, right? 05:01 get_action contains all the information it needs to know about how to get the action, 05:05 and that's where we'll make sure that the input is valid, before we 05:10 ask do_action to do what it knows how to do best, which is to actually perform that action. Collapse this transcript Adding restaurants  00:00 Now that we are handling the user input well, we need to start responding to 00:03 that input with some meaningful results. 00:05 The actions inside our food finder. 00:07 And I am going to start out with the add restaurant action, because we can't 00:11 really list or find restaurants until we have some. 00:14 So, here we are going to concentrate on getting the information about the 00:16 restaurant, and then saving it to that restaurant's text file. 00:19 Inside the guide.rb file we have been working with, we already have a do_action, 00:23 that takes a string, and if that string is equal to add, then it's going to do 00:28 our add action, right. 00:29 Well, we could put all the code for that right here inside the case statement, 00:33 but I think it's better to use this as sort of a signpost, telling us what 00:36 method we are to call from here, and then we'll have one called add. 00:40 And then I'll make a new method down here, just call it add, and so when it's 00:45 equal to add, we'll call the method add. 00:47 So, all of this will get executed. 00:49 So, what do we want to do? 00:50 Well, the first thing I am going to do is just output something that says all right, 00:53 we are adding a restaurant. 00:54 It's sort of like a title. Then we want to actually create the restaurant, 00:58 give it its values, its correct attributes, and save it. 01:02 Those are the steps we are going to go about. 01:03 Well, we know how to create a new restaurant. We create an instance of 01:06 the object restaurant. 01:07 restaurant = Restaurant.new. 01:12 Now in order to add attributes to it, we need to have instance variables. 01:17 Those are the attributes. 01:18 So, if we come back over here to restaurant, we have written lots of class 01:21 methods so far, self.filepath, self.file_exists. 01:24 This is a class variable. We don't have anything for the instances. 01:28 So, in order do that let's create attr_ accessor, so it can both read and write 01:34 for name, cuisine, and price. 01:39 You can add others if you want. I am just going to keep it simple by 01:41 having those three. 01:42 So, let's jump back over to our guide now, and we can start to put in values for that. 01:46 So, we are going to have restaurant.name = and restaurant.cuisine = 01:55 and restaurant.price =. 01:58 So, in order to fill those out, we need user interaction, right? 02:02 We know how to do that already. What we want to do is give the users some kind 02:05 of a prompt first of all. 02:06 So, let's say print Restaurant name: , 02:11 and then that will then stop and prompt them using gets.chomp.strip. 02:17 So, that will take any spaces out of it at the end. 02:19 I am going to use that all three times, but instead of Restaurant name, we are 02:23 going to then ask for the Cuisine Type, and let's ask it for the price. 02:32 We'll call it Average price, so it's clear that that's what we are 02:34 really looking for. 02:35 It's just a rough estimate, Average price. 02:37 Let me go ahead and make that lowercase as well so it matches. 02:42 Okay so now we've got an instance and we've given that instance attribute its values. 02:47 Great! What's our next step? 02:48 We need to save it to that file, so how we are going to are going to go about doing that? 02:51 Well, saving it, what it really means is appending this data to the file, so we 02:57 can read it back later. 02:58 So that's what we are going to do. 03:00 I am going to do it using restaurant.save. 03:03 We'll have to write that method. 03:04 That's going to be an instance method on the restaurant class that we'll save. 03:08 And if the save succeeds, we'll return one thing and if it fails we'll 03:11 return something else. 03:12 So, if restaurant saves then let's just say puts and I'll just put a line return here. 03:18 Restaurant Added line return, line return and if it fails, I'll just copy that. 03:28 Let's change it to say Error: restaurant not added. 03:36 And let's actually change it to be Save Error, so it's clear that's what 03:40 we're talking about. Save Error: 03:42 Restaurant not added. 03:43 That will give us some meaningful results. 03:45 Our results of our save we'll need to return true or false. 03:48 So, we'll just want to make sure that the end we return either true or false for that. 03:50 So, now let's write that Save method. 03:53 It's going to be instance method. I like to put my instance methods below my 03:57 class methods and I think most Ruby developers do as well. 04:00 So, I am going to have all my class methods at the top, then my instance methods. 04:04 So, I have got an instance now that needs to be saved. How does it go about saving? 04:07 It's going to open up the file and write to it. 04:10 So, we need to make sure that that file exists, return false unless file, 04:17 actually let's say usable, so we can make sure it's writable. 04:20 In the past when we were doing create file for example, we were just using 04:24 file_exists and file_usable on their own, just calling those methods, but this was a 04:29 class method itself. 04:30 Now we are using an instance method. 04:32 So, we need to ask the actual class Restaurant.file_usable, all right? 04:38 Otherwise it's going to look for an instance method called file_usable. 04:41 We need to class method. 04:42 So, we have to tell it, hey! you have to ask the class for that. 04:45 So, now let's go-ahead and open up the file. 04:47 If the file is usable then we didn't return false, then we are going to do a 04:50 File.open, and we know where that's going to be located, set filepath, and 04:56 we want to append to the end. 04:58 So, we are going to be in Append mode that'll put our file pointer at the end of 05:01 the file ready to write a new line, and do File and then what are we are going 05:07 to actually put in there? 05:08 Well, we have this file block variable that we are going to be working with, 05:12 and we can tell it puts, and then what do we want to actually put here? 05:16 There are a lot of different ways you could do this. 05:18 I am going to do it by saying okay, everything I want to input is going to be 05:21 inside these brackets and then I am going to do a line return at the end and 05:25 what I am going to put in here is going to be an array made up of name, 05:29 cuisine, and price. 05:32 And then I will just take that array and join it together with tabs. 05:37 So, that's the \t for a tab. 05:40 And notice I used double quotes in every case so that these get 05:43 handled appropriately. 05:44 Single quotes would not handle these escaped characters right. 05:47 So there we go. That will output a tabbed version of this array. 05:52 So, it will be name tab, cuisine tab, price and then a new line return, and that 05:57 will be essentially how we'll export a line to the file. 06:00 So, then we'll say return true at the end, so that we know that it did succeed. 06:04 We did get down there. 06:05 Let's try it out, see if it worked. 06:07 So, let's go to our command line, ruby init.rb. 06:11 Welcome to the Ruby Food Finder. 06:12 What do we want to do? We want to add. Restaurant name. 06:15 Let's start with Hot Tamale and it is going to be Mexican and the price, 06:22 the average price we'll just say is$25. 06:23 And it goes back and says Restaurant Added. 06:26 Great! Did it actually add it? 06:27 Let's just hide these results, so we'll switch here, and let's open up 06:31 restaurants.txt and sure enough, there it is. Hot Tamale, there is actually tab 06:35 here between it, and a tab here and then a line return. 06:38 It's ready on line 2 for me to add another restaurant to it. 06:40 So it succeeded. We were able add it. Collapse this transcript
 00:00 In the last movie, we successfully coded the add action, and it does exactly 00:04 what we would want it to do. 00:05 It lets the user input the properties of the restaurant that they want to create 00:09 and then it saves them to the restaurant's file. 00:11 In this movie though, I want us to look at how we can refactor that action. 00:15 If you haven't come in contact with that word refactoring before, 00:18 code refactoring means rewriting the code to improve it, to improve its structure, 00:23 its readability, maintainability, performance, or its extensibility, being able 00:27 to extend it, without modifying behavior or functionality. 00:31 So that's what we are going to try and do here. 00:32 The end result is it's still going to save it to the file, exactly the same. 00:36 But we are going to see how we can improve our code. 00:39 At the moment, our add action is creating a new restaurant and then setting all 00:43 of these values one by one and then saving it. 00:46 That's the behavior I want to change. 00:47 Instead of creating an object and adding attributes, I think an improved way 00:51 to do it is to add that functionality into the restaurant class, into the initialize method. 00:57 So initialize, and it's going to take some arguments. We will make it a empty hash, 01:01 in case they don't pass any arguments to us. We will still just go ahead 01:04 and do the initialization. 01:05 But if we do get a hash, then let's go ahead and set name equal to args and then name. 01:13 Now if we don't get a name sent, we will get nil. 01:15 Instead of getting nil, I am going to have it default to an empty string because 01:19 I would rather have an empty string in the name than I would nil. 01:22 We can do the same thing for cuisine. 01:24 Cuisine equals args and cuisine and once again and an empty string. 01:31 And last of all, price equals args and price. 01:36 Now this is a very common pattern that you will see in the creation of 01:39 attributes in the initialize method. 01:41 We will book for some value in the arguments. If it's not there, then we 01:45 default to a default value. 01:46 And also, you'll often see these spaces here done so that everything lines 01:51 up nice and pretty. 01:52 It's up to you if you want to do that or not but that just kind of gives it a 01:56 nice clean cut look. 01:57 So you will see that in a lot of people's code as well. 01:59 So at the end of this, we will create a new object and either all these values 02:03 will be sent to empty strings which still could be saved to the database if we 02:06 want to tell it to do that. 02:08 We could also say well you know what, don't save it unless you have all three values. 02:12 We could add something in here that would actually do some validation. 02:15 I am not going to do that. 02:16 I will leave that at something that you can do, but the save could fail just 02:19 because the attributes aren't ready. 02:21 But if we are passed those attributes, they'll all get set. 02:23 So now, back on this page, we can set up something here and say args, this is a 02:28 different args, of course. 02:30 This is just going to be a local hash that we'll be working with and for each 02:33 of these, we can now say args, and let's put the brackets around it, and we'll make it a symbol. 02:39 Right, so in the args name, go ahead and put whatever they give us. 02:43 Let's do that for each of these. I'll just copy this and paste it in for 02:48 restaurant both times. 02:49 We will need our closing square brackets on both of them as well. 02:52 So now at the end of this, I have a hash that's ready to pass to Restaurant.new. 02:57 So Restaurant.new can move now, it can drop down here, Restaurant.new(args). 03:02 So we will just pass in whatever args have got set to the restaurant and then save it. 03:07 So that's what refactoring is. 03:08 It's very common thing to be refactoring in your code to make 03:11 slight improvements. 03:12 I think this is slightly better because now we have an initialize method that's 03:16 smarter and I think it's a good thing if our objects can gain some intelligence 03:19 and be able to have some features and functionality we might reuse other places. 03:23 There is more that we can do though. 03:24 It bothers me a little bit that we are doing all of this information about 03:28 filling out a restaurant in the guide class. 03:31 Seems like the guide class isn't the right place for it. 03:33 What I would really like to be able to do is just tell the restaurant class to 03:36 handle all this data gathering for me. 03:39 What I would want to be able to do is to say instead of restaurant here, 03:43 let's say restaurant, and I will just copy this line, 03:46 Restaurant.build_using_questions, let's call it. 03:50 So I am just going to tell the Restaurant, hey, ask the questions you need in 03:53 order to build yourself. 03:54 And all of this then can be removed. 03:55 I am going to cut it, so that I have it still. 03:59 It's on my clipboard. 04:00 So right now, all I am doing is saying, "hey Restaurant, do your thing and when 04:03 you are done, save." 04:06 Now let's just jump over here. We are going to need a new method here that will 04:09 handle that for us, right. 04:10 This is going to be a class method because we are telling the Restaurant class, 04:15 build_from_questions. 04:21 And I am going to paste everything that we had in the other one in there. 04:24 We are going to need to just do a quick check to make sure the code is still 04:27 good for us and it's going to set arguments. 04:30 It's then going to print the restaurant name. 04:33 It's going to get a response. 04:34 Then finally, at the end, it's going to just do self.new with the (args), and 04:41 we don't need to set it equal to restaurant. We can just return that. 04:44 So return the result of creating a new object, a new instance, using these arguments. 04:49 Now all that logic is contained inside restaurant. 04:52 If we had now other areas of our guide class where we wanted to be able to build 04:57 these questions or maybe we would want to be able to reuse this class and have 05:00 other classes be able to call it. 05:02 It would still have that knowledge about how to go about building a restaurant 05:06 instance built into it. 05:08 So I am going to save both of those. 05:09 Again, the net functionality of this is exactly the same. 05:12 We have cleaned up things a bit, we have moved things around but we haven't 05:16 changed the functionality. 05:17 That's why we consider it refactoring. 05:19 So let's try it all out from the command line and make sure everything still 05:22 works exactly the way we want and we didn't make any mistakes. 05:25 ruby init.rb, here we are. Let's add. 05:29 Oh, we have build_using_ questions is not a method. 05:33 Why did we not do that? 05:34 build_from_questions, ah, using_questions, there we go. 05:38 So I did make a mistake. 05:39 So now let's go back and try it again, ruby init.rb. 05:42 Now here we are, add. 05:43 All right, what's the restaurant name? 05:45 Let's say Cafe Masala, and that's going to be Indian food. 05:50 And the average price, we will say it's $30. 05:52 Okay, restaurant added. 05:54 Let's just double-check and make sure that that's true. 05:57 We'll jump back over here and we'll take a look at our restaurants.txt file, and there it is. 06:01 It made a new entry for it. 06:03 So it did successfully add it. We didn't change anything. We didn't break any functionality. 06:08 We just improved our code in the process. 06:10 And that's what refactoring is all about. Collapse this transcript Listing restaurants  00:00 Now that we have a few restaurants saved on our file, we can now write the list action. 00:04 We will actually display the restaurants that are saved, so the user can review them. 00:09 The process of starting the action is the same as we did for add. We are 00:12 going to just have list here called as a separate method and we are going to 00:15 define this method here. 00:17 We also created a stub for saved_ restaurants over here as a class method on 00:21 restaurant, back at the very beginning. 00:23 That's what we want to flush out, right? 00:24 That's going to pull up an array of all those saved restaurants. 00:28 Then the list can actually go about listing them, right? 00:31 So, let's have the list action. We'll just copy this first line from adds, all right. 00:36 So, it's going to be Listing restaurants, here we go. 00:42 So, that will be the title here of the page, and then we know that we want to do restaurants. 00:48 That's going to be equal to ask the Restaurant class to return 00:52 the saved_restaurants. 00:54 And that will give us an array that we can go on to display. 00:57 So, let's go ahead and write that saved_restaurants method and then we'll 01:00 worry about the display. 01:01 So, the saved_restaurants. 01:03 Read the restaurant file, then return the instances of the restaurant. 01:07 That's what we want to do. 01:08 So, reading the restaurant file, we know how to do that. 01:11 That's working with the file system again. 01:12 Let's just try out with empty restaurants = and then we are going to read from the file. 01:19 If file_usable. 01:23 This is a class method, so I'm able to use file_usable without putting the class 01:27 name in front of it. 01:28 So, if the file is usable, then let's try and return the restaurants. 01:33 File is going to be equal to File.new. 01:35 I am going to do it using the instantiation of the file technique, instead of 01:39 the Open command, you can do either one, filepath, and we are going to be 01:43 reading from the file. 01:44 So, we'll just declare it as an r. Let's go ahead before we forget and do 01:47 file.close that will remind us to close it at the end. 01:50 file.each_line, that's the method we'll use. 01:54 That will take a block and we'll go through each one of the lines in the file 01:58 and it will do something with it. 01:59 So, I am going to then say, well, restaurants. The array will be appended with 02:05 an instance of my new restaurant. 02:07 So, I want to take that line and convert it into a restaurant. 02:10 Now, I could do that do that here, so that I had the line ready to go as 02:14 attributes I could pass off to new. 02:16 I am going to show you a different technique though, which is Restaurant.new. 02:20 So I'll create a new instance, no attributes being sent and as soon as I have 02:24 the instance, I immediately turn around and ask that empty instance to import 02:29 the line, and I'll say line.chomp and that will make sure that we get the line 02:33 ending off of there. 02:34 Now, let's write a method, actually this is going to be an instance method, so 02:38 I'll just drop down here below initialize. 02:42 This is an instance method called import_ line and it will take a line as its argument. 02:49 Could I've written this as a class method and done it like I did build using questions? absolutely. 02:54 I just wanted to show you a different way to do it. 02:55 So, you could've condensed this all down to a class method just like we did here, 02:59 than then would have passed something into initialize. Instead though, 03:03 we are going to create an empty instance, then populate it. 03:07 The line itself is tab delimited, right. So line_array = line.split and we'll 03:14 split it on those tabs. Now, we'll have an array and each item in the array 03:18 will be name, cuisine, and price in that order. 03:21 So, once we have that, how do we go about setting all those attributes? 03:24 Well, we could do @name = line_array 0. 03:30 That would pull in the first value, or we could be even fancier and we could 03:33 say .shift and that would pull the first item out into name 03:37 and then we could shift again, because the next item at that point would be the cuisine. 03:42 But an even slicker way to do it I think, since we just have so few of these, 03:46 is to go ahead and use this triple assignment in this case. price. When I provide an array, 03:52 this is an array, even though it doesn't look like an array, because I don't 03:55 have the square brackets, it is, and I say equals to another array, then it 03:59 attempts to assign the values that are in the line_array in 0, 1 and 2 to these 04:03 three values, boom, boom, boom. 04:05 So, that's a nice way to do it. 04:07 At the end, once we are done importing the line, we are going to say return self. 04:12 Now, think about why we want to do that. 04:14 Why do we want to return the object itself? 04:17 Because essentially what we've done is populated it and the result of this whole 04:21 operation, creating new returned an object. 04:24 It returned the Restaurant instance to us. 04:26 import_line also needs to return that same instance, because we need that 04:31 instance to go into that array. 04:33 Otherwise, if it return let's say true or false, then what we get pushed into 04:37 our restaurants, would be true or false, all right? 04:39 So, it's important that this whole operation returns an instance. 04:43 I want to make sure that you understand that. 04:45 So, that will take care of it for us. 04:47 That should take the line, put it into the attributes of the object and then 04:51 allow us to put the object into restaurants. 04:54 The very last thing of course is that we need to return restaurants. 04:58 And I'm having the return restaurants outside of file_usable, so that it'll 05:01 just return the empty array if the file is not usable. We'll just get back no restaurants. 05:06 You could return an errors or something like that. 05:08 I am just going to have it say here were no restaurants to be found. 05:11 One other consideration that I want to point to you here is that when we are 05:14 designing this method, we also have to ask ourselves if we want to get a fresh 05:17 copy of these restaurant each time, do we want to go back to the file system and 05:21 reload whatever is there? 05:22 Or do we want to store these results in a variable, so that they are now booted 05:26 one time, and we never need to check again? 05:28 In that case, we could turn this into let's say @restaurants, right? 05:31 And we could check to see if it had been set already or not. 05:33 I am going to have it so that it actually reads every time. 05:35 That way maybe if there's more than one person working on this file, or if we 05:39 change the file by actually going into the file and typing something, we will 05:43 get a fresh copy each and every time. 05:44 So, just consider that about this saved_restaurants method. 05:47 So, now that we get back an array of restaurants, let's actually output that array. 05:51 You should know how to do this. 05:52 It should be nice and easy. 05:53 We are just going to say restaurants.each do and I'll just abbreviate it as rest. 06:00 So, for each restaurant, puts rest.name and then lets put a pipe. 06:06 That's the upper right, + rest.cuisine and then let's put another pipe, and 06:15 finally, puts rest.price. 06:18 Okay now that we have it, let's try it all out. 06:20 Let's save it and let's go to our command line. 06:22 I am inside the Food Finder, rubyinit.rb, and this time we have already added, 06:27 so we can just say list, LISTING RESTAURANTS and there we go. 06:30 We get our list of restaurants back. 06:32 It went to the file, read them in, turned them into instances, passed them 06:36 back as an array to the list action and the list action then went through that 06:39 array and output the contents of the array, by calling these attributes named cuisine and price. 06:47 In the next movie, we'll take a look at how we can improve this output, so that 06:49 it looks a little nicer. Collapse this transcript Improving output  00:00 In the last movie, we were able to read the restaurants back from the text file 00:03 and output them for our user to see, but the output is not that pretty. 00:07 In this movie, I want us to explore couple of techniques we can use to make that 00:10 output a lot better. 00:11 The first thing you'll notice is that I've added a couple of files in. 00:15 So these are going to be in the exercise files or I'm going to show them to you 00:18 and you can copy them down. 00:19 But I've created a new folder called support, and in there is number_helper.rb 00:23 and string_extend.rb, and we're going to be using both of those. 00:26 I've called it support, because these are just kind of the support files that 00:29 will help me with my library. 00:31 It doesn't matter really how you organize them. 00:33 So let's open up number_helper. 00:35 This is a module that we can use as a mix-in into our classes and it 00:40 borrows heavily on a method that's inside the Ruby on Rails framework, 00:43 called number_to_currency. 00:44 So that's what I've called it again. 00:45 It's number_to_currency. 00:46 We provide a number, and then a set of options, and those options can be the 00:50 unit, whether it's a$ sign, by default. 00:52 The precision, how many decimal places it should have. 00:55 What it should use between the numbers as a delimiter, so 1000 would be 1,000, 01:01 and the separator, the decimal separator. 01:03 So all of those are configurable. 01:05 Notice that the format that we're using to set all of these is the same way that 01:08 we did it in the Restaurant class. 01:10 Just to quickly walk you through the rest of it, we're going to remove the 01:13 separator if we've been told not to use any decimal places. 01:16 Then we're going to take the number, and we're going to split it on the decimal. 01:19 We're going to turn into a string, split it on the decimal so we have two halves. 01:22 The integer half and the decimal half. We'll work with them separately. 01:25 The integer half, we're going to loop through, and we're going to insert the 01:28 delimiter after every three places. 01:30 We're counting backwards is what we're doing. 01:32 So we're going just move backwards through it, every three numbers, stop and put 01:36 in the delimiter that we need. 01:37 For the precision, now the precision is equal to 0, well, then there is no decimal. 01:41 We can just have a empty string for our precision decimal. 01:43 Otherwise, let's go through and figure out what that decimal ought to be, make 01:47 sure it's not nil, and then, make sure it's not too large. 01:50 We're taking the string. This bracketed call we're doing on the string is saying 01:54 okay, starting at the first letter, return everything up to however much 01:58 precision -1, because the count starts at 0. 02:01 So that's why we did the -1. 02:02 So that makes sure it's not too large. It clips it if it is. 02:06 If it's too short, then we want to pad it out. 02:08 So if the number we got like was 10.2, and we told the precision was 2, 02:12 we need to add an extra 0. 02:14 We do that with ljust. 02:16 That's Left Justify. 02:18 Let's see how that works. 02:19 I think that's going to be useful for us in other places as well. 02:21 I'll just pop into irb real quick, and let's just say hello, as a string. 02:25 If I do ljust, and then tell it a number, let's say 30, and then I provide a 02:32 character that I wanted to use to fill in the remaining characters up to 32. See what it does? 02:38 Hello, and then however many other characters it takes till I get to a 02:42 total length of 30. 02:44 Let me show you rjust and I think it will make sense. 02:48 Now we've right-justified it and we've filled it in with those extra characters. 02:52 Now if we don't specify characters, it gives us spaces, which really makes it 02:56 look left-justified and right-justified. 02:57 We also have center, which does the same thing. 03:02 It just centers it. 03:03 So it pads it out so it's exactly the same width, 30 characters every time, but 03:07 either left-justifying, right- justifying or centering our string. 03:10 We'll make more use of that a little bit. 03:12 But for here, what we're doing is we're saying okay, pad out until you get to 03:16 the number of precision, pad it with zeros. 03:19 So that's what we're using ljust for. 03:20 So now we've got four strings. 03:22 We just smash them altogether and return them, the unit, the integer, 03:25 the separator, and the precise decimal. That does it. 03:28 That will give us something that with then we can output, number_to_currency. 03:31 So again it's a module, ready to be mixed in. 03:34 You can pause the movie if you need to copy that down, or you'll find it in 03:36 the exercise files. 03:39 Now where do we want to add that to? 03:40 Let's go to the Restaurant class, and let's require it here, require 03:45 'support/number_helper'. 03:49 That'll make sure that we have the code loaded and we're ready to use it. 03:52 So let's include it then as a mix-in, NumberHelper. 03:54 That's the second step. 03:57 Now that functionality is inside the Restaurant class. 04:01 So where do we want to use it? 04:02 I'm going to make a new method down here called formatted_price, and 04:08 formatted_price is just going to return the price, but with formatting. 04:12 So number_to_currency, and we can provide different options in the hash as a 04:17 second argument, or I can just let the defaults kick in, which is to look like a 04:21 number with two decimal places. So I'll save that. 04:24 Now, this is an attribute we can call. Just like we can call price, we can call 04:27 for its formatted price. 04:29 Let's close that up and open up the guide. 04:32 Instead of price, now let's ask for formatted_price. 04:37 We'll just jump back over to our Terminal and let's try it all out, 04:40 ruby init.rb, and list, and there we are. 04:44 We now get this formatted_price back. 04:46 So that's an improvement. 04:47 We saw how rjust and center work. 04:50 It seems like we can actually make this look a lot nicer and look a lot more like 04:53 a table, if we use those. 04:55 So let's quit out of that and let's just jump back over here to list and let's 05:00 try and make that look a little better. 05:01 For outputting the header, let's center that. 05:04 Let's actually take that, I'm going to cut it out, let's center it, but I'm 05:08 going to do it via a different method, private method here that I'm going to 05:11 call output_action_header, and we'll pass it a bit of text and it will then 05:22 take that text and output it, whatever we've asked it to do. 05:25 So in this case, Listing restaurants would be the text. 05:31 So up here I just have to call output_action_header and then 05:37 Listing restaurants. 05:38 Right, so that will now call this method, pass this argument and do the exact 05:42 same thing that we were doing before. Okay? 05:44 I call it output at the beginning. I like to do if it's just really something 05:47 that's going to do output for me. 05:48 That just sort of reminds me that that's its purpose. 05:52 So instead of upcasing the whole thing, I'm going to move that inside in just 05:55 up-case the text, and then I also have the ability to apply center to it. 06:01 Center, and we're going to give it a width of 60. 06:04 So I'll save that, after making it uppercase. 06:06 It's just centered within a 60-character space. 06:08 Now for the rest of the output here, let's also put it in that 60-character column. 06:12 Rather than have you watch me type the new formatting for that, I'm just 06:16 going to paste some code in here and then we'll walk through it. 06:19 So I'm going to do output_restaurant_table. 06:22 If we pass it in a set of restaurants, it auto-output the header. 06:25 Here we've got the Name, and then it's going to left-justify that by 30, 06:29 the Cuisine, left-justify that by 20, and the Price, left-justify that by 6. 06:33 Those numbers altogether plus the spaces are going to add up to 60. 06:38 Then I'm going to use the multiplier here to just get 60 dashes to go across in 06:43 a row, and then I'll go through each restaurant, and I'll do the same thing. 06:46 I'll tell it, all right, let's build up a line. 06:48 We're going to now put a space, followed by the name, justified, and then the 06:51 cuisine, justified, and then the formatted_price, again justified. 06:55 At the end, once we've built up that line, we'll output it. 06:58 So that's what I'm doing there and I've also added something here that says 07:01 well, let's also output something friendly, if there were no listings found. 07:04 Let's just say that the restaurant was empty and then we'll have a line at the end. 07:08 Now instead of doing this loop through, we've already got the loop built 07:11 into our output method. 07:13 So all we have to do is replace all of that with output our restaurant's table. 07:17 So it's real clear what we're doing here. We're outputting the action 07:19 header, we're getting the restaurants, we're outputting the restaurants, and 07:23 I don't need this here. 07:24 So let's try running this. 07:25 We'll go back to our Terminal, ruby init.rb, here we are, list, there we are. 07:30 Look how much nicer that looks. 07:32 Now we've got a nice centered title, we've got columns that make it feel like 07:35 it's a table, much better output. 07:38 I'll quit out of it again. 07:39 The last thing I want to show you is this string_extend.rb that I've written. 07:43 This is a very simple class. 07:45 It opens up the core Ruby string class and adds another method to it. 07:50 We saw how we could do that earlier. 07:51 I wanted to make use of it so you could see it. 07:53 I created something called titleize. Ruby has a capitalize method that will just 07:58 capitalize the first word. 07:59 It's like capitalizing the first word of a sentence. 08:01 But we don't have a way to capitalize the first letter of every word. 08:05 So what I've done is said, all right, take a string, split it up on the spaces. 08:09 That will turn into an array, go through each of those and capitalize each word 08:13 that you come across, and then rejoin it back together with the spaces. 08:17 So that will then capitalize every single word. 08:21 So this will add to the core string class. We don't include it as a module, 08:25 right, we're not going to have to mix it in or anything, but we do have to make 08:29 sure that it's loaded. 08:30 So we do up here at the top of the Guide class, if we're going to make use of it, 08:33 we want to say require 'support/string_extend'. 08:40 That will make sure that it's loaded, it will get added to the class, and now we 08:43 can make use of that. 08:44 So let's do that on our output restaurant table. 08:47 Let's go ahead and say that the name will get titleize and the cuisine 08:51 should also get titleize. 08:52 So those are now just methods on a string that we can call. 08:57 Let's try it one last time, ruby init.rb, list, and now it's titleized. 09:03 Let's try add, just so that we can add restaurant name and let's put this one in 09:07 lowercase, pita pocket would be the restaurant, and let's just say it's going to 09:12 be fast food, and the price will be \$10. 09:17 Restaurant added, now let's list it. 09:19 Notice that Pita Pocket got capitalized. Fast Food also. 09:23 Each of the words got capitalized as well. 09:25 The one other thing we don't want to forget to do is now that we have this nice 09:27 output_action_header method, we want to make use of it. 09:30 So if we come up to add, we can actually use it here on the action header. 09:35 Right, let's put the parentheses all the way around it. We don't need to do the 09:39 upcasing anymore, and we don't need to do that. We just have to pass it a string 09:43 and it will take care of the rest. 09:45 So this output_action_header will give us a nice consistent way to output the 09:49 header of every single one of our actions. 09:51 So, I think that the formatting improvements we made really helped. 09:54 It starts to give our program a more of a professional polished feel to it. 09:57 And in the process, you were able to see a variety of techniques that we can use, 10:00 either by using mix-ins, by extending Ruby's core classes, or by using some 10:05 of those built-in formatting classes, like ljust, rjust, and center. Collapse this transcript