We're sorry but this page doesn't work properly without JavaScript enabled. Please enable it to continue.
Feedback

Eastward Ho! A Clear Path Through Ruby With OO

00:00

Formal Metadata

Title
Eastward Ho! A Clear Path Through Ruby With OO
Title of Series
Number of Parts
65
Author
License
CC Attribution - ShareAlike 3.0 Unported:
You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this
Identifiers
Publisher
Release Date
Language
Producer

Content Metadata

Subject Area
Genre
Abstract
Messy code often begins with a simple "if". Continuing down that path is a road to ruin, but there's a simple way to avoid it. East-oriented Code is an approach that helps you follow Tell, Don't Ask. It guides you away from Feature Envy and toward better encapsulation. See before and after code and walk through easy to repeat steps that will make your Ruby programs better and easier to maintain.
39
TwitterNumberMachine codePresentation of a groupComputer animationLecture/Conference
Presentation of a groupData miningRule of inferenceLine (geometry)Meeting/Interview
Rule of inferenceMetropolitan area networkDataflowSoftware developerInformationXMLUML
Query languageInformationDataflowCompass (drafting)Machine codeQuery languageComputer animation
Machine codeRule of inferenceStatement (computer science)InformationShape (magazine)1 (number)AngleComputer animation
Rule of inferenceObject (grammar)Query languageIdempotentRule of inferenceNumberComputer programmingQuery languageObject (grammar)Factory (trading post)WordEncapsulation (object-oriented programming)Polymorphism (materials science)Machine codeInstallable File SystemXMLComputer animation
Address spaceString (computer science)Electronic visual displayMachine codeMachine codeInstallable File SystemParameter (computer programming)Address spaceImplementationInformationElectronic visual displayGoodness of fitSoftware bugNumberRow (database)Decision theoryRule of inferenceObject (grammar)Right angleReading (process)CausalitySocial class2 (number)Type theoryView (database)Computer animation
Rule of inferenceMachine codeRule of inferenceNumberAlgorithmObject (grammar)Price indexPrincipal idealComputer animation
Division (mathematics)CausalityFunction (mathematics)Social classInformationDivision (mathematics)RoutingMetropolitan area networkBitPerpetual motionDependent and independent variablesProgrammer (hardware)Computer animation
Query languageFunctional (mathematics)Goodness of fitInformationSoftware developerAddress spaceQuery languageObject (grammar)1 (number)Commitment schemeRight angleXMLUML
Address spaceQuery languageQuery languageInformationMachine codeObject (grammar)Address spaceXML
String (computer science)Address spaceMachine codeTrigonometryAddress spaceMachine codeOrder (biology)Object (grammar)Social classGraph (mathematics)Sampling (statistics)Data structureMetreParameter (computer programming)Physical systemPhysical lawImplementationConnectivity (graph theory)XMLComputer animation
Machine codePower (physics)Object (grammar)Network topologySampling (statistics)Programmer (hardware)MathematicsPoint (geometry)Closed setRight angleSocial classControl flowComputer animation
Address spaceMachine codeString (computer science)TrigonometryElectronic visual displayAddress spaceRule of inferenceInformationQuery languageRight angleElectronic visual displayComputer animation
Rule of inferenceObject (grammar)Query languageElectronic visual displayCompact spaceMachine codeLattice (order)Rule of inferenceAddress spaceNumberMachine codeFile formatSoftware bugPointer (computer programming)Line (geometry)Existential quantificationControl flowClient (computing)Different (Kate Ryan album)SpacetimeRight angleComputer animation
Address spaceSocial classElectronic visual displayControl flowLine (geometry)Parameter (computer programming)Right angle
Electronic visual displayParsingCompact spaceMachine codeAddress spaceTemplate (C++)Object (grammar)Address spaceTemplate (C++)Process (computing)File formatLine (geometry)Standard deviationElectronic signatureGreatest elementAlgorithmFunction (mathematics)Computer animation
Template (C++)Address spaceElectronic visual displayRule of inferenceRule of inferenceElectronic visual displayFactory (trading post)Right angleTemplate (C++)Electronic signatureAddress spaceObject (grammar)CASE <Informatik>Social classDefault (computer science)Computer animation
Address spaceTemplate (C++)Electronic visual displayDialectCompact spaceMachine codeModemElectronic visual displayAddress spaceDomain nameLibrary (computing)Order (biology)Object (grammar)MereologyFunction (mathematics)Dependent and independent variablesType theoryTemplate (C++)Process (computing)Social classDifferent (Kate Ryan album)Statement (computer science)InformationGraph (mathematics)Attribute grammarCASE <Informatik>Goodness of fitRight angleStudent's t-testOpen setMoment (mathematics)Electronic data processingBlock (periodic table)Product (business)MetreComputer animation
Machine codeTemplate (C++)Electronic visual displayInformationMachine codeObject (grammar)Right angleAddress spaceMathematicsTemplate (C++)Imaginary numberProfil (magazine)Information privacyElectronic visual displayWeb 2.0Form (programming)Computer animation
Machine codeAddress spaceCompact spaceTemplate (C++)Electronic visual displayRule of inferenceControl flowMachine codeRule of inferenceAddress spaceCASE <Informatik>NumberObject (grammar)Parameter (computer programming)Query languageInformationComputer animationXML
Address spaceTemplate (C++)Electronic visual displayFunction (mathematics)InformationAddress spaceString (computer science)Template (C++)Computer animation
Orientation (vector space)Machine codeRule of inferenceOrientation (vector space)Right angleMachine codeMixed realityStatement (computer science)Cohesion (computer science)Query languageXMLComputer animation
Query languageLeakInformationPrice indexLeakDependent and independent variablesDirection (geometry)Right angleMachine codeQuery languageComputer animation
Polymorphism (materials science)Encapsulation (object-oriented programming)IdempotentEncapsulation (object-oriented programming)Object (grammar)Dependent and independent variablesMachine codeForcing (mathematics)InformationAddress spaceSocial classMultiplication signXMLUML
Rule of inferenceObject (grammar)Query languageControl flowControl flowObject (grammar)Factory (trading post)Rule of inferenceComputer configurationComputer programmingInformationDifferent (Kate Ryan album)TwitterQuery languageMachine codeComputer animation
Table (information)Squeeze theoremSocial classSqueeze theoremResultantInformationMachine codeQuery languageRight angleCASE <Informatik>Computer animation
Installation artTable (information)Content (media)Squeeze theoremDirection (geometry)Message passingMiniDiscSoftware testingRight angleProjective planeMachine codeComputer fileContent (media)Object (grammar)Graph (mathematics)Squeeze theoremTable (information)Computer animation
Squeeze theoremTable (information)Social classDirection (geometry)Object (grammar)Machine codeProcess (computing)QuicksortMathematicsOperator (mathematics)Greatest elementRight angleSqueeze theoremSocial classResultantWordComputer animation
Table (information)Content (media)Squeeze theoremDirection (geometry)Message passingMachine codeOperator (mathematics)ResultantSqueeze theoremDirection (geometry)Right angleSocial classSoftware testingObject (grammar)Query languageComputer animation
Computer configurationBlock (periodic table)Direction (geometry)Object (grammar)MereologyGreatest elementProgrammschleifeMachine codeBlock (periodic table)Key (cryptography)CASE <Informatik>String (computer science)Social classParameter (computer programming)Configuration spaceDirection (geometry)ResultantHash functionMetaprogrammierungCuboidRight angleLoop (music)Computer programmingComputer animation
Squeeze theoremTable (information)Social classDirection (geometry)Control flowMachine codeMathematicsTerm (mathematics)Object (grammar)Software testingComputer animation
SoftwareEvent horizonVideoconferencingDependent and independent variablesXMLComputer animation
Transcript: English(auto-generated)
So I'm going to be talking about a concept called
East-Oriented Code, which is related to tell, don't ask. I'm Jim Gay, I'm Saturnflyer in most places in Twitter land, on GitHub, and everywhere else, so tweet at me and tell me what you thought. Or during, but I'm not going to be checking.
I traveled west to tell you about going east, and I brought these people with me, so if you see them, say hello. I practiced my presentation with them, this morning, they didn't quite get all the concepts, so it taught me to go a little bit slower. And this is funny, actually, when I posted this picture online, a friend of mine asked about my daughter there,
is she referring to all of us as losers? I don't think so, she hasn't said that, but alright. East-Oriented Code, so I'm going to be talking about this idea, it might not make sense at first, and that's okay, I just want you to stick with me, and we'll get to some rules and things that you can do,
you can walk out of here and try it on your own code, and see where it takes you. This is an idea coined by a man named James Ladd, who's an Australian developer, and we'll get into more of what it is and what he says about it, but we're going to be looking at our code and see which way the flow of information goes in our code,
so this idea of information traveling eastward can help us, and information traveling westward can actually be a problem. So if you were to look at a compass to get around in your code, you'd see queries
traveling westward, this information going in a particular way, I should be pointing that way, and commands traveling eastward. When I first read about this, I didn't quite get the concept until I realized that I could actually look at my code and see this happening in it.
Sometimes you can look at your code and you can see how deeply nested it is, and you realize a bunch of nested if statements, you have this gigantic angle or rocket ship shape or whatever you want to call it, and you can see that that's bad, but you can also look and see where there's the information going. Is it traveling westward, or is it traveling eastward?
So let's go over a couple of rules. We'll get into the code and how we will apply them. Rule number one, always return self. Rule number two, objects can query themselves,
and factories are exempt. So it's okay, it doesn't make any sense, it will. One of the great things about programming is that we get to use fancy words, words like polymorphism and encapsulation. And coupling, which has nothing to do with Valentine's Day.
Item potent. These concepts matter, but sometimes it's really difficult to see them or know how they work into our code. We also get to use some not-so-fancy words, like this one. Does everybody know this one? Yeah, there's some liars in the room.
This is probably the most, the simplest and the most powerful word that we have, that we get to use, and we can do amazing things with it. This is awesome, right? Although sometimes this kind of gets out of hand.
Too much of this, and you think, well, I'm not really sure what's going on. It's okay, you can refactor it. It sounds like some people have done that. I've heard about people talking about not using ifs and avoiding ifs, and sometimes you think about the argument
and it doesn't quite seem right. What's the harm in one little if, right? But the problem isn't that one. It's that our code ends up looking like this. It's all over the place. So let's start looking at some code. Here we have a class person that has an address,
and we want to display it. I know I've done stuff like this in active record when I was first learning Rails. It was amazing. You just put code in your active record object, and it displays properly in the view. So here's what we might want to display. Here's some information, right?
Depending upon what data we have, we'll display it in a certain way. We want to have a carriage return if we've got a street and a city or a province and postal code. We don't want to have a comma if there's no city followed by its state or province.
So we have to make some decisions about the data and what we do when there are missing pieces. I'm not going to keep the type that large. I'm showing a good amount of code. You're probably not going to have to read all of it, but I've got to shrink it down. So let's look at an implementation of this.
Don't read it, but how many ifs are there? How many? Five, six, maybe there's more. There's some down below that we probably can't see. How many responsibilities?
Too many, too many. I think that the correct answer for both of those questions is too many. And there's actually a bug, too. We'll still get a comma, even if we don't have a city. But it was hard to see, I actually wrote this code and I thought, well, let me use these ifs and let me find out about this information
and see if I can piece all of this together and ran it and I still had a bug. I'm sure a few more ifs would solve that. So let's start following the rules. Rule number one, always return self. So here's what it might look like, right? Down at the bottom, self.
All of our algorithm for displaying this is still there. So we follow the rule, what happens? It's broken. So this is an indication, if we follow this rule, maybe there's something missing, right? Maybe there's another object,
at least another object that we need. So I think, okay, well what principle can I follow to help me know what to do? I think this will help us understand and I'll talk a little bit about it and how confusing it is. Tell, don't ask is an idea coined by Andy Hunt and Dave Thomas
in an article they wrote where they wanted to encourage you and all of us to create a division of responsibility without causing excess coupling. I know when I first read about it, I didn't quite get it. I thought I understood and I moved on
and I know I hear this topic come up often. It seems to be a perpetual discussion topic for programmers. Another way to think of it is this. Command, don't query. Don't ask for information, right? We're not gonna ask an object about information and then perform some function for that object. It would be much easier if we just told it
to do what it had to do. I think about this actually raising my kids, right? We're gonna walk out the door, let's go. You gotta know, put your shoes on. You gotta know, you gotta have your socks on. You gotta know that shoes go on the correct feet. All that kind of stuff. I don't wanna have to do it.
So, let's look at this. When I first learned about tell, don't ask, I thought, this is great. Ruby is awesome because I can put question marks at the end of my methods and that's obviously a query. Right? It has to be. And the ones without them, those are commands. I'm telling the address to give me a street
or I'm asking if it has a street. Good enough, done, moved on. And I actually did this and I know that there are developers who think this and you're wrong. All right, so let's look at the code. Command, don't query. We don't wanna ask an object for information
about itself and do the work. We just want to tell it what to do. But here we've got queries. We're asking the address for its street and checking that value to see what it is. That's a query too. That's not a command even though there's no question mark.
Are there any commands here? I mean, it's hard to really say. All right, what else is wrong with this? This code is in person. We're displaying the address. This makes me think of this. Have you heard of the Law of Demeter?
Does anybody understand it? A couple of hands go up. Okay, so in this code, we're breaking the Law of Demeter because what it says is that an object really should only be able to access itself, any parameters that we've given it,
any objects that it created or directly held components, right? So it's internal structure is really what you should work with when you're creating the implementation of how an object behaves. We're looking at address. We're going through address to get to street, checking its value, and then there's all these. So we have to know the full object graph
of the relationships of these things in order to get this thing to work. So there's a lot of knowledge about the system wrapped up in this. This is bad. This is the person class, right? This isn't even address asking questions about stuff. So Andy and Dave, when they first wrote about this,
this was their sample code, right? What if we were programming a television and we had to figure out a way to turn it on? How would we organize it? Maybe this is close to what we might do. We've got our television. It has a front panel. We've got to get from the switches the power button and turn it on.
But what happens when the way on works changes? We decide we want to make it toggle or something like that. So that breaking change is going to go back through the power button. Switches isn't going to work anymore. Front panel isn't going to work anymore. My television isn't going to work anymore.
And the code that calls this is broken now. So at any point in that tree of objects, this user class, this method here will break. Congratulations, everything is broken. You're a programmer. All right, now what? So what did I say?
Do this. Just make a power up button on my television, right? Command that thing, just start up. I don't care how you do it. Just turn on. So this method can take care of talking to related objects. The code that uses it only has to know that
there's a television and it has a power up method. Back to this. What can we do? So clearly, there's an address involved here. Maybe we should do something with the address, right?
So we're returning self. We're still following the first rule. We could just tell the address to display. Is that a command? So here we move the behavior down into address.
There's still lots of queries though. So we're still asking a lot of information. Is that bad? So that's rule number two, right? Address can ask questions about itself. That's totally fine. This is okay. It should be able to know about its own data.
All right, but I want to fix the bug. So let's clean up the code. This is what we would get in different scenarios. The way it would display with some pieces there, some pieces missing, we add province and postal code together, we compact them to remove any nil values and then join them with a space.
And then here, we are adding city. So if you have a city, we need to add a comma afterward and then do the rest of it. And then finally, the street has to be in there. So we add the street and if there is a street, we're going to put in a carriage return and we're done.
But that line break, something's not right because we're working with an address and the address knows about the formatting. What if we did a different format, right? That thing doesn't really belong there.
So an easy way to do it, let's just put an argument in there, line break, right? That's great. Now, I can throw whatever I want in there. I can break it up however I like. And then of course, person needs to know about line breaks which that doesn't seem right.
Regardless, person is still returning self so we're never going to actually see anything. So that's not right. We still need another object. We can introduce a template to handle formatting, right? This object's job could be determining the format.
It can take an address and its method. The address would contain the data and it knows about the data but the template says, I am going to output this to standard out. You see there, the line on the bottom is just going to say, here's standard out. That's how this template works, right? And even it returns self.
The algorithm for the formatting is still the same but it's moved into the template. And if we add an HTML template, that thing knows how to write to a file and put in a break character.
So the templates control the output. How do we use this? We could change the method signatures of the person, right? To accept a template object. We can tell the person, hey, display your address on this thing, right? And then person can take that and send it down to address and say, here, you display yourself on this thing.
One of these things isn't following the first rule, follows the third rule. Factories are exempt, right here, where we set our default template. It would be kind of foolish of us to have new returned self because self in this case would be the class and then we'd never have a new object.
So it's okay. You eventually need to initialize objects. If we always return self, we'd never get new objects. So person sends the template along with the display command to the address and the address passes itself to the template, returns itself, and then the template
does all its data processing, outputs what it's supposed to output and returns itself. So what do we gain? Each object tells the next what to do. Each object is responsible for its own part of the domain
and we were forced down this path just by returning self. So there's still a problem that I want to cover up and that's that the data is still accessible from anywhere.
Demeter is calling. So I want to remove the ability to add if statements. I don't want to be able to query at all. I want to be forced to command. If this is a good thing and if I can put all these responsibilities in different objects and I'm forced to do it, then I'll do it. But if I can query and I can ask objects for information
and operate them on them, not only will I do it, but the next person will come along and see that that's accessible and add an if statement to put processing in the wrong place. Or maybe not the wrong place, but a place where it'll be difficult to maintain or change later. So I want to remove the ability
and I know this doesn't really remove it. You can always get to private methods, right? You can use send, but that's ugly. It's at least some syntactic vinegar to make you wince when you have to type send in order to get address or street or city or province. So now this stuff is protected. I only have these commands.
And what happens, here's our address, private attributes, and we broke it. So, was that the right thing to do? Person's blocked from querying, that's good, but template is blocked from querying as well.
So we can't have any demeanor violations and we don't have the ability to traverse the object graph inside a person, but we can't use a template either. So here's something that you could do to fix it. We can give the address an ability
to provide a value object to the template. So in this case, if you're not familiar with OpenStruct, it's part of the standard library. You can just require it, add a bunch of attributes to the object, and then you can call them, just like we do in our template. So there it is, right?
Instead of passing itself into the display address method of template, it just calls to value, which is also a private method, so person can never get to value, no other object can get to value unless you go through send and make it ugly. But why do this?
Why bother with all this? Because of this. We might have data that we just don't want to display. Right, there's sensitive information. Maybe you don't want somebody showing up at your home address. This is a web form, and you want to be able to mark something as private.
So if we add the feature to only have private information, maybe we still store where I live so I can get packages, but I don't want to display on my profile exactly where I live. Maybe I just want the province, or just the post-lecoup. I'd like to be able to mark things as private, private information. So what object should decide how to do this?
I think it makes sense to have the object that owns the data be able to decide what should be displayed and not how. So we can change this code to do this. Now this is an imaginary method. I'm not implementing protect privacy here inside of to-value.
But when we call to-value, maybe that's the thing that checks. Is this information available to the public? And there might be other ways that you want to expand that. Or you might want to check the protect privacy up in your display method. But wherever you put that, I think it makes sense for address to decide.
And you can come up with all kinds of ways to show or not show data and do different things. But the benefit here is the address is deciding what data to display, but not how to display. We're still using the template. We've made zero changes to the template, and it will still work.
We've made zero changes to person, and it will still work. So let's look at the template again. Right now in this case, we've got queries all over the place, right? Address province, address postal code, those are queries. We're getting information out of this thing. So that's bad.
Didn't we talk about queries being bad? They're everywhere. So this is rule number four that I didn't mention, but you can break the rules, it's okay. A value object is intended to be queried, and only that. You don't really do anything else with it. It basically simplifies our parameters.
So we can break that rule for that. But we can look at this code, and we now see that everything is traveling eastward. I don't ever get information in person about address. I don't get information about the template inside of address.
And even inside the template, we command standard out to put this string to its output. So this idea of east-oriented code, this is what James Ladd says about it. East orientation decreases coupling, and increases code clarity, cohesion, and flexibility.
I think that code, to me at least, shows it. We had a lot of queries, a lot of mixed concerns inside of person, which I know I've written code like that and it seemed totally fine. And you go down the road, and you have if statements
all over your code base, and individually they seem all right. But if you just took that first rule and applied it to your code, what would happen? You can look at your code and see the direction of information. And if things are traveling west,
that's probably an indication that you're leaking information, right? You should see it like this. It leaks information, it leaks knowledge, it leaks responsibilities. Queries actively encourage you to put code in the wrong place.
But commands encourage polymorphism. East-oriented code and commands enforce encapsulation. They push responsibility down into objects. It loosens coupling.
And I didn't talk much about this before. If you want to make code idempotent, you can tell the address to display itself. And if it has already done that work, you can tell it again, and it decides whether or not it needs to, right? So you can tell it a thousand times, and if it's done the work,
it can say, don't worry, it's done. You don't have to do it over and over again. Whereas if we queried for information from the address, we would have to keep all the knowledge about whether or not it was done inside of that person class as well, which could become a little difficult to manage. So to be east-oriented, always return self.
Objects can query about themselves. Factories are exempt. Break the rules sparingly, like in value objects. And James Ladd is on Twitter. I would love it if everybody said thanks to him
for coming up with this idea and adding to a way we can enforce tell-don't-ask in our programs. This is the difference. I know he's talked about east-oriented code before, and people said, well, that's tell-don't-ask. But the difference is you must tell.
If you're returning self, and you hide information about an object so that you cannot query it, then there's no other option. So I love making tools for myself to give me and my team guiding principles so that it's easy to do what we want to do.
So what if we had a tool to help guide us, right? Let's try it. Okay, so this code, I'm gonna walk through how you might make sure that you do something like this in your code. Say we've got a person, person has a friend,
and I wanna be able to tell that friend to make me a sandwich. Sudu is not required. So this is what we could do. We just define a method, make-me-a-sandwich, that goes and tells the friend to make me a sandwich. I don't like that.
I like to use Forwardable. If you're not familiar with it, that's pretty much what it does. It's very simple. I think it really hides a lot of the mumbo-jumbo of just setting up methods and just shows you, hey, all of these things that you specify up here, I'm gonna send that on to friend.
So in this case, if I do use Forwardable, it's really a query because the information that I get back is whatever the result of that method is, right? So I might tell a friend to make me a sandwich, but I'm gonna get whatever information
it returns from its method as a return value for this. So if I'm telling person, make me a sandwich, I don't continue operating on that person. I get whatever that method returns. So I wanna be as concise as Forwardable allows me to do that, but how do I do that?
Don't install this gem, right? I wrote this gem just to show a way that you could write your own code in your project. It is very small. To add this to your gem file would be ridiculous, but it's there. It's on GitHub.
You can read it. So let me just show you some tests that I wrote for it. I've got a friend here. The person's friend is set. Before the test runs, I clear the table, and when I tell the person, make me a sandwich, I assert that the contents of the table has a sandwich on it,
but I could still go through person wanting to get the friend to make a sandwich, right? And I don't want to do that. I wanna protect that relationship. I don't wanna have the object graph available. I wanna hide it. You should just have the commands.
So traversing the graph is bad. So Forwardable allows me to specify which one I'm going to send, and because it's a method on the class itself,
I can hide friend here. I can make it private, which is what we did before, right? Now, we can't traverse the objects related to person from the outside, and this is how you would use the gem. You just change from Forwardable to direction,
and I chose the word command instead of delegate. Just so you know, that's gonna be a command. You're gonna get back the object that you're currently working with, not the one that you're forwarding to. So it's a very simple change, and now the code still works,
but down at the bottom, I get the original receiver. So I assert that the person is the result of making me a sandwich, right? Sandwich is made, and I get the person back to continue operation. So that's what that test looks like, and I can continue telling that person to do things for me.
So we can only send commands to that object. I can't ask person for friend
and then go query about friend and what its favorite thing to do on a Wednesday morning is. So how these things are implemented is totally up to person. Some of these might be forwarded onto friend. Some might not, but I don't care. I just wanna get that person back
and continue operating, right? All I care about is that there's the person there. So all these return self, and I can continue operation. So this is pretty much it. This is that gem, so don't install it, right? And I can walk you through so you understand. The module direction is extended on the class,
and that adds class methods. So I have a class method called command, right? I can pass in a hash, and what this does is just loops through the hash where I've got the keys are the methods that I want and the values are the accessors that I wanna send them to, so the related object. Whole bunch of stuff.
It generates a string to define a method, takes whatever arguments or block that you might pass and sends it along to the value, right? So the value there would be friend in this case. And that's it, but the important part of this is that it returns self.
This is fairly close to what forwardable does, but it returns self. So I can do the exact same thing that forwardable does. I can just write configuration at the top
for all these methods are now exposed on this person, but I'm just gonna send them off to that thing to happen, right? But when you use forwardable, you get the result of whatever it was. You're not explicitly stuck with commands. In this case, you are. So you can still write short code. You don't have to define every method now and return self at the bottom of each one.
You don't have to just say, okay, well, we can't use forwardable. I've gotta now define them all and return self. You can just do some simple metaprogramming and that will work. So that's what it would look like. Very small change from what forwardable has. You can see, you can list other methods
that you wanna send on. So when you leave, go change your code, return self, and run your tests.
Find out. Did you break it? Maybe there's missing objects. Maybe responsibilities are in the wrong place if it did break.