DAVID PADILLA: All right. Let's get started.
Hello everyone. My name is David Padilla.
You can find me at dabit. That's my user name
on Twitter, on GitHub, and basically
everything that matters. Hold on.
I work for a company called Crowd Interactive, which
is a Ruby on Rails consultancy based in Mexico.
We also organize the only conference down there that
takes topics like Ruby, JavaScript and all that. You
can go to that website if you want more
info.
And I'm here to talk to you, or more
like to, sort of, how to write web applications
with Ruby but not with Rails, nor Sinatra nor
Padrino nor any kind of frameworks.
First, I want to give you a little bit
of background of, of, of why I'm here or
where this, where this talk came from. So, I
basically invented MVC on ASP Classic. I don't know
if you guys are old enough to hear about
ASP Classic. Those were the good days.
So I invented MVC on ASP. Although not really.
Not formally. But, when I was working back then,
my first job, it was ASP, and I was
doing things like this. I remember, they threw me
into a project that had code like this, that
was like a page. They want that ASP. And
we had all that code at the beginning where
you set up your connection and, and everything relevant,
and then on the, on the body of the,
of the, of the page, you have, like, the
dynamic stuff and, and, and you ran SQL queries
and all that. And then whenever we needed to
do another page like this, we would just copy
all the code into page two, dot asp, and
just change what's relevant. Right? But everything was copied
and pasted into page and page and page, which
was kind of odd.
So when I, when I, when I began working
with this, I was wondering like, how, how can
this be possible. What happens, like, it was, it
was painful to change something on the layout, for
example. Just the page title, because you had to
go page one, one by one to change whatever
needed to be changed, and it made no sense
to me.
So I came up with some sort of hack.
I changed the code to do something like this.
I discovered there was some sort of include, so
I said, I, I put all my database stuff
in a single file. And then I decided to
use the query string to drive to the application
and do something like this. So I only had
index dot asp, and then through query string I
said page, product list. And then it, with a,
with a fancy case, I will say, OK, if
it says products then you have to call products
dot asp. If it's another page, call this one
and so on. And this made things easier. If
I needed to change something on the layout, you
will just switch it in index dot asp and
not the whole application.
Now, before you judge me for this code, let
me remind you that it was the year 2000,
right. That is Nickelback back there. I was listening
to a lot of Nickelback back then. So you
can think where I, where I was, right. So,
it's 2000. I moved on from ASP. I went
into the world of Java. I learned about Spring,
about Struts. And that was when someone introduced me
to the concept of MVC, and I was like
oh. Yeah. This is what I needed back there,
but it didn't exist there.
And, and if you think about it, it was
sort of the same concept, right. Like, separate your
concerns wherever it matters, and, and stop reusing code,
basically. And after a couple of years of doing
Java, I was introduced, then, to Rails. I was
lucky enough to get a job doing Rails programming,
and that's what I've been doing for the last
seven years. And I've been very happy about it.
So, if there's someone that has recently moved from
Java to Rails understand this feeling. So I've been
happy for the last seven years. Until I hit
what I guess I'd call my mid-programmer life crisis.
This, what happened to me is that I was
thinking, that was, that was, those days were cool,
where you had all these problems that you needed
to solve with programming and all that stuff. And
right now I think that everything is just there.
You just need to bundle install and the, and
the world is solved.
So I began thinking, programming web applications with Rails
it's, it's kind of boring. That's, that's, that's like,
I, I, I hit my mid-programmer life crisis, and
it's because I began thinking, like, building websites with
Rails is not enough programming. There's, there's a lot
of magic like, like Akira just said. Just a
lot of magic going on. It's, it's not enough
programming for me. It feels like, if you called
yourself a great programmer because you do Rails, it's
the same thing as calling yourself a carpenter because
you bought your carpenter at Ikea, right? Like. Yeah.
I built that, that, that table. Oh, oh you
have skills, right? You know what I mean?
And, on top of that, on top of that,
things like this happened. If you know Konstantin Haase,
he was ranting the other day because Rails is
like the worst Rack citizen there is. And he
was telling, he was telling us that it was
because they never contribute features back, implemented encrypted sessions
in Rails, while it should be on the layer
of Rack. You know, things like that.
And the problem is that, that we, as Rails
programmers, don't want to contribute back. It, it occurred
to me that it's maybe because most of us
don't know what's going on outside of Rails, right?
Like, if, if you were at that last talk,
there's all these concepts that we probably don't know
where, do, that they belong to Railties or to
Rack or whatever. We just think that everything is
Rails, and it's not.
And also, well, we used to have all these
problems. We used to actually write SQL, you know,
and now we don't do any of that. So
maybe, maybe our mind is not challenged enough. So
we need to, we need to start thinking about
everything that's outside there. We need to bring programming
back basically, and so that's why I'm here. I'm
gonna try to show you how to write a
web application with Ruby, but not Rails. And maybe
just a little bit of Rack.
You, I'm gonna explain to you a little bit
what Rack is. And it's basically just a super
cool interface that acts as, yes, an interface between
web servers and applications. So basically what happens is
that the web server, whether it is Unicorn, Puma,
whatever, has to a hash with requests headers, right.
And that hash is sent over to the web
application that, by convention, needs to be any object
that responds to the call method and receive that
hash.
And then the web application does its thing, you
know, and the only thing that it needs to
do is return back an array with three elements.
One should be the http_code of the response, that's
an integer. The, a hash with all the headers
of the response, and a body, which will be
any object that can respond to the method each.
And that array travels back through the web server,
and then the web server just turns it into
something that the browser can understand. So, like I
said, like you've heard, all you need to run
a Rack application is the file config dot ru.
And define on that file, using the run directive,
the object that will respond to call, and that
will receive that hash of, of headers.
So, what you're seeing right here is basically the
smallest but most useless web application ever. But it's
just those three lines and that works. So, this
is the dangerous part. This is where I do
some live coding. Call me a rebel if you'd
like.
So, let's, let's do some live coding. I'm gonna
get out this here. And let's begin with that,
you can see that, right? Yeah. That's good. Let's
begin with that config dot ru thing that we
mentioned there. We need the run directive - we'll
create a proc, because a proc can respond to,
to call. We'll ask it to print that hash,
you know, that we get, that we'll get from
the server, and then just answer 200, a hash,
and just some empty array, which is an object
that responds to each, right. An array can do
that.
So, let's see what happens. To start any Rack
application, all you need to do is run the
rackup command, whatever, to reset that config dot ru
file. And then it just boots up basically. So
it's telling me that it's booting up on port
9292. So if you go to a browser and
we reload, there's nothing here because we're not doing
anything. But you can see here that we get
that hash, right.
This is, this is what the browser sent over
to the, to the Rack application, and it got,
well, all that information that the browser is sending.
So, that is great.
Next thing, let's actually do something with this. So,
we're gonna create a body like, like all web
application have, and some, well. It will be body.
I'm not very good at html so please forgive
me. Let's do Hello World. We'll be, close the
body tag, close the html one. And well. Let's
just send that body back. See what happens. We
restart our web server. And boom. We have a
working web application that serves us a single page,
basically.
All right. Things are working. The problem right now
with our web application is that it's only serving
one page, no matter what the path is. So
if you go, like, to, slash, like the root
path, it's Hello World. If you go to slash
admin, Hello World. it doesn't matter where you go.
It's always, you know, the same thing. Because we
need something to actually grab the request path and
send it over to whatever it needs to be,
wherever it needs to go.
So, we're gonna use. That's, that's what's called a
router, usually in the MVC pattern. And the router
just does, does that. It basically just takes the,
the request path, and then send it over to
whoever needs to handle that request. Specifically, this is
very complicated logic, in general. It's way beyond just
doing a case structure. But we'll do that for
now.
If you want to learn more about the router
in Rails, it's a gem called Journey. And as
you can see, it has features and problems, which
is basically just designed for Rails and that it's
too complex right now. And you can imagine why.
Like, if you, if you define routes, there's all
these cases that need to be handled. Whatever you
have. If it, the method, and if you have
variables and all that, so it's, it's complex logic,
right. So we're not getting into that. We're just
gonna use the same technology that I used ten
years ago, which is define our case, yay.
So, first thing we want to do is say
something like path equals env. We're gonna get it
from those setters. And then say case path, and
you know, when that path is the root path,
just serve this. And if it's something else, just
say hey, not found. And return a 404, the
headers and return that body. And that's it. Yes.
That's our router. Sweet.
So. We restart our application and see what happens.
So, now product not found. And if you go
to root path, Hello World. Great. So, as you
can see, the server is getting the same. You
know, it's sending the right codes. 404. 200. And,
and so on. So, we now have some sort
of router, right.
So, what's next? If we, we probably don't want
to have this code, you know, just pasted in,
in our whole, in a single file and do
our whole web application here. So let's move this
to a class. To, to make it a little
bit more understandable. So I'm gonna steal this, and
I'm gonna put it into a app.rb file. OK.
Instead of this, I'm gonna say, like, class App.
It has to be, it has to respond to,
to call method and receive env, or that hash.
And I'm gonna end the method. I'm gonna end
that. And ident it. And that's it. Now we
have to include that to config dot ru.
I'm gonna require that file. And then I'm gonna
tell Rack instead of, of doing stuff here, just,
you know, send the hash to that, to that
class, right. Because now it's an object that can
respond to each. Sorry. To call.
So this. Let's just make sure that this is
working well. I don't want to write a test
for this because, you saw the keynote. So, there
you go. It's still working. We have now abstracted
our code to, to, to a class. So that's
great. What's next? So, I guess what's next is
to go to our, here, and create controllers, right.
Instead of having this laying around. We probably want
a class that says, that does this.
So let's create a folder real quick that's controllers.
We'll put them there. And I'll say, let's say
root_controller.rb. What? I know what's gonna. Hold on. Yeah.
root_controller.rb. We say OK. RootController. And have, I don't
know, the action show. This is where code will
be.
We probably want to have this controller class receive
that env through the initializer. And then say, self.env
equals env. We added , let's see. And there
you go.
And here, instead of doing this, we'll just say
RootControll.new, get env and show. We want to require
that controller here. And let's make sure this works.
It doesn't. Perks of live coding. I know what
it is. Don't worry. OK. K. So. We start
our web application and it's still working, which is
perfect.
So what else, what else does a web application
need? It needs views, right. We need views. We
need to set our html logic. We need to
set it apart and put it in a different
component. And, that's like the controller html pasted into
a controller. So let's create views. Hope you guys
like haml. I do. So, we're gonna use that
to create our views. First of all, we're gonna,
we, we need to create a folder. Views for
this controller in specific. All right.
So, let's create our views. Show.html.haml. We need, like,
head. Title, Railsconf. We'll create a body. And h1.
Something like that.
Controllers, root_controller, and then here. Let's create a method
that will be render. And that method, I want
it to receive like the, the, the template path.
And what you need to do to, to render
like a template with haml and stuff, you need
to open the file first and read it. You
need to add some sort of variable, which we
call template, and then just say, Dear Haml::Engine, can
you please render this?
See, it's easy. That's why I chose haml. So
we don't need this anymore, because our superview's gonna
do it, and we're gonna say render('views/root/show.html.haml'). And I
think that's it. We probably just need to require
haml. We don't want it to blow up. There
you go.
And, let's see what happens. Yay. We're still there.
So now we have views. The title changed because
on that other version, we didn't have. But now
we do.
And, the whole point of what we're doing right
now, it's because we didn't want to sort of
duplicate code and copy and paste, and so we
probably need layouts, right? Like, like, like with Rails.
So, let's create a layout. First thing we do,
views/layouts. Yeah. That'll work. And let's open what we
have right now. And just steal the first four
lines. No, actually, we need everything, all the way
here. Yeah.
And we indent it. Let's open views/layouts, let's call
it app.html.haml. Views/layouts/app.html.haml. And here's where we paste it.
And, like good old Rails, we're gonna do a
yield, and there's where we want the view code
to show up. I'm on time. That's good.
We go back to our RootController, and now we
define a method that will be render_with_layout. Same thing.
We need to know the template path. And let's
see, first we open our layout file. We're gonna
hard code it because that's how we do. Layouts.
No magic here. Read.
And do the same thing. Dear Haml::Engine, can you
please render that layout? The trick here is that
you can pass a block to render, and then
whatever you render inside of the block, that's what
gets rendered to yield. So we simply call render
again with that template path that we were looking
for up there.
So, now we change this code to render_with_layout. We
restart our application. And it's still working. So. Now,
if I go and change the title, for example,
to 2014, then it should, should change. Yay! So,
now we have a layout. That's great. And so,
what else do we need from web applications?
So, we usually need to have dynamic information on,
on our web application. Something like this. Like, maybe
the name comes from the database, or comes from
a query param or whatever. So we need to
be able to change data on the views, right.
So, let's see if, first of all, we just
add that variable there, like, like Rails, as if
Rails will do it. But we can see that
it's not that easy. It, it doesn't happen magically.
So, what we need to do for this to
work is we go back to our controller and,
well, first of all, we need to define it,
right.
Let's say, world.
And second of all, the render method can receive,
as parameter to context of where the layout is
going to be sort of processed. So basically we
just need to tell it the context should be
the same object. And, and that way this instance
variable will be part of the package. So what
we do here is we are going to as-
to add the context to our render method. But
as default we'll just say hey, just graft self
if there's nothing there. Same here. And we need
to pass that context to this render method. Over
here and over here. So it's all the same.
And this is gonna happen sort of magically because
of that default, where we set it to self.
And yay. It's working.
So now we can populate that variable or, couple
of variables with whatever we want basically. The most
typical thing that we could do would be to
send it over in a, in, in, in, like
params. Like something like this. If it comes as
a parameter, do something, or else just do world,
right. But params is a method that we need
to basically define. So let's define that. Params. I'm
not going to write code to like go to
a query string. We're just gonna use a method
that already exists in Rack::Utils which basically does that
- parse_nested_query. What it's gonna do, it's gonna take
the query string and it's gonna turn it into
a nice hash that we can access. So we're
gonna add that here in params name and it
should work at, at the first attempt. Let's try
it.
So, default is Hello, World. It will say name,
I don't know, RailsConf, and it works. Hello Jane
or whatever it is, you know. It just works.
And, well, I guess the last thing that we
could do for a web application will be to
properly abstract it. And we probably want to grab
all this code and set it on a controller
class, right. Because. Over there. And, OK. Now, let's
go back to our root_controller and just say hey,
just inherit from controller. And that's it. That way
we can, in the end, we can add more,
more and more controllers to our super web application.
The only thing we need to do is require
controller. Here, and. Let's make sure that it still
works.
It still does. That's good. That's always good. Yup.
There you go. So that's it on the coding
side. As you can see, you have a working,
perfectly working web application right there from using no
frameworks at all, only your Ruby code. Now, I
probably don't have the time to add models and
all that because that's different logic, but. You could
just, you know, inherit ActiveRecord::Base, they, they, you know,
they've already done it. And it's, it's hard logic
you don't need to redo. All you need to
do is just, you know, define classes and say,
inherit from ActiveRecord::Base, and you will get, like, all
the magic that you get on Rails.
If you're a little bit more hardcore, then you
can use sequel, and just write your methods like
so, and you can get access to the database
that's, as if it was a hash, and just
write your dot all, your dot find, all that
logic. And if you're even more hardcore, you can
directly use the pg gem or the mysql gem,
and just, you know, do this sort of crazy
stuff where you actually write SQL. Yay SQL. And
then just, you know, iterate through the results and
put them in the hash and all that. This
is, this is fun. Like, this is programming, right.
And one last piece of advice I have for
you is don't ever actually do this. Don't ever
actually do this unless you're some sort of speed
freak or performance freak. When I run my tests,
my super web application was able to handle 700
requests per second, and every request will take one
millisecond, which is pretty fast, right. But when I
created the same application using Rails, I had like
2x slowness, right, right. It will take two milliseconds
to, to run, which is unacceptable. I want one
milliseconds. But this is unfair because Rails solves all
these problems, right. We have logging, caching, database pooling,
sessions, cookies, security, all of that, that we're not
considering on our own application.
But even if you don't want to do this
in real life, you do want to read code,
and you do want to go and, and, and
try and see how Rails is doing things. You
want to try to understand, how is it doing
it? Like, right, you want to do find that
code that connects to the database, that code that
turns dot find into select star from that table,
you know, all that, you want to go and
read and try to understand how things work. And
then once you understand, try to write your own
framework. Well, why not? There's, there's space for, for
a new framework. Maybe, maybe you'll be the next
DHH and will, and will be able to afford
and race car and all that.
But the most important thing is that you will
understand, like, master this topic. You will know, you,
you will have that feeling of saying, hey, I
know how this works. This is great, now I
can go back to doing my Ikea software. But
I at least I know how it works, right.
And then if you see the Rails core team,
give them a hug. Because you will understand also,
like, the pain that they had to go through
to get Rails to where it is right now,
because it's not easy, right. We just, we just
wrote like thirty minutes of code, but it does
basically nothing. It needs to take care of routing,
like I said, caching, security, and all that. And
that's, you know, a lot of lines of code
that need to, that need to happen. So, so
you see a Rails core guy, you say, hey,
thanks for that. Give him a hug. And my
last disclaimer is that I do not actually think
that writing web applications with Rails is boring. That
was just added for drama.
And I hope you understand that. That's it. I
will Tweet or, whenever you see the slides you
will see like the code example on, on my
talk on that, on that url. And thank you.