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?