Talks
The Case Of The Missing Method - A Ruby Mystery Story
Summarized using AI

The Case Of The Missing Method - A Ruby Mystery Story

by Nadia Odunayo

In Nadia Odunayo's engaging talk titled "The Case Of The Missing Method - A Ruby Mystery Story" from RubyConf AU 2019, she takes the audience through a captivating mystery involving Ruby programming. The story begins with Deirdre Bug, a private investigator and a pseudonym for Odunayo herself, who is visited by Mike, a junior Ruby developer in distress over his friend Jenny's notes on method lookup. Here are the key points discussed in the video:

  • Introduction to the Mystery: Deirdre Bug, bored and contemplating a career change, is drawn into the world of Ruby programming mysteries when Mike seeks assistance with unresolved issues surrounding Ruby methods.

  • Understanding Ruby's Object Model: We learn about Ruby objects, their classes, and the method lookup chain, as Mike explains the context behind Jenny's notes, which contain inaccuracies regarding methods and classes in Ruby.

  • Investigative Process: Deirdre initially struggles with Jenny's notes. By exploring objects using the Pry tool, she attempts to solve the mystery of the missing 'edible' method and the class hierarchy.

  • Discovery of Singleton Classes: Throughout her investigation, Odunayo introduces the concept of singleton classes, demonstrating their significance in Ruby. A board established connection opens her eyes to how methods can exist uniquely for specific instances.

  • Realization of the True Villain: As the investigation unfolds, it’s revealed that Jenny deliberately misled Mike to secure her place over him for a coveted internship. Deirdre aids Mike in overcoming this betrayal.

  • Experimenting with Domain-Specific Languages (DSLs): In a conversation with Ellen, a friend and developer, Deirdre learns about creating DSLs using singleton classes to simplify development processes.

  • Conclusions and Insights: The talk concludes with an emphasis on the importance of understanding singleton classes, especially when working with Ruby DSLs, which are integral to powerful Ruby frameworks like Rails. These methods can help developers manage complex scenarios effectively, especially during debugging.

Overall, the presentation combines technical insights with a narrative, illustrating the subtleties of Ruby's object-oriented structure in an accessible and entertaining manner, while reinforcing the importance of knowledge in programming.

00:00:00.450 So our first speaker for our conference is Nadia Odunayo, who has come all the way from London. She's also managed to bring with her fantastic parents, Lara and Wale, who are sitting in the front here. Can you give them a warm welcome? It’s Luke. Where's Nadia? I told her that her mum would love that I did that, and her dad would hate it. Is that right? So, Nadia almost didn't end up as a software engineer. She started studying at Oxford University, which is amazing because the whole UK is just so cool, and she studied Philosophy, Politics, and Economics with the idea of becoming an investment banker.
00:00:20.910 However, after doing an internship, she realized she did not want to become an investment banker. Instead, she ended up winning a competition to attend a boot camp at Makers Academy. Yes, I have my notes, but they're gone. After that, she worked for quite a few different companies, including Pivotal and Cloud Foundry. Since then, she's been involved with a whole bunch of different projects of her own. She's been involved with Speaker Line, a project all about demystifying the Call for Proposals (CFP) process.
00:00:51.539 She has worked on the Ruby Books caliber podcast, which is available online and really worth a listen. Additionally, she served as the CTO of Code Newbies, which is a podcast and community that has been super inspiring for many who are learning how to code. She's also an amazing hip-hop dancer, according to her Instagram, which is full of her dancing everywhere. It's incredible how she manages to balance all these activities while being a developer at the same time. I'm exhausted just describing her impressive track record.
00:01:06.480 So, I'm going to let her do the talking now. Can you please give a very warm welcome to Nadia Odunayo?
00:02:22.980 Okay, hi everyone. So, I've got a question for you: Do you have a side gig? You know, something that brings in a little extra each month? I do. At the time of the story I'm going to tell you today, I was a product manager for a software team at a bank, but by night, I'm a private investigator.
00:02:30.930 And what is it I investigate? Ruby crimes! No, not jewel theft, but the programming language. Why did I decide to go into private investigation? Well, there's been rumors of a shady mastermind causing havoc and confusion among Ruby developers worldwide with his constant meddling in the source code, and I wanted to help put a stop to it. I decided that I couldn't do this job under my normal name; I needed to protect my identity, so I chose the name Deirdre Bug. Why did I choose this name? Well, only for the sole purpose of making debugging a lot more fun.
00:03:06.840 Today, I want to tell you about one of my more memorable cases: the case of the missing method. Chapter One. I was sitting alone, bored in my flat, when the doorbell rang, and it was Mike. He didn't look too happy. Let me tell you a bit more about Mike: he was an acquaintance of mine; we had some mutual friends and saw each other now and again. He was a 24-year-old junior Ruby developer who got excited about test-driven development.
00:03:51.690 He had been applying for an apprenticeship at the prestigious Ruby Institute of Professionals and had managed to go through all of the stages, beating hundreds of applicants to the final stage. It was happening in two days, and there were two people left fighting for the final spot: himself and a woman named Jenny. Jenny was 27 years old, also a junior Ruby developer who loved all things Rails. She was also Mike's best friend and housemate.
00:04:36.200 They had quit their consulting jobs together to enter into the tech world and not long after decided to live together. For the interview, they agreed to work together to prepare to ensure that each had the best shot. "May the best person win," they said. They had been asked to research a series of topics they would be grilled on, and one of them was method lookup, which they assigned to Jenny.
00:05:07.240 This was the reason Mike had come to me. He said, "Something doesn't add up in Jenny's notes, but she's been so stressed and panicked, unusually so for an interview, that she won't hear me out." He continued, "So she's convinced that she's right, and she says they don’t have time for my doubts. If you can help me find the answer to this mystery, I’ll have what I need to confidently correct Jenny’s notes and save us from interview failure." So, he reached into the satchel he had brought with him and drew out some sheets of paper containing Jenny’s supposedly flawed notes.
00:05:50.690 I asked him to walk me through them. Jenny had used boxes to represent the concept of a Ruby object. All objects have a label called 'class' that acts as a reference to the parent class of the object in question. Some objects are instance objects, and their class labels refer to another Ruby object of a type called 'Class.' All objects of type 'Class' also have their own class label and a methods label, which lists all methods that instances of that class can call.
00:06:35.850 Jenny wrote, "Any class you define is an instance of a class object called Class." So, if we were to write 'class Cake' in our code, we are creating a class object named 'Cake.' It's an instance of another class object named Class. She had also included a method definition in her notes: an instance method called 'tasty' that returned true if the flavor of the cake was 'carrot' and a class method called 'edible' that always returned true.
00:07:15.180 Then she explained, "Imagine if we had an instance of a cake called carrot: 'carrot = Cake.new'. This is what the method lookup chain would look like: the 'carrot' object would have a class label that points to a 'Cake' object. The 'Cake' object would also have a class label, and it would have a methods label that points to a table with the entry 'tasty.' Since all instances of 'Cake' can call 'tasty,' the class label for 'Cake' would point to a Ruby object called 'Class' and hold a methods label pointing to a table with the entry 'edible.' Jenny wrote, 'a method definition always comes from an object class.'
00:08:05.520 At this point, Mike shook his head in frustration and said, "It cannot be that the edible method lives on the parent class for all classes. Can I show you something?" He asked. "Can I jump into your computer?" I said okay. He went over to my desk, opened up a terminal, entered 'pry,' and loaded in the cake class from Jenny's notes. The first thing he did was demonstrate that the class of 'cake' is indeed Class, and then he searched the instance methods of Class for 'edible.' What a mystery!
00:08:43.780 I was stumped. I wasn't sure how to proceed with this one, but Deirdre does love a challenge, and if anyone can do it, she can. This meant a lot to Mike; he was prepared to pay me handsomely, so I agreed to take on the case. Chapter Two: Ever heard of Google?
00:09:01.560 If you care about privacy like I do, you might go for DuckDuckGo. Well, as Deirdre, I don't believe in these tools; I don't trust them. It’s no coincidence that with this approach, I've become the best Ruby PI the industry has to offer. But what did I have? Books, books, books, and more books. I spent a whole day quickly skimming through a load of books, but I couldn’t find anything useful.
00:09:40.080 So, I thought, why don’t I form a hypothesis and go from there? I decided the edible method, while not in 'cake,' must be somewhere in the ancestry tree. So, what’s the ancestry tree? It shows all the classes and modules that an object inherits from, indicating all the possible places that methods could come from. I thought, why don’t I create a method to help me search this ancestry tree? I created a method called 'where' that took two parameters: an object and the method we were looking for.
00:10:12.000 It searched through all of the ancestors of an object's class to try and find the class where the instance methods of that class—and only that class—included the method we were looking for. I thought, let’s give it a go. I created an instance of 'cake' and wanted to find the 'tasty' method in relation to that instance. 'break = Cake.new;' and now it was the moment of truth: where was 'edible' hiding?
00:10:51.120 Nowhere at all! What a mystery! So, at this point, I was confused and thought, okay, time for some fresh air—a change of scenery. I decided to head to my favorite co-working space. Here, I feel at home, surrounded by people hacking away, and I quickly settled down at a desk. However, given my naturally inquisitive nature, my eyes couldn't help but stray to the screen of the guy next to me.
00:11:19.180 He was an IRB user, playing around with something called 'ObjectSpace.' I thought it looked interesting, so I asked him what that was. He said, "Well, it's a way you can interact with all of the live objects within a Ruby session. So, if you were to type 'ObjectSpace.count_objects' and pass in the symbol 'T_CLASS' into IRB, then you could see all the live class objects that you have in a Ruby session." I figured I had been working hard, so it was time for a break.
00:11:47.490 So when I got into IRB, I counted all the live class objects: 936. I said, "Okay, let me create a class and see if that number goes up to 937." 938! That's strange. Let me try that again: 940! What a mystery! But I couldn’t spend too long pondering this; it was just then that the phone rang, and it was my friend wondering where I was. I had completely forgotten I had agreed to go to a tech lecture with her.
00:12:24.540 I was anxious because I didn't have much time to solve Mike's case, but I had been canceling on this friend lately, putting my PI duties first. So, I thought, on this occasion, I'm going to make a bit of time for her. I rushed out the door. Chapter Three: I remember I turned up just in time for the start of this lecture about a language called Smalltalk. I didn’t care about it much, after all, I only have space in my heart for Ruby. But you know, I listened; I was there for my friend after all.
00:12:49.890 The lecturer said, "Okay, Smalltalk was created in 1970 and led to the birth of object-oriented programming." However, I couldn't concentrate for much longer. I couldn't stop thinking about my play with 'ObjectSpace': each time I created one new class, two new objects were being created. "Just pay attention," my friend said. I looked up and saw the lecturer asking the room, "Well, what is the class of a class in Smalltalk?" She had this polygon object and she'd asked the system, "What's the class of Polygon?" It returned PolygonClass.
00:13:21.279 Then, she asked the system, "What is the class of PolygonClass?" and it returned Metaclass. The lecturer reiterated that the class of a class in Smalltalk is called a Metaclass, and in fact, all languages inspired by Smalltalk have their own concept of Metaclasses, including Objective-C, Java, Python, and Ruby. Something clicked in my mind: one class, two objects. I made my apologies to my soon-to-be no longer friend and rushed out the door.
00:14:03.890 I thought I'd try my luck, but of course, that would be too easy! I decided to see all the methods that exist on 'Cake', and I found this list pretty overwhelming. I thought, okay, let me look at all the methods of 'Cake' that contain the word 'class.' This was much more manageable, and two methods stuck out: one was 'superclass' and the other was 'singleton class.' So, the first thing I thought to do was remind myself of the ancestry tree for 'Cake.' I knew the 'edible' method did not live in any of these places.
00:14:43.570 So I checked what 'superclass' returns: 'Object.' Not what I'm looking for; it's in the 'Cake' class's ancestry tree. Let's try the other one. Ooh! This was different; I hadn't seen this before. When I looked at the ancestry tree of the 'Cake' singleton class, I saw three new objects that I had not seen before. Very interesting! So I thought, let me go back to my 'where' method and instead of searching for the ancestors of an object's class, let me look through the ancestors of the object's singleton class, so I could explore those new objects that I just discovered.
00:15:32.250 Then, it was time to give it a go! I thought to check that what I thought was happening was indeed happening, and in my excitement, I forgot how to type, but I finally confirmed that the 'edible' method lived on the 'Cake' singleton class. Case closed! Chapter Four: At this point, I'm super delighted and eager to share the news with Mike. I also took a moment to think, you know, maybe I should retire because this would prove to be one of the biggest successes of my career, and they always say you should quit while you’re ahead.
00:16:29.340 I gave Mike a call. I said, "Can I come round? I solved the case!" He replied, "Brilliant! Of course, come round; I'll have the notes ready for you." But although I had solved the case for Mike, I wasn't satisfied. I had just discovered a whole new concept in the language that I hadn't heard of before: singletons.
00:17:08.850 So instead of going directly to Mike's, I decided to take a detour and visit a friend of mine. Her name was Ellen; she was 43, a freelance developer who regularly contributed to the Ruby language codebase. I proceeded to tell Ellen all about the case, and when I was done, I asked her, "So, singleton classes, what are they?" She responded, "Well, the hidden class is created internally behind the scenes in Ruby; they're there to hold methods defined for only one particular instance.
00:17:46.550 Take your instance 'carrot' of the 'Cake' class. Its singleton class would hold methods specific only to 'carrot' and not to another instance, say if you had one called 'red velvet' or 'chocolate.' I said, okay, giving them this working way behind the scenes, when does knowing about them become useful? She thought for a while and then told me about one of her recent clients, Budgeting Inc. They were creating a clever artificial intelligence machine learning personal finance tool for small business owners.
00:18:39.480 They needed to roll out slightly unique versions of their software for each new city they entered. Ellen recalled how horrified she was when she first looked at the codebase; different developers had been responsible for each new city, and it looked like there was a new approach for each one. This meant there was a lot of duplication; sometimes it was obvious what had been copied, and sometimes it was less obvious. It resulted in a lot of wasted time in development because the developers were often reinventing the wheel and doing a poor job.
00:19:30.040 There were lots of bugs, some things had been copied, others left out, and it was difficult to identify what was vital. The developers were unhappy; they had a lot of tasks on their plate, and the cognitive load was high. The product owner was also unhappy because delivery was either very slow or things spun out quickly and were bug-ridden. Ellen thought, "Well, why don't I create a DSL, a domain-specific language?" She asked me if I knew what she was talking about, and I said, "It’s a mystery to me, Ellen; you’re going to have to explain." So she beckoned me over to her computer.
00:20:20.580 She opened up a Pry session and input a class called 'CityInstance'. It had a class method called 'construct' which took a block. Then there was a variable called 'city,' which was instantiated by the 'new' keyword. Then we called 'instance' passing in the block on 'city.' She said, "Pay attention to this line because it's going to become important later." When we return 'city,' we have an attribute reader called 'taxes.' The initialize method sets up an instance variable 'taxes' as an empty array, and we have a method called 'tax' which takes an argument 'name' and we push that tax into the 'taxes' collection.
00:21:43.150 Ellen said, "Let's give it a go!" She created a city called New York and added a couple of taxes before printing out the taxes. "There we have it," said Ellen. This is a very simple DSL, and we can quickly spin up lightweight city objects. Then she said, "Well, imagine if we had other properties— not just taxes but maybe a list of banks or a list of different finance schemes that you could find in each city, or imagine if we had more information about each of these properties." So, perhaps the taxes could have information about the rates and thresholds.
00:22:42.580 Using this simple framework, it’s not hard to extend the city class to create incrementally more complex city objects. Well, imagine taking that to the next level," said Ellen. "Imagine interacting with this CityInstance class in the same way on the command line, but instead of just creating variables in our Pry session, we’re actually spinning up subclasses of 'city' and other related models for each tax scheme or bank that you list." She explained how this was the sort of thing she ended up developing for Budgeting Inc, a DSL which allowed for quick and easy scaffolding of each new city subclass and any related classes.
00:23:38.630 So now we know how to create identical city instances with different names and different taxes. I asked Ellen, "What about if one city had a quirk?" She said, "Okay, think of a place in the UK." After some thought, I chose Bath because that was the scene of my first case. She said, "Okay, let's create Bath and add a tax." Then she said, "Think of something that makes people in the UK really unhappy. Did you have any suggestions?" Unfortunately, she didn't like my suggestion; she thought it was controversial. So we decided to focus on the fact that it rains all the time, and when it rains, the Government clears everybody’s taxes because they feel sorry for everyone.
00:24:35.250 So, we have Bath, we see their taxes when it rains, and they call it a 'Rainy Day Amnesty.' Now there's no more taxes! But remember our friends in New York? Ellen said, "Well, they've heard about this Rainy Day Amnesty in Bath and they want it too!" So it rains, and they decide to do a Rainy Day Amnesty, but it doesn't work: 'undefined method: rainy_day_amnesty for CityInstance.' Well, Ellen, do you know what these characters are? Let me show you something." She called 'singleton class' on 'NewYork' and I could see that it was the same object.
00:25:33.600 Ellen said, "When we enter the realm of DSLs, calling methods like 'instance_eval,' we leverage the existence of singleton classes because what instance_eval does is store any method declarations passed in via the block onto an object’s singleton class. Hold on to that for a second," said Ellen. She wanted to take a step back to a high level for just a moment.
00:26:29.130 She explained that creating a DSL like this enabled the developers to spin up each new city instance effortlessly, abstracting out all the key similarities between any city. Developers had a frictionless way, via the command line, to spin up the foundational elements they needed. The codebase was better maintained because all of the scaffolding had been well tested at once by Ellen. None of the developers had to worry about it; they could focus on the interesting bits, which was the customization required for each new city.
00:27:23.670 The scoop was much more refined, so now the developers were happy because interacting with this system was a joy. It was easier to have a high-level overview of the whole domain in their heads purely by looking at the documentation for the DSL. The product owner was also delighted because there were far fewer bugs, delivery was faster, and she could speak in the same language as the developers by expressing new requirements using the DSL's terminology.
00:28:29.490 But let’s go back to singleton classes and why knowing about them is useful. One of the main reasons I was able to complete this project to a high standard was because I understood exactly where I was defining methods at any given time. Once you enter the realm of dynamically creating classes and methods, all singletons, the class hierarchy and method lookup gets much more interesting.
00:28:57.860 You might get error messages that you can’t make heads or tails of, and you need to spot when singleton classes might be involved and where they’re hiding because that could save you a lot of headaches. But beware! I've been going on about DSLs, but they’re not the answer to everything. Perhaps you have some complex, repeated business rules and you need to customize behavior in specific cases, then you can consider DSLs, but even then, approach them with caution.
00:29:38.170 You don’t need to be writing DSLs for it to be beneficial to understand how they work. So are you using Rails? If so, you see DSLs every single day. Ellen went over to her computer and showed the Rails Guides website. She said, "Active Record migrations are carried out via a DSL when you specify how your table behaves using commands like 'create_table' or 'string.' That’s a DSL! When you specify how your Rails app handles HTTP requests, that’s also done via a DSL.
00:30:15.420 I was just doing this stuff by rote! I didn’t stop to think about what was happening behind the scenes every time I typed the 'resources' keyword into my 'config/routes.rb' file. And there's more of these tenant DSLs. She said that when people talk about Rails magic, it's not really magic; it’s more just a collection of well-written DSLs!
00:30:58.040 Ellen said, "I hope you’ll TDD all of the time." I said of course! Would you take me for someone who wouldn’t? She said, "Well, aspects like described contexts and blocks, those are all DSLs. With all these Rails and RSpec DSLs, knowing about singleton classes can help because you might find yourself in a tricky situation, and you can’t make heads or tails of it. You’ve spent a long time debugging, and this is particularly true if you inherit a codebase.
00:31:35.800 So if you’re seeing a funny bug to do with methods, you never know; singleton classes might be the answer. Having them as part of your toolkit for debugging is useful. I was feeling super leveled up by the end of this conversation and armed with this new knowledge, I headed over to Mike’s.
00:32:25.520 But when I arrived, I found Mike had tears in his eyes. He’d obviously been crying recently and had crumpled pieces of paper in his hand. He raised his arm towards me, offering the papers. They looked exactly the same as the method lookup notes Jenny had brought to me the other day. I took the notes from him and looked through them, but I couldn't see what was wrong; they looked exactly the same.
00:32:56.450 There was the 'carrot' object with its class label pointing to the 'Cake' object, which had its class label and methods label, with the entry 'tasty.' Then there was a class object with its methods label that had the entry 'edible.' But wait! It didn’t say 'class' like last time. This time, it said 'Cake singleton.' When Mike noticed this difference, he fell to his knees and broke down in tears.
00:33:33.990 "Jenny knew about singleton classes all along!" he cried. He had gone into her room to find the notes in advance of my arrival, and this was the copy he found. It turns out Jenny was so desperate to secure the job for herself that she set out to intentionally mislead Mike in the hope that he would fail a whole section of the interview and therefore look underprepared.
00:34:22.480 I felt disappointed in myself because I had been so focused on the main villain, the shady mastermind, that I failed to spot a villain right under my nose! My best friend had tried to sabotage him! Mike cried again, and he started wailing, 'I’m not going to go to the R.I.P. interview tomorrow!' I said, 'No! You cannot give up now!' I crouched down next to him, gave him a comforting pat on the shoulder, and said, 'You can do this! I have just the thing to set you apart from Jenny.'
00:35:34.370 He looked up, hopeful. 'Have you heard of DSLs?' I asked him, and I proceeded to tell him everything that Ellen had just shared with me. Although Mike still looked devastated as I left him, I had confidence that I had inspired him with the power of singleton classes. I believed he would pull himself together and go secure that R.I.P. internship for himself.
00:36:10.021 Two months later, I managed to drag myself to a Ruby hack night. As I arrived, I was milling about, enjoying the free food and drink when I heard a couple of people whispering in the corner, 'Whoa, she's really famous!' One of them said. I looked across the room, and who did I see? Mike!
00:36:41.470 So, I walked over to catch up, and I told him I had been reflecting on the case of the missing methods and my takeaways. We discussed singleton classes being there to hold methods defined for one particular object, and that when you understand them, it opens up a whole new world of Ruby, enabling the dynamic creation of classes and methods on the fly in more complex applications using things like DSLs.
00:37:14.900 But I said I still felt like day-to-day, I could get by ignoring them. 'Not really,' Mike said. 'I mean, like you said, if you're using DSLs, it's a good idea, and they underpin popular frameworks like Rails. But day-to-day, you can get by. However,' he said, 'understanding why singletons are there is super interesting and empowering.'
Explore all talks recorded at RubyConf AU 2019
+10