(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)