 Welcome to Polymorphism and Rails 5. I'm Michael Cain. If you're interested in getting with me on Twitter, Code Train is my tando. I was a musician for a while, so if any jazz head's in the room. So we must address the elephant in the room, of course. This is Michael Cain. I am Michael Cain. I do a pretty good impression, so I'll see if I can bust that out later on. So what's the scope of this talk? How many people in this room have been developing Ruby on Rails for a year or less? One to three years. More than three. A lot of vets. This is probably going to be a review for you, but for everybody that's new, we're going to try to take a fundamental look at how Rails utilizes polymorphism and try to find some practical examples of when it is most prudent to use it. The goal is, so when you leave here, you'll have an introductory understanding of how polymorphism works and a practical use case or a practical understanding of when this is a good tool and when this is maybe not such a good tool. And I know people love to give stickers away, but I've got a hundred bags of free candy since we're talking about candy. Let's come up and get one afterwards. Who doesn't like free candy, right? So what we're going to be covering, what polymorphism is, respective to the Ruby on Rails stack, of course, we'll be using our candy shop application to discuss the practicality and the real world-ness of when polymorphism is a good idea. And then there are some kind of complex concepts outside of just vanilla polymorphism that we'll go over as we go. So what is it? So polymorphism does not just exist within the confines of software development. In short, it means when one thing can have multiple appearances. So for example, in biology, polymorphism is when a species can have multiple distinct appearances. So for example, there are species of snails that have very different patterns on their shells or there are species of jaguars that have light and dark fur. So they're the same species, but they look entirely different. So relative to how it applies to us, polymorphism refers to what a type of association where multiple models can belong to a single association. So multiple things expressed through one association. So why is this a powerful tool? Why would you want to use this? So we often refer to convention over configuration. That's big in our community, which has served us well over the years. So you'll hear a lot about single responsibility principle or solid. The idea being that simple code is good code. So polymorphism will free us from the complexities of coupling. So the idea being you can have a lot of complex relationships on one side to pump them through this very simple interface. It's also very dry. So as you'll see later, as our complexity with packaging changes, we can isolate that through like a polymorphic association. Instead of having to deal with as the types of packaging change, that's all managed through in the end one line of code. So let's talk a little bit about our new business. Candy on Rails, a sugar as a service platform. So our application is a web service that allows customers to order handmade confections from all over, which are made by our open source chefs. So we're putting our recipes out there. People who want to get involved actually make the product and send it out locally for us. We call it our distributed kitchen system. So like I said, our recipes are open source. Our open source chefs can tweak and update the recipes. They are inspired by some of our contributors. This is why, for example, we'll use the Michael Cain impression. Aaron Patterson's inspired toffee love, a rich, fun mixture of English toffee, dry chocolate and a hint of wasabi. Arlene Ushuteli's inspired Ushuteli's, a light Italian shortbread with a little taste of Venice and maple glaze. Or perhaps Sean Griffin's daughters inspired Ruby's session cookies. Viennese inspired finger cookies with vanilla cream frosting, lemon, strawberry, old vanilla cookie there. That's my Michael Cain. I'm so glad you laughed at that. I tried making jokes last year. It did not go well. So since our goodie makers are distributed, we need to keep track of who is making what, ideally because people want to get paid, right? So the goodie itself is like the object of interest for our business. Below you'll see some of our standard packages. So they're pretty self-explanatory. They change over size. Our packages we do plan in the future of being able to change for themes like holidays or birthdays or even if you wanted a custom design stuff. We can create that for you. Here is a really terrible UML for the back end of what our application is going to look like. So you can see naming things is hard. We have our goodies on one side, which have a name, a little description of what it is, who made it, and perhaps the contributor that inspired it. In the middle there, you have all of the different types of packaging we have. So we've got bags. We've got gift boxes. We've got rail cards that look a little bigger. All of those belong to an order, which has like who ordered it, where they live, which is related to the address model on the right. So this is essentially how the model and the models in our Rails app are going to appear. So there's a potential hazard already. Does anybody see it? Give me a hint. The problem we have in this particular instance is a goodie is only one thing. So it can only go in one container. But if you have these belongs to relationships, inevitably there's two things that it can't belong to. So now you have a series of nils. If anybody's ever tried to iterate over a nil in a view, it's a real P in the A. Every package is going to require its own ID. So like we talked about, what happens if we have like five new holiday packages? If we set it up this way traditionally, now we're going to have five more IDs we have to manage. So it makes this association very hard to scale. This is where polymorphism comes in. One association to rule them all. So what polymorphism does essentially is includes the what with the whom. So if you think about like a typical belongs to association, you have explicitly the ID. So say where we're going with the traditional way of doing it, a gift box is going to have a gift box ID. Rails is going to know, okay, there's probably a gift boxes table somewhere with this ID. I'll go fetch it for you. Polymorphism allows us to tell this association up front the models of the tables you're going to be looking for are going to be dynamics. So check first what I'm asking for, and then go get the who. So with polymorphism, this is what our associations are going to look like. It's much, much cleaner. It's very dry, so it's also easy to scale. So as you can see at the top, the Goody model doesn't care what package it is explicitly. The polymorphic association will tell you whether it's a rail car, whether it's a gift box, whether it's a Christmas tree, whether it's a fun package that we want to make, the Goody model doesn't care because the polymorphic association will alert it to this is what I need and this is the ID of what I need. So it's dry, flexible, and easy to scale. All the fun things we like in software development. So how does this work at like a sequel level? We talked a lot about the compression of all this stuff with the EJH earlier, so you'll never have to touch any of this stuff, but if you're interested in how the guts of it work, the highlighted part of the bottom is how the association knows what model it is concerned with. So every polymorphic association is going to have an ID or like who am I getting and a type, what am I getting? So knowing that, take a minute and see if you can guess what the sequel statement for this query would look like. I'd play Jeopardy music if I just thought of that, pardon me. It's going to end up looking something like this. So obviously we know who we need, we need number three, but the what is going to come from the type that we give it is going to say this is the model that I want you to go to. So herein lies the problem. When is it prudent to use polymorphism? Because this is like a lot of things in Rails, this is like giving toddlers sharp tools, like cool, maybe you're really good with a pair of scissors or maybe you're going to stick them in your eye. We don't make those kind of rules for you, so you're allowed to stick polymorphism in your eye. So the concern is like when is it a good idea to use this and then when are you just simply masking a problem. So some things to consider. What are the benefits of using polymorphism? So as we've talked about, it allows for a significant amount of flexibility and provides some dryness with these associations. So like we said, as our packaging continues to grow, shift and change, we've isolated all that complexity to this one association. Cost. So it is in itself abstraction, and abstraction can be dangerous. So you have to be very conscious when you are abstracting things, you are doing so for a good reason. Namely because as things become abstract, they become harder to read, they're harder to maintain. Because we just kind of push them off to the side, oh that just does that, like kind of like a black box thing. So we talk a lot about convention over configuration. Ruby does a lot of magic by way of this convention. One of which is able to interpolate the tables you need from the associations you give it. So like that little inverse of. I didn't have any of these specific problems with our little baby app that we're working here. However, when you get distantly related associations using polymorphism or not, sometimes rails cannot interpret through your association what you're looking for. So the inverse of, you can just pass it like, I know my association is called, I don't know, confection, but this is, it's the inverse of gift box, so that bi-directional association will work. So just file that away in the event that your associations are working, you don't know what the hell's wrong, hopefully you won't have to spend eight hours chasing it around the Google like I did. Here are some things to consider when introducing polymorphism. So ostensibly this is because you have a series of new models you want to associate. So I would offer, is your new model sufficiently original? I was a musician before I became a programmer and one of the things that we talked about in copyright law when it comes to copywriting your song is, is your song sufficiently original to copyright? So are my lyrics, are my chord changes original enough to say this is mine? So what does that mean when it comes to a model? There are times when we may be creating models, I have in my experience created models out of ignorance because I didn't know there was a more concise way of doing which I'll, we'll get to in a second. Timeliness. So one of the great adages from the Scrum methodology is put off decisions until the last possible responsible moment. So because polymorphism is in its essence abstraction and we want to use abstraction with care try to leave that decision to when it makes the most sense to implement. Like for example, right now we only have three packages. Perhaps this isn't the right time for us to implement this. Maybe we need 10 or we need 15. So it, and that's really kind of up to you as the, as the software developer use prudence. Concision. So what does that mean? Is your polymorphic association solving a problem or is it masking a problem? So for example, maybe our packages do not have unique enough attributes to live on their own. So for example, all of the package details like its dimensions, its color, what, how big it is. Maybe we can just condense all those into like simply a settings JSON object and just save that as an attribute. So now we went from three models to one. You know, this is a good example of maybe you don't use polymorphism in this particular instance. Maybe you do. Something to think about before you introduce this level of abstraction to your code. So imagine that we're a hit, people love us. Our customers are interested in more of the details of some of the goodies we provide. So for example, a lot of kids have allergies. Like you don't want to order some fun looking cookie out of the internet. It's got peanuts and kill your kid. That'd be bad. So maybe we need to tell people ahead of time here are some of the allergies that this particular goodie has. Or some people like saltwater taffy, particularly if you're from Jersey. Maybe you don't like saltwater taffy. Maybe you want the option of ordering either or. So for the sake of argument, let's say these models are sufficiently original to set up their own kind of polymorphism with regard to our current system. So how would you do that? A suit. So there is, as you can see, an optional param for the belongs to association. I scoured the change log to see if there's any differences between this from four to Rails five. There is one, and it's this one. So belongs to now by default is strict. If you have a belongs to association, you have to have an ID. If you don't require that, you have to pass this option. So why would this be important? Well, as we have created at the moment, not all of our goodies require their own model. Some of them will, some of them won't. So we want to make sure that it's optional, otherwise you're just going to break everything. And again, I've been through that. I do not wish that upon you. So how would we fill this association out? So say I'm selling rail cars and I want to add some of these new goodies to what I can sell. You can see up here, if you can't read it through the lights, it has many cookies through goodies, and then we specify the source, which in this case is the name of our new association, and the source type. Because this is a good example of Rails magic will only get us so far. So in a more distantly related association, you have to be a little more explicit with this is where I want you to go to find this new thing. So all the good belongs magic when you get into this level of polymorphism, still works. All of the accepted nested attributes works, as you can see. I can see that there is a problem brewing looking at this code right now. Imagine if this is what all of our new package models look like. And as we continue to add associations, we're inevitably going to have a problem. While on one side we've isolated the complexity of the packages to the goodie, now the packages become laden with managing all of this themselves. So what we can do is create a concern, and I'm sure most of you in this room know what a concern is. The idea being the concern can encapsulate all of what could very well become complex association code. We want to put all of that in one space, so ideally, bang, all of our package data goes in one line. This is when polymorphism is awesome, because now as our package types change, all we need is one line of code, and all of that association stuff and all that polymorphic logic gets put in one place. Love Rails. So, what can you take away from this today? Polymorphism is a useful way to abstract what would otherwise be complex associations. So like we saw up front, if you have very vanilla associations, in our case, with packaging to goodie, as the packaging changes, the complexity is inevitably going to impact that particular model. So we can isolate that in a way that the package world expands and contracts, it doesn't negatively impact the goodie model. That said, you are in effect hiding code via abstraction, and this is not something that should be done carelessly. Polymorphism is sometimes seems like the first tool to use. I know when I first started working with this, I was like, oh, polymorphism everywhere, this is great, and you just cause a lot of problems on the line when a more simplified implementation of your code might have been warranted. So when in doubt, I always think of the use case. So when it comes to our product, for example, does the customer need to see the details of the package? Who is that for? Is that for them, or is that for us? And if it's for us, can we just dump it in a JSON and be done with it? Or maybe not. Maybe the shipping department needs to be keenly aware of the dimensions and details of every type of package that comes through. In that case, the way that we have it engineered now is probably the best solution. So at the end of the day, just remember the user. Thank you very much.