 Okay, so what I'm here today to talk about is modularity. There are two possible talks I could have given here. One of them is sort of a general Ruby modularity talk and that'll be awesome. And what I am giving today is a talk on sort of some case studies and rails around modularity. So I could go into more depth and give more useful techniques, but what I'm going to instead do is talk about some specific cases that hopefully will be representative of some things that you could do. I write a lot about the stuff on my blog, so if you want more in-depth information about specific techniques, pretty much everything I'm talking about today, I've written long blog posts about, so feel free to go check it out. So the first thing I would say about modularity is you ain't going to need it. What do I mean by you ain't going to need it? What I mean is that basically when you write an application, there are two ways to approach it. The first way to approach it is that you start with a bunch of requirements and then you go and you build your app and it takes like a year to build your app. And some people do that, take like a shorter amount of time, but they're still fundamentally taking a bunch of time to find some requirements and then after they have the requirements in place, they go and they build their app. The way that most of us build our apps is you make a basic plan, a basic idea, and then in short iterations, you build your app and then you gather the requirements rather than upfront, you gather the requirements from feedback from the actual application. So again, there's two ways to do this. The sort of waterfall approach is that you first gather a bunch of requirements, decide what your app is going to look like, and then build that. And the agile approach is that you actually gather your requirements from the app. So it's not just a lot of people look at agile and they're like, it's doing it faster. It's actually different than that. It's actually that you're flipping where your requirements come from. Instead of the requirements coming from your brain or committee meetings or grant letters, they're coming from real users telling you things about your application. And really what it comes down to is do you start with a bunch of requirements or do you start with a basic plan? Again, agile is also not about not planning. You know where you're going. You can't actually start writing something if you don't know what you're doing. So you do have a basic plan, but requirements look like this. And this is a real thing, the virtual observatory architecture. This is a bunch of guys who built a standards body purely to put together some standards for astronomical data. So here's this whole system. There's like 17 standards that they're working on in concert and kinds of stuff like that. And if you actually look at what their document for creating the body comes, talks about, it says that the objective of the virtual observatory is shown at the top of the figure to improve and unify access to astronomical data and services for primarily professional astronomers, but also for the general public. That's the basic plan, right? They should start with that. And then if you actually look, so I could stop there and like we could all have a laugh and they're doing crazy architecture, but let's look at what they've said they've achieved out of this. And I just looked at their last document. Of course they have really long documents with roadmaps and explaining what they've done. So here you can look at this and as more applications make use of the registry, the issues of service compliance stands as a major back barrier to user success. Recent monitoring and validations indicated that as few as 7% of registered standard services are fully compliant, right? So the problem is that if you start with a bunch of requirements, create these huge architectures and then just go to build them, you just aren't getting enough feedback rapidly enough and your requirements are just coming from the wrong place. So again, agile is this, you have a basic plan, you build your app and you do that a bunch of times, right? So you do it a bunch of times, building your app, gathering requirements, building your app, gathering requirements and then at some point in the future, that's where you do the modularity, right? Because if you do the modularity upfront, if you leave this talk and you say modularity is awesome, I'm gonna go in and actually start every application I build and spend two weeks figuring out what the architecture is, you will end up guessing wrong, right? It's not simply a matter of, I'm not a good enough programmer yet to do this right. It's a matter of it would be wrong, even if you're the best programmer in the world, to start out your application and think about how the architecture is going to work. That's something that you figure out later. And I think while Rails came to this party too late, Rails should not have come to this party in Rails 0.5, right? That would have been the wrong time for Rails to do it. Probably it would have been the wrong time for Rails to do it in Rails 1.0. Fundamentally, the way that you build applications at work is you start with a plan and you keep iterating until you get to a point where the lack of modularity is obviously hurting you, right? Where you could move quicker, obviously, if you start to decouple things. So what is modularity? When we talk about modularity, we're really talking about two things. We're talking about reducing assumptions in order to increase reuse, right? So when we're talking about modularity, a lot of times people talk about just the top one, the reducing assumptions. The reason why we're reducing assumptions is to increase reuse. It's very easy to go crazy with modularity and reduce assumptions everywhere, but if there's not an obvious reason why you wanna reuse something, probably you should wait until there is, right? Now, I'm gonna explain later that often those reasons come sooner rather than later and we just don't notice the signals. But definitely don't just go off and start making everything as modular as possible simply because you can, right? And one of the things that sort of hold us off from having to do this for good in the beginning and then for ill later is that we have a really dynamic language in Ruby. And I'll give you one really silly example that just demonstrates how you can avoid for a long time ever being modular, right? So let's say we have this class, class person, and it has a bunch of attributes and we have a name that goes and gets attributes. And when we ask for the attributes the first time we do hash.new. And then I go do this and it's like ycats equals person.new and then I go and grab the attributes, set the attributes to me and then I do puts my name, right? Now let's say I don't want to use a hash anymore there. Like I want to use something else. Let's say I want to use this Rick Roll hash that every so often returns Rick Astley for the key if it's name. So imagine I want to use this. Now we actually have in Ruby we can do this, right? It totally work 100% of the time without any major negative side effects. Go in, be like, ah, I changed my mind. I don't want to use a hash anymore, right? And now if I do this and I say attributes that name equals me some of the time you get back Rick Astley and it totally works, right? So that leads to alias method chain which is a really working approach that works totally for specific individual cases but is definitely not the approach that you need to take when you're building large modular systems, right? It just, it eventually breaks down because instead of building modularity in from the beginning what you're doing is your monkey patching modularity in. I don't know if anybody's ever described it that way before but that is fundamentally what the Rails 2 approach to modularity was. It was you can just monkey patch modularity in afterwards. Right, and that doesn't work. But it does work, right? It works. People say that all the time, it works. What are you telling me? I don't understand. So it totally works but it's definitely not, it starts to fall down in the face of reality. I think most of us probably have seen it falling down in the face of reality. So a good approach to doing this, a good approach to building an application is you have this basic plan, build that modularity. The appropriate time for the hacks like alias method chain is in the middle of place, right? You haven't really built your app, you're getting something into place. Don't bother trying to figure out what the perfect edifice is. Don't build castles in the sky just yet. Now feel free to do my hash, Rick Astley hash equals hash, Rick Roll, right? That's fine, that'll work fine. Later on, when that starts falling apart, what I'm here today to show you is sort of how you untangle that mess, how you deal with it and what you should be looking for. So again, what this is about is reducing assumptions specifically so that we can increase reuse and I'll just repeat. You wanna make sure that there's a reuse case that you're dealing with, right? It's very modularity in a lot of other languages as this very abstract concept that you just do because it's a good idea, right? And what I'm trying to say here before I go into real code is don't do that, right? That isn't good, that doesn't result, it's not only that it's a waste of time, right? Because it is, it actually causes you to get locked into architectures that are not sustainable in reality because you don't know what you're doing yet. A good example of this is Twitter. If Twitter would have started and tried to build an architecture up front, they would not have built what they need today to build a really good message queue. They would have built something else and then they would have been locked down into it. Now was it fun for them to switch from hack system to what they have today? Definitely not, but it would have been less fun for them to switch from perfectly architected edifice that had nothing to do with their actual users to what they have today. So just build what you need, make your users happy, they will tell you what they need and then you'll build the right stuff, which I'm gonna talk about now. The first thing about modularity is, in Ruby there are these things called constants. Those constants are essentially globals. Constant is a global variable, okay? In Rails 2.3 we did this, include action controller routing routes. By doing include action controller routing routes, what that essentially meant was include this global thing which has a bunch of helpers in it. If you want to change that, you have to change the global thing. So if you wanna have two different route sets and different parts of your application, you just cannot do it. It's impossible, there's only one router, it's global, that's how it works. This went all the way through to how the URLs were generated, how URLs were recognized. It wasn't simply a matter of like, oh, there's probably an object somewhere that this thing represents, I'll just make another one of them, that was just totally impossible. And that worked fine because most applications, all 2.3 applications have only one router, right? But what's better is to make the rule that you're doing this. You're saying include application.routes.routhelpers, because now instead of, even though it's functionally exactly the same thing, from a perspective of a Rails user, there is like a global concept of the application, it has a route helpers on it. But now fundamentally, if you want to go and do something else, if you're a plugin author who's doing some crazy engines plus plus, right now you can actually go and have a separate router. And I think the interesting thing about this is that we're not gonna see really good benefits from stuff like this right away. Because right away, the important thing for us, from the Rails 3 team is to actually make sure that it looks and feels almost exactly like Rails 2.3. That's the highest priority for Rails 3. So you're gonna see it feel a lot like Rails 2.3, like I don't understand, they did a whole bunch of modularity, where is it? And the reason why you're not gonna see it is because we've purposely hit it, so that it feels like the upgrade path is clean. But over time, plugin authors will be able to heavily leverage this. We've already seen the desert plugin, which is sort of an engines plus plus, go from some thousands of lines of code to 50 lines of code. Just because they no longer have to hack around these global concepts, things became more sane. And here's an example. Here's how testing worked. There was this thing inside of Rails, because in Rails tests, we cannot rely on one global router, right? There's a bunch of them that we're using to test various things in Rails. So there's this thing called with routing and it pulled out the action control routing constant and then it set it and then in an insured block, it went and reset the routes module back. So this is what you have to do when you have global state, right? You're forced to actually modify things globally. We could do this in tests, but of course in a thread safe scenario, this is just totally impossible, right? So basically what we have to do, and this hides it, right? You have a with routing block, so it looks all pretty. It's just like, oh, with routing, do. And then suddenly everything looks great. But in fact, what's happening is that we're having to munch globals in the background and that does limit what you're able to do. If you zoom in a little bit, you'll see that we're having to actually do like module eval do, concept, right? And we're yielding temporary routes and then we have a separate insured block. Like I said, that does this stuff. If you look in URL writer, which is the thing that generates routes, again, it does routing routes dot generate blocks, actually explicitly generating the routes from this global object. In Rails three, this changes to some instance of router dot URL for options, right? And as an example of this, you can do this. You can say rack apps equals action dispatch routing routes dot draw, and you can match hooray to hooray rack app and match sad panda to sad panda rack app. And then you can actually go in into a controller and say include rack apps that URL helpers instead of the normal ones. So the normal ones, of course, are included for you by default, but now you can include this other URL helpers rack apps and you can generate routes for it and all that. Now again, this is not something that you as a user of Rails are likely to do, but it's totally the fact that you can do it will open up the door for other plugins to do interesting things. And it also means, of course, that testing becomes conceptually simpler and the pass of the code inside of Rails become easier to understand. As you probably know from having looked in Rails, trying to track down globals is much harder than trying to track down instance variables or variables that are being passed around. You're like, what is this? Where was this set? Who set up this action controller routing routes thing in the first place? I don't know. Start like grepping around for the actual constant. That is much worse than like there's a router instance that's on the controller and it generates routes. So pretty much all rack apps that URL helpers does is it defines a router instance and that router instance is the right, it finds a method called router, which is the right one and it also defines all the named routes. So the hooray path is a method that's on a module that sits in rack apps that URL helpers. So the first rule of modularity, step one is to eliminate things that are global. And tests, in this case, are really the canary in the coal mine because when you first build an application, you usually are happy with global state. It's just one application, things are global, that's fine. And when you start to write tests, that's where it's like, wait, I don't want a foo hash here. That hits a database or that hits some global web service. I want my own fake thing here and it's not so much that I wanna mock it out as this code here doesn't actually care if it's a foo hash or a bar hash or a my fake hash, it just cares that it gets back some data from something, right? In Java they call this technique dependency injection. I try not to use the term dependency injection in Ruby to much consternation of some people because I feel like there isn't actually a hardcore technique here. It's just don't hard code things and instead of hard coding constants, pass things around. And that actually, if you look up, if you like Google dependency injection with a lot of words and a lot of Java code, that's what's happening, right? They're passing things around. So like constructor dependency injection means pass a class into the constructor instead of an instance of an object, right? Or setter dependency injection means set the class on a setter instead of hard coding it in. So it's a good technique and I just feel like the word is sometimes hard to understand and when you Google it, the code that comes up is much harder than the equivalent Ruby code. A corollary of this is don't, this is not always bad, but be suspicious of constant.foo, right? If you do, if you hard code constant.foo, then you can't actually replace constant with something else. But if you have variable.foo, then presumably you can actually change what is looking up the constant. So when you're trying to reduce coupling, a good thing to do is to make it so that there may be a default, right? It may be that there's an optional parameter that says class equals capital constant or you may set it in the constructor, right? At constant equals capital constant, but provide some way of overriding that, of overriding the default and creating something in. This actually includes constant.new. When I say be suspicious of any methods on a constant, that means in constant.new. Usually that is in fact what I'm talking about, right? You wanna make sure that if you want a different thing here than a hash or a different thing here than a factor like some AWS thing that you can actually swap it in. And again, I'll reiterate this one more time and then try not to say it again. This is not the first thing you do in an application, right? The first thing you do in an application is hard code constant.new, right? But later on, when you start running into real problems, that's when this is a good thing to look out for. Your pattern recognition in your brain should say, ah, I'm obviously doing this constant.new thing. That's the problem here. So bottom line is don't hard code. Here's an example of this. In Rails 2.3, Action Controller Base had this perform caching thing. So Action Controller Base.perform caching told the entire system whether or not you could cache, whether or not you should cache. And if you go in, for instance, JavaScript and Clue tag, that's not on Action Controller Base, it's on some totally other object that's saying if can cat or Action Controller Base.perform caching and cache, then do something, right? And page caching tests had set up. Action Controller Base.perform caching equals true. Action Controller Base.perform caching goes false. And this again is a perfectly good assumption that you can make. There is a state of your application, either you want caching or you don't want caching, right? But what if you actually want to turn off caching for a particular request? Like, I don't know why you would want to do that, but maybe you do, right? The fundamental thing here is that there's an assumption, which is that caching is global. Why would you want to do it? I don't know, right? I'm not actually sure why, in fact, you would want to turn off caching per request, but certainly it could happen and this totally forecloses that possibility. So Rails is preventing you from doing it without going in and munging globals, which makes your application not thread safe and is also scary. But the thread safe thing is like the practical reason why you can't do it. What we've done in Rails 3 is essentially, just made it a class attribute. Rails 3 class attributes can be manipulated on an instance level, so they get inherited throughout the process. So Action Controller Base has a perform caching, then every one of your controllers has a perform caching so you can override it on like, articles controller dot perform caching equals false and then every instance has its own instance to perform caching as well. And then inside of JavaScript include tag, instead of looking at the controller class, we say, hey, controller instance, do you have perform caching on? And then that allows the nice default of Action Controller Base dot perform caching equals false to work because that'll propagate all the way down, but if you want to change it on an instance level, that works. So this is really just saying, don't store things in global places, right? You could have a global setter for convenience, but don't actually use that global setter throughout your application. Instead provide a mechanism for, make that go into the instance and then provide a mechanism if necessary to modify it. I think what's really cool about this scenario is that it demonstrates that actually going in and ripping out global state doesn't always need to be really hard, right? Here we basically maintained exactly the same API that you had in Rails 2.3, although it is deprecated for other reasons involving the hope that we have of being able to have multiple apps in one process at some point in the future. But we didn't have to break the API to gain the benefit of being able to modify the perform caching setting on a per controller basis, right? It still works. And a lot of this is due to just Ruby being awesome, like Ruby being very flexible. So now in your index, you can say self dot perform caching equals false and everything works. And in your test, in our test we can change it to controller dot perform caching equals true and not have to worry about tear down, right? Because every instance is getting its own controller anyway and so all we're doing is we're changing on the instance. Is this useful? I don't actually know. But what I know is that at this point in Rails Lifecycle, which is many iterations down that two week cycle, we should stop making assumptions about what people are actually trying to use Rails for, right? And that's, again, the appropriate time for you to talk about it. The second big deal is objects. First thing, stop worrying about allocations. A lot of people are like, I don't want to use an object here. It's going to result in allocations, okay? Here's something that you do a lot. That's an allocation, that's an allocation. Stop caring about it, okay? Having an object that's foo as opposed to making a hash is not expensive. It might be expensive in a tight loop so don't be careful about tight loops. Profile your tight loops, right? But don't say, I'm not going to use an object here. It seems expensive. Object allocation in Ruby is essentially free compared to everything else you're doing. Even other cheap things, okay? So don't worry about it. Stop worrying about it. The second thing is that objects themselves present good opportunities to memorize. When you have procedural code, it becomes very hard to actually cache things because you have this huge method and where exactly do you cache it? You have to store it in locals and make sure the locals are having the right state. Objects actually provide very good opportunities to memorize and I'm going to give a case study here which is like most of the rest of my talk. So in Rails 2.3, when you say render something, so I'm going to teach you how the rendering stack works if you don't know it yet. In Rails 2.3, when you say that you want to render something in your controller, it goes through a few objects. So first of all, this isn't how it used to work. It used to be procedural code. Rails 2.3 was like, okay, objects are good, yay. But all the objects in this stack hard code, the fact that the templates are actually stored in the file system. So the controller passed a path, the view path set asked each of the view paths to look up a path and the template took a path. And the template eventually is an instantiation of an object that represents the actual file in the file system. The method that you call is def bracket path. So for instance, it has a hash of all the paths and it takes a full path. So foo slash bar dot html dot erb, right? And that load bang thing goes and loops through all the templates. So here, sorry, if I go back, first we load unless it's already loaded and then we get the templates in the path. So what load does, by the way, I don't know why but it seems like every one of my slides is pushed over a little bit so that's why some of these things look weird. So load bang goes and loops through all the templates in the path and does template dot accessible path, write a lot of logic that has to do with the file system here. And when you look at actually who's calling this, right? So somebody's calling this with a path, right? The controller is setting up. It's doing, this is a slide-ified version. The lines here are very wide so I've moved things into local variables. Maybe that should actually be how it would work in real life. But we get the locale out, we get the path out and then we start making a bunch of different paths and trying them in sequence, right? So we have paths like foo.html.erb, foo.html, foo.erb, right? And these are a bunch of actual paths on the file system that have to be checked in sequence because the abstraction is, hey, view path object that knows how to get templates out of the file system, give me an actual path and I will try to look it up. There are two big problems here. The first one is like I've been saying, path is an abstraction that you can't therefore replace with something else. Now there's nothing stopping you from cutting off the stack where the controller calls in and putting some other object in there that does something else but it has to basically pretend it's dealing with paths, right, because it gets paths so it has to pretend it's dealing with paths and that can get really crazy. Like if you want to do templates in the database which again is a, I don't know who wants to do this in real life but if you want to do this, you just have to pretend it's a path which isn't really the ideal way of doing it. So here's another example, right? There's like file.directory question mark file, yield create template. So the first problem is files, files everywhere. Can't do templates in the database. The obvious case for templates in the database, by the way, is some kind of CMS like WordPress where you just want to let people put the templates in and you don't want to have to write things to the disk. Like you're on Heroku and you want to be able to move things around. You just want to store the template in the database or you're on Google App Engine, right? So places where you don't have a right file system or you have a lot of boxes, right? So regardless of whether or not you have access to the file system, you might have like 20 boxes and you want to be able to let users update the stuff without a redeploy. Again, this is definitely not the domain that we live in, I think. We definitely live in a domain where a programmer is making everything and deploying but there's a whole other domain that lives outside of the normal Rails comfort zone where people are doing this en masse where there's like a box where you could put in a template and edit it and you could hit submit and the next time someone goes to your site it actually updates. And I think part of the reason why Rails hasn't really picked up in that space is because it's hard to do that sort of thing with Rails because PHP is basically reloading the app on every request, so it's easy to do that. Whereas Rails is caching a lot of things like I was showing and so it becomes hard. The second big issue with this approach is that it's hard to cache because we're calling in a path, right? We're calling in with a path which means there's a bunch of other people outside that have to build that path. So it's not easy to actually cache the process of going from I have some information and I want to build the path and get a template back because so many people have so many different ways of building the path. At this point it's way too late, right? So here's an example of something that uses this API. Yeah, it's tiny on purpose. And it's the method that figures out whether a template is a valid candidate for layout. If you zoom in over there you'll see that at the end of all that code does self.viewpass.find template and then it passes in a path and a format, right? But you can't like if you just cache at the endpoint which is what happens in Rails 2.3 it's too late to avoid the cost of actually converting all that information that's up that big chunk of code into something that we don't have to repeat over and over again, right? Another example is how you figure out how to get the partial template. And this if you zoom in after this code that goes and makes paths and munges strings and tries a bunch of things, right? The end game is just self.viewpass.find template pass. So again, we have a big chunk of code that we can't cache. The bottom line is that the logic to make the paths is everywhere. There's a bunch of it all over the place and my first attempt at making things faster in Rails 3 was literally just to go into all these places and make local caches. That actually really sucked, right? That meant that I had to make 10 caches where one would suffice. And the solution for making one cache is to have an object, right? You have one object that is responsible for that cache that you're always going through. So not only are allocations basically free, but when you actually go and you make objects, you provide better opportunity to cache things that would otherwise be tricky, complicated, or just way too hard to do in practice. So what we have in Rails 3 is this different stack. Sorry, this is Rails 2.3. I'm gonna go through it real quick. Rails 2.3 has this bunch of stuff, calls the view pass set, which calls the view pass, which calls the template. The end of that stack is template.new. So it makes a new template. Template.new takes a path. So template.new has to be aware of everything about how to actually get something from the file system and convert it into a template. The next step up the stack is view path takes a path. Again, it takes a path. So there's no opportunity here to like abstract it into something else. The next thing is the view pass set, which is a list of all the paths in your system takes a fine template, takes a path in a format, right? That path in a format again is a path on the file system. And basically what ends up happening is that there's all these things in Rails that call these different parts of the system. And you have to, in order to make this fast, which again is what I did in the beginning of Rails 3 for performance, you have to go into all these places and make sure that there are local caches that use class variables or something with mutexes. You have to figure out basically how to do this caching behavior in all these different places. And in fact, you think like, oh, I'll just go to layout lookup and that will deal with the action controller. In fact, you can actually have, putting caches in all the steps in the process actually provides better performance. So the right way to make this fast is to put like 15 different caches throughout the system to make it fast. And that's not great. The way it works in 3.0 is that, and this is actually the new object just landed like last week. The way it works in 3.0 is that the entire system is agnostic to the storage. So no part of the system actually cares where you get the template from. And the bottom of it template.new takes just source. So another object goes to wherever the template is actually stored, which would be in the database at the bottom of the file. Maybe you have all your templates in one file. Doesn't really care how it gets it. It just gives a source file. So template is this object that doesn't is able to just handle anything, right? Then the next step up the stack is resolver. And instead of taking a path, it takes four pieces of information. It takes a name, a prefix, a partial, whether or not it's a partial and a bunch of details. An example of each of those things is application could be like the name of the template. Layouts is the context, the prefix. It's not a partial and the details are format HTML. And the path resolver, which is a specific kind of resolver, takes that information and converts it into layout slash application.html dot whatever. But you can have another thing that goes in that place that goes and gets the information from somewhere else. So the path resolver is responsible for doing the thing you're used to having happen in Rails 2.3. It's just on the file system. That's definitely the right default, right? But if you wanna do something else, put something else in place, you can do that. And all it does is get this very same information. Sorry. So it just gets this exact same information and it can do whatever it wants. So a SQL template, for instance, could store all these four things in separate columns in the database and not have to do some path munging to make it work. The next step up the stack is view pass. It has the exact same API. So view pass is just an object that holds a bunch of these. So what you might know from Rails 2.3 that if you have an engine, that engine has templates that could, that exist at lower priority. So if you put a food.html.erb in an engine and there is a food.html.erb in your application, the application one wins, but lacking the one in your application, it falls back to the engine. So that's just, there's just an array of those things. And as I showed you before, in Rails 2.3, they had totally different APIs. In Rails 3, they have the same API. They just take the same name, prefix, partial and details. What we discovered really looking at the real way that these applications are built is that for the course of one request, the list of details is the same. It's locked. This isn't always true. You can actually change the format. You can say I specifically want XML. RJS wants HTML even though it's a JavaScript template. So there are some exceptions. But in the vast majority of cases, the list of details does not change throughout the course of an application. For that, the course of request, I should say. So what we've done is we've created this separate object called lookup context. And this is the performance piece. The lookup context starts by having the same list of view pass, but it's a per request construct. So instead of there being this global idea of view pass that exists for the controller, every instance gets its own copy of something that has both view pass and it has a set of details. And instead of just passing view pass, what I said before, those four things, we also pass it an opaque key. And that opaque key is basically just something that is formed from the details, but it's formed from the details in a way that would be fast. So instead of having the resolvers having to say, hey, I have these details, let me go look it up again, it can say, hey, did I already look this up for this opaque key, which is, again, a fast key? If so, I'm done. I'm just gonna look up further the things for this particular request. So instead of having to say, I have HTML, I have locale of English, this is a mobile request, these are all always the same for each request. So as I'm looping through and I'm rendering an HTML on a partial and a collection of a thousand elements in RJS throughout one request, if I'm doing all those things, I only have to go and make a key, the fast key once, and then everything else is just doing simple lookups. This looks weird, I'm just gonna draw it all in, I guess. So the API for lookup context is, it just takes name prefix and partial. Those are the things that differ across a request. You might render a partial, you may render a layout, you may render an actual template. So these three things are different, but the details stay the same. So you set the details once, we generate a key for the details, and then we pass all that information over to view pass find. Lookup context that, so basically, essentially what's happening internally here is something like this. You say, lookup context.updateDetails formats is HTMLJS, okay, I'll move. So you update the details for your request. You say, hey, my request is HTMLJS. And then you say, hey, go find me, index in the articles directory with false, and then the lookup context actually says, hey, here's the formats, that's the details that I talked about before, and this opaque key. Basically how it works is that there's the path resolver, and it has a cache, which is running, existing internally, has a bunch of keys, right? And for each key, it can actually drill down further to index, that's probably not really readable, index articles false, and it gets that object, index.html.drb, or for the second key, it can drill down index articles false, write the exact same information, but in this case, it's actually index.js.rjs because the details say I want JS. If it can't find the key, then what it does is it does a full lookup, which is slow. It's slow, but exactly what happens in two, three all the time, right? And it stores the result in that key. So basically what ends up happening is that after the first time that you lookup index articles false for formats.html.js, every subsequent time all you're doing is a hash lookup for index articles false, which is a fast hash lookup as compared to a hash lookup that includes this other hash inside of it, which would be slower, you would have to make a key out of that all the time. So in Rails 3.0, the whole thing is storage agnostic. The main thing that I'm talking about here is that we were able to put in an object there, the lookup context. So the thing I'll say is that all that is, I think interesting, but not very important to what I'm trying to say, which is that sometimes putting in objects is useful. Sometimes putting in objects can allow you to get performance where it was theoretically possible before, but very hard. So not only should you not worry about objects being slow somehow, you should think that objects can allow you to do caching and performance work that would otherwise be annoying. So the second rule is use objects. Again, for objects, tests are the canary and the coal mine, once you start writing a situation where you wanna swap out an object for something else where you have a bunch of code in your tests that looks like really it's just an object, right? Like all these things represent one concept, make an object. We're writing Ruby, not C, right? So we have objects, make use of them. The corollary for this is, don't hard code a specific class, right? If you go and make an object, that's actually nice, that could work well, and it will allow you to improve performance, but ideally you're not gonna hard code a specific class. So in this case, we have a pen view path and you can pass in your SQL resolver in there, and that allows you, the user, to put in something else. We've kept the same 2.3 API of a pen view path string. If you do that, then it goes and makes the path resolver for you. So from the perspective of your user, it looks exactly the same, but from the perspective of the internals, it allows you to drop in anything that you want. I have very little time, but I'll just do one more rule which I've talked about a lot on my blog, so I don't wanna go into, which is use modules, and I'll show you what I mean by that. So here's a class person, it speaks a word, puts the word, yay, and here's how you would, let's say I wanna yell the word now. The way, the sort of traditional way of doing this is that you say class person, alias speak to old speak, and then define it, and then call old speak with word.upcase. This is sort of like the standard thing. And the way people used to mean when they said use modules is that they would make a module with an included hook, that in the included hook did a class eval to do that alias, and then this other method would be included. In Rails, a lot of people would actually have made a separate module called instance methods, which they would include in. I don't really understand that at all, but that was common. Obviously, if you're including a module, that method will get included. And then in person, you include that, and then that works, right? But that isn't what I mean when I say you should use modules. What I mean is that, sorry, and this is how you would actually make use of that. You would say, I'm sorry, this is how you make use of it. What I mean when you, when I say use modules is don't put implementation that you intend for people to be able to override in the body of the class. Make a module with a base implementation. Here's speaker, right? That has that method speak word. And then if you want to actually override it to provide yelling capabilities, you make a module yell or you define speak again. But here you can actually call super because when I include this module in, now that module is underneath the other module. So the other module is right on top of it. And I can call super without having to do any kind of crazy aliasing. So Ruby already has the feature of alias method chain built in. All you have to do to make use of that feature is start off by not putting things directly in the class and everything works, so do that. Again, this is not the first thing that you should do because the first thing you should do is make it work. But once you start noticing that you're overriding classes a lot, literally all you have to do is take the body of the class, put it in a module and include that module in, or even say include module.new, do put the thing there that would work, you can abstract that. But the point is that if you put all your implementation in a module that actually automatically provides a mechanism for overriding that is as powerful as aliasing method chain with none of the drawbacks. So we've done this a lot in Rails 2.3. A corollary to this is use access for concern. Access for concern is a mechanism for making it easy to declare both included hooks and dependencies. So an annoying thing that happens when you start using modules a lot is that, for instance, the layouts module depends on the rendering module, cannot work on its own. Access for concern gives you a mechanism for saying, hey, if somebody ever includes layouts, make sure that rendering is there first. So when I call super, it actually works. It's very small API. It's literally just extend access for concern. And then when you say include into that, it defers the include till when you include the module, includes that other module. Anyway, the point is that it provides good resources for really complex module-based architectures, but you probably aren't gonna get there, right? What you're probably gonna happen is that you're gonna notice that you're doing alias method chain sometimes and you're gonna wanna do modules instead. One last thing about modules is it's pretty clear to me that this technique is not well known because everything in kernel is a module and RubyGems, when it overwrites require, it does alias method chain. It does alias require without dependencies and then it defines and then it calls the old one. When all it really has to do is make a new module called RubyGems require, include that and because kernels are ready a module, they can just call super to that. So I think if you don't know this technique yet, definitely look at it more, like try to understand how Ruby's inheritance system works and the main thing that it does for you is it reduces the brittleness of alias method chain. So you get the benefits of being able to isolate code into their own little buckets without the drawback of like, oh, there's code running that's doing weird things. Now I have two things doing the same. I don't know what's going on, right? Modules are actually designed for this. Thank you, I have this live for questions but I don't think I have any time, so that's all. Thank you very much. Thank you.