RR REST Done Right with Steve Klabnik
- What is REST?
- Architectural style for building systems
- Fielding's REST Paper
- Uniform Interface
- Code on Demand
- HTML yields a larger payload than a binary protocol
- Stateless enables scaling because with all required information in the request it can go to any server prepared to handle it.
- Cookie-based sessions lean more toward stateless
- The PUT HTTP verb is misconstrued by Rails
- PUT does not mean "edit" it's an "upsert" (create or update)
- PUT is idempotent
- POST is more generic and non-idempotent
- Powerful Caching
- You are supposed to PUT the entire resource rather than the delta.
JAMES: I'm imagining just an hour of Steve yelling at us.
DAVID: “You're so stupid!”
CHUCK: Hey everybody, and welcome back to the 21st episode of the Ruby Rogues podcast. I'm your host, Charles Max Wood. And this week, we have six panelists. I always wanna say ‘five,’ because I forget to include myself. Anyway, our guest panelist this week is Steve Klabnik. So, get your Shoes on, go Hackety Hack something up, and why don’t you introduce yourself, Steve?
STEVE: Hey everybody, I'm Steve, and I do lots of stuff, including the aforementioned puns; Hackety Hacky and Shoes. I do a lot of open source work, and I'm running training classes at Jumpstart Labs, nowadays. So yeah, that’s me.
CHUCK: All right, terrific. Also on our panel this week, we have Avdi Grimm.
AVDI: Hey this is Avdi, and I can't speak for anyone else on the panel, but I am wearing pants.
CHUCK: [Chuckles] That’s a positive thing. Of course we can't see you, so. Anyway, the next panelist we have is David Brady.
DAVID: Hi, I'm David Brady, I run Shiny Systems, and Avdi cannot speak for me.
CHUCK: [Chuckles] All right.
DAVID: But I am probably wearing pants.
CHUCK: Probably. Do you need to check?
CHUCK: [Chuckles] All right, our next panelist is James Edward Gray.
JAMES: Hey everybody, I'm James, and I'm afraid of Steve Klabnik.
CHUCK: Also on this week’s panel is Josh Susser.
JOSH: Hey, good morning. It’s Josh Susser from San Francisco, basking in the revelation that I don’t have a conference to produce this week.
CHUCK: Yay! And I'm Charles Max Wood. Two things that I'm working on is Rails Rookies; you can go sign up for my Testing Ruby on Rails course, and I forgot what the other thing is, so I will just not worry about it.
All right, let’s get started. This whole thing kind of got started with the Twitter conversation, where Steve basically said that every time we talk about REST, he wants to break something. So why don’t you go ahead and explain what's wrong with the way we talk about REST.
DAVID: Actually before we do that, can I jump in over the top of him? Can I give the wrong definition of REST?
JOSH: Sure. Thank you David for not making me ask for a definition. [Chuckles]
DAVID: Yeah, I dreamt last night, Josh and James are going to like, “Can we get a definition.” And I'm like, I'm going to be there for them. So REST stands for “Representational State Transfer.” It's a brilliant idea for allowing services to talk to each other. In theory, it lets you universalize everything.
The way Rails programmers use it is they take these four verbs: GET, PUT, CREATE and DELETE. And they use it to push the database up to the service level, so that basically any server can use any other server as the database connection. And it's stupid, it's not scalable and it's wrong. And REST is a bad thing; you shouldn’t ever use it. Now, kill me Steve. Go.
CHUCK: You know, I think they should build a framework around that whole REST thing that you are talking about, Dave. Maybe call something like, active resource or something.
DAVID: They should clean that up. Maybe call it soap.
AVDI: Yeah, they should throw that away and burn it.
JAMES: For those keeping track with the scotch drinking game at home, Steve’s already had like three.
STEVE: I won't be able to talk because I have aneurism. I´ll be dead right here.
I guess the first thing I wanna say in general is that, I tend to use hyperbole extensively, so I don’t actually wanna break anything when you guys talk about REST. It's just that everyone talks about REST wrong, is the real core of the problem. And you know, this podcast is normally so awesome on like every other front, but I just kind of go, “Oh man, you guys too have the same sort of misconceptions.” And it's hard, because unless you read a bunch of impenetrable papers written by academics, it's difficult to sort of figure out why this stuff doesn’t work.
And so, I think that anti definition was kind of a good one in general, but basically it's like REST is not about pretty URLs. And it's not even necessarily directly about using HTTP verbs in the right way. I mean all of that helps. It's really all about hypermedia. And one of their latest REST Conf where they were actually talking about rebranding REST as sort of like hypermedia APIs, because that’s actually where it's really important. And REST is sort of like gotten dragged through the mud at this point. I mean, there’s the whole RESTafarian’s thing, right? To make fun of people who were like, “No, no that’s not real REST!” I'm not the last or first person to ever really talk about this stuff.
JAMES: So that’s right. And there's actually key components of REST, right? And it's been a while since I've read them all, but I can remember that one of them -- my favorite actually -- is addressability. And it's the idea that a given resource on the web should have an address. And that’s kind of the point of the web. And that’s important for things like being able to throw somebody a link to it in an email or something like that.
And often, that particular attribute of REST is at odds with things like AJAX. We are always trying to get around that with Twitter’s hash bang or whatever they do. And then GitHub’s new technique, where they do the AJAX stuff to replace like file listing, but then they go ahead and modify the url, so you maintain addressability and stuff like that. What are the other attribute of REST? That’s the only one right off the top of my head.
STEVE: So if you go through Fielding’s dissertation in order that… these are all constraints, so like the positive definition of REST, it's an architectural style for building systems. It's sort of talking about paintings, right? Like you can say, this is an impressionist painting, and you can sort of argue with the edges whether something is or is not impressionist, but there's definitely like characteristics of something that make it impressionist. And I have no idea what makes for an impressionist painting, but as far as REST goes, there are sort of these like traits where if your system has all these traits, then you have a RESTful system.
DAVID: Did you just say then that it's possible to build a RESTful system that does not use HTTP?
STEVE: Yes, it's actually more generic than just HTTP. And HTTP gives you most of those constraints for free, if you use HTTP in the way it's intended. And that’s sort of where Rails verb usage comes in is that it gives you closer to the actual… you get all of the stuff for free because REST was invented to sort of back justify these decisions that were made when architecting the web in general. We developed the web first in HTTP 0.9 first, and then Fielding did all these work to like formalize all these decision that have been made, and that turned into like HTTP 1.1, sort of like fixing all the little consistencies.
But anyway, main constraints, the biggest one is client server. And those are separate; and that’s where a lot of the ajaxy stuff gets into a weird place because you want your client to also be a server, right? When you are doing like ajaxy stuff, you're sort of blurring these things. It has to be stateless, so this means that everything that’s needed to address the request happens inside the request; which means that it has great benefits for things like scalability, but it also means that the individual messages are larger.
JOSH: So Steve, maybe I missed it because you are like brain dumping [inaudible], what about hypermedia as the engine of application state?
STEVE: Yeah, so these constraints have sub constraints, right? Those are like really high level things. And so, HATEOAS actually gets discussed… I forgot exactly where it is, but I think it's part of describing representations; it's not actually in the constraints of the overall system design, that’s how you design the representations of the resources you are serving, over one of these kinds of systems.
JOSH: I think that’s one of the things that drives me a lil crazy, and I think that’s true for a lot of other people trying to work with REST is that, Fielding considers that to be a very important part of how REST should work. And there isn’t very good explanations of it, and there aren’t any good examples of it. And you just said, it’s not even really called out in the big way, because it's a sub constraint of something.
STEVE: Yeah, I mean his whole paper is really short, right? And it's very dense. I don’t think it's in similarity, but I'm reading Mark’s around the same time as I was reading Fielding’s dissertations. [inaudible] in the brains that were like this impenetrable jargon.
DAVID: Somehow, I think REST is about seizing the means of production. I can't remember…
JOSH: [Chuckles] Yes. From each server according to its means, to each client according to it's needs….
STEVE: So anyway, the other part of all these too is when you get into those kinds of high bandwidth, very jargony discussions, you lose the fact that this is not about academic wankery, right? This is supposed to help us in a tangible way. And there are times where you would not design a systems via REST, because REST makes certain tradeoffs, just like any engineering effort, you have to pick what things your system needs, and what things your system doesn’t need. And REST gives you certain benefits with the expense of other things.
So as a great example, transferring HTML is much much more… the payload is much bigger than if you had just a proprietary like binary protocol, right? Like you can send messages instead of saying like, this whole HTTP header, you can send 1 or 0 down the wire to make something happen, and it will be much more efficient, right? So REST is primarily about designing systems that will last forever, and be very… interact with other systems very easily. So much more like long term vision than short term.
JAMES: So how is something like the statelessness… it seems like almost all web programming, we we always trying to get around the statelessness of web programming. And how is something like sessions and stuff at odds with the statelessness of REST?
STEVE: So the big thing about the statelessness constraint is that it enables scalability because since every message is self-descriptive, you could route any request to any sub node that you are working on, since theoretically, they are independent. So once you start sort of going outside of this statelessness, then you run into all of the problems like session affinity, right? Or like if you have five application servers, you need to make sure that you request from one user, continue to go to the same application server afterwards. And if you stay within the statelessness constraint, then you could just serve any request to any server, and it would be totally fine.
But then, one of the bigger ones actually is authentication, which sort of goes along the statelessness thing, is that like without… authentication is one of those things that’s like difficult and sort of hairy. And luckily, OOP has made it pretty awesome by this point. But you pretty much have to use HTTP’s built in stuff, or else you sort of get around this statelessness situation, and things get a little hairy.
JAMES: So it seems like Rails move to the cookie-based sessions, was actually a great step to the right direction, as far as what you were just talking about because when that request comes in, it basically has the entire contents of the sessions, so we could write that anywhere and there’d be no change as far as the server handling it.
CHUCK: So I'm kind of curious. It seems like… to me at least, it seemed like you were talking about not necessarily the way we were discussing REST, but that we kind of assumed that REST meant like Rails REST?
CHUCK: So what is the problem or what is the difference between Rails REST and REST?
STEVE: So here's a great example that's actually pretty simple. The PUTS HTTP verb, it has two semantics to it that are important, that Rails gets really wrong. I think… and maybe newer versions of Rails are changed to one of them, but at least it used to be true for one. So PUT is actually not an edit. The four verbs dont actually mean Create, Read, Update, Delete. PUT is actually an upsert, not an edit. So if you were to PUT a representation of a resource to a certain URI, it would create it if it didn’t exist or modify it if it did. And so we sort of take away that creation aspect with Rails and just solely use it as an edit.
DAVID: Did you just use the word ‘upsert?’
STEVE: I did. I think that’s a great database terminology term to represent that sort of semantic, of like ‘create or edit.’
DAVID: Or update or insert, okay.
STEVE: Yeah, update or insert.
JOSH: What's wrong with end date?
STEVE: I don’t know.
CHUCK: That sounds like something they do in Oklahoma.
STEVE: Yeah [Chuckles]
JAMES: Nice. Very nice.
CHUCK: [Chuckles] So then I have to ask if PUT does not strictly mean edit or update, then does POST not directly mean create or…
STEVE: So the big difference is that… PUT is idempotent, I think that’s how you actually say that word. I'm not sure. I was forced to read it as a small child and it means that I don’t know how to pronounce half of the word with [inaudible] syllables, so I just sort of make it up, and my brain thinks that it's right.
JOSH: I always say ‘idem-potent’
STEVE: That sounds better to me.
JAMES: Really quick for those who don’t know, can we get a definition?
STEVE: Yeah, I was getting there. So basically, this means that you can do something multiple times, and it doesn’t have a bad outcome – which is a terrible description. It actually comes from some Math stuff. I don’t remember off the top of my head, because it's nine o'clock in the morning. But basically, the deal is that certain verbs are idempotent and certain ones aren’t. And that’s important because if your network connection gets flaky or fails, you can retry the ones that are, with no bad consequences, but you can't retry the ones that are not because they had side effects.
So POST is more of like the generic non idempotent verb that does stuff. So Create fits in there very nicely, right? Because you post to some sort of factory or container, and then it gives you back a new resource, and create in general, if you make that post five times, you get five things, so it isn’t idempotent.
DAVID: So idempotent means that if you run an operation on something that's wrong, it will fix it and make it right? If you run it on something that is already right, it will do nothing to it?
DAVID: So the function will basically settle, and eventually all the data will be right and it will stop doing things.
JOSH: So if your mom tells you make your bed, and it's already made, it doesn’t get messed up.
CHUCK: Right. But if I tell Dave to give me twenty bucks and he gives me twenty bucks, and I tell him to give me twenty bucks and he gives me twenty bucks, that’s not idempotent?
DAVID: No, you would get zero dollars.
CHUCK: [Chuckles] It's still idempotent because Dave is poor.
AVDI: Correct me if I'm wrong; you can take like some of the etag stuff and the optimistic locking of web development, and make a post and put as idempotent. Is that a correct statement?
STEVE: So web dev adds its own verbs and its own extensions to things, and there's also this technique called post and… there's some sort of thing that you can do to like make POST to be idempotent, if that is the semantics that you want. But the problem is that if you start adding some stuff to HTTP, then none of the caching levels or layers or anything about like the backbone of the internet style stuff knows what you are talking about.
AVDI: I was just talking about like adding… I'm blanking on the header, but basically a header that says if you are this version… I have an etag of the last version I saw, if you are this version of the resource, then do this POST or do this PUT, otherwise, ignore it.
STEVE: Yeah, I mean that’s like caching stuff; because caches are item potent, you would never cache them as the thing. So theoretically, I guess you can return an etag on a POST request and maybe that would work, but it will be sort of weird, I guess.
AVDI: I'm talking about like you get the resource that you are going to post to, and then your post say, this is the last version of the resource that I saw, so do this unless it has updated since then.
JAMES: I think that’s one of the problems of like fixing POST to be idempotent is that whenever you have to do two things instead of just the one, that’s when it all falls apart, right?
STEVE: And incidentally, this is why like soap tunnels everything over post is because then it sort of bypasses all of the knowledge that HTTP has about the kinds of request you’re making, so that it makes no assumptions and that it goes through. And that’s one of the reason why it's difficult to cache stuff like soap as a protocol.
AVDI: Speaking of batching, that actually brings up something that I've looked at in the past, and I've noticed that there are a lot of sort of hand rolled implementations of batching in some of the APIs out there. So a lot of times, it will be like, this resource receives a singular json object, but this special batch resource can receive an array of JSON objects, which the other one would have just received one of; and each of those represents an operation and I´ll return you like a list of results for that. And something that struck me funny about that, and I'm just curious if anybody has done this is, can't you just use the existing multiple part media types… the existing MIME multipart media types for batch operations? Why do we see these hand rolled ones? I realize it's kind of down and nitty gritty, but…
JAMES: Yeah, it's a good question. I mean just guessing why it is done that way, is when we are working at the application level, it's like difficult to generate a proper multi part request that has the right sections, whereas it's easy to call to json on something.
AVDI: In Ruby at least. Like it's actually surprising how hard it is to find a decent generic multi part library. They are all heavily tied to mail.
STEVE: Isn’t multipart about the bytes though? It's not about really intended for multiple representations; it's like saying there will be multiple… like it’s about byte ranges. This happens when people use the… there's an HTTP header called like content range or something. I forget what it is…
AVDI: I'm talking about something completely different.
STEVE: I know you are, but I'm trying to make an analogy, I guess. Where isn’t or maybe… I'm not that familiar with multipart specifically…
AVDI: So the MIME multipart specification has several types of multipart messages, so you have like the idea of an email that has some binary attachments embedded in it with pictures or something. Then there's another version of it where it's like, “Okay, here are three different representations of the same thing; pick the one that you like.” But then there's another one which is basically, “Here is a list of unrelated objects, but they are all packaged together.” And each have their own media type. And each have their own name, and they are all packaged together in one request. And you can just iterate through them and do whatever you want with them.
STEVE: Yeah, it's probably just that nobody knows that that exists, and that’s why everybody hand rolls things. Because that’s where a lot of the stuff is, it's the blog post that sort of that sparked a lot of this is that, people just don’t know. These specs are really complicated, and there's a lot of them. And it's really difficult to know all these stuff, you know? It's sort of like how we laugh about people of like PHP and they just throw SQL into their view because they are using templating. And were are like, “Oh we build real applications.” And you know, there's lots of these like stuff that’s in the spec, that we just don’t know about because we never learned it because it's hard and there's a lot to know.
JOSH: So if we are like laughing at PHP people for SQL and their templates, they are laughing at us for what we are doing with REST.
DAVID: People that know REST.
JOSH: What are they using it for? Who are these people? Are they like the financial transaction messaging people?
STEVE: REST is really bad for those people actually. A lot for them are like academics, or people who are interested. Some of them are new to the industry; so for example, one of the guys at Comcast is super huge. The whole reason I'm speaking of the Twilio Conference tomorrow… or not tomorrow, Thursday is because there's a guy at Twilio, who is like really into REST, and he's trying to like bring that stuff into Twilio‘s API and it's actually a pretty solid RESTful API.
So like, it's just that it's difficult... There's a lot of scattered individuals who know about this, but they are working in larger context, so it's difficult to tell your boss like, “Yeah, we need to take some time to like make this stuff right, because I read this paper and you know, it says it will give us these improvements.” Programmers are really bad at making business cases for architectural decisions, right? So when I tell people like if you implement REST, you can change your server and not break everyone’s client, they go, “Wow.” But when you say like, “Oh, I wanna make it in line with this academic paper this dude wrote,” nobody cares.
JAMES: I think there’s a lot of tradeoffs too though, kind of going back to the idempotent, put post talk we are having earlier. Basically, what Steve was saying was that PUT is actually supposed to be Create or Update; and then POST being… well, it's almost the bad one, right? You wanna try to avoid POST whenever you can, because it's not idempotent, so we don’t wanna be messing with it whenever we can.
However, the decisions Rails had to make, were kind of difficult. For example, to have a good example of create or update, S3 accepts PUT request when you put files into S3. And so what they do is you take the file, you put it in there, and so if it wasn’t there, then it adds the file; if it was there, it replaces the file. But in order to do that, the client library doing that request has to know the url it's going to, right? You have to PUT to the final url, which is simple in S3 because you know the name of your file, which is where S3 is going to store that resource. Also, S3 is not typically… you don’t put a file to S3 with the browser, for obvious reasons because browsers won't do PUT, and how would you go to the exact right url, etc. etc. the interface doesn’t really allow for it.
Whereas the problem Rails was trying to solve, is we want to allow someone to add something to this collection, but they don’t know the final place that that will live. For example, in Rails, just using the normal routing, something would be at resource/id, and they are not going to know what id that thing is going to be assigned. So they can't PUT to the final URL. Or in some Rails applications, they generate like a parameter for a PUT url or something, and you are not going to know what parameter they generate to. So Rails wasn’t able to make that choice in my opinion.
STEVE: Well, no. I mean, the POST for Create is fine, like that’s a great use of POST. The difference is what I would like to see is that if you make a PUT request to a url that wouldn’t normally exist, it would send you to the create action instead of the edit action, because then you would get that semantic, right? So that whole scenario you just described is why you use POST and Create to make resources because you don’t know the final URI. That’s the whole thing. I mean, the Create side of PUT is lesser used in general, because of that reason like you are letting the client use the URI as opposed to the server.
We sort of got lot deeper into this fox hole about some of these details, but the other one that Rails gets wrong with PUT is that you are supposed to put an entire representation of the resource, not the delta. And this is actually why the HTTP patch method was added relatively recently, is because putting a whole representation over the wire is big, and is larger than just putting the dif. So the original intension, it was supposed to be that you put the entire representation, and it would appear there. And then that is sort of why it has that effect of like make your bed, make your bed, make your bed, you are describing the whole task, instead of just the change. And so you sort of get to that end result.
But I definitely think the pragmatism aspect is important though, right? Like Rails has done more to help people understand that REST exists, and it's important that anything ever before. I also don’t wanna say like Rails should have done everything perfectly to the spec, because it was a massive, massive improvement over earlier Rails and earlier web frameworks, so I don’t think that it deserves like blame either, right? Like there’s the ideal where you like to get and then there's the way that you get there. Those two concerns are totally separate from one another. I love Rails. [Chuckles]
DAVID: Steve, I have kind of a technical question for you. So one of the things I see Rails getting wrong, and this is exactly why I misspoke about REST in last week’s Ruby Rogues, and it's also part of the anti-definition… I know, okay I´ll just go…
CHUCK: Yeah, there are speakers and misspeakers.
DAVID: Yes. Oh! Can I get the job, ‘misspeaker of the house?’
CHUCK: I think it's taken.
DAVID: [Chuckles] I think you are right.
CHUCK: They all have that job.
CHUCK: Just is a little bit.
DAVID: We worked on it a couple of years ago. And they draw their neighborhood in the map and they fill everything out and they click ‘submit.’ Now, I've got a customer, who has many addresses. I'm creating a neighborhood which has a name and some geo spatial data. All of these resources are unique resources which will be can be accessed RESTfully and have individual resources. But, because there's about 300 millisecond round trip to the server to do this RESTful API, and because there are not transactions because it's stateless, because you can't have a transaction across multi state, we have to do this in a single PUT. And that according to all of the REST out there is not RESTful, because we are saying, “Hey subscribed user,” which is not a RESTful thing, because it's not idempotent, it's not safe, how do you turn that into something that is REST. Or should you? Or is it really RESTful, and we just don’t recognize the fact that this compound resource is a resource on to itself, and we need to treat it like a compound resource as a resource.
STEVE: You got to it right at the end, finally. Not a bit as marginal, but it's like that is the answer. Have you ever read Steve Yegge’s… something something in the kingdom of nouns article? Steve Yegge is like a great blogger, if you have not read all the stuff he has ever written, you should go do it right now. But, her has this article called, Execution in the Kingdom of Nouns. And I love this article very dearly, and it's because it's making fun of Java, which is one of my favorite past times. But he talks about that Java is difficult to do things in because everything is a noun and there are no verbs, right? Even stuff like the strategy pattern is nounifying a verb. You are turning something into a class.
So nowadays, I'm sort of like conflicted about this blog post because that’s basically what you do with REST, is you nounify all of the things. And so the other thing too is that resources don’t have to be unique, in the sense that like your address needs to be only addressable as an address by itself. So if you treat that customer as sort of a composite resource, that you post the entire thing of the user and its associated resources and then it creates those if you access them in some other way, that's totally fine, right?
So, you can think about it sort of like how to access the… so technically, all seven… so you do REST generate scaffold posts, right? So we think about that as generating a post resource, but actually, you are generating a three separate, maybe a separate resources. You are generating a resource that shows you all the posts that the system has. You are generating a resources that gives you a form to create a new post, and you are generating a way to look at post that exists like a...
JAMES: I actually do that… just to be clear, I do that as two separate resources; you are generating the collection and then the items inside of it.
DAVID: Wow. You just opened the fridge and the light came on. Okay, so POST isn't a resource. The collection of post is a resource, and a view onto a post, is that a separate resource?
STEVE: Anything you’re going to hit with a URL, is a resource.
DAVID: Wow. Okay, mind blown.
STEVE: The only thing that a resource… so actually, I have Fielding’s dissertation up because of course, I do. And here's the definition of resource. They key abstraction … this is why I talk about being priesthood because I'm like Fielding section 5 211, resources and resource identifiers. The key abstraction of information RESTs is a resource. Any information that can named is a resource. A document image, a temporary service like today’s weather in Los Angeles, a collection of other resources, a non-virtual object like a person and so on; anything that could be a target of hypertext reference is a resource. So it doesn’t matter what they are at all. And that’s literally how big the definition of what a resource is.
DAVID: so a lot of those people are insisting that they are writing RESTless things really could make an argument, no this is a different resource. And it really could be RESTless or RESTful.
STEVE: Yeah, because it's not about like there's this… they are all individual and unique; it sort of like violate SRP in our brains, I think that’s why we kind of like shy away from this sort of thinking, is because if there's multiple ways of accessing an address, aren’t we violating there's one uniform place to access and addresses thing in the system. It’s sort of at odds of some of the other ways that we think about software.
JAMES: So kind of getting back to David’s transaction question, it is totally viable to treat the transaction itself out as a separate resource; settle that up and then kick it all at once, basically.
STEVE: Yeah, so you can say like, “I have a transaction resource, and I want it to do this action.” And then you send it a POST request that says like, “Okay, commit.” And then that commit will return whether it is like successfully been committed or not.
DAVID: So you are saying you hit a URL, that says start my transaction, then you start another URL that says create the address, now hit another URL that says create the customer link about that address and then you send commit. Aren’t those stateful? Aren’t those temporally bound to each other?
STEVE: No. Think of it as like, is editing a user resource not stateful? No, it's not because you have the user’s URL and you are just editing some attributes of it. So in a transaction, you are editing some attributes of this transaction, and then you are saying like, “Perform this particular action,” like complete yourself or kick off some sort of computation. It's no different like anything. And transaction is one of those weird things where it's always transactions and authentication, and something else where people are like, REST, blah, blah, blah transactions. You sort of…
DAVID: So you can do it both ways then? You can get a transaction, you could send the neighborhood, you can send the address and get ids for everything and then say, “Here's the costumer linked to everything.” “Oh, the consumer failed. Back out all of the things that were linked in this transaction.” And that would be slow; you'd have all the server latency of every single request, but that would be RESTful. Or you could say, “Here is the composite object; please create or update everything. Here’s the customer with the address and everything, do it all at once and that will be fast.” And it would be a big hairy controller method, but it can also be considered restful, then.
STEVE: Well yeah, and it doesn’t have to be big, hairy controller methods. So for example, think about a job queue implemented via REST. So you post, “I would like this bank account balance to occur,” so you give it sort of the final result. You say like, “I wanna transfer $20 from this account to that account. Please do so.” And the server will return like two or three created with a link to job 752 or whatever, and then you will pull that job while the actual transaction occurs and it does all that processing. And then eventually it would say, “Okay, it is created and finished,” or what not. So you wouldn’t even like sit and wait for all of the latency necessarily, and you wouldn’t even necessarily build it up either. You could also say like, this is the final result that I want, and then just pull for it. And that wouldn’t be a hairy controller action, because you would be just creating a job and putting into the key, right?
DAVID: By hairy controller action, what I mean is a composite controller action, right? It would delegate to all of the sub controller actions for the composite object.
STEVE: Yeah, I mean one of the big deals with this REST too is that like, you can't think about this stuff in terms of exposing… this is why the original definition was kind of hilarious and sort of true, is you can't think about it as like, “I'm exposing my database over the internet,” or “I'm exposing this controllers and models over the internet.” It's a totally different thing. You are exposing this like these resources…
JOSH: Well, you have to think about it as domain objects rather than database records. But I followed what you were saying about what exposing a transaction object; you say, “Create me a transaction,” and then you use that URL for filing up all the pieces of it and then you can edit when you are done. And that makes total sense at one level, but it also sounds like it’s… like for all the criticism that Rails gets for exposing all the database crud actions as REST verbs, this sounds like way worse than that. [Chuckles] You are taking something that’s atomic at the database level, transaction…. you have no place to keep state in the application except for the database. You are turning one database transaction into many database transactions.
DAVID: I was about to say Josh, this is making itch even more… but he says I can still have my composite, controller, resource, so I'm okay, I'm just going to go back to my cave and hide.
JOSH: Yeah. [Chuckles]
STEVE: I also might be misrepresenting…. this is a very heated subject, I guess I should say. And because this is the morning, and I have not have coffee yet, I should have done that but I didn’t. There's a lot of talk about how to implement transactions effectively, and there's a lot of differing views. But again, it's like a style, right? So you are sort of asking, “Well, if I'm trying to paint an impressionist painting, how do I properly use blue?” And there’s like varying arguments about how good or bad. And I should probably stop with these analogies, since I don’t know anything about impressionism. There's nothing to do with it. But you know what I mean, though. I guess.
DAVID: And I'm actually ok with this, by saying that you could actually push the transaction off as a separate resource. I cannot readily think of an example of why on god’s green earth would ever want to use it. But the important thing is that it feels true and complete when you say it that way. And there may come along a design in which you have no ability to post a composite object. You have to post a little thing, and some other service have to hand it off to some other service. You have to get that back. It has to be synchronous. It has to be returned before you can handle the next thing.
And so having basically a transaction noun, instead of a transaction wrapper, that actually make sense. You are taking… it won't be a database transaction, heaven help you if you open a database transaction on that connection and then just wait for the server to come back. But if you basically wrote your own explicit transaction locking sequence at that level, you could do it. And like Josh says, that absolutely is the wrong way to do straight up Rails database crud, but I'm going to think on that, because I'm the guy that always tries to figure out the most perverse way to build something. So I'm going to think on those. Something with that architecture.
JOSH: That does sound potentially useful if you have to do distributed transactions, and you wanna use two phase commits kind of thing. I have one question for Steve that I've been holding out for a while. And that’s the question of the redirect status code. So Rails, when you say redirect to in the controller, where it creates a 301 status code, which is move permanently, which I guess is one of the more abused status codes. So now we have 303, which is see other, which seems like it's more pedantically correct, but I don’t know how that’s going to interact with the caching layers and things like that. It's that what we should be doing in our Rails applications now? We should be doing 303, once we…
STEVE: Yeah, so I believe that there's ideally, it should be 303. But I seem to remember, there is some sort of weirdness, where browsers interacted with certain status codes strangely. And so I don’t remember the exact details, but there was some sort of thing where like we'd love to use a 303, but you end up using a 302 because like Firefox 2 doesn’t know how to handle 303s or something. And so I know that there's a little bit weirdness there.
So really it should be 303 in the sort of like post to create, and it gives you a resource back, and then it returns like… And actually I think theoretically, there's actually 201 with the location on a create, but if you were redoing a redirect, it's probably 303, unless there was some sort of like you had it in this one place before, and you moved it and you want to tell everybody like when you moved to a blog engine and you wanna point everyone in your new posts, use a 301. If you are doing a redirect do a 303.
JOSH: Well, 303 says if you post to URI, and you need to do a get from another URI, then that’s what 303 is for. And you should never cache a 303, I think is the part that gives problems.
JAMES: I think you guys actually hit on an interesting point right there though that I always think about is that it isn’t REST… that's one of the things, that doing it right is often at odds with how browsers work. I mean, think of all the code in Rails that’s around providing the restful verbs, because you can just get that out of a web browser.
STEVE: That’s the HTML’s fault, not the browser’s faults by the way. The HTML spec says that they can only implement get and post in forms.
JAMES: Gotcha. But at the same time too, like going back to my s3 example, even if you could do the PUT from the browser, it's hard to imagine an interface that would make that easy. You know what I mean? Where you would have to give the final URL and then somehow set the file to be uploaded. It's hard to imagine how you would easily enter that into a browser. Does that kind of make sense?
STEVE: Yes. Since we are almost at picks, we can't really necessarily talk about this stuff. There is a bunch of things that all around those. You could totally design a browser that would do this. And you know, we didn’t even really get to talk about HATEOAS at all yet, which is sort of disappointing because that’s the biggest area where everybody falls short and provide the most benefit, but nobody does.
DAVID: I vote we have Steve back for a second episode.
JOSH: [Chuckles] So Steve, if we don’t have time to talk about HATEOAS, could you leave us with some pointers about where we could go to explore that on our own.
STEVE: So basically the two things, and I'm not entirely 100% happy with the way that I wrote it, but the timeless article about HATEOAS is probably one of the best things there is about it in general. Basically, it's just the idea that you don’t let clients construct their own URLs; you return URLs into your representations and they follow the links. It's like the way that the web works is that, you don’t remember every url on the website; you go to the root and you click some links. APIs should work in the same exact way.
JAMES: So the idea there is like if you go to a web service and it has 10,000 resources, but it has like pagination or something, then it should give you the first thousand or whatever , and the link to the get the next thousand basically, so that you could walk through it.
STEVE: Yeah. And this is why pretty urls are irrelevant, Because Rails Actually explicitly says that URIs don’t matter as long as they are unique and they uniquely identify a resource. But what they are, like we could use those old Rails 112 style URLs and it would be to 100% RESTful, because there's nothing to do with it.
CHUCK: All right, it's interesting. And I think we can probably talk about this for another hour. But we do need to get into the picks. Maybe we'll follow this up in a few weeks or something, and see…
JOSH: We can do an episode on APIs.
DAVID: Oh yeah, seriously. +1
CHUCK: All right, lets plan on doing that some time. And in the meantime, let’s go ahead and jump into the picks. Josh, why don’t you go ahead and give us your picks first.
JOSH: Okay, well I have a completely self-serving... not it's not self-serving. My first pick is Confreaks came to Golden Gate Ruby Conference, and did a great job of recording all the sessions. And a couple of days after the conference and there's already a couple of them up online, by the time people are listening to this podcast. I imagine most of them will be available. We had some really good talks there. The lightning talks ended up really being awesome. And Yehuda Katz and Jose Valim’s keynotes… I got to go back and watch everything. I never got to chance to really observe my own conference, because I'm too busy working there. But yeah, go check out the GoGaRuCo videos. GoGaRuCo 2011. So that’s my one pick.
And my other pick is something that I've been playing around with a little bit recently, and that’s website called klout.com. And this is sort of like how to rank people on the social networks. It seems like it's primarily focused around Twitter, which is cool by me. I don’t really use Facebook or Linked In for professional networking. But it's pretty cool, in that you can take a look at people, and how they tweet and what the topics are they tweet about and where they are authorities or not. So if you just go look at JEG2 on the Cloud site, and we get to see James’ analysis, we can see that he’s an authority on atheism -- which is pretty awesome.
[Chuckles] So I showed this to a couple of friends recently, and they all got interested in it. I think it's a lot better than you see somebody on Twitter and you have no idea what they are about, and should you follow them, just go here and you can see a much better information about what they are up to and why you should be reading them. And then you can start playing games about trying to get your klout score up there, which is hard. [Chuckles] Okay, so that’s it for my picks.
CHUCK: All right, those are both things that I use and really like, so thanks, Josh. Let’s have Avdi go next.
AVDI: So let’s see. My first pick, and who knows, maybe this is something somebody has picked before, but is the Ruby Koans project, from Edge Case. And this is something that I had been sort of dimly aware of in the past, but over the weekend, I watched some people sit down and start going through them. So what this project is, is it's kind of a course in Ruby but it's structured a little differently than most.
So it's just set of executable Ruby files, and you execute the top level file, and it gives you some instructions, and it basically starts to run a test, and the first test in the sweep fails and it gives you sort of a pointer on what file to look in, to figure out why the test failed. And it's like the simplest possible true = false or something like that. And so you go and you edit the file and fix the test so that it passes. And then it says, ”Great, you have advanced. Now another test is failing.” And it points you to the file where that test is failing, and it's another Ruby problem that’s just a tiny, tiny bit harder.
And it basically walks you through the Ruby language, fixing test one at a time, so they pass and giving you like a nice feeling of achievement each time. And I think it's a wonderful, interactive way to learn the language or to learn more about the language. And the neat thing about it is that it's also sneakily teaching you TDD in the process. So like, you are learning to program and learning TDD at the same time. And really neat concept, and it works really well. So I was really impressed by that.
Also, my other pick is on conferences. The thing that kept me away sadly, from GoGaRuCo this weekend was the Ruby Dcamp in Northern Virginia, which have gone for four years running now. And these conferences are always one of the highlights of my conference going year. For those who don’t know, they are self-organizing; they basically get a bunch of people together, and they decide what they wanna talk about on the day they are going to talk about it, and they split up into sessions and they talk about interesting stuff. So very energizing, very engaging.
It's definitely not the kind of passive experience that you get from the traditional conference. Like the first time I ever went to an conference, which is also the first technical conference I ever went to when I was just like a kid. Imagine that I would just listen and I actually would find myself just speaking up and hosting sessions. So, and it's a really kind of engaging and empowering experience. Highly recommend if you could find a Rails camp or a bar camp in your area. Look it up and try to attend.
CHUCK: All right, sounds terrific. You know, David made the Ruby Koans pass in a nontraditional way. You wanna tell us about that real quick, David?
DAVID: So I wonder if Avdi have actually has heard this story because he was at the Ruby Dcamp with the Brandon Hayes who helped with this.
AVDI: I believe I've heard this story.
DAVID: At URUG, we sat down and tried to make the Ruby koans pass without editing any of the koans. Basically we said, we are going to include a monkey patch file, how much of Ruby do we have to break in order to make the koans pass without changing anything of them. And it took us about an hour at URUG and then I went home and stayed up until about one in the morning finishing it. it was actually about 70 lines of code. I´ll put it in the show notes. I published it, and Jim Weirich and Joe O'Brien who wrote the koans wrote back with kind of…, they gave me evil stamp of approval on it.
AVDI: so the story I heard was a little bit different; it's about the game of life. This was not about you, but it was about the game of life, I´ll share real quick now because it's great. It was a code retreat, and everybody was solving the game of life together, and the instructions are here are the rules of the game of life on the Wikipedia page, and you have to implement that in Ruby. And so somebody solved it by writing a Ruby mechanize program that would rewrite the Wikipedia page…
DAVID: [Laughs] Awesome.
AVDI: [Laughs] And then they implemented that.
CHUCK: That’s awesome. That's amazing. All right, good hack stories. Steve, are you ready to go next?
STEVE: Totally. So I guess it's sort of already kind of had one pick which is everything Steve Yegge has ever written, specifically Execution in the Kingdom of Nouns, which is an awesome blog post. And my HATEOAS post on timeless, which you guys picked before the first time we started talking about this REST stuff.
But there is one thing that I've been really super ultra-mega jazzed about lately, and that is Destroy all Software by Gary Bernhardt. I don’t know if any of you picked this recently, so if you did, my apologies. But coming back on the bus from Canada recently, and I literally watched Destroy all Software instead of sleeping. It's that good. It's like an intermediate podcast about interesting stuff. Mostly dealing with Ruby, some of it is about git, some of it is about Vim, some of it is about using the shell better, but it's basically just Gary Bernhardt showing the rest of us who to be as awesome as he is.
And specifically the fast test ones are very similar to what Corey Haines has been talking about as well, which is awesome. But basically, it's all stuff like that. So, “Here’s how I test,” “Here’s two examples of going through and refactoring a controller that has gotten way out of hand.” And I just really enjoyed all of them. And it's like ten bucks, but you get his entire back catalog, so by now, there's 25 or 30 episodes. So even if you pay them once and just grab everything he has and don’t subscribe in the future, it's totally worth it. And I'm still paying to subscribe because they are all really awesome in general. So yeah, Destroy all Software.
And then there's a REST discussion mailing list that is specifically about discussing the abstract REST stuff. And a lot of the people that are very knowledgeable on it, including Fielding himself will answer your question sometimes. And so it's a Yahoo group, which sort of sucks, but you can just subscribe your email or not deal with Yahoo’s interface at all. But you know, I´ll post the link to that like the REST discuss mailing list. And #REST on Free Node is also really full of helpful people if you wanna ask questions about these stuff. Those are the two probably best places to do on the internet to get your specific questions answered about some aspect of what is or is not RESTful, or how do you design something to be RESTful, and all that kind of stuff.
CHUCK: That’s awesome. I actually interviewed Gary Bernhardt for the Teach Me to Code podcast and he’s a super guy. He’s really, really smart. So you know, I can definitely back you up on that pick. All right David, go ahead.
DAVID: Okay, so for today, I'm going to blow your minds -- old school. So I have two picks today that are weapons grade hard thinky stuff that you will be so much better programmers for having done. The first one is an obscure little book by Roger Sessions called Class Construction in C and C++: Object-Oriented Programming Fundamentals. This book is written in 1992. Don’t let that scare you. What this book does it is sits down and it says, “Okay, C is not an object-oriented language. It's a procedural imperative language. Can we build an object-oriented system out of C?” And the answer is almost very much yes.
He walks you through the guts of how to build an object-oriented system. So he walks you through how to build like the vector table, which is the thing in every OO system that tells an object, when I send a message to an object, what function do I invoke? There's a thing called the vector table, and every OO language seems to have one some way, somewhere, somehow. So when you move on to… like I read that book and finally, C++ made sense. I had to go back to C and read this book in order to have C++ make any sense to me. One thing you can't do to C very well is inheritance, but you can get around that by recursively including macros, but don’t do that. That’s evil.
Class construction in C and C++ by Roger Sessions Object-oriented Programming Fundaments is the full title of the book, and I´ll post a link to that in the show notes. I cannot recommend it highly enough. If you want to know what is happening under the hood in an object-oriented language, this is the book for you. You will suddenly understand things like when a base class calls or when somebody in Ruby says, “The super class or the meta class is the meta class of the super class,” that will make sense after you read this book.
And my second pick, I cannot believe this has not been picked before, but according to our show notes’ list of picks, it hasn’t. so I'm going to pick, Structure and Interpretation of Computer Programs, by Hal Abelson and Gerald Jay Sussman. These lectures will piss you off. And the reason they will piss you off -- or at least they piss me off – is because I have been programming for almost 20 years, and I sat down and I watched these videos and in lecture 1A which is an overview and an introduction to Lisp, now these lectures, you are going to learn Scheme, which is a dialect of Lisp, but in the very first lecture, you learn how to computationally determine the fixed point of a function, and then use that to find the square root of a number. That’s really cool math, and I didn’t know how to do that. And they show you how to do it that in like seven lines of code.
For me to do that like in C or in Ruby, I would be writing 200 lines of code to try and figure that out. And they are like, no that's easy; you do this, you do this, you do this and boom you are done. And the videos, there's about 8 hours of videos, maybe more, I'm not sure; might be 16. But it's an introductory programming course taught at MIT. The reason it will piss you off is because you will learn some amazing computer science fundamentals that you never knew. And these videos were recorded in 1986.
And so you will go, “I have been programming my whole life, and this has been out there on the Ethernet, ready for somebody to come pick it up for 25 years.” And I didn’t know this, and nobody around me knew this. And that will make you crazy. I also recommend the textbook that comes with the course. It is hard; it is an actual college textbook; it's an actual MIT college textbook, but if you are just going to do one, do the videos; they are free, I will post the link to the videos on the show notes. And I´ll post an Amazon link to the book as well.
STEVE: The book is free too.
DAVID: The book is free online as well?
STEVE: Yeah, yeah.
DAVID: Okay, I will dig that up.
CHUCK: There are a million PDF copies out there that you can get from all kinds of places.
STEVE: Also, MIT itself hosts free copy of the book, so it's not even illegal.
DAVID: if you want a dead tree copy new, they are $100 and used, they are about $37. Those are my picks.
CHUCK: All right, sounds terrific. James, go ahead.
JAMES: Wow, waiting to go this late in the queue, I've just been sitting here going, “Wow, I need to do that, I need to do that, I need to do that.” So I'm too busy reading your guy’s picks to do my own. Anyways, I wanted to add Avdi’s just a tiny bit. His Ruby Koans, I definitely wanna plus one that one. I did it way back a long time ago, when I was reading through it and one of the cool things about it is how easy it is to do. Like I was sitting there playing with it. And at one point, you get to a test and it has to do something very simple with regex or used to. And then it would say about regex are beyond the scope of this tutorial and it just left them out. I was like, “Oh, no they aren’t.” And so I just went and forked it, and do a new file for regex, and wrote up a bunch of basic regex test and put them all in there, so now there's regex test at Ruby Koan. And so I would encourage people to play with it if you are new. But also, for experience you should really go through and consider adding a little to it because this can be a great repository that we all just grow together. So anyways, that was Avdi’s pick; not mine. I just want to say that.
My pick is actually going to be on topic for once. If you enjoyed the discussion today about REST, everything I know about REST, I learned from one very good book. And unlike paper Steve talks about which are all dense and horrible, as far as accessibility, this book is great. It's totally straightforward and it's in plain language. It talks about everything we talked about today; the verbs, the HATEOAS, the transactions, and the different approaches to them. All of that stuff is in here. And he examines it from a really neat way. Like for example, it does use a lot of examples from Amazon’s S3, which is often held up as a fairly good RESTful API. And he says, “Yeah, it is pretty good API, but it does get several things wrong.” And it shows you the things that you get right, like PUT and the things that you get wrong and stuff. So can't recommend this book enough, if you really wanna learn REST the right way. And the name of it is RESTful Web Services, it's by Leonard Richardson and Sam Ruby. So it's a great, underappreciated O'Reilly book that I highly recommend.
STEVE: I would have to recommend it.
JOSH: So James, I hate to rain on your parade, but that was my pick over a month ago.
JAMES: Oh! Ouch!
JOSH: Repeat! [Chuckles]
JAMES: Well okay, it's actually relevant to this conversation.
STEVE: Make it a cookbook. You can just say it's a cookbook you picked this time, because the cookbook is actually pretty solid. But I just wanna say that that book is awesome, but it's also not gospel and it does get some things really wrong. So it's like 90% awesome. And it's definitely good introduction, just don’t think of it as gospel, think of it as an aid to help you learn more.
JAMES: I like it because I feel like it's so much more accessible than books.
STEVE: Totally. It's great, but not perfect -- not that anything is.
CHUCK: Yeah, so basically, what Steve is saying is read the book, and then email him and pester him until he tells you which 10% is wrong.
STEVE: Yes. [Chuckles]
CHUCK: [Chuckles] Sorry I interrupted. Are you done, James?
JAMES: I'm done.
JOSH: Steve, can you say something about your book that’s coming out, just so people can know about that?
STEVE: Yes, so I'm writing a book on REST, because the best thing to do is… so like basically when… to fully formulate my own opinions, if I'm going to run around and tell everybody they are wrong about stuff, I need to put myself up in a place that I can be criticized too. So, I decided to write a book on REST. And actually, I was largely inspired by Avdi in Exceptional Ruby, as like hey, self-publish a book and good things will happen. So I decided that since love my blog post about REST, I would write a book on REST from the discussion point of, let’s talk about how to build RESTful APIs with Rails, and not make it specific to Rails, make the code examples specific to Rails, and discuss in plain terms why any of these stuff matters to you and what parts of it you can't do, where you have to make compromises and where you don’t. And where Rails goes wrong, where it goes right and just sort of how to do things.
So it's going to be project-based, where we take a little micro blog and take it from the standard way that you would implement in a micro blog, and then turn it into a fully RESTful web service, according to all the specs. It's called Get Some REST, and I have an email form up there where you can put in your email. I'm going to do the whole beta book thing, so when it's half done in about a month or so, maybe two, don’t call me on that too hardly, but I'm going to do the whole, “Here’s the of the book and you can check it out while I'm finishing it off,” kind of thing. So yeah, I'm going to write something about this too, because I think it's fairly needed.
CHUCK: All right, super. That’s really exciting. And then it's nice to be able to get some great content on something that's not well understood, so thanks for that.
JAMES: We can do that book in the future on the Book Club.
JOSH: I´ll let you pick it first.
CHUCK: [Chuckles] All right, well I guess I'm the last. There are couple of things that I wanted to pick. The first one is something that I feel like people really need to be involved in -- both from the standpoint of asking questions, as well as helping people out -- and that is Stack Overflow. Just a super resource as far as getting help, finding answers. Half the time when I Google something, I wind up on that website, where somebody else have asked the question. And even if you get part of the answer off of it, then you have the opportunity to post and say, “Well, in my case when I was doing this, then this other thing apply.” And I like it for that reason.
I also like it for the same reasons that Michael Hartl likes it. I was talking to him and he has Railstutorial.org. And when people email him, asking for questions about Rails, he actually tells them that they have to post their question on Stack Overflow and send him a link and he will go and answer it there. And the reason is, is because it's kind of a giant FAQ for everybody, and it puts the answer out there, accessible to anybody who needs them. So it's kind of a community collaboration, that's sort of documentation but really addresses particular problems that people have. And so I really, really like it. you know, there are other ones like Quora some of these other ones out there. In fact, it seems like every time somebody duplicates it, I see on Twitter that… the guy that owns the company that runs it, he always have some comment, and then will link to the copycat of the Stack overflow.
Anyway, the other pick that I have, I was taking to my sister yesterday, we were talking about TV shows and I just finished watching Stargate SG1, which I have picked in the past. And I told her I was like, well it's probably like the second best TV show that I've ever seen. It's either that one or Firefly are the second best TV show that I've ever seen. So we were talking about… well she's like, “Well, which one is the best one you have ever seen?” And that’s my pick for today; the best TV show that I have seen to date is Battlestar Galactica. And I just love that show. I couldn’t stop watching it once I started. So that’s my pick. I've been watching it on Netflix, but I'm sure you can get it on other places.
Anyway, those are my picks. I think that’s pretty much it. So we'll go ahead and acknowledge our panel one more time. We have our guest Rogues this week, Steve Klabnik.
STEVE: See you later, guys. I don’t know whatever I'm supposed on this space.
CHUCK: David Brady.
DAVID: Apparently, I am a RESTful resource.
CHUCK: James Edward Gray.
JAMES: I think this episode might need to be renamed from, “REST Done Right” to “Steve Klabnik Educates the Ruby Rogues.”
CHUCK: [Chuckles] Yeah, no kidding. Avdi Grimm.
AVDI: Man, I need to go get some REST.
CHUCK: Josh Susser.
JOSH: Okay, [Chuckles] I was looking for the appropriate HTTP response code. I think I´ll go with 304, not modified.
CHUCK: all Right, and I'm Charles Max Wood. Few things that you may wanna know about the podcast, basically on October 4th, we're doing the Book Club on Smalltalk Best Practice Patterns with Kent Beck. And I've actually read about a quarter of the book to this point, and I have to say that it's probably not what you expect it to be, so go pick it up.
Kent is going to be on the podcast and he's going to give you a whole bunch of cool information related to that. And we'll be talking about what we thought of the book. You can get the show notes at rubyrogues.com and you can get us in iTunes; if you go to iTunes, do a search for Ruby Rogues. Please leave us a review, let us know your thoughts, and that will really help us out. So that’s it. We'll catch you next week, and thanks for listening!
DAVID: We love you. Bye!
JAMES: I, for one, am glad Avdi wore pants for this episode.