RailsConf 2019

The Elusive Attribute

The Elusive Attribute

by Chris Salzberg

The video titled "The Elusive Attribute" was presented by Chris Salzberg at RailsConf 2019 and explores the concept of "attributes" in the Ruby on Rails framework. The talk begins by illustrating the complexity of attributes, which can take multiple forms—such as methods in models, database columns, or parameters in requests—highlighting their importance and ubiquity in the Rails ecosystem.

Key points discussed include:

- Introduction to Attributes: Attributes appear simple but are central to Rails functionality, representing a bridge between various elements like views, controllers, and database records.

- Bicycles as a Metaphor: The speaker uses the analogy of bicycles to explain attributes—they seem straightforward but reveal complexity upon closer examination. Just as most people cannot accurately draw a bicycle despite knowing how to ride one, many Rails users may misinterpret the workings of attributes.

- Internal Structure: The talk delves into ActiveModel and ActiveRecord, examining how attributes are defined through methods in Ruby, underscoring their role in the hierarchy of these modules. Salzberg mentions a pull request regarding generated attribute methods that highlights the nuanced differences in how attributes interface with models.

- Method Missing Mechanism: A thorough examination of the Method Missing mechanism demonstrates how Rails handles attributes dynamically, allowing for aliasing and the possibility of accessing attributes not explicitly defined in the schema. This functionality illustrates Rails' flexible architecture but also introduces complexity that can confuse users.

- Optimization and Performance: The speaker critiques the existing implementation's complexity, suggesting the methods could be restructured for better simplicity that is easier to understand when unpacking Rails.

- Main Takeaway: Emphasizing the importance of not only understanding but also questioning framework conventions, Salzberg encourages developers to explore Rails deeply, pushing beyond reverence to practical engagement and disassembly of its elements.

The conclusion of the talk serves as a call to action: developers should strive not only to use Rails but to interrogate and repair its functionalities, akin to learning to draw a bicycle by attempting to create one from scratch.

00:00:20.599 Thank you, Sebastian, and thank you, Cookpad, for organizing this track. My name is Chris Salzberg.
00:00:27.840 The title of my talk is "The Elusive Attribute." Thank you for coming to this presentation. I was really worried that being the last talk in the last set of presentations at the conference, nobody would show up. However, I'm very glad to see some people here.
00:00:34.050 This track focuses on unpacking Rails. One of the other speakers mentioned that Rails is really big and that there are many components to explore.
00:00:39.960 Some parts we are familiar with, while other parts remain unknown to us, but we are aware of our ignorance. I believe that there is a lot more to discover.
00:00:45.690 This reminds me of a famous quote by Donald Rumsfeld that many of you may know. In a press conference in 2003, he stated, "As we know, there are known knowns; there are things we know we know. We also know there are known unknowns; that is to say, we know there are some things we do not know. But there are also unknown unknowns—the ones we don't know we don't know."
00:01:03.899 I've been riding bicycles for as long as I can remember, basically before I could walk. Here’s a picture of me when I was about four years old, riding with my dad. My father is Dutch, and the Dutch have a strong fondness for bicycles. He raised my brother and me with a deep appreciation for their importance. I grew up in Montreal, Canada, which might surprise you because, despite its cold winters, it's a genuine cyclist city. People bike through all sorts of weather, and I was no exception.
00:01:34.349 In my twenties, I moved to Amsterdam, the biking capital of the world, where I continued to ride bikes regularly. Now, I live in Tokyo, where I've also been biking for years. Bicycles remain an essential part of my life—I use them every day, and I really couldn't do without my bike. So, I know that I know bicycles as a user, but there are certainly many things I don’t know about them.
00:02:07.660 For instance, if my bike breaks and it’s anything beyond just putting the chain back on, I feel clueless. I usually take it to the shop to get fixed. Recently, however, I discovered something I didn't know I didn’t know: I didn't know how to draw a bicycle. If you haven't tried this, I invite you to do so. In your head, picture the frame, pedals, and chain of a bicycle, and you may find it surprisingly difficult.
00:02:54.569 Cognitive psychologists have studied this phenomenon because it demonstrates how our minds can convince us we know things we actually don't. These drawings are from a study where participants were asked to draw bicycles, and none of them actually worked. Each drawing features various inaccuracies. Here's another image of a bicycle where it isn’t immediately obvious if something is wrong, but the design would fail if ridden.
00:03:09.000 When people attempted to draw bicycles, many, including myself, struggled. An artist named Gianni Luminati became fascinated by this phenomenon and asked many people he knew to draw bicycles. He discovered that while most attempts had some issues, they were all unique in their designs. He compiled these drawings in a project called "Villa Velocipede." It's interesting to note that it can be difficult to immediately identify flaws in some of the designs.
00:03:39.120 What Giannini found was intriguing: even those who ride bicycles regularly often struggle to draw them accurately. Conversely, he noted one group of individuals was statistically better at drawing bicycles—those who physically fix bicycles. They understand both the form and the function of the bike's parts, which embodies an important understanding about bicycles. In his own words, 'People who are really into bikes start out from the frame. They represent that small percentage of people who get the drawing right.' Most others tend to start from what they know best.
00:04:51.630 So, what does all this have to do with attributes? I believe attributes are somewhat like bicycles. For many of us who use Rails, we interact with attributes constantly; they appear everywhere in our views, filters in controllers, and channels. They seem simple on the surface.
00:05:10.050 However, what I want to present to you today is that attributes can be elusive, falling in the category of things that we think we understand but don’t fully know. To bring this point to life, consider those bicycle drawings. If I asked you to illustrate how an attribute operates, I’d expect your sketches to share common elements. My guess is that many of you would start with a method, like the title method on your post model. You'd likely visualize this as a wheel; something you instinctively know must be there. Then, you would probably illustrate a column from the database, representing the source of the attribute’s data, like the frame of the bicycle.
00:05:58.170 As we go deeper, we might explore how ActiveRecord connects this method to that column. But I want to analyze this over three parts. First, we will implement a bicycle structure like that above, and we will observe how it fails. Next, we’ll recreate the bicycle, beginning with the frame, to achieve a more accurate representation of how it functions in practice. And finally, we'll examine what went wrong originally and how we can enhance it.
00:06:28.140 Allow me to introduce myself further. My name is Chris Salzberg, and my handle is Sheila Emma—a translation of my last name, which means 'salt mountain.' I work for a company called Educative, which generously funded my attendance here. If you're looking for a great place to work in Tokyo, let me know! I write about subjects like the module builder pattern on my blog, DejaMetacom, and I also have a Gem called Mobility for translating model data.
00:06:57.120 Now, getting back to the topic at hand, I want to discuss methods and columns. To start, I’ll reference a pull request I recently made to Rails, titled 'Give Generated Attribute Methods Module Name.' This pull request is quite trivial, comprising only a five-line change with a single line addition. It has been merged into the Rails master branch, so if you check there now, you'll find it present. It doesn't dramatically alter functionality, but it does provide a more informative naming convention.
00:07:34.740 If you take any ActiveRecord class, like a Post instance, and examine its ancestors before the new change, you would notice various mixed-in modules and classes. However, the newly added module is unique; it is an anonymous module labeled as 'ActiveRecord:Attributes:GeneratedAttributeMethods.' While I won't detail its architecture, it serves an important role.
00:08:01.410 If we now create a post and give it a title—"The Elusive Attribute"—and use Ruby introspection, we can explore this module further. By using the method object for the title method, we can ask where this method is defined, which will reveal that it resides in the 'post:generatedAttributeMethods' module. This will allow us access to a full range of instance methods related to the title column and additional columns in the database. These are termed 'attribute methods,' which correlate directly with methods defined in ActiveModel and ActiveRecord.
00:08:42.880 While attributes may seem straightforward, ActiveRecord overrides many of the existing methods present in ActiveModel, making it less simple than it appears. Moreover, understanding attributes and how tree methods function is essential if we aim to learn more about the workings of the system. Therefore, let’s return to our diagram: we had a method linked to a column, which we’ve addressed briefly.
00:09:38.240 Now, we want to observe the role played by the module in our example. We can assign this module to a variable, 'mod,' and experiment. Imagine this as a genetic experiment where we eliminate a specific gene to observe the effect. Since removing a module outright in Ruby isn't feasible, we can take an alternative route by deleting all methods from the module.
00:10:11.230 Once we've removed the methods, inspecting the module again will reveal that it’s empty. When we call the title method afterward expecting it to raise an error, it surprisingly returns the value "The Elusive Attribute." How can this be?
00:10:36.530 Well, if you’re familiar with Ruby, you understand this can only occur through 'method_missing'—a method that handles calls to methods that aren't explicitly defined. This also demonstrates that the title still maps to the original value through a 'missing method' pathway, which is what we should next analyze.
00:11:12.740 Next, we transition to the other side of our diagram. Previously, we manipulated methods in our module; now we are taking a similar approach by considering columns in ActiveRecord. This time, however, we won't actually clear columns from the database. Instead, we'll trick ActiveRecord into thinking there are no columns on the post table by manipulating the schema cache, which contains the cached schema information.
00:11:59.080 We can access the schema cache and set the value for the 'post' key in this columns hash to an empty hash. This manipulation will signal to ActiveRecord that there are no columns in the table. When we check the column information for the post class, it will manifest without any defined columns, leading to the behavior expected when attempting to query any method related to these nonexistent columns.
00:12:34.660 If we instigate a SELECT statement requesting the first record from this model, we'll still be able to retrieve values accurately from the database, since the database inherently understands that those values exist. The database returns all column values even while the ActiveRecord considers there to be no columns present.
00:13:22.330 This disparity highlights that ActiveRecord handles attributes in two distinct manners: the known defined attributes that exist in the schema and what the database returns. If we execute a direct query through ActiveRecord without going through the schema cache, we can ascertain the underlying column values irrespective of how our application interprets the schema.
00:14:01.780 So now, after having seemingly knocked out the columns from the schema, when we call the title method, we trigger another round of method resolution. Since there is no title method defined in the module, the call traverses to method_missing, which effectively links back to the original database call through the querying results.
00:14:44.520 Next, we take a slight deviation in our exploration while considering starting points for our discussions going forward. The illustrations are important, so we can envision the differences in operations. One aspect of Rails revolves around how it handles dynamic attribute names, as seen with aliasing when using query methods. For instance, if we use a database query to select a column and alias it to a different name, this creates a new dynamic interaction.
00:15:32.830 This scenario implies that the alias 'foo' would not have an explicit method defined in ActiveRecord, leading us back through method_missing. Since it’s dynamically set, method_missing will query the matching method in the original results. This approach challenges our initial assumptions around method definitions because, one way or another, the query results are returned individually and can be represented as dynamic methods.
00:16:31.640 It's important to visualize that there is a front-door and back-door method resolution. The attribute methods defined in ActiveRecord serve as the easy entry points for accessing data, while method_missing captures the more elusive elements. While it may seem convoluted, there are extensive optimizations that affect the performance of such queries. Hence, we can trace method resolution pathways based on whether the resolution is through direct method calls or leveraging method_missing.
00:17:19.440 Next, we delve into a more accurate representation of how attributes work through diagrams and routes. The underlying mathematics includes managing constraints and their dynamic natures. Attributes can unpredictably manifest as different names while still holding their core purpose, notably through aliasing scenarios. As we explore these interactions and routing definitions, we’ll better understand this orchestration.
00:17:44.410 I think it's clear now that there are several methods that iterate around these functional dynamics. To illustrate this, let's refer back to the matcher concept. We've thoroughly analyzed this method resolution framework, where one path represents a defined interaction model while the method_missing pathway serves as a secondary fallback. We categorized methods based on whether they directly draw from defined attributes or utilize the intermediary resolution method.
00:18:24.050 Turning now to a practical takeaway, my goal is to examine areas for improvement by coming together through shared knowledge. With bicycle frames, we see efficient and straightforward designs winning over complicated ones. Attributes should strive for similar elegance, accomplishing their core purpose without unnecessary complication, achieving clarity comparable to well-designed bicycles.
00:19:02.360 We are at a critical juncture; the question arises, how do they perform their intended duties without resorting to convoluted methods of operation? With the flattening of these methods or unnecessary complications—much like our bicycles—assists in avoiding confusion, allowing Rubyists to appreciate core interactions while still achieving desired outcomes.
00:19:44.490 To sum up, I want to emphasize how crucial it is for developers within our Rails community to explore the definitions and workings of attributes clearly. Many struggle to articulate the underlying mechanics of attribute definitions, which concerns me. I believe we ought to strive for transparent dialogues around this, better equipping ourselves to navigate the complexities of Rails development.
00:20:27.580 Taking integration to heart, I hope this exploration helps uncover insights on Rails' architecture, pushing us collectively toward deeper understanding and solution-oriented dialogue. Engaging in practical exchanges strengthens our collective grasp of how Rails functions, while also allowing us to challenge existing paradigms. I encourage everyone to delve into methods with a sense of curiosity, asking how they work before simply accepting them.