ANDREW WARNER: Welcome. I an Andrew Warner. I am the director of engineering at RapGenius dot com. You can follow me on Twitter at wwarner. And you can also follow RapGenius on Twitter at RapGenius. So Tweet at me during the presentation, after the presentation, tomorrow. Whatever. I'm down. I will respond to your stuff. So, this is really exciting for me. This is my first RailsConf speaking. And so, you know, I'm super excited to be here. Come on. Little round of applause. Yeah. Yeah. This is very exciting for me, not just because I'm speaking, but also because you all, you know, this is a pretty full room. You all chose my talk over the other two talks! This is, you know, it makes me feel good. It also is crazy, because, how do you even know what I'm gonna be talking about? I had some summary that's kind of vague. Like, maybe I'm gonna say some stuff, but, you know, maybe I'm gonna change it a little bit. Now I've got this slide on the screen that says Rails Holy Grail. Who knows what that even means? So, you know, I'm excited that you chose me even besides all this stuff. So, you know, I'm gonna be talking today about the Rails Holy Grail. I'm a little bit disappointed that DHH kind of stole my thunder with his Holy Grail slide. But, you know, I, I still, I'm gonna use it. I couldn't find another replacement Holy Grail slide in the meantime. So, I guess, what am I gonna be telling you about? Well, first, I'm gonna be talking to you about what the Holy Grail is. So what I mean when I say the Holy Grail. I'm gonna be showing you some existing Rails solutions that kind of get us close to this. I'm going to be offering up a new solution for your consideration today. And I'm gonna show you what else this new solution gets us. So, first off, what is the Holy Grail of the web? This background is like the song Holy Grail by Jay-Z. I don't know. So, the Holy Grail of the web, what I actually mean when I say Holy Grail, is the best possible user experience combined with the best possible developer experience. So it should be very easy for me to rapidly develop an application, as a developer, that is also easy to use for a user. And so what is the best possible user experience? Well, I'm talking about sort of a, a single-page app thick client feel. So you shouldn't have to reload the entire page every time you, every time the user actually clicks on a link. So in 2004, when Rails came out, it was really great because it allowed you to create, very quickly, websites which were kind of a thin client, where every single click reloaded the whole page. And this was revolutionary. Before that, you know, it was really difficult to make a site rapidly. And around the same time, actually, Google released this Gmail thing. Kind of hard to see the screenshot, but I think you're all familiar with what Gmail actually is. Gmail was this single-page app where every single click actually loaded right on the same page that you were on whatever you content you wanted to see as the user. You didn't have to wait for your whole browser to refresh. This is pretty good. So it's just no full page reloads at all. This is the best experience for the user, and it should sort of feel like a desktop app or a mobile app in terms of the native feel. The best developer experience is a little bit different. So, you should be able to use a developer-friendly framework. You should be able to rapidly develop something. You shouldn't have to reinvent the wheel. Easy things should be easy. So, Ruby on Rails is one of these frameworks. You might have heard of this. Kind of why we're here today. So you should be able to use a developer-friendly framework. You should be DRY. You shouldn't have to repeat yourself. D-R-Y. Don't Repeat Yourself. But actually, what I mean here is more like DRV. So what is DRV? Well, I mean, you shouldn't have to repeat views. You shouldn't have to write one set of views that lives on the client's browser and one set of the views that lives on your web server. You should be writing mostly one language. At least, in so far as views are concerned. So you shouldn't have to context switch. Context switching is very expensive. Sort of switching from Ruby and Rails to JavaScript. This is difficult for us, as developers. And your website should be SEO friendly. So I guess this part really applies to content sites. So I work at RapGenius dot com. Content and SEO friendliness is extremely important for us. We lost a bunch of traffic when we actually got banned from Google for a little while. They showed us exactly how important SEO was. So your site should be able to show a crawler, a search engine crawler. HTML that it can index and than make available for users' searching. So so what is the closest thing to this Holy Grail today? Well, it's kind of the node.js. There's a node.js solution that I think is the best thing. Come on, boo node.js! Give me some boos. So there's this Airbnb Rendr framework, right. So this says render your backbone JS apps on the client and on the server using node.js. So this framework is, you know, pretty easy to use. You write your backbone JS client-side app and the client can sort of incrementally update the page and this sort of Gmail-like experience. And then the server can also render full HTML pages for arbitrarily deep links. So the user can still see a full HTML page if they get deep-linked, and a web browser, or sorry, a web crawler for a search engine can also see HTML. So this, I think, is the best solution right there. Right, out there today. And here, I've actually built like a little demo app using render to show you how cool it is. So this is a little bit washed out. But, this is sort of a mini version of our site RapGenius. It's, so RapGenius allows you to view documents and read documents and also read inline annotations on those documents. And the annotations are meant to provide context and also explanation, if something's complicated, and just add to the text. So here I've built RailsGenius, which allows you to add context to, like, Rails talk abstracts. So this is my talk. And I've added some inline annotations here, and the, the point I'm trying to make is that we should be able to, when I click this link, to add views, which is actually an annotation, this shouldn't refresh the whole page. It should just load-in the part of the page that changed. Which is a much better experience for the user. So if I actually click on this, it just loads that part of the page. You know, this is something that, you know, might be familiar to you from using apps, but it's, it's kind of hard to like do this out of the box in Rails. And, you know, also you should be able to like edit it right there and that should just snap in the edit view, et cetera. And this should not be too, too difficult to build. Looks like I lost my internet connection. There we go. So this kind of stacks up pretty well. It's DRY. We have one set of views. It's SEO-friendly. You know, we can serve HTML the crawlers. It's a thick-client feel, so single-page app. Mostly write one language. It's backbone JavaScript, et cetera. But it is not Rails. So I think it takes, it still took me awhile, even with this render thing, to like, create an app that was good. And Rails is just much easier to create like, the vanilla example apps using, like, scaffolding or even not using scaffolding. It's just way faster for development. So I still think this is bad even though it's the closest thing to the Holy Grail today. But, my point is that Rails, I'm, I'm worried, because I think Rails is kind of getting left in the dust in this respect. Like, it's kind of, there's not really like an established solution for building like a thick client good experience Rails app. And so now I'm gonna show you some sort of solutions that get part way there. So let's look at those. So one option, and, you know, this is kind of a joke but it's a serious thing that people actually do, is creating one version of your app that lives on the client, and one version of your app that lives on the server. And so, you know, in this example you would write erb or HAML on the server, and then you would have an API that returns JSON from the server to the client, and then the client would figure out how to render new parts of the page that it wants to snap in using like some kind of frontend like handlebar or underscore.js templates. Right, so this seems kind of reasonable. But the problem is if you forget to update both templates at the same time, you're gonna end up with some inconsistency between the client and the server in terms of the logic. And this can be very, very dangerous. It can lead to some hard to suss out bugs and, you know, we all know DRY is a good thing to strive towards. So this doesn't stack up very well. It's not DRY. It's SEO-friendly. It's a thick client. You didn't write mostly one language, though. You had to write a bunch of stuff on the client and the server. And we only get a half point for Rails. So you've gotta spend a lot of time still on the frontend noodling around there. All right. So Turbolinks are something you also might have heard of. You might be thinking, well I can just do this with Turbolinks. This is like a one-liner, right? So Turbolinks, the way it works, is you write just one set of views, and then when the user clicks on a link, you can just pop in the whole new page but save the browser instance. So you save on this overhead of, like, parsing and compiling CSS and interpreting JavaScript and, you know, running all this stuff. It, it's actually kind of expensive to spin up a browser instance for the client. So you, you eliminate all that overhead and your client is extremely simple. You basically have to write no JavaScript. So the problem is that Turbolinks kind of ends there. There's nowhere else to go. If you want to snap in, like, just a new part of the page, you've gotta write some kind of custom controller action that just returns that part of the page and then you've gotta use like some jQuery soup type thing to like snap that in and then you're relying upon your, your page structure. So that is not a great solution. But this works pretty well if you're trying to do, depending on what you're trying to do. So, you know, this is DRY. It is SEO-friendly. It's not really like a proper thick client, though. It's kind of like a fake thick client. Feels almost like a thick client. Not quite there. Mostly write one language. We've got that covered. We've also got Rails covered. So this is kind of why Turbolinks is so popular I think. Because it basically does what you want without much hassle. So there's also this Ember/Angular/Backbone type movement now. You might have sort of seen, there are a bunch of talks about this. People allude to it all the time. This is sort of the new way, this is the new wave of building Rails apps. So you just have a JSON API in your server, that's it. Your entire app experience lives on the client, so it lives in JavaScript or CoffeeScript, what have you. And the initial page load, the first time your user visits the site, they download like this whole JS app thing, and then that sort of boots up and figures out how to like render the site. And so this might remind you of new Twitter. So Twitter in like, 2010 I think released this new experience where they were claiming this, exactly. They were saying this is going to be much better for our users because we can just sort of like snap in a new Tweet when you click. And they actually found that this was kind of costly. Like, sometimes it would take ten seconds to render a 140 character Tweet. Because you had to download like all these assets and then like that would sort of like boot up, spin up and build the page. This isn't like an actually good experience for users, at least the first time they visit the page. It's actually pretty bad. So it doesn't quite stack up great here. It is DRY. It is not SEO friendly, though. Now, there's some tricks to sort of, making it, sort of making it SEO friendly, but, you know, for the most part it's kind of hard to make it, you know, allow your app to like serve HTML to a crawler. Now if you're not building a content site, maybe it doesn't matter. But, you know, my claim is this is pretty important on most websites. It's a thick client. You don't get to write mostly one language. Half of it's JavaScript. So, again, half point for Rails. So the point that I am trying to make here is that each of these things makes like a key, key trade off that we can't live without. So what are we actually going to do about this? Well, now I'm gonna show you like a new solution that, you know, maybe it's good, maybe it's not. And actually, this starts with a, sort of like, admission. So I kind of lied to you before. I told you I built this app that I was showing you before using render which is this like, node.js thing. But the truth is, I don't even know JavaScript. I don't even know anything about node.js. Render. I just read about it, right. I actually built this app using this new sort of like technique slash library called Perspective. So again, here's the same app. And just to remind you, you can kind of click around here and, you know, things just sort of load. And this is a very simple example, but things are snapping in, you know, just the parts of the page that are changing. Need to get changed. And you can sort like edit and save stuff. And we're just sort of updating parts of the page that need to change. And so this is this perspectives thing, which I'm claiming is better, right. So let's see how it stacks up on the chart that I just made. It's DRY. It's SEO-friendly. It's a thick client. You write mostly one lang- I think you see where this is going. And it's Rails. So I checked all the boxes in my own talk. This is a key, key thing. You have to be able to do this if you're gonna speak at RailsConf. You gotta have a chart, checks all the boxes. This is good. So, you know, the key sort of thing here, the key part of Perspectives, is that we want to be able to share our templates between the client and the server. So like the server should be able to render full HTML. And the client should be able to like receive JSON and like render a template, right. This is sort of the appeal of something like render. So, here, you know, let's, let's say we thought, well maybe we can sort of share templates. Maybe we can share our existing erb templates. Those are pretty easy to write. So here's like a simple erb template, right. We're just generating like a link to a user. We're using the name as like the anchor text. We're using user_url helper from Rails to like generate a link to it. This is pretty simple. You might imagine that using something like Opal, which transforms Ruby in JavaScript, you could like pour it your whole server, kind of like, instance, including all these helpers link link_to and user_url, to JavaScript, and then you could just jump down your erb template and then that could get sort of rendered on the client. This might, you know, you might think that this works. But, actually, you can kind of do more stuff in erb than you might sort of be aware of from simple examples. Like, you can, here's an example, where I'm actually just running like a SQL query in an erb template. And this, I think, demonstrates like, the fundamental difference - yeah, boo! This, I think, demonstrates like a fundamental difference between, you know, the client and the server, is that, they're kind of in different environments. Like, the server has access to resources that the client, not only doesn't have access to but shouldn't have access to. The client should not ever be able to like hook up to the database and ask some questions. Right, so. And the client is the same. Like, the client knows stuff about the user's browser. Like, they can inspect local storage and all this stuff. Like, they're fundamentally different environments. And so I don't think that any of the existing templates out there really get you this sort of shared environment for free. So, the way, you know, so the way to get to a shared template between a client and a server is to find the lowest common denominator. So, like, if you've taken, you know, calculus at the graduate level, you might have heard of the lowest common denominator. It's the way that you are able to like, if you're trying to add fractions, you can like, multiply the denominator- The point is, it allows you to like find some common ground between, you know, the client and the server, right. So we need lowest common denominator. Which is the dumbest possible template. So we need something that's really dumb, that has, that basically can't do anything. And so what is that? Well, that's mustache. Mustache are the dumbest templates out there. Don't tell them I said that. They're probably in the other room or something. But they're, they're pretty dumb. You know, you might have heard mustache templates referred to as being logic-less. I actually think logic-less is kind of like a bad way of describing it. Because when I hear logic-less, I'm kind of thinking, like, OK, I can't write like an if statement. There's probably no like for loop situation. Like, what else can't I do? Well I probably can't do like an if-else, like, you know, the reality is you can actually kind of do that stuff. It's called a different thing. So I don't like using the word logic-less. It's more like you can't run arbitrary code. It's more like no code, code-less or something. So you can't run arbitrary code. This is the sort of take away from mustache, right. And so, you know, this is kind of cool because, because you can't run arbitrary code, we can write mustache template renders in all these different languages. So you can easily render mustache templates in Ruby or JavaScript. If you want to use one of these other languages, these are terrible, but I guess you could use like dot net or something if you wanted to render a mustache template in dot net. I don't know what you're doing but you might be able to do that. So, to show you like how to create a mustache template, I want to look at like a specific example. So here's, like, the RailsGenius app, and, you know, in the left, lower left-hand corner here there's like an annotation, right. So this is sort of annotating the line node.js. And if we sort of zoom in on this thing, we can see there are a few different components. There's the section of text that's annotated. So we'll call that the referent. This is like a fancy word for like reference or something. So we'll call that the referent. That's the line that I highlighted and explained. And then we'll call this the sort of body. And you'll notice the body can contain like arbitrary HTML. There's a link to node.js dot org. A link to Ruby on Rails, et cetera. I'm sort of adding, like, you know, I'm adding hyper media here because it's the web. And then there's this edit link, right. And so this edit link, you might imagine, should appear for some users and not others depending on, like, what your permissions are or what your sort of situation is. And so mustache, the, the template for this, the whole template, looks like this. So I'm gonna go into like the specifics of how I would actually, you know, what this is doing. But here's the template. And to render this, I just need this hash of JSON data. So I can render this anywhere as long as I have this hash. Key thing to remember. So, if I can't run code in mustache, like I can run code in erb, how do I actually like put data in the markup, which is like the whole point of templates, right? So you can use these tags. Right, so given this section of JSON, you have a referent mapping to node dot js, right. We can render that data into this template with a, you know, inside of a block quote, by using this double curly brace thing. This just says, like, basically, gsub, or actually not gsub, sub this referant into, inside this block quote. Pretty simple. So this is just gonna print like block quote node.js, you know. That should hopefully be fairly self-evident. So if you want to sort of do something where you're printing raw HTML, the double brace will actually escape it. So that's most the time what you want. But in this case we're actually like, you know, we want hyper media in that annotation. So this, you know, actually has an href. An a tag, rather. So we're gonna use the triple brace, which is another type of tag that says print this HTML raw. So this will actually print out, like, you know, real anchor tag. Which is, in this case, what we want. So here is the sort of meat of mustache. This is probably the trickiest part of mustache. There's another type of tag which is this sort of double brace hash block, right. So here is the block that I'm talking about. So you can see this double brace, and then there's hash edit. This hash is kind of where the logic by another name comes into play in mustache. So this is going to, if edit is like, a boolean type thing, it's going to run, or, or go into the block. If edit is true, if edit is like a list of something, it basically is your for loop is the way to think about it. There's one more thing that is like if edit is like a hash, it kind of just will run the intersection if the hash is there. Otherwise it won't. So in this case, we're just saying, you know, we should show the edit link. That's what edit true means. This is our logic by another name. And then we're actually, you know, grabbing the edit href from the JSON hash as well. And so this will result in just putting the, printing the edit link. Now if edit were false, it wouldn't print that. And so you might be asking, like, well what if I wanted to do something else. Like I'm used to writing these if-else statements. So mustache also has this other sort of tag, which is a carrot. SO there's like double, double squiggly, double curly brace, carot edit, which just means sort of go into this block if edit is unset or false. So this is the kind of last tag. So in this case, if edit were false, we would just print can't edit. So this is mustache. That's basically all you can do. So how does this actually help us? Well, I don't know if you guys remember, but at the beginning of this mustache section, I showed you this template and I said all you need to render this template is this hash. And I also told you that there's a Ruby library for this and a JavaScript library for this. So I think you might see where this is going. If we have some way of generating like a hash, anywhere, then we can render it, we can ship the template and the hash to any environment and like render it there. That's the point I'm trying to make. So how do we actually generate this hash? That's the sort of tricky part. Well I'm gonna do some handwaving here and say, well, this, Perspective's library might help us a little bit. So you know here's a traditional Rails view. This is like an erb template, right. And so what I'm saying is that if we split this into a template, which is mustache, and a perspective, which is something, it's a new type of object, then we can just have this data transport thing, which is just the hash, which is generated by the perspective. And then we can render the template anywhere. So this is how we render on the client or the server. So what is this perspective thing look like? Well, as you might imagine, it's a bunch of stuff that specifies what is in the hash. So, here's the whole perspective. I'm gonna dive into different parts of it. But you can see it's just basically a Ruby object with like some macros thrown in. That's, that's sort of the goal of the library. It should just be a Ruby object. Plain Ruby object. So you can specify inputs, which you specify with params. So you can say param annotation, this perspective expects an annotation as input. You can specify outputs. This perspective outputs reference, which is the annotation's referent property. It also specifies body as a key. And that's the annotation's body as HTML. So this is gonna be raw HTML. And so properties our output. And so these are actually just keys in this hash. So we can just turn this perspective object into a JSON object and then that can be rendered anywhere. So now that we know what this perspective object is, it should be pretty self-explanatory how we get from the right over to the left and render this template. Now you might be thinking, isn't this just like a fancy version of like helpers or something? Like, helpers with like a namespace? And it's actually different because, you know, helpers are sort of operating at, they seem like they kind of live in their own separate object, like annotations' helper or something. But actually they're in a global namespace which can be called from any template at any time. And so they can kind of like override each other. They also are direct function indication. So this is just data. This hash in the middle is just data. SO like if you reference a property twice, you're always gonna get the same value, unlike a helper, which could theoretically return different things if you call it different times. SO it's kind of like a safer thing for us as developers. And so to build like an app with this, you can imagine the initial request that the client makes will receive HTML so the, the server will say, OK let me take this perspective thing, turn it into JSON, render in mustache template and then send that back to the user. And, in the case of like new Twitter, the user would just see the 140 character Tweet rendered. And in the case of like a web-crawler for a search engine, it would see like the full HTML that it can then index. And then subsequent requests, just like we would do with like Ember or Backbone or whatever, we just request JSON from the server and then we render the part of the page that needs to change. So, you the know, the key thing here I think is that HTML is unstructured data. And so a lot of like sort of Rails patterns will tell you just return like the snippet of HTML from the server that you need and then snap that in somewhere. And the eventually leads to like soupy code and coupling to like classes on HTML elements, which leads to a lot of like weirdo bugs that are hard to track down. JSON, on the other hand, is structured data. It's a lot easier to deal with this, so, you can either render it or you could do something, you know, on the client that's a little fancier. If you want to do, update different parts of the page, whatever. The point is that it's structured and it's easy to deal with. And if you're building out like a thick client, you want to couple your client experience to structured data, not unstructured data. All right, so I'm gonna do the quick getting started. You know this is, this is sort of about this library, but I don't want it to be all about this library. So, you know, it's just a gem. You can just generate the install for it. That'll like make your app perspectivified. You can scaffold stuff. You know, you break your erb templates into mustache templates and a perspective. And you just add this one line to your controller, so this just uses a responder to figure out if the client wants HTML or wants JSON, and turns the perspective into whatever you want to. In this case, it's just, you know, find this annotation by ID. Use this little macro to create your perspective and pass it the data it needs, which in this case is like an annotation. And then we can render a JSON version or an HTML version. So what else does this buy us? So clearly this rendering on the client and server thing is pretty good. But actually, I didn't like set out, when I was thinking about this, to like build some sort of like new thick client like framework thing that competes with Ember. That wasn't my goal and I'm still not sure that's my goal. The benefits I'm about to tell you about were actually like the initial inspiration for like wanting to build this library. So, number one is separation of concerns. So, I really hate seeing erb templates like this. Here's like an erb version of the template I just showed you. So instead of having like a nice edit property, there's often like some logic in here, if you look at the bottom there, that's checking, like, whether or not to show the edit link. And this is kind of ugly. It's kind of ugly no matter where it lives. But it's especially ugly if it lives in a template. And it is so easy to do this with erb, cause, you know, erb literally just means embedded Ruby. So you can just write anything. Like I, like I've seen saying. SO like here we're saying, the person can edit the annotation if they created or they're like a super admin type, type, type user. So, mustache and perspectives force you into this separation of concerns, where you literally can't write any code in your template. So you have to put it in the perspective, which is just a Ruby object, which is where it kind of belongs. And there's no, there's no love for this in erb land in Rails. You're kind of, you're kind of off in this waste land of like, I guess I'll throw some view logic in here. I don't really know where it's supposed to go. And so this, this leads me to like, so here's the, sorry, here's the perspective version of it, where actually just generate this edit property and say whether or not we want to display the link, based on that logic I showed you before. So this also gives you like, testing. So I now DHH was like, you know, shitting on this in his talk. I still think testing is important. Maybe not TDD. But like the point is, if you wanted to test like an erb template and had some logic in this, in it, you would have to like, render, the only thing you can do is render the template. You can't talk to it. It's not an object. You can't ask it questions. All you can do is render it and then do like a string-match on it. And in fact, I've seen a ton of tests like this. Web driver tests are basically just doing like, advanced regexes to like figure out if the logic is right. And this is really bad. A perspective is just a freakin' object. Like you just new it up and pass it the data it needs and then you can test your logic. SO here I'm just creating a test double. You know, test doubles are important in testing. Test double for a user, test double for an annotation. I'm creating this new perspective and then I'm asking it if the edit link is true and verifying that it should be showing that. So another thing is caching. So this is like maybe a little bit difficult to motivate, but like, this sort of like cache digest thing, Russian doll caching thing is kind of hard for me to like wrap my brain around, right. Like, you have to, you have to write, sometimes, like a special comment to get the templates to know about each other. Not as good as just having objects that know about each other. So if you want to cache an annotation show perspective, you just use this cache macro and that says, use the annotation as the cache key, right. And so if you're familiar with, like, cache keys in Rails, if you're using generational caching, this will just say, the cache key - hopefully you can see this - is just the type of object, the id of the object, and when it was last updated. So this will like fall out of cache if anyone updates the annotation or if it's like a different annotation it'll have a different cache key. So this sort of, hopefully this is, is somewhat familiar. If you have another template that you're rendering, like here, this annotation show perspective, is rendering a nested user slash avatar perspective, so you can see that at the bottom. You might want to say, well, if the user updates their avatar, we also want this to fall out of cache. This is sort of the, the Russian doll caching of it all, right. So if the inside Russian doll is uncached then it should bust the outside Russian dolls cache key. So this thing, you can also assign a cache key to. You can say, cache based on the user. So if the user gets updated from the user's avatar perspective, we want this to fall out of cache. And so, like I said, we want the whole thing to fall out of cache if the user gets updated or the annotation gets updated. And so what perspective will do for you is say, OK, we know that there's a nested user's avatar perspective here, and so to generate the cache key for this whole thing, we're just going to concatenate the cache key from the annotation and the cache key from the user and produce this ugly, terrible, long cache key, which you don't have to worry about at any point as the writer of the code. So this is all sort of handled transparently for you. So are we ready to sail off, to drive off into the sunset in Rails land now with this new stuff? Well, not quite. The, the point I'm trying to make here is that, or one thing I want to get across here is that this is sort of a nascent library slash weekend project motivated by, you know, having, making it annoying to like, do caching, and it's hard to kind of separate your, your templates out, and also I'd like to be able to like render the same thing on the client and server. But there's still a bunch of stuff to be done, right. Like there's still some work to be done to like integrate this with existing like frontend frameworks if you want to add like a bunch more rich experiences on the client. And I want to get to the point, though, where we can have a good answer to these node.js libraries. Like this is a big motivation. Like I'm tired of node.js being, like, well let's just embrace JavaScript. Like, no let's not embrace JavaScript. Ruby. Ruby is better. So let's, let's get at them. But, the key take away is you shouldn't be sort of creating one version of your app that lives on the server and one that lives on the client. The ideal situation is move most of the stuff to the server but be able to like still create this thick client situation. So that's it. That's my talk. You can check out this library by going to RapGenius slash perspectives on GitHub, or if you're lazy, there's this bit.ly link. bit.ly slash RG perspectives. And also, as I said at the beginning of the talk, we are hiring. So if you're sort of interested in this kind of stuff or other stuff Rap Genius related, get at me. Hit me up on Twitter. Hit me up via email. Andy at RapGenius dot com. And I think we have time for some questions.