Decorator

Summarized using AI

Presenters and Decorators: A Code Tour

Mike Moore • April 23, 2012 • Austin, TX

In the session titled "Presenters and Decorators: A Code Tour," Mike Moore explores the concepts of Presenters and Decorators as design patterns in Rails applications, emphasizing their role outside the traditional Models, Views, and Controllers framework. Moore starts by sharing his personal nervousness and excitement about discussing topics he is passionate about, particularly the pain points developers face with complex Rails applications. He highlights that Presenters can alleviate these issues but should only be employed when necessary due to potential overhead.

Key points discussed include:
- Definition and Purpose of Presenters: Presenters are advanced constructs aimed at easing pain in complex Rails applications. They should not be considered until a significant pain point is recognized within the application structure.
- Real-Life Example of Complexity: Moore illustrates the complexity developers may encounter when trying to understand and modify code that employs advanced concepts like components. He uses a recent experience at work to stress the difficulties new developers face within complex paraphernalia.
- Advantages of Using Presenters: Moore details how an Author Summary Presenter can encapsulate logic that enhances the organization of a template, separating presentation logic from business logic effectively, thus allowing templates to be simplified and easier to understand.
- Decorators: He briefly introduces theDecorator pattern, explaining its flexibility in enhancing functionality without altering the original objects or models. Libraries like Draper and Active Decorator can facilitate the implementation of this pattern.
- Serialization Challenges: The presenter discusses how serialization of objects can be cumbersome and presents the solution of creating a separate Serializer class, which can be unit tested separately from the view logic.
- Presenter Spectrum: Moore suggests understanding the spectrum of presenters, where they may lie closer to model or view, depending on their complexity and role.
- Real-world Applicability: Throughout the talk, Moore emphasizes that while presenters can enhance maintainability and organization, they also introduce dependencies that developers must be aware of. He shares a framework of rules regarding when to deploy presenters, based on project pain points.

In conclusion, Moore encourages viewers not to rush into using Presenters but to recognize when their implementation can lead to cleaner, more maintainable code. He reiterates that the presenter pattern is about simplifying views by delegating responsibilities to specific objects, thus promoting a clearer separation of concerns within the codebase. The talk blends theoretical knowledge with practical insights, aiming to improve developers' experience in Rails application development.

Presenters and Decorators: A Code Tour
Mike Moore • April 23, 2012 • Austin, TX

Presenter and Decorators are design approaches that can be used in Rails applications outside of the standard Models, Views and Controllers. These approaches are becoming more and more popular as teams search for new ways to identify and manage the complexity within their applications.

In this session Mike Moore will defined the Presenter and Decorator approaches using simple and clear terminology. Common design problems in Rails applications will be shown using real-life code examples and refactored toward Presenters and Decorators. Code will be improved and strengthened by identifying and respecting the dependencies within large applications.

RailsConf 2012

00:00:24.789 First off, I am incredibly nervous and I don't know why. I've spoken before, but for some reason, I'm really nervous today. So, you know, take it easy on me. Thank you. I'm doing great, thank you, yes. Thanks for the public affirmations. I'm also incredibly excited, though. This is a topic that I've had thoughts about rolling around in my head for a while, and I'm really excited to have gotten it out and to share them. I hope that it doesn't suck.
00:00:40.460 So, show of hands: how many people have used presenters in your Rails applications before? Wow! So, like a third of the audience. That's terrific. All right, let's go ahead and start. This is me. You may have seen me online or not; it's okay if you haven't. My name is Mike Moore and I go by the handle BlowMaj, or you can call it BlowImage; it doesn't really matter as long as you can spell it correctly.
00:00:54.680 First off, I have this small disclaimer: Rails is a big tent. We have lots of divergent ideas within the Rails community. We have Test Unit and we have RSpec. We've got ERB and we've got HAML. So, what I'm going to show is one approach. There are flaws in it, like there are flaws in everything, so you don't need to be that person who calls out every little trade-off. Please be nice.
00:01:15.990 Also, I'm going to try to show a lot of code. Although I'm looking through my slides, I'm nervous I'm not showing enough code, so you may have to squint just to see the code. So, move on up if you can.
00:01:30.250 First off, I believe presenters should ease pain. Presenters are a more advanced approach within your Rails applications and they're specifically designed to solve a problem. If you don't have that problem, then you probably shouldn't use them. Presenters are not something you should use early on; you shouldn't be looking, as you're developing your application, for when you should use a presenter.
00:01:55.620 It is my firm belief in pain-driven development that you only do something new outside of the typical approach when you feel pain. Because if you don't follow that, then you're going to be part of the problem. Before I get into talking about presenters, I want to share with you my pain. I'll tell you a story.
00:02:12.640 Recently, I was working at a company where a new developer joined the team. He was a pretty good developer, familiar with Rails. He was given a task to change some display on the website. It was in the dashboard, which is kind of like one of the home pages for users. Now, the app at the time we were working on was really important.
00:02:37.720 Because it was so important, the developers who originally wrote the app demanded an advanced approach for dealing with the view. So, they built this approach they called components, and in their minds, they were years ahead of everyone else. They thought that in a few years everyone would be doing this.
00:02:56.380 The new developer goes off to change the dashboard. He goes to the dashboard index template, and this is all he finds: render component. Well, that's kind of odd. Okay, that's all right; let's look into the application controller and see if we can find where that's defined.
00:03:09.680 Sure enough, in the application controller we find render component, but this component renders a partial shared component and it passes in the component as the argument. All right, that's cool; we can do that. So, they go to the shared component and, I kid you not, this is the moment they ask for help. Seriously, why? Does anybody have any ideas what's going on here?
00:03:27.510 It's actually in a helper, and because of the scoping, the helper is being called within the templates and not the application. So, okay, good, we're back on track; we got some help at this point.
00:03:41.670 In our render component, we get an object, and if that object doesn't exist, we just return a string. If it does exist and it has a render method, then we're going to go ahead and call that method, passing ourselves the template to that render method. Otherwise, we're just going to call to string on it and be on our way. Now, pretty much every component has a render method, so we immediately start looking at, well, what actually is this object.
00:03:57.380 It might be a good idea to go back up to the dashboard controller and see exactly what is being put in this component variable. So, we go to our dashboard controller, and sure enough, here’s our @component instance variable, and we're getting it from this dashboard component builder object. We're calling build on this builder and giving it the current user.
00:04:12.660 Now, there's a builder involved, not a component but a builder! So let's go and take a look at that. We look at our dashboard component builder and sure enough, here's our build method. It's passing in a user. All right, that's pretty cool.
00:04:28.970 Now this dashboard is going to go and get a list of recent posts from the looks of it off of the user, and then there it is: our dashboard component. Thanks! Finally! All right, so we know our dashboard component has post items, and then it’s got some authors’ status component.
00:04:46.440 Not sure what that is, but we know that the change we want to make is in the display of the posts on the dashboard. So we’re just going to look at the post item component builder. We've got a builder calling a builder, and when it’s calling this post item component builder, it's not calling build like the other builder that we just saw, it's actually calling build list.
00:05:02.640 So hopefully we'll see a build list. We open up, and I also want to know here is that our component builders are all in lib right now, so just make a note of where they're at. So we find our post item component builder; remember we're looking for a build list. We don't see build list, but we do see build, and we're thinking that's probably useful because build is probably getting used as some convention.
00:05:14.130 So build is going to take a single post, and we're going to go ahead and generate a whole bunch of stuff off of this post. We’re going to look for a description, we’re going to look for like a view count. We’re going to call that views. We’re going to have another variable for comments. So we're kind of like going up and down the domain hierarchy pulling some data out specifically for this component.
00:05:33.530 So we're good to go. But you know, there is still no build list, and I wonder where that’s defined. I'm guessing it's going to be defined in the component builder that our post item component builder in the lib directory is inheriting from.
00:05:47.210 Now notice that component builder lives; it's no longer in lib; now it's in app/components. Not sure why, but there it is. We found it, and there is our build list! So it looks like build list is going to check to see if we respond to build. We're going to call it.
00:06:03.600 So this is great! We’re going to take all of our items, we’re going to call a post item component for each post that we're given; otherwise, we’re going to raise an exception. Okay, so we’re good to go. Let's go back to the post item component builder. We’ve got a lot of stuff going on here, right? But at some point, we’re going to build a new post item component, which is what we’re looking for.
00:06:13.750 We want something that has a render call, and we're hopeful that our post item component object has indeed a render call. So let's look for that again. Post item lib component builder is in library; post item component is in components, of course. Now, we don’t actually have a real implementation; we're just taking a constant and setting it equal to some new object that is getting defined off of partial component. We have a string, and then we've got a bunch of symbols, so obviously no render call here.
00:06:30.140 Let's go ahead and look at partial component. I'm hoping here that we're gonna get around to that render call. I don’t see a render call right off the bat, but actually, okay, these guys were smart. This is new metaprogramming, and this is incredibly difficult to understand.
00:06:46.120 So yeah, this is really important in your app, and it's so important they're going to go ahead and dynamically create a new class, and in that class, if you look at the second defined method, they’re going to define the render method right there. So we’re good to go! So now we’ve defined off of partial component that’s going to build a new class, and going back to our post item component. Post item component is now a class!
00:07:05.580 All right, it's not a class in the way that we typically see it, but it’s still a class. And we know going back here that we have a locals method, and that's going to be all those symbols I get passed in, and it's going to have a render call. So we're good to go! We know that render is going to be using the very first thing that was passed to define.
00:07:20.490 So, again on line two in define, we're gonna send in that partial string, and then we're gonna have all the variables. So let's go back and look at our post item component. Sure enough, we've got the partial string and then a whole bunch of symbols. Okay, good deal. Now, remember we’re going to call that render, and it’s going to bind, and actually render a partial.
00:07:40.030 Well, here’s what we’re getting: our partials! So now, okay, I think we have it! We’re gonna go ahead and look in posts/post_item looking for a partial there, and we find it, but it's in HAML. And I hate HAML! Right? It just keeps getting better and better. And remember, this was his second day at the company; so welcome to the team!
00:07:58.300 Right, they're gonna be very productive in our codebase. Won't someone please think of the new developers? And I'm not talking about newbies, right? We should think about the newbies, not just people who are not familiar with your application. This is a hairball. There’s no excuse for this; this is complete and total hostility towards people unfamiliar with your app!
00:08:16.080 There is an overhead to going outside of what Rails or any framework will give you by default. Right? So if you're gonna pick some advanced approach to try and move some complexity around within your application, please be very cognizant of the trade-offs you are making.
00:08:32.780 It's terrible, in my opinion. You should only use presenters when the pain that the presenters solve is exponentially more than the cost. Yeah, that the man—see, I'm nervous! I told you guys I was nervous. What’s that? All right, yeah, we’re good to go!
00:08:49.210 Yeah, so you want more benefit than the cost, right? There’s gonna be a maintenance cost; you want more benefit! Now, I firmly believe that the view in your model-view-controller in Rails apps is a total bike shed. Does anybody else share that view that the view is a bike shed?
00:09:05.340 I'll tell you why I believe this: because a bike shed is when you have a lot of people who are going to be arguing about some cosmetic detail of what's actually going on, but it doesn't have any real bearing on the system as a whole. And so you expend an awful lot of energy—right—a lot more heat than light discussing these details.
00:09:21.960 When you know the things that really matter to the application, the things that are maybe more on the back end or more germane to the actual user experience are ignored. The thing is, is that pretty much once you get familiar with Rails, you can have an opinion on the view, and so we like to argue about the view quite a bit.
00:09:38.060 So fair warning! Working in the view, arguing about the view, trying new things in the view, it’s a total bike shed and when you do something new, again, there’s hostility towards developers on your team.
00:09:54.600 So that said, let's look at kind of a typical summary example for presenters. So here's some, I want to say, here's some good code, but there's still some bad code. All right! So let’s say, for instance, you've got a site where some content that users are creating.
00:10:07.440 And somewhere in your navigation you've got a partial that's going to just render some author details. Now, this is going to be on your navigation, it's going to be used on every single page on the entire site, right? It's either in the header or it's in the footer, and you need to grab out information about whoever the current user is and display that.
00:10:24.840 So this is a really common approach and I see this a lot in Rails applications. This is just a partial, right? So you could be showing foos or widgets or whatever, but we know that we have a current user, and if you have a current user, we're gonna display that information.
00:10:41.270 So first off, we're going to get all the counts of the posts and the videos, add them together, and we're just going to show that in a list item for published content. And then we’re going to look for unpublished posts and unpublished videos, and if you have any of those, we're going to show that in a list item for drafts or unpublished posts.
00:10:57.970 Because they couldn't fit it all on the same page, and I wanted to leave the bottom third free for everybody, there’s more! If you're an admin, then what we're going to do is we're going to go to like the organization or the site that that user belongs to and we're going to look for all of the unapproved posts that are not yet out there, and we're going to go ahead and display that.
00:11:14.720 And maybe we’re going to display a link, but I didn't want to clutter it, right? So, we’re just showing counts. Now, if your count is zero, we're not gonna show anything. Right? So has anybody seen this approach before in your Rails views?
00:11:29.320 Okay good! In your templates! This is like nails on chalkboard, right? Just makes my teeth hurt. I don't like this! But we're kind of out of solution. It’s like, what are you gonna do, right? I mean, so let's throw some ideas out. How do you typically solve this?
00:11:43.030 You’ve got like all these accessors, they're going out there iterating over your models and a partial. How do you not do that in your templates? What do you normally do, anybody? Yeah, you put it in the model! Yeah, absolutely! So we could do that, right? But like, what needs this?
00:11:59.820 Really, does anybody in your domain really need to know the published count of posts and the unapproved count or draft count of posts? I mean, is there any reason for your domain to have this complexity in it? I mean, I don't know. My user model does an awful lot and this just seems like, you know, like a straw that would break the camel's back.
00:12:14.640 For example, your domain models are used in a variety of places. Let's say that you've done a really good job and you've extracted all of your domain models into an engine, and you're sharing that engine across maybe your administrative site and then your customer application, and then you're also using that for like your background jobs as well. Right?
00:12:30.250 Does your administrative site really need these methods on the user? Or does your background job really need this? Right? This isn’t really domain logic; this is really kind of front-end presentation logic here.
00:12:43.310 So I posit that the user model is not a good place for this; we shouldn't be just jamming stuff in the user model. Alternatively, we could create some other thing that could host this, and we'll go ahead and throw it in lib for now. This is just an author summary. Right? The author summary takes a user and it knows how to query that user for the necessary information.
00:12:58.620 Now what we have here is a new entity within our application that can own this behavior, but more than that, we have something new that we can test without confusing the rest of the user. Right? And then our front-end presentation can go ahead and take use of this.
00:13:12.850 And I can't, like, this approach! This is typically when people talk about a presenter; this is what they're talking about. Talking about something else that knows about your models, that can present it in such a way that your templates are expecting.
00:13:28.520 So let’s take a look at how we can use that. Let’s go back to our shared partial. Let’s go back — this is what this was before, right? So we're pulling out published count, we're pulling out draft count, we're pulling out approval count if we're admin.
00:13:41.270 Here was the object that we’ve created, the class we’ve created, and here’s the partial and it all fits on one screen now, which is kind of nice. I like it when we can remove code by refactoring. So now, our partial goes ahead and creates a new author summary for the current user.
00:13:56.460 Then, it’s going to query that summary for publish count, for draft count, and for approval count. Now, we don’t care whether or not the user is an admin in this partial; we just need to know whether or not we’re supposed to show this list item. The logic for whether or not to show the approval count, remember, is back here in this presenter.
00:14:11.650 Right? If we’re not the author admin, then we’re just going to return 0, and if we’re 0, then we’re not going to display that interface. So this is nice! Now we’ve created a contract for our templates to expect, and we've taken all of the behavior and put it somewhere else. It’s not in the templates, and that’s really important to me.
00:14:27.840 Because as your team grows, you’re gonna find that there are more people that are going to be in your templates than just developers. You’re gonna have the designers that are in your templates who are trying to change the DOM, start to get CSS to work. You’ve got like the user interface consultant who doesn’t really code, doesn’t really do design work, but he's in there mucking around anyway.
00:14:42.830 Right? So, I like this approach! Now, we don't need to do this ourselves; there are libraries that will help us build these. Draper's one, which I think probably most of us have heard of. There's another one called ActiveDecorator, which is by, is it Akira Matsuda? I forget his surname or whatever.
00:14:58.700 But this is a really, really terrific decorator library. I really like it a lot! This is what the user decorator would look like here, right? So it looks pretty much the same, except we’re not passing in the user. It’s just kind of found by itself in some way. Not really sure how.
00:15:13.460 It’s not a class anymore; it’s a module, which is fine because we can still unit test the module without doing a full integration test. And if we look at our template, it’s actually smaller because we’re not creating that presenter. That author summary; we’re just going to use the current user.
00:15:33.630 Now, the way this works is that every time you have an instance variable that's passed into a view, whether by a render or through a partial render, what this library does is it looks at the class name and it looks for that class named decorator.
00:15:48.700 And if it finds that, if that constant exists, it just includes it on the object. So here we get to define application-specific logic for our models without actually putting it on our model. Again, in the case where you’ve got a Rails engine from an admin interface to a customer application and then also on your backend jobs.
00:16:04.750 Not all of those applications need this logic; this is really specific to the front-end of this application. So, we just put it on the front end and leave it off of our model. So, yeah, it helped! Right? Isn’t that cool?
00:16:15.260 Yes! All right! Begging for applause! I love it. All right, so let’s look at another example. Typically when people talk about presenters, serialization always comes up as well. So we look at our post controller again, got our current user. User has many posts. We’re gonna go ahead and show this post.
00:16:33.280 And then if we’re in the HTML format, we're gonna let the template just, you know, use it. Otherwise, if we’re doing JSON, we’re gonna actually render our post as JSON.
00:16:56.420 Now, when we do this, the render is going to call that to_json on the object, and it’s going to return a hash that’s serialized to JSON and then returned back to the user.
00:17:09.740 But if we want to change that, what we’d have to do is we’d have to say json post.to_json and then like add that hash of what the structure we want it to be, and that’s kind of a pain. There are ways around it.
00:17:22.720 You can have private methods that define that hash structure for how you're gonna serialize, and I've used that before. Another thing is people are starting to use actual templates to define the structure of the JSON, so you've got Jbuilder. And here I’m showing Rabl, which is kind of cool.
00:17:36.500 So you know we say our object here is a post; we want the ID and title attributes, and then we want author name, but we want it to be called author within our JSON. And then, if the current user is an admin, we’re going to have a couple of other pieces of information.
00:17:52.000 We’re going to go ahead and expose the view count, and then we're going to expose the popularity index as views and popularity. Right? Okay!
00:18:06.530 So Rabl is kind of cool, but I look at this and I think, how am I going to test this? Right? And the only way to test this is to do an integration test.
00:18:21.130 So in my integration test, I need to set up a controller, I need to invoke this action with the proper format, I need to render this template given an object, then I need to parse the results of that rendering in JSON, and then I need to go ahead and do all my asserts on that structure.
00:18:30.890 Right? So if it comes back as author name and not author, then I have to go through all of that in order to know whether or not this worked. That’s an awful lot of work, and I don’t want to do that much work just to write the test.
00:18:46.310 Right? This is one of those things; if I’m starting out and I need to serialize something, I don’t want to build this because this is a pain to test! So we can use another presenter-type approach.
00:19:01.150 With that template example, you can actually just drop the render JSON as well because it caught by their template; it'll use that template for you.
00:19:20.730 All right, what we can do is we can create a new class, a new object here that's going to be a serializer. We’re gonna go ahead and take the post and whatever the current user is because we need the current user to determine whether it's an admin and if it is an admin, then we’re going to show some different, a little bit different data.
00:19:34.370 So we get our post, we get our user, we call .to_json on this, and if we're the admin, we're gonna get the full detail; otherwise, we’re just going to get the summary.
00:19:49.890 And then there you can see the definition of full detail in summary. If you're in the back, you probably have a hard time seeing summary; it’s just a hash; don't worry about it.
00:20:04.510 Using this is pretty easy; we don’t really change our controller that much in our JSON format. We just create the new serializer and call .to_json on it, right? And this is a really great approach if you're doing a lot of different types of serialization.
00:20:18.320 Let’s say maybe you've got like different versions of an API and you want to return a different structure to the different versions of the APIs. This is a terrific approach.
00:20:33.030 Not only can you vary the format of the data that’s being passed back, but you can test it and you can unit test it, and that’s the greatest thing, right? Because those tests are gonna be fast.
00:20:47.390 Right? It's all so simple! You can just look at this implementation and you can pretty much follow that! You know what that’s doing, right? It’s not all hidden around!
00:21:08.860 There's another really terrific library called active model serializers. I love this, right? We can do the same type of thing, but we can use this library, and it cleans up our approach just a little bit.
00:21:24.580 We create our post serializer, but now we're going to inherit from the active model serializer, and then we have our attributes. We have our author name being mapped to author, our view count and popularity index being mapped to views and popularity.
00:21:38.579 And even though we're not passing in the user, we have user available as scoped, and this is one of the things that the active model serializer does: it’ll go out and grab the current user of the web request and give it to you as the scope variable.
00:21:52.210 That’s pretty sweet, right? And also looking at this, this DSL looks a lot like the Rabl template too. So if you like Rabl because you think it's more readable, this serializer looks really similar, but you can also unit test it.
00:22:08.290 Our post controller is actually, we're back to where we started, right? We're not invoking that serializer, and it's because what active model serializer does is it looks for the rendering of JSON.
00:22:22.580 And instead of calling .to_json on the object, what it’ll do is it’ll look for that, again, that class name prepended with serializer. If it finds a constant with that, then this is gonna go ahead and use that API on that constant.
00:22:35.280 So, this is a really neat indirection. We can create our serializers and then we don’t have all this kind of like injection code to use them. So it’s pretty cool!
00:22:49.500 I like it! And this is, I think, where most people stop with presenters. But I hope I’m not alone; is that all this really what presenters are for?
00:23:05.000 Is this why you guys are here? Is this really the problem you’re having? Are you having a problem serializing your models into JSON, or are you really in your complex views? Are you having problems with having to put lookups on your user model to find posts and some navigation elements somewhere? I mean, is that really the end of it?
00:23:25.320 This isn't for me! If I look at the applications that I've worked on over the last years, the problems I've had in the view are not because of this. The problems I have are because of the inherent complexity in the view layer.
00:23:42.230 The view does an awful lot; there’s a lot of complex behavior in some of our views that I’ve worked on, and this doesn’t really solve my problems for me.
00:23:55.700 So maybe let’s take a step back now. It’s really hard to have a definition of presenter, and this has been a hotly debated topic over the last year or two.
00:24:09.580 Does anybody have a good definition for a presenter that they could share with us? What is it?
00:24:17.760 An object that groups together objects to present them. Okay, anybody else? Bless your views! Be dumb! I like that! Yeah, that's what I'm going for! That’s how I judge whether or not a presenter is being helpful; it’s how dumb my templates could be.
00:24:29.650 All right, so let’s take a look at some design patterns! Right? We already talked about this before: the decorator. Right? This is actually a Gang of Four design pattern. The definition of this design pattern is it attaches additional responsibilities to an object dynamically.
00:24:50.330 Decorators provide a flexible alternative to subclassing for extended functionality. That’s kind of cool! That’s actually what we did! We didn't really subclass our post model, right? We dynamically added new functionality either by creating a new wrapper/proxy object or by dynamically injecting a module into that object and using that functionality.
00:25:04.700 So that sounds like what we've shown so far. There's another pattern, though, in the Gang of Four, called the mediator. Now, the definition of this is it defines an object that encapsulates how a set of objects interact. Mediator promotes loose coupling by keeping objects from referring to each other explicitly.
00:25:19.440 And lets you vary their interaction independently. Now, this actually sounds more like the problems I have in some of my complex views. I would love to not have these concerns being laid out explicitly in my templates. Right?
00:25:35.590 I’ve got a bunch of objects; they need to interact together to get some sort of behavior that my application is looking for. A mediator seems like it could be a good fit! Now, in practice, not every presenter is a mediator, and every mediator is a presenter! I think we're getting closer!
00:25:51.039 So we have a spectrum. So if you have a double jazz hands, that’s okay! Anybody who knows me knows that this is coming, and I apologize to everybody! All right, we have a presenter spectrum. On one end, we've got the models; on the other end, we've got the view.
00:26:06.500 We're in a model-view-controller framework, right? We've got models and we've got views! The presenters can go anywhere in between models and views.
00:26:22.130 On one end, you have a decorator; a decorator is specific to a model. A decorator is not going to decorate more than one model at a time.
00:26:37.020 Now we’ve got something else! The thing that I'm looking for is more on the presenter side or more on the view side. Right? Now I may be kind of old and critical, but five years ago, that was called presenters and we’re not really called presenters anymore.
00:26:52.430 So now we have a spectrum, kind of like autism, right? You can be on the spectrum but not actually have autism. You can have some other thing! That’s how presenters are to me, just like autism!
00:27:06.230 All right, so let’s take a step back and go back to where this idea of presenters came from originally within the Rails community. It comes from a blog post by Jay Fields, where he calls out the presenter pattern as it specifically relates to Rails.
00:27:21.250 Now, if you click on this link in this article, he calls out Martin Fowler’s work on Martin Fowler’s BlueKey about enterprise application architecture work that he had.
00:27:37.470 And what Martin was doing was he was taking notes on some of these patterns that didn’t make it into his book. I hope that most everybody is familiar with the Enterprise Application Architecture book.
00:27:54.250 This is the book that David Heinemeier Hansson followed when he created Rails. This is why we have a library called ActiveRecord, because ActiveRecord was in this book.
00:28:07.960 Now, on his BlueKey, there’s a page on a pattern called a presentation model. Now this presentation model has a description; it may interact with several domain objects.
00:28:24.340 But presentation model is not a GUI friendly facade to a specific domain object. Instead, it is easier to consider presentation model as an abstraction of the view that is not dependent on a specific GUI framework.
00:28:39.520 Well, several views can utilize the same presentation model, and each view should require only one presentation model. In the case of composition, a presentation model may contain one or many child presentation model instances, but each child control will have also only one presentation model.
00:28:54.750 So that actually seems a little bit closer to the view side of the spectrum, and in fact, on Jay Fields’ blog, he even makes this statement paraphrased: that the presentation is a representation of the state of the view!
00:29:12.970 Right? So I posit that a true presenter is a presentation model, or as close as we can get to within the Rails community. If you've been in the .NET world, you may have heard of it as a view model.
00:29:27.930 Or if you’ve done anything in Mustache or Handlebars, you’ve got your view model. Right? We’re basically talking about the same thing. We've got an object whose responsibility is to represent the state and behavior of the view.
00:29:42.150 Right? And we bind that to our template to get the behavior we’re looking for within our application.
00:29:57.050 So let’s take a look at a slightly better example! I know I don’t have much time left, so I’m gonna go a little bit quickly. Has anybody seen code that looks like this?
00:30:07.740 Just like, just makes your eyes hurt! Believe it or not, I actually had to clean this up a lot! Originally, I pulled this off of an open-source Rails project, and there were no partials.
00:30:22.920 So I had to look at what the actual HTML was doing, give it a name, and then put a render partial call in there. And actually, it's only by doing that that I could figure out what those conditionals are doing.
00:30:38.460 We’ve got like these three main conditionals where we’re looking at a course object; we’re asking if it’s available, whether self_enrollment returns a truth evaluation, enrollment returns the truth evaluation, and we either don’t have a course enrolling object or it's not active.
00:30:54.320 If all of those conditions are met and we don't have some value jammed into our session list, then we’re going to go ahead and render some join course user interface. Right?
00:31:09.180 Same type of thing! If we have an enrollment, and it's active, and you can self-enroll, and we don’t have that weird session thing (which I'm not really sure what that session thing is doing), then let’s go ahead and render the drop course partial.
00:31:21.980 Otherwise, we're going to pull that value out of this session, and we’re gonna display some temporary access interface! So again, this is hopefully not unheard of. I hope that I'm not the only one who has worked on a piece-of-crap application, right? This is terrible! And it's par for the course.
00:31:36.280 Unfortunately, this is how I would love for it to be, right? I want a single object that represents my view, and I just wanna say: did I show the join course interface? Yes, of course you can. Here are the objects that you're gonna buy into your partial! Can I show the drop course? Yeah, here are the objects! Should I show that we have temporary access? Yeah, you should! Okay, render that partial!
00:31:51.160 This is a dumb template! Now, if I'm a designer or if I work with a designer, I’m going to be a lot more comfortable giving them access to this template than I would this template because who knows what they're gonna do on this template, right?
00:32:04.960 So this is kind of my ideal! This is really what I want to get at! So, how do we get there? Well, the first thing we can do is we can take this complexity out of the template and drop it in some new object!
00:32:19.330 Right? We can also do this with helpers! Right? And there are a lot of varying approaches on what you should use helpers for and what you shouldn't use helpers for. This is a presenter's talk, so we're gonna use presenters! Don’t be that guy!
00:32:34.030 So we have all this logic about, again, the same logic: whether we can join and we can’t drop whether we can drop and whether we have temporary access. Now, I showed this to James over Gray, and he was just like, "Ah! This is terrible! You don’t need a presenter, you need a adequately modeled domain!" And I agree with him!
00:32:50.640 This is what it should look like! Even in the presenter, we should ask our domain if this user can join the course. We should ask the domain if the user can drop the course.
00:33:07.750 But there’s something else going on here. This is not just whether or not you can add the course or whether you can drop the course; it’s whether or not we're going to show this interface to the user! Right? There’s a wrinkle here!
00:33:22.820 They may have some sort of temporary access; who knows what this application is doing? They may be masquerading as the user with an admin or something.
00:33:36.010 But to be, the application is going to behave differently than what the domain is going to behave! We don't want to take these application concerns and jam them all the way deep into our domain because this is a front-end thing only.
00:33:51.260 So a presenter is a really good alternative here! Yes, you should model your domain objects so they make sense, 100%! Yes! But there is still a place for presenters even if you model your domain perfectly!
00:34:03.100 Again, this is how the view should look, right? I think it’s pretty cool! I like it! All right, so that was a pretty good example! I think this is a better example!
00:34:17.620 And I'm running out of time, so again, my apologies! I'm going to go over this pretty quickly! This is a website that I used to work on—a tool very recently! There’s a lot going on on this homepage!
00:34:30.980 Right? An awful lot! There’s a lot of data that’s being presented, and there are actually some pretty sophisticated complex rules for what is going to be displayed. So let's go ahead and focus on categories.
00:34:44.730 Right? How we're going to display categories, win and which categories we're going to display. So let's go ahead and crack the loop in our partial.
00:34:57.690 And on the sidebar, this is part of our sidebar. We’ve got a lot of code here, and I'm sorry for this! I’ll step through it real quick. We have a current organization, which is the company whose account the site is running on.
00:35:12.420 We’ve got a current site, so the organization is going to have more than one site; they sit on a subdomain, and then they've got users and content that exists within that site.
00:35:28.430 The plan that an organization subscribes to—you can turn on; you can have features if you pay enough, or you know, if the categories and other features are if you pay enough.
00:35:40.750 So according to what your plan is, if you pay more, you get more features turned on or off. Alternatively, on the site, you can disable features.
00:35:54.440 So if your plan allows you to have categories but on the site you’re like, "Man, I don’t want to display categories; that’s not what we’re using the site for," you've got the kill switch here, so you can turn them off.
00:36:07.770 So, on our sidebar, we need to be an organization that has features or has categories. The site needs to be able to display categories, and then we’ve got some more conditional stuff if you're logged in and or if you’re not logged in.
00:36:24.440 But the current site wants you to show all categories, then we’re just gonna go ahead and ask the site for all the top categories, right? Which is all the categories sorted by how frequently they’re used.
00:36:42.440 Otherwise, if you are not logged in and you don’t have this setting about showing all content, then we’re only going to show the top public categories. And that means that we’ve got content that is specifically flagged for public consumption that you don't need to be authenticated in order to see.
00:36:57.760 And we might have a lot different lists of categories; the public content, we only have two categories, whereas all content may have 20.
00:37:14.490 We only want to show the categories two of the posts that that user can see with their current authentication state. All right? So now we've got a list of categories.
00:37:29.920 If we have any categories, let's go ahead and render the HTML snippet and bind to the partial, which is going to go and add a list item.
00:37:44.070 Right? Again, I hope this doesn’t look like foreign; I see this all the time! What we can do is we can create a presenter that will encapsulate all this information and I only have two decision points: do I show categories, and if so, what categories do I have?
00:38:00.070 So let’s just put that in the presenter: do I show it? Well, if the organization has the feature and the site has the setting, and I've actually got some categories, then yes, display it. If I don’t, if any of those are false or faulty, then no, we’re not gonna display categories.
00:38:14.900 So, again, this was the original template; here’s the new template: if I show categories, then do it; if not, then don’t! And there are those categories that I’m going to show if we, you know, if the site wants to inflict like a maximum number of categories, we can put that in the presenter and we can set that behavior in that object.
00:38:29.890 And not inflict it upon our template! Again, again, I will give this to my designer! There’s the presenter!
00:38:45.139 Again, we can go a little bit further; like our categories is a little messed up because we've got like three things going on: you can either be logged in, or you can be showing all content, or you're only showing the public content. Those are the three states!
00:38:59.920 So we can refactor our presenter! But in the end, what we did is we just created three different presenters, right? Because we're doing this for everything on that homepage: for posts, for questions, for categories, for contributors.
00:39:15.030 Whether or not you're the current user and you've got onboarding steps, whether or not we’ve got calls to action; what we want is we want our presenter to tell the template whether or not to show something and not have the template determine whether or not to show something.
00:39:30.350 So we created three different presenters, and we can unit test all of these, and these are all just objects, and those unit tests are fast! What I love about this and the reason we did this is because it’s very important that we get this behavior right.
00:39:44.290 Right? And so now I’ve got something that is easily testable! Here’s what the controller looks like: we just build our homepage presenter, we have that logic for which one we’re going to use, we set current organization’s site and user, and we’re good to go!
00:39:59.180 And everything's tested! All right! So I really like this approach! Again a word of warning: if you’re gonna, if you open up your terminal and type in rails new foo, do not start with presenters! Right? This is an advanced approach that should be used when you feel pain.
00:40:14.840 In our case, we had pain because we could not verify easily the behavior of the homepage, and we had a lot of very important logic on the homepage.
00:40:25.970 I wanted some object that would represent that well and that I could test. So we want the presenters, and it was a terrific solution for that, but there is overhead.
00:40:43.200 Hopefully, it's not the same amount of overhead as the components were, right? Remember the components—like that huge dive to find some object that knew how to render itself? Like, what a waste! But there is overhead with dealing with presenters.
00:41:00.270 So here’s a tweet from James: "I think code and presenter objects is some of the worst. I don't think it reflects on the pattern, but it is troubling." This is true, right? This is a 5% solution; a ninety-five percent solution.
00:41:15.580 Real quick: by the Rails view, this is fantastic! It really sets your approach to doing a maintainable view! I love this book! These guys are terrific!
00:41:26.710 They talked about decorators and serializers but not actual presenters! Maybe I’m off, but I think that’s what a presenter is! This is a view model, and there’s not really a whole lot that talks specifically to that!
00:41:40.320 The other one book you should buy if you don’t have and read on the airplane home is Rails Anti-Patterns! Terrific book! A little bit older but fantastic!
00:41:54.080 Thank you very much! Oh, one more thing! Sorry! Applause after I say this, you guys should like hire me and stuff because I lost my job like a week ago and I don’t have a job. So I’m a pretty advanced, like, I’m a good Rails developer.
00:42:07.250 People say nice things about me! So if you're looking for someone, come talk to me! That’s my website. Any questions?
Explore all talks recorded at RailsConf 2012
+65