SF Scala, Peter Potts: Cake Pattern in Practice
Recording: SF Scala, Peter Potts: Cake Pattern in Practice
So, I'm going to talk about the cake pattern that we use in our team at a cake sigh. Um So, you know, the cake pattern is well is hello. That's it. Okay, so the cake pattern. So, what is it? Well, it's a software design pattern. Uh it's used for dependency injection in Scala and well, also do a little bit of aspect-oriented programming. So, you might not believe that, but I'll show a little bit of that. Um and there has no dependencies on anything
So, there's no framework, nothing like juice or such like. And it's type safe all the way. So, even so, you won't have any runtime things are missing or anything like that, for example. Um it was first explained by Martin Odersky and Jonas Boner. Um uh wrote a nice paper on the web, but actually there isn't that much on the web about the cake pattern. Um surprisingly, I think. Um So, we really had to learn a lot in in actually using it in the real world to see what kind of ways we can use it. So, I was trying to think why why is it called the cake? Well, I guess it's cuz it's got layers on it like a cake
Um and if if that's the way you want to look at it, then there's the interface layer, implementation layer, and the wiring layer, which is pretty much common actually to any dependency injection framework, right? Um So, the top layer is the interface layer or what I call component interface. So, So, when you're setting up a the cake pattern, you want to have a component which basically contains the piece that you want to inject. So, that's the idea. So, it's a kind of a trait inside a trait. So, we've got you know, a interface for a vehicle which doesn't contain much um and it's inside the vehicle component. So, the vehicle component is just a container of what you want to inject into the application. And this case, it's a valve vehicle. But, there are different ways in which we can use the component
So, the one of the important things about the component is that about the cake pattern is it's easy to get into a real mess. So, what I've been trying to find in the last year or so is to find a way to use it in such a way that you don't get in a mess. And one of the ways to do that is to stick to a strict set of rules about how you're setting it up. So, the first rule really is to have one access component per component. So, one access point per component. So, for instance, we're setting up a vehicle component. So, what we don't want is multiple access points like capacity, shape. We want to make sure we just have one access point, the vehicle
Um and then define or if you want to have multiple things in there, then you can define that within side vehicle itself. Yep. what's What led you to that conclusion? What problems did you encounter with Right. that that to that decision? Well, I think if you don't do that, then you can quickly end up Question. Oh, the repeat the question. Uh why did I come to that decision about one access point per component? Um I would say that if you try and not have that rule, then you end up with components that contain a lot of different pieces that need to be wired up and it becomes very unclear and it becomes really messy. I mean, if you've only got a few components in your system, that's fine, but then if you've got a lot, you need to keep track somehow of what you've wired up and what you haven't wired up. So, if you've got multiple access points, you could wire up some of them and not all of them
Um whereas I keep a strict naming convention allows us to keep track of what has been wired or not. And if there's just one thing to wire up, then it's easier to keep track of that. So, would that would would that imply that you also have one trait inside or multiple traits would have that same problem then? Right. Where you just have just the one trait inside. Yeah. So, and then we come to the implementation layer. So, in the implementation layer, we want to keep track of a few things. Firstly, we want to keep track of is well, we want to implement it first
So, here we've got a car which is extending vehicle. So, car is our implementation of vehicle. And in a car, we want to do things that are specific for a car such as, you know, keep track of its fuel, keep track of the road. Uh so, they would then end up being dependencies. So, that's why another uh useful thing I found is to keep um a uh a companion class to keep track of the dependencies for your implementations. So, here we've got a car component uh object and we have a type alias called dependencies which shows that we depend on So, car depends on a fuel and the road. Um and then in the actual in the actual trait here, sorry, in the actual uh trait car component which is extending the vehicle component, we can actually just use in the self in the self type, we can just refer back to the dependencies there. So, we don't have to This really avoids having to repeat the list of dependencies, especially as you as they go down the stack of dependencies
Uh and then if you change one somewhere along the stack, it be kind of it can break everything cuz you don't you've got to change that all the way in other places. So, you kind of become dependent on your dependencies. Um So, in this case just note in the in the car implementation, we're actually calling the hash the hash hash is just a hash code for is nothing fancy. It's just calling a function uh on fuel and road when we create the car. So, just remember that cuz I'll I'll come back to that later. And then the the third layer is the wiring up. So, the wiring up is where we actually uh wire up the actual car and say that it's got to be a new car. So, now in this case in our component, we we did a var um uh var vehicle
I think I made a mistake there. I've been through this so many times. It should be a lazy var vehicle equals new car there. Um okay, so cuz we're specifying the actual implementation of vehicle uh there and the the lazy there is because when we wire up all these components, there's no guarantee in which order the vars are going to be uh wired up. So, if we put lazy there, that avoids having to think about the order in which things are wired up. And it will guarantee we won't get any null pointer exception. So, and then we come to you know, modules. How how you you bring to get all this wiring into your big wiring for the application
So, you might layer this and so I might have an intermediate sort of multi-component wiring. So, here we uh uh actually, let's just go back a slide. You notice here I've changed the name convention. So, so call car car wiring extends car component. So that way, if we see anything called wiring at the end, we know that it's already been wired. Whereas if it says component at the end, we know that it is yet to be wired up. There is no wiring yet. So when you got multiple component wiring, we're really just bringing together different wirings or components together
So in this case, transport wiring is depending on the car wiring, a diesel component, and a highway component. So because it says diesel component, we know that it hasn't been wired. So that means at the bottom here, we have to actually specify that the fuel is the new diesel and the road is the highway. Otherwise, it's not going to be wired up. And I would be lying when I'm saying it's transport wiring cuz it has to be fully wired up. Uh so it's really important to keep to these kind of conventions cuz uh another reason is cuz uh the various IDEs, like IntelliJ, when you don't wire it up right, it doesn't come up with very useful errors. Um so you need some discipline in doing this. So what is wiring? So So it's important to keep the difference between the wiring and the component
So the component, we should not have wiring in a component. So if we see a component, it's not wired. So wiring is actual Think of wiring as configuration. Think of it as like your XML configuration in Spring. Okay. So it's programmatic configuration. Um like So you know, we're trying to keep the wiring separate from the actual implementation as much as possible. And finally, application wiring
So this is where finally we have a class as opposed to a trait, right? At the top. So we can actually instantiate it. So if this compiles, that means we have fully wired the whole application. So and and your top class in the may say may have a whole long list of all the things you want to wire together. And so, each of these like transport wiring, home wiring, office wiring, they may be just kind of modules of sub-wiring that you're putting together. So, you might have all your database wiring in one place, all your event bus wiring another place, and you want to bring it all together finally in the application wiring. So, mixing the cake. So, I mentioned earlier on that we got three layers, you know, the interface, implementation, and the wiring
We can actually flatten this cake a little bit. So, in the in the top row we've got the full cake. So, we've got the interface component, and then we've got an implementation component, and then finally we got the wiring. So, just to remind you, so in the interface component is basically a trait inside a trait with a a val which is your access point. It's kind of pseudo Scala code. Obviously, that's not real, but and and then the next one we've got the self type which represents the dependencies in your implementation presuming that they do. And then the class cuz you're actually implementing the trait that was in the in the interface. And finally on the right hand side you're actually telling what the what the val is, so that's the wiring
So, we can actually take any two of these, so the left two or the right two, and we can actually collapse them in so that we just end up with a cake that's two layers. So, in in the in the first case we we're just going with the keeping the interface component free. And then we're actually doing the implementation and the wiring all in one, and uh the convention that I we found very useful is to call that a wired class. So, the distinction being the wired has implementation and wiring all together. Now, that may not sound like a good idea, but it's actually very useful and you don't really lose anything because at the end of the day you can still mock the original interface component or replace it with a different implementation. Cuz usually the wiring it for a single component is usually pretty trivial. And the last case is where we're actually bringing together the first two. So, you know, maybe there isn't any kind of interface and implementation layer
It's all just the class straight off. But we still want to be able to um mock it or put in different things. So, we need to leave the wiring out of that particular case. Um cuz if we collapse it all down to one layer, then it there isn't really it's not really dependency injection anymore. So, just to reiterate that. So, wired is basically implementation plus wiring together. So, so in that case we must extend the component interface, right? Uh to give us the wired and we've got to do both the wiring and the implementation together all in one place. And the the wiring is usually so trivial that you just instantiate in the class, for example, in this case
Now, for um So, for the component interface I've I've only really mentioned that we can have a valve, but there are actually lots of different options. Uh and I was anticipating that, but before I go into that, so so say you want to um mock you want to test these components you built. So, you know, let's take the car wiring for instance. We know that depends on fuel and road. So, if we want to wire it up, we need to effectively make like an application. So, we're making a test application that was called class test wiring. So, that needs to fully compile. So, we have to So, we've got car wiring, so we know that's wired
Then we've got fuel component and road component, so we know they're not wired and we want to mock those. So, we're just going to fix them in with a mock of the fuel and a mock of the road. And then when we test it, we just instantiate new wiring. We just call So, if we call the hash code on car, that means cuz it was a lazy valve, it's then going to be instantiated. It's then going to fire off and call fuel and road hash inside that class and therefore we're just verifying using Mockito. So, I like Mockito because, you know, you can mock anything even fully uh fully implemented classes. And scoping. So, you know, I've showed you really the first case, which is singleton scope
So, you just want to put one instance of a planet or something and you want to inject that into your system. But what if you want to do different kind of um injection? So, traditionally that would be called scope. So, and all I've shown you so far is singleton scope. So, let's say we have no scope. Well, that's just you change the valve to a def and then you basically get a new instance every time you make a reference to it. And usually or sometimes you want something which is a little bit in in between, some kind of managed scope. Um so, this there are a few different ways of doing this, but one way to do it is like this. So, So, we just have a trait which says with like with connection component and then every time you call with connection, you're going to get a you're going to manage that
So, you're going to create an instance of connection and then after that function has been that block or block of code, the function has been called, it then can close off the connection automatically. So, it's nice to So, it doesn't put any responsibility on the caller to make sure all connection's closed. You're carefully managing the scope of connection. So, that's pretty handy. So, I've used that in all sorts of places like, you know, um RabbitMQ event bus connections, etc. Um another possibility is something called a context scope. So, here rather So, you know, we can actually have So, in the actual So, like the service component here. So, the access point is service, but we could actually have arguments that it takes
So, in this case, we're doing context uh and we're making implicit. So, it um you know, context. So, we've kind of got implicit injection and Cake Pan mixed together here, but it it works. It works very well. So, in this case, maybe we've got some kind of health service uh and as you see on the bottom, this is fully wired because we've got the wiring. So, we're saying that service implicit context i- is equal to the new health service and the health service when it's instantiated, it'll automatically get the context will be injected in by second point as well. So, this is a good way of, you know, putting in the context of some kind of web call, for example, like, you know, uh the session, if you like. Uh you know, the the username or any kind of context related to that
And then I mentioned a- aspect-oriented programming at the beginning. So, this is um this is another uh good, you know, good example, I think. Uh so, we we definitely use this. Uh so, this is a way of controlling transactions, for instance, on a database. So, we can have a transactional component. Um and then and then in there we've got the transactional and then we want this function to return any type however you happen to be using it and we're passing in a block of code. So, this is like a DSL, domain specific language or it's like well, it's call by name and effectively extends the grammar of Scala. So, you can do some uh, really funky things
So, if you look at the cake pan usage, it's basically saying that the in this add function we want to add one and two and we want to make sure that's a transaction transactional thing. Probably doesn't mean anything there, but if it was doing something with a database, then you could make that a database transaction for example. Yeah? You mean like so you've got one method which is transactional and it calls another method that's that's Okay. Right, so the question is what happens if you've got, you know, multiple transactional. So, in other words, you'd have something like in transaction for example. Um, yeah, I mean we we we do that. So, we actually use Slick which is a really great ORM, recommend that and so and one of the ways you can use it it is effectively putting um, the transaction state on thread local. So, when when you actually implement this transactional component, you can say, well, am I already in a transaction? Pick it up from thread local
If I am, then I'll just reuse that. So, that's how we do it. Um, so and and then if you compare that to the way you do that in Spring or one way to do it, I mean the one that compares. So, you could just annotate in Spring you say at transactional on your method and it will somehow weave in some magic. So, it almost looks the same except we haven't really got that much magic other than Scala magic of the language. Um So, you know, you could argue maybe that isn't fully AOP since, you know, I have to actually if you like annotate it with transactional here, I can't just leave it completely empty and somehow I'm going to weave it in externally. Um but it's pretty close and um you know, and it's pretty unintrusive. So, and when you're doing tests, of course, you can just have an implementation of transactional components that basically does nothing
Just lets it pass through. A kind of no transactional wiring component. So, don't eat too much cake. Now, the problem with the cake pan and another problem is that it can really you can really get carried away and you can end up with having components for everything and things that shouldn't be in components. Um So, one way to break out of that kind of never-ending tree is to actually not necessarily define traits inside traits and to actually So, if things are relatively simple objects, like a a clock for example, don't define that necessarily within a clock component. So, here you can see for example that um I've got a trait for clock and I've got an implementation which is called system clock. And then I've actually built a clock component which has which holds uh a way in which I can inject the clock, but I haven't defined clock inside the clock component. So, and sometimes that is pretty useful, especially for the simpler objects in the system
And probably necessary especially if you want to inject things that are externally defined somewhere else, then you can't obviously put it inside your trait component. Yeah. Okay. Um implicit sub injection Um So, you notice on the on the last one that I've actually got an implicit on the valve. So, you know, you can play all sorts of games there. With So, it's got a cake pan, but I've got an implicit valve, not just a valve. Um And so, that means that we can So, for instance, we've got a ticket a ticket case class and in there we want we've got a purchase time on it. And when we instantiate it, we want it to pick up the time from the clock there
So, now if we define clock inside a component, then suddenly we'd have to have the ticket case class would have to be inside another component and then, you know, it goes on and on forever. So, but this way because clock is defined it's a relatively simple object, doesn't need to be encapsulated in a in a component. I mean, the only reason to keep it in a component really in the first place is so that you know, you're forcing people to use that particular object within the cake pan for a good reason. But in this case, there isn't a good reason. So. Um so, here you can see how we've made the cinema component depend on the clock component. And it's because it was an implicit clock, it's automatically got set up into the ticket when we buy a ticket. We do the ticket film
And it's picking up the implicit clock and reading the time. So, you know, people complain about implicits because you can really get carried away with invisible things, but so you've got to keep it to moderation, you know, things that really are universal through your application. Obviously, your whole application is probably interested in a clock. You probably your whole application may be interested in actor system, for example, or an execution context. You know, these are the things that I are useful to have as implicit, but you certainly don't want to get carried away. Um set up and tear down. Um so, when you're wiring in things like, um you know, databases or event buses or anything that involves or even a web server, like soccer, um then, you know, once the whole thing's been wired together, you need some kind of a way in which you want to set everything up and tear it down. And we never really found a way to do that that was pure cake, so it lies as a bit of an exception outside of the cake pan, um but this is close to cake-like as we were able to get it
Um so, we've got a setup hook component. So, so it's a setup hook component and it's got an access point called setup hook. So, that's good. Uh but it takes a method, which is essentially a call by name. So, it's a piece of code. So, it's the piece of code you want to do when you set up. And there's the actual setup hook wire, so remember that's the implementation plus the wiring all in one. So, we've So, you know, where the the way this um falls down a little bit in the cake pan is it actually's got another setup method there
So, it's actually got two access points in a way, but at least the act the second access point, i.e. the actual setup function, at least that's not accessible from the original trait. So, it's only really accessible when the whole thing has been set up, which is actually a good thing cuz you don't want people calling setup anywhere else except when the whole thing has been put together. So, I'll I'll show an example of how the setup uh hook is used in a couple of slides. Um so, actors. So, actors are a bit strange in many ways in Scala cuz we all like statically typed and type safety, but actually actors, or the the actors that we use most of time, are actually not really typed at all, you know. Uh um So, and and so, it makes uh using the cake pattern with actors is a little bit um tricky. So, uh one and and also within the um one of the things in the actor system, it says if you try to find out how you're supposed to use dependency injection with actors, the literature on the web is pretty unhelpful and probably inconsistent as well
Um so, this was probably the best that we could come up with. So, it's a little bit around in circles cuz I don't want to put the echo actor, in this case, into an actual um component. So, I don't want to put it into an echo actor component. So, the only way to inject things is to actually have some a concept of injector. So, this is beginning to look a little bit like um structured type dependency injection. I don't know if you've ever looked at that. It's not quite there, but So, we can see you've got the injector. So, we want to pass in anyone anything in there which implements that, which at the top, the echo actor dependencies is a listen component
So, we've got this echo actor. The basically what we wanted to do is any message you throw at it, it's going to it's going to reply the same message. And it's going to and it's going to um call the hash hash hash code function on the actual listen thing that is dependent on. So, that's that's what it does. You can see this extends actor, and then the receive method it's got a case class sorry, case match where it's matching any message that comes in what any message of type message and then it's calling the hash on the injector.listen.hash, and then it's replying the message back to the sender. So, actually just go back to that. So, try and remember that cuz I'm going to show how to test that later on. And so, we got props wiring
So, this is the thing about the types. So, we want to have a props for the echo actor. And as you can see, it's a type props. So, really you can put anything you like there. It's not really very tightly um you know, coupled there in terms of dependencies. Um but that's actually pretty useful cuz that means when you're mocking it, you can put anything in there. Um you know, such as the test probes and stuff uh that come with Akka testing. Uh so, if you hadn't noticed, we use Akka quite a lot at uh Kickstart, and it's super fast and really really nice
Uh so, in the uh Akka props wiring here, so we're extending the Akka props component, we are actually doing the wiring as it says. So, in the wiring, it's just props new actor self. Um So, this is actually not the recommended way of starting an actor, but it's the best one I find because it means that it's actually checking to make sure that the types all tie up cuz if you do it a different way, I props class of Echo Actor, comma self or whatever, then even if you get the types all wrong, it will still compile and that's not great. So, and I think there's some flux in the Akka development as to which way this is going to go in the future anyway, so. And you know, arguably you could say this is actual some implementation going on here, but you know, the props new Echo Actor self, you know, it's not just new something, is it? There's something a little bit extra there. But usually in in our code, if it's just a one-liner and it's pretty simple, we turn a blind eye and say that's wiring. So, I just want to show you uh so, bringing a lot of things together here. So, we've got uh you know, Akka
We want to use So, we want to test this actor called the Echo Actor. So, we're going to use Scala test. We like that. Uh we like Mockito. Uh and we like Akka testkit. So, it's all kind of in one. This is a bit of a challenge to get it. I hope Yeah, you probably need to come closer if you can't if you're further than halfway to see this, but um so, you can see an Echo Actor should So, I'm using WordsSpec, which I is my favorite spec, so that's a bit like Specs2, which I used to use before using ScalaTest
Um and it gives this kind of uh behavior-driven development type of feel to it. Um and I'm using matches and Mockito sugar. So, an Echo Actor should echo a message. So, the first thing I do is set up uh the actor system using testkit. And you can see that I shut it down at the end. Uh this is just to try and get it all in one page. So, it's probably not the perfect way to write this, but uh it shows all the different pieces. So, we can see that we've set up the actor system and we've extended that with Echo props wiring and listen component
So, you can see that wiring means it's already wired, so we're finished there. And the listen component, that's a component, so we need to wire that. So, the first thing I do is set up val listen equals mock listen. So, we want to create a mock for that, cuz we don't want to use a real implementation. We create a uh message, cuz we want to chuck a message at the Echo actor. And then we're going to actually All we've got actually here is the Echo props, so we don't actually have an actor. So, we're going to fire up an actor using system.actorOf. And then we're going to use test probes, so they're a really useful part of Akka, where you can sort of emulate sort of uh actors
So, the test probe is a bit like your own kind of test actor. And then we're going to send a message. So, Akka.tell. So, the reason why we're using tell as opposed to the exclamation mark, which you may be more familiar with, is because you want to specify where it's coming from. So, I want to say, although I'm sending a message to the actor and I'm sending that message, I want to make it look as if the probe sent it. So, that way I expect the the reply is going to come back to the probe. So, that's why the next line I'm saying probe I expect the message. I That's the message I expect back to reply
And then inside this actor, it should be kind of pinging the the listen component with with the hash function. So, we're just verifying So, we're using um Mockito to to verify that that method's being called. Cool. So, you So you got through that. So and finally actor wire. So I did the props and how to wire that. So how do you wire up an actual actor? So when you think about it, um there's quite a lot going on with an actor. So I I need to have the props
I need an actor factory ref. So an actor or factory ref factory ref is either the whole act system or it's an actual context of an actor. They both implement the same thing. So you know, so this is a more generalized kind of component. And then I'm also using the setup hook component. So this is where I promised I was going to use it. So if we look down here, so it's echo actor wiring at the bottom. So we're doing wiring
So we're saying lazy val echo actor equals and then we're basically instantiating the actor. But remember this is lazy. So when this is all been instantiated, this actor still won't be running. So the only way to get it running is to actually do something on that reference. So I'm just doing dot hash hash code. So it's my kind of way of touching or pinging the uh the lazy val. So that's what the setup hook will do. So when the whole thing's been um wired up and started up and somebody calls setup, then it will go through all the different blocks there and it will basically start that actor going
And why there? Because you know, some of the the dependencies that that our actor has may not have been set up by the time that particular um val has been instantiated. So you know, it's just erring on caution cuz you don't know the order in which things are wired up. And getting near the end here. So, I've been through quite a lot of things and with the cake pattern and really talking about the conventions that we've sort of evolved to. And so, you know, you might I don't know how many of you are using the cake pattern. So, how many people are using the cake pattern in their company at the moment? So, okay, not that many. So, I mean, a cake pattern obviously has some detractors. So, the main one being that it's got a lot of boilerplate
And I probably agree with that, but it does have a lot of flexibility in the way that you can actually do it as long as you stick to some rules cuz it can lead to a lot of chaos. Um and I but I think there's might be some hope in the future to reduce the boilerplate. You know, I've been trying to think about how to do that. Maybe somebody else can think of a way of, you know, using some of the macros in Scala 2.11. So, you know, I had that talk a couple of weeks ago on MacWire. So, that was obviously an interesting variation called the thin cake. Um you know, but I which I I I like that. But I still think there is something undiscovered here
So, anyway, let's just go through the conventions that that we we've kind of narrowed in on. So, first one is the one I mentioned at the beginning, which is I have one access point per component. You know, I find that try keep some of the complexity down, although it does increase the number of classes. So, that's kind of the downside there. Um but at least you kind of know where you are. And then I stick to the strict set of surfaces for the um classes. So, like component, wiring, and wired. So, with wiring and wired, you know that everything's been wired
So, it really helps keeping track of what you want and what you want. And the type alias is for dependencies. That's That's I haven't seen that anywhere. So, we kind of invented that one. So, but that's really turned out to be really helpful when you've got a lot of things being injected in. Um you know, it means that uh you know, these lists of arbitrary dependencies in certain places, you know, you say, "Well, it doesn't really depend on this." So, it becomes very, very coupled as it goes up and down the the tree stack. So, that really helped. And the last one is to have at least two layers in in in the cake
Well, that's kind of obvious. Um cuz otherwise you're really cheating and you know, there's no way it's not really dependency injection anymore. It's all just put together. Um So, So, why have all these conventions? Um well, we we've just it makes it work effectively in a team. You know, if working with a team of people, if you have some rules about how the cake pan is done, it really helps. I mean, if you're just on your own, it probably is okay. But once you you beyond a few people, then it really helps. Um and you keeps track of the wiring problems
So, you know, the it really the IntelliJ and presumably Eclipse, too, although I haven't tried it, has real trouble finding out where you have if there's if some kind of wiring goes. I mean, the beauty of the cake pan is that if it doesn't compile, you haven't finished, right? It's not going to run as opposed to other dependency frameworks, which may compile and it may but it's only at runtime that you finally tells you that something's missing. Here, if it compiles, you know you're there. But unfortunately, if it doesn't compile, the errors can be very, very obtuse. Um So, really helps to have some conventions. And it's easier to extend and rewire. I mean, these kind of obvious benefits, I think, but they do really help. And that's it
So, Kixeye, we make, as we said earlier, make uh computer games and um I'm part of the shared game services where we create services and we we create any service which is used by at least one game. That's our kind of remit. Any questions? So, I assume you don't use uh Spring dependency injection or you would say these dependency injection, you know, ways of that you've shown are sufficient for your needs or other dependency injection? So, the question is is this sufficient for our needs and do we use Spring? Um this is all we use for our dependency injection. Um there are lots of different teams at Kixeye. It's a lot of people working here. So, you know, there are teams that are using um Spring. And in fact, I've used Spring myself in the past. I've used Juice and I I prefer Juice to Spring
Um I've used Subcut, which is a Scala dependency injection framework. That's not bad. Um MacWire looks good. Um but I haven't used that yet. That's pretty new. Um What else? I mean, if I think of I abandon Cake, I'd probably go for Juice at the moment. Any other questions? Hey, so I'm I know I have missed it, but um is the class at the top that extends your components, is it its responsibility to call the setup for your other components? Yeah. Will it iterate through and call all the setup functions that you call other components that you mix in there or just Yeah, absolutely
So your top application wiring here. So this would typically open it would run set up as the final thing once everything's been set up. Yeah. And here it So this would be with office wiring then obviously with set up hooks wired. If we go back to that. Yeah. So with set up hook wired. And there's a similar one for tear down
So as you can see there it basically iterates through so it keeps track of all the all the set up hooks that you've all the different components have put in there and right at the end you click set up and it will go through them all in no particular order in this particular case. So you might want to embellish that. We actually have a version where you can specify some kind of number by ordering. So I like default is zero and then if you would minus one that will be done first and if you plus one it will be done last. So you can embellish this. I'm just giving the kind of simplest case scenario. Um so one of the you mentioned this problem that one of the big problems we have with Cake is really really poor error messages when something is wrong with wiring. Which like you mentioned the functions that help you kind of do that
Which one is it the tiny releases or what really helps with that? Right. Okay. Yeah. So he's asking question about how how do we track down wiring problems in the Cake um and which feature is particularly helpful. Uh yeah, I found the the dependency having their listed having the dependencies listed really helps cuz you can you know if you really stuck you can just copy and paste those in up the stack until until the actual and you can keep on going down until it actually highlights the the one that's actually you know responsible for it not wiring up. I don't know if that makes sense. But it does. Yeah, it's difficult for me to show that in a in a talk
Um but but yeah, I'd say the type aliases is probably the the number one thing that helps. But definitely uh having this convention of wiring components or what what you know comparing component with wiring and wiring, that was probably the second most useful thing in in terms of controlling that wiring difficulty, tracking down wiring problems. Any other questions? Okay. Well, thank you. Um you know, carry on having some food um and they say that Kickstart are hiring um and talk to me afterwards. Okay.