RubyConf 2019 - Learn Enough Ruby by Michael Hartl

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
(dynamic music) - All right, well, good afternoon, everyone. My name is Michael Hartl and I'm hear to talk to you about how to learn enough Ruby. First, let me tell you a little bit about myself. In the Ruby world, I'm probably best known as the author of the Ruby on Rails tutorial, some people out there. (audience applauds) All right, thank you, all right. My story checks out. So the Rails tutorial is a book, a video series, and an online course, teaching you how to make professional grade web applications with Ruby on Rails. The Rails tutorial is part of a company called Learn Enough that I founded a few years ago with a couple of friends. The Learn Enough tutorials actually started right at the beginning with the command line. But one of the Learn Enough tutorials is Learn Enough Ruby to Be Dangerous. Today, we're going to be taking some of the material from that and turning it sort of a talk-length presentation. Now, Ruby is a relatively big language, but the good news is that you don't need to learn everything about Ruby to be productive, you just have to learn enough to be dangerous. Now, we're not going to learn even that much Ruby in a 30 to 40 minute talk, so I want to emphasize that you don't need to worry if you're not following all the details of this talk. This talk is about getting a taste of Ruby, just getting a sense of what it can do, regardless of what your current level is. We're also going to talk about some of the things that Ruby has in common with other languages and some of the things that makes Ruby stand out from other languages. But more than anything, I hope that we get excited about what you can do with Ruby. It's really a beautiful language. It's really remarkable the kind of things you can do with it in just a few short minutes. So to cover the things we're going to be talking about we kind of have a play in three acts. Our first act is going to be interactive Ruby, or irb. We're going to do something reasonably useful in irb, useful enough that we'll want to encapsulate it into something that we can share with other people, and we'll package that as a Ruby gem. After we package it up as a Ruby gem, then we're going to use that gem in a simple web application. So in order to do these things, we're going to do something dangerous. This is not recommended to try at home. We're going to do some live coding. So the rest of this is going to be a live presentation of some of the things you can do with Ruby. We're going to start off with irb, or interactive Ruby, and this is a REPL, Matt's used this term this morning in the keynote. This is a read, eval, print loop. So I'm sure many of you have seen this already, but we're just all going to get on the same page. We can start with, say, two plus two. Ruby, or in this case, irb, the REPL, is going to read that expression, evaluate it, and then print it. In this case, two plus two is four, and it's a loop because it returns us back to the interactive prompt. - [Audience Member] Can you make it bigger? - Is it really, even bigger? It's about as big as it's going to get. Is that better? - [Audience Member] Font size? - This is as big, I think this is as big as the font gets. Even bigger? - [Audience] Yeah, yeah, woo. - Is that good? (applause) - All right, cool. So we're almost full size here. All right, so let's do something else with this. Let's create a variable called, we'll just say assign s to a string, say, "hello, world". This is in double quotes, single quotes work, too. In this case, strings just evaluate to themselves, so when we evaluate this it just returns the string itself. If you want to print it, formally, you can do put s for put string, and in this case, you can see that it printed out without the double quotes, and then it returns nil, which is a special Ruby value that stands for nothing at all. This is unusual, most languages don't have something exactly like this, Ruby has this special value that means nothing, literally nothing. What are some of the things we can do with this string? Well, a string, like all things in Ruby is an object and we can call methods on it. So, for example, this is actually might not even be a method, this might be an attribute, but in Ruby, they're the same thing. If you don't know about that distinction, don't worry about it. So s.length here returns the length of the string, which is nice, because it's long enough that you can't just eyeball it. Something else we can do with a string is we can ask it if it's empty or not. Now this is characteristically Ruby. You can see here there's actually some punctuation in this method name. Most languages do not support that, but it's really nice in Ruby because this empty question mark method is a special kind of method, it's a Boolean method that returns either true or false. We can see at a glance because of the question mark that it's a Boolean. In this case, it's false, but we can also call this on an empty string, and that's true. One of my favorite things to do with strings is to split them into their component parts. Split, in this case, I'm going to split on a space and what this does is it takes that hello comma and then splits it into hello comma and then world. This actually doesn't do what I thought it did. I thought it split on literal spaces, so I thought if you did this that I would get an element for each of those spaces in here. I thought I was splitting on a literal space. Does anyone know what this does? This is actually splitting on white space. It considers all those spaces to be one space and in fact would do the same thing with tabs or new lines. Oops, what did I do here? Oh, I forgot the split. So look at that. I did not expect that. I've been using Ruby for a long time and I was surprised by this behavior. Part of the reason I was surprised was because this is actually the default behavior for split, if you just give it no arguments, then it does the same thing. So to split on the thing that I expected, you need to use a regular expression. This is a pattern-matching language for text. In this case, the regular expression with just one space. In this case, you get what I expected, which is a separate empty string for every one of these strings inside here. Part of the reason I like to split is because one of the things I do is when I'm learning a language is I like to do a simple string manipulation that is just hard enough to exercise the language but easy enough that you can do it pretty quickly. In particular, I like to test whether or not a string is a palindrome. Whether it's the same forward and backward. In order to do that, you have to be able to reverse the string. One way to do this is to split on the empty string, in order to get all the component characters. Now in Ruby you can actually do this, there's a method just for this, chars, like that. But let's go with this because this is common to lots of languages, splitting on an empty string like this. So what this gives you is this array of all the characters and then you can reverse the array, like this, and then you can essentially undo the split by joining on the empty string. And that's the default for join, so you can just do this. So at this point, we can test whether the string is equal to its own reverse by saying s ==, and then the reverse version, and it is false because hello world is not a palindrome. Let's define a palindrome. This is my favorite one-word palindrome. It's deified, because it doesn't really look like a palindrome. What's going on here? But it is, as we can see by doing this. Now, unusually among languages, Ruby actually doesn't require you to do this sort of convoluted thing. You can actually reverse a string just by itself. So Ruby supports string reversal, so we can make this nice, compact expression. This is a way of testing whether this string is the same forward and backward. You can see I'm doing up arrow, I'm actually getting the previous expressions, I'm doing up arrow to do that, but it would be nice to be able to encapsulate this behavior in something that we can re-use. The way to do that is to define a function with def. Let's call it palindrome, how's it going to end? You need a question mark because this is a Boolean. It's going to return true or false. In this case, we'll give it an argument, string. Then we can just compare the string to its own reverse. Now you can actually return things in Ruby, but Ruby by default just returns the final expression, in this case, the only expression, so we're done at this point. You can see the return value in the REPL here is a symbol, as indicated by the colon here. This is just a label. It's something else characteristic of Ruby. Very few languages have a symbol data type like this. So let's test it out. Palindrome? So the string, hello world is false. It's not a palindrome. And then deified is a palindrome. All right, well, that worked. We saw before we could do an empty question mark on the string. Wouldn't it be cool if we could ask a string if it was a palindrome? Like that? Doesn't work because there is no palindrome method on strings, but there could be. Unusually among languages, Ruby let's you actually change the default data types, and in this case, it's the class string. We can just open it right up. Define the palindrome method, but in this case, we actually don't need an argument because strings know about themselves. So we're going to compare itself, it's a special variable that refers to the string, and then we'll do self.reverse and now we can test strings directly. All right, so this is cool enough that we're going to package this up as something that we could potentially share with other people. We're actually going to make a Ruby gem. I don't know if you've ever done this before. How many people here have made a Ruby gem? We've got a few people, that's good. So we've got some people who are going to learn how to make a Ruby gem here. For those of you who've made it, we'll do a few novel things here, I think. All right, so, to make a Ruby gem, we're going to use a program called bundler. We're going to type bundle and, so bundler does lots of things, but we're going to do bundle gem, and then we're going to generate something called rubyconf_palindrome. So I'm going to have it be a name that isn't used elsewhere. This is going to generate a skeleton for a gem. We can cd into it. Now let's take a look at it. All right, so this is the skeleton for a Ruby gem. You can see actually here we've got application code on the right, see there's a lib, that's too small to see. There's application code here, and I've got test code that's been generated for me, automatically, which is really cool. When I see test code, what is think is I'm going to run that test code. Let's run this test code. We have to run it inside of bundler for maximum compatibility, this is a technical point, not important right now. We're going to use the rake program to run the test suite. What happens when you just generate a gem and run the test suite, ah, it's an error. But this is actually a really useful error. This is great design on the part of the people who made bundler, because what it does is it sees that there are these to dos in the specification file. It fails completely as a reminder that you cannot publish this thing, you better do this first. You better change this specification. So here's the spec file. I'm just going to go through and remove these to dos. And then I need a home page here. This needs to be a valid URL, so let's use example.com. This is actually a dedicated site in the http spec for example sites, kind of cool. So let's just fill these in. Let's see if this works. So now, see if the test suite runs. All right, great. So now the test suite is actually running, but it's failing so you can see there's one failures there. Why is it failing? Well, it's failing because the way these tests work is you assert that something is true, and the skeleton that's gets generated, again, really great design, it fails by default. It gives you this little bit of friction to encourage you to write a passing test. So, if you assert false, well, false is always false. It's never true, so how do we get this to pass? What's the minimum we can do to get this to pass? We can just assert true. We're going to leave it in, just for demonstration purposes. All right, there we go, now we're passing. Now this is not as evocative an output as I like. I like to think of passing and failing in terms of the traditional test-driven development cycle of red, then green. So I'm just going to open up this here. I'm going to add in one of my favorite little things, it's a mini-test reporter. And this goes in here. For the demonstration purposes, it will be a little nicer. So let's install that. Did I save it, yeah. All right. And, oops, ah. Ha, you really need to be online. I did not realize that. That's annoying. This is the danger of doing it live. Let's see if this works. We can do it without this but it's a little prettier if we do it with it. All right, so while that's thinking about it, let's write our first test. We're going to test the, here we go. I see this worked. Okay, there we go. So now, let's run the test. Now it's nice and green. So I'm going to clear it before running the test so we can see it at the top there. There we go. So now we're green. Now we're going to write our first test. We're going to write a test for a non-palindrome. So we're going to assert, we're going to assert that something is not a palindrome. I need negation, which is just exclamation point in Ruby, this is common in most languages, actually. Let's do hello world, as we did in the REPL. And then palindrome? So we're going to assert that this is not a palindrome. Any guesses as to what's going to happen here? Is it going to fail? It's not quite going to fail, it's actually going to give an error. I suppose you can see there are two differences, there's failures and errors. This actually is not even going to work at all. It's going to give an error because there is no palindrome? method at present. We get a no method error here. Right, so there's no method error, undefined method, palindrome? With test-driven development, where we go red, green, what we do is the minimum necessary to get this to pass. So over here, let's look at the application code. This is a module, we're not going to talk about modules today, this is covered in Learn Enough Ruby to Be Dangerous. In this case, we're just going to do what we did in the REPL. We're going to open the string class and add this method. So palindrome? Remember what we did, we did self == self.reverse, but the philosophy of test-driven development says do the minimum necessary to get the test to pass. We actually don't need this. We can comment this out and just return nothing, literally nothing, because nil is false in Ruby. Now, nil, is actually the only thing that's false, other than false itself, and this is quite a difference from other languages. Most languages have zero, the empty string, the empty array are all false, but in Ruby those are all true. Even zero is true, only nil and false are false. So here we have, we're returning nil so we're going to assert not nil, which is assert not false, which is true. So this should pass. There we go. So that's the minimum and now let's test palindrome. We're going to assert those to deified. And this should fail. We're going to go red, green. So there's, now there's red. Now we can uncomment this to get to green. Great, so now we've got a palindrome detector. Now we're ready for the third step in the red, green re-factor cycle, so re-factoring is changing the form of the code without changing the function. So we're going to make a couple of refinements. First, in the application code. Anyone see a minor refinement here? It's a little, a really little thing. - [Audience Member] Remove self. - Yeah, we can remove self. We need this self because we need to know what we're talking about, but inside here we can remove this self dot. Just saying reverse by default is just whatever the current object is. Are we really sure? Oops, it's a typo there. Oh, wow, there was a typo, it's red, so that caught the error. It's actually just self == reverse. Ah, now it's green. Now this is kind of a reverse re-factoring. We're going to change something in the test and use the application code to test the test. There's a little refinement here in the test framework that I'm using here, mini-test. Instead of assert not, you can say refute. Do that and see if that still works. There we go. So we've re-factored both the application code and the test. Now we're ready for one of my favorite things which is a situation where the application code is a little tricky, but the test is really easy. So this is a great situation for test-driven development. Like Learn Enough Ruby to Be Dangerous talks about some of the trade offs when you might not want to use test-driven development. But this is a really good situation where it's super-simple to write the test. What I want to do is write a test for a palindrome phrase. For example, it's one of my favorite palindromes. It's a very famous one. It's a man, a plan, a canal, Panama. The question is, is that a palindrome? Well, it's not literally the same forward and backward. This is going to give me a red test suite. But it is a palindrome in an important sense. So let's take a look at that. This is another really useful technique. We're going to pop back into the REPL. In what sense is this a palindrome? Well, it's a palindrome in the sense that if we look at just the letters, they're the same forward and backward. There's all this other stuff in there. There's the spaces and the punctuation, but if we could just scan through this, a little foreshadowing for you, if you're in the know, we're going to scan through this string and just pull out the letters. The way you do this is with the scan method. We're going to scan using a regular expression we mentioned briefly before. So that's indicated with forward slashes, and then I'm going to say the set of a through z. We're just going to match Latin characters, Latin alphabet. That set is the way to do that with regular expressions is with square brackets, and then the second slash. What this will do is scan through and pull out all the letters, except we're actually missing a couple. We're missing the capital A and the capital P. One way to do this is to add in capital A through Z, so now that's all the characters, all the letters in the string. Now we can do what we did before. We can join on empty string to get a string back. We now we're almost ready to compare this to its own reverse. Now we can just do .join and we can either standardize in upper case or lower case. We'll do a down case to make it lower case. Now we're ready. Look at this. We've got the code, it's right here. I'm just going to copy it. Now we can make a letters variable. Then just say letters == letters.reverse. Is this going to work? It might actually work. And look at that, we're green. So with just a little bit of test-driven development and some work in the REPL, we have written a Ruby gem that actually detects now these more complicated kinds of palindromes that consist of a phrase with punctuation and whatever else. I think we're ready now. Let's install this on the local machine. Now I want to take a look at the third act here is the palindrome app. It's a web app for detecting palindromes. Let's take a look at it. All right, so this is a simple Sinatra app. Sinatra is a minimalist web framework in Ruby, but it's not a toy. This is a real alternative to Rails. Quite a few companies use this, for example, the biggest example I know is Stripe, the enormously successful online payments processor, uses Sinatra heavily. You don't necessarily have to use Rails in building web apps. This is a really nice way to learn because it's so simple to get started. In particular, let's look at this here. This is a Ruby block. Technically, it's a closure, which is an anonymous function with data attached that I don't think anyone ever learned what blocks were by thinking about that definition. It's best just to look at an example or two. In this case, we've got a get function. This responds to a regular kind of web request. If you just hit a web page with a browser, it's issuing what's called a get request, and this is now saying, if someone hits this web page with a get request, to the root URL, so this is just like example.com or learnenough.com, then do something. This is a really nice thing. In some other micro-frameworks, for example, there's a framework called Flask for Python, you have to give it some extra information about the URL, but in Ruby you don't have to because of blocks. It's a really elegant way of responding to this URL. In this case, we define an instance variable, indicated by the @, so @title defines the title. Then we use embedded Ruby to evaluate a template, in this case, the index template, the main template. So let's take a look at that. I'm going to do this here. The way to do, this automatically reloads the Sinatra app. It's an application called rerun, a program called rerun. Let's take a look here. Oops. And it should be four, five, six, seven, why is it not running? That's weird. Oh. There we go, all right. So there we go. We've got our sample Sinatra app. So this is now the index page. Let's take a look at the heart of this. It's a palindrome detector. There's a text area here where we can put in a palindrome. Or not, we can find out if it's a palindrome or not. So what's going to happen here is, well, we're actually going to do this. I submitted here and Sinatra doesn't know this ditty, doesn't know this song, but it gives us a hint about how to handle it. What happened here is in the forms, this is the code to make the form, what we're doing is, we're submitting to the action, check, and the method is a post action, so this is the default method for submitting a form on the web, and so, in here, there's nothing for check, so let's just follow Sinatra's suggestion and say post check do hello world. There we go, a little small, but you can see that it's actually working. What are we going to do in here? Well, we want to actually detect whether or not this is a palindrome. Let's define, without a loss of generality, we'll say, a phrase, @phrase, we'll be using this in the template, so we use an instance variable. So @phrase and now we have to pull this out of the request somehow. The way to do this is with one more really important Ruby data structure I'm sure many of you have seen. We're going to define a hash here in the REPL. Let's take a look, so this is a params with open curly brace, close curly brace, which is an empty hash, and then let's use a symbol here, phrase, and set that equal to deified. So params now is just the set of a key with a value. Hashes are just key value pairs like this. The reason that this is relevant is because when we submit something here at textarea with a name equal to phrase, Sinatra automatically creates a params hash that we can use to get the thing that was submitted using the key phrase. Then we can evaluate the result template, which I'll show you in a moment. There's a result template right here. Here in the result template, it's a mixture of HTML and embedded Ruby, so if you've done Rails application development, this will look familiar because this is the same sort of thing. Here we've got if, fill in, we're going to fill this in, then it is a palindrome, else it isn't a palindrome. Inside of here, let's just say if, @phrase.palindrome? Let's try this, refresh it, resend it. Any idea what's going to happen here? Is it going to work? It's not going to work because unfortunately, I haven't added the palindrome gem yet. So it just doesn't have any idea. Oh, actually, what's happened there? Actually something else happened, oh, I forgot the colon here. So there were two reasons it wasn't working. Yes, and now it says undefined method palindrome? So all we need to do is add our palindrome gem. By default, the version is 0.1.0, and then I need to require it here. Okay, rubyconf_palindrome, and. And there we can see it. This is the kind of thing where we need to restart the server. But at that point, now, we're still submitting deified. Ah, fill_in is a palindrome. Oh, it is a palindrome, so let's fill that in. So now we need embedded Ruby with this characteristic angle bracket percent, but then an equals sign will actually insert it into the template, like this. Let's put it in single quotes. To set it off from the things we're surrounding. C'mon, atom is trying to be too smart here. It is a palindrome then this one here is not a palindrome. All right, so deified is a palindrome. And then say hello world is not a palindrome. It's working. We can also say, uh, use a phrase. Here's one, madam, I'm Adam. That's a famous palindrome. So that is a palindrome, so our phrase is working, too. Now there's one more thing, there's one thing I'd like to do here. If we, when I see an empty form like this, one of the things I like to do is just hit submit. We see here that empty string is a palindrome. I really don't think of empty string as being a palindrome. Let's take this opportunity to go back to our code here. Let's go back to our gem. There's one little refinement, let's do a little refactoring before we start. I'm going to change this regular expression. There's a simpler way. I like to think in terms of doing a case-insensitive match so we can actually change this just to a through z, with i for insensitive. Let's make sure we're still green. Okay, and now let's write a test for that case. This is a very simple test to write. So test empty string. So we actually want to refute, we want to change the behavior of our application. So refute the empty string is a palindrome. So we should be red, all right. And then in here, I'm just going to override it. I mentioned before briefly that you can return with the return keyword in Ruby. When it's an exception to the other parts of the method, I like to just do an explicit return. We're going to return false. This is very characteristically Ruby, to be able to put this in one line. Return false if empty. Remember we can omit the self dot. So we're going to immediately return. There we go, so now we're green again. Now we can do this. We can reinstall it. Refresh the server. And just like that. Now it's not a palindrome anymore. So we've actually made a change using test-driven development to our Ruby gem, reinstalled, and are now using it in our web app. This is a good example of how all these things work together. We're using the REPL, we're using our gem, and we're using a web app all together. So, we've covered quite a bit of ground here. But, there's of course, a lot more, so if you're interested in seeing what else there is in the same direction, you can check out Learn Enough Ruby to Be Dangerous, which is at learnenough.com/ruby. It's been a great pleasure speaking with you this afternoon. It's always a pleasure to talk at RubyConf. I don't get this opportunity very often. I've been to a lot of RubyConfs and I've only ever gotten two talk proposals accepted, including this one. This really is a rate treat for me. I really appreciate you all coming this afternoon. Thank you. (audience applauds) Looks like we've got a few minutes for questions. In case there are any. We'll give you a minute to think through if there's anything. You can ask me about anything, like it doesn't just have to be about the talk, if you have any questions. Yeah. - [Audience Member] What is dangerous? - The question is "what is dangerous?" It's kind of a double meaning. Learning enough to be dangerous, you might do something bad, right, but it encapsulates this idea that you can actually be, you can be productive without learning everything. So it has kind of an edge to it. I like having that sort of double edge. Because it really does underscore this idea that, all right, I don't know anything about this subject, but I'm going to learn enough to be just a little bit dangerous. Any other questions, questions about Ruby, Learning Enough, Rails tutorial? All right, it looks like we're good to go. Since I've got a couple of minutes, I'm running an event. Give me chance to explain something. So I'm hosting an event tonight at the hotel bar on the first floor of the GW Marriott. It's the tenth semi-annual, so twice a year, the tenth semi-annual Rail tutorial beerware night. So let me explain to you what beerware is, if you don't know. The software license for the Ruby on Rails tutorial source code, just the example code, is the default license is the MIT license, which is probably the best known open-source licenses. It's very permissive. But there's a second license, called the beerware license. This was started by a European programmer who was specifically tired of the GPL, the general public license because it's really long, it's got all these different terms and conditions. He just wanted a simple license. So he made a license called the beerware license. It says, do whatever you want with this code, and if we meet some day, maybe buy me a beer, if you thought the code was worth it. This is now the tenth time I've hosted the beerware night. If you want, you're under no obligation, but if you want, you can come buy me a beer. (audience laughs and applauds) It's tonight at eight o'clock at the GW Marriott hotel bar. I hope to see some of you there. (applause) Thanks. (jaunty music)
Info
Channel: Confreaks
Views: 7,938
Rating: 4.9826088 out of 5
Keywords: Ruby, RubyConf, Programming
Id: NGXp6_-nc4s
Channel Id: undefined
Length: 37min 5sec (2225 seconds)
Published: Tue Dec 03 2019
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.