ActiveRecord
Off the Tracks - Challenging the Rails Mindset

Summarized using AI

Off the Tracks - Challenging the Rails Mindset

Nick Sutterer • February 20, 2013 • Earth

In the talk "Off the Tracks - Challenging the Rails Mindset" by Nick Sutterer at RubyConf AU 2013, the focus is on exploring the advanced features of the Rails framework and how to effectively manage application complexity through Object-Oriented Programming (OOP) techniques. Sutterer begins by addressing the innate prejudices people have, even applying that concept to programming, and argues for a more nuanced understanding of Rails.

Key points discussed include:

- Complexity in Rails: Rails provides powerful abstractions such as ActiveRecord, but using them without a clear understanding can lead to bloated controllers and models.

- Object-oriented Techniques: Sutterer advocates for breaking down monolithic ActiveRecord classes into smaller, standalone classes. This can alleviate the 'fear of the class' that many Rails developers experience.

- Patterns for Management: The use of Plain Old Ruby Objects (POROs) is demonstrated through a chat application example, illustrating how to handle domain logic separately from persistence.

- Data Mapper: Sutterer introduces Data Mapper as a way to manage persistence while keeping domain logic clean and unobstructed.

- Views and Components: The talk emphasizes the importance of encapsulating view logic to avoid complexity, recommending the use of the Cells gem for better organization of partial views.

- Controller Complexity: It is suggested to decompose controllers using the Objectify gem, adhering to the single responsibility principle.

- API Development: The speaker covers effective practices for creating APIs in Rails, including the use of the Representer pattern for managing document handling more coherently.

Concluding, Sutterer stresses:

- Embrace Object-Oriented Principles: Developers should not shy away from using more classes as it leads to cleaner, more maintainable, and better-structured code.

- Rails Flexibility: Rails does not strictly enforce conventional methods, offering the flexibility to adapt architecture incrementally to enhance code quality.

Overall, the talk encourages Rails developers to rethink their approach to application structure and to utilize OOP techniques for improved software architecture.

Off the Tracks - Challenging the Rails Mindset
Nick Sutterer • February 20, 2013 • Earth

RubyConf AU 2013: http://www.rubyconf.org.au

Rails - a word that stirs programmer's blood. It brought us testing, easy web app building, database abstraction and hundreds of extension gems. Why not take it to another level and discover how Rails turns into a real OOP framework? By exploring chosen gems let's discuss what MVC really is and how it boosts your AJAX user interface, how to apply patterns like DCI, dependency injection and POROs to your database layer and how to expose your Rails system through a real RESTful API. Also, have you ever tried using Rails helpers outside of Rails, maybe in Sinatra? Let's do it and learn about some refreshing aspects of this framework.

RubyConf AU 2013

00:00:05.240 So you guys can hear me, right? Do you know what this is? It's a camera. Not just a camera, but a shockproof and water-resistant camera. However, it’s also a weapon—a weapon against prejudice. What is prejudice? It’s categorizing or classifying something without having sufficient information about it. That's a somewhat ridiculous definition I made up. But here's the thing: you all have prejudices. I know it, and I can tell you what they are; it’s pretty obvious.
00:00:24.480 It's natural for us to categorize or filter out information. Our brains are designed to analyze things and look for specific indicators, which leads us to assign judgments without knowing much about a person. For example, while you watch me walking around here, your brain tells you, there are people to your right and people to your left. But if I asked you who is right next to you, it might be your mother, but your brain just categorizes them as 'nerds' right? This is because our brains filter out and abstract information.
00:01:12.960 Let’s take an example. You knew there were going to be Germans at this conference, and I know you thought, 'Okay, it’s going to be a fetish, sunburned guy with a beard and a belly, maybe wearing lederhosen, and of course, sandals with white socks.' Do we have any Germans in the audience? I think maybe... well, Constantine isn't here, but if you see him, check out his feet; he's wearing sandals, but he's not wearing white socks, which proves that your prejudice about Germans is wrong! I can put it another way. I was living in Munich for about half a year, and I went out with friends one beautiful summer night. The wind was in my hair, and I was wearing flip-flops, or as you call them, thongs, with short pants. My friends asked me, 'Aren’t you going to wear something warm?' I replied, 'Why? It’s a warm, beautiful night!' So, we went to a bar.
00:02:13.440 Interestingly, when I walked into that bar wearing thongs, two or three people, I can't remember because I was drunk, came up to me and started a conversation in English. I thought, 'What the hell?' in German. It turned out those guys thought I was from Australia. So, this camera you see pointing at you, if you see me taking pictures, it's not to use for pictures all over the world at Ruby conferences. No, it is to bring the truth back to Europe and to fight prejudice!
00:02:37.480 Now, I want you guys to take a look at this picture. It’s a train, terribly loaded with people. By the way, this picture was taken in Australia; it’s traveling from Melbourne to Perth. The main reason for this train's existence is simple: it takes people from point A to point B. However, it might be slightly slow since there are a lot of people. Not to mention, if there's a problem, the air conditioning might be great for those sitting outside, but the maintenance guys will have trouble getting to the problem inside the train because they have to fight through all the people.
00:02:58.600 Also, if there’s a kangaroo on the track, the guy in the front might break his leg. This might not be a cool experience! Whatever. Let’s move on. Are you ready for some coding examples? What does this class have to do with this train? I have no clue, whatever. Let's consider an ActiveRecord-based class where we do different things in it. We also have an after-save hook to send out SMS notifications after saving this very record.
00:04:11.280 All of you have done this before, right? Everyone is looking at the floor right now. So, I used this API; I used this record in my controller, and I called save. Of course, the controller, or maybe the model, would send out SMS notifications to 500,000 users saying they were saved, which was not my intention! The cool thing about Rails is it provides us with all these tools, like testing, Sprockets, ActiveRecord, and best practices. However, a lot of these so-called best practices can lead us to problems once a project becomes complex.
00:04:57.440 We often end up with huge controllers, huge models, and fat views—this is what Google image search looks like. I’m not giving this talk to criticize Rails, just to point out that there are two alternatives: we either disregard Rails and use something else, like Sinatra, or we learn to coexist with Rails and use appropriate tools to modify its default behavior. This talk will discuss that coexistence.
00:05:44.240 Let’s consider our train as our ActiveRecord-based class. I know it’s a stupid analogy, but every person on this train is a feature—like logging, saving, notifications, and so on. So here we return to this class, and now it begins to make sense, right? To manage complexity in your application, we can shift from this monolithic train to an approach where you have hundreds, even millions, of independent units that work autonomously.
00:06:52.160 Imagine if a problem arises on the street. The bicycle can take shortcuts—eliminating the hidden cows or kangaroos. If you experience complexity similar to this train and can no longer manage it, you have to reduce the complexity. This usually works by applying object-oriented techniques. Over the last year, applying object-oriented patterns to Rails has become a hot topic. We need to choose between one monolithic ActiveRecord model or many small, independent classes.
00:07:53.840 The issue is that many people suffer from a sickness I invented: fear of the class. We all know this person; they're from the Rails core team, and they’re fantastic. The patterns I’m going to show you are meant to alleviate that fear of using more classes and more instances in your applications.
00:09:07.280 Let’s start by checking out some patterns for the model layer. I want to split my domain logic from the persistence layer. For illustration, let’s take the scenario of two cowboys sitting around a campfire.
00:09:38.360 Do you have cowboys in Australia? What do you call them? Jack? Whatever they’re called, we have two of them: one named Jim and the other named Steve. All I want to model in my application is a chat with these two cowboys exchanging messages. To achieve this, I’ll use the PORO pattern, which stands for Plain Old Ruby Object.
00:10:13.280 Here’s how this application is supposed to work. I instantiate two cowboys, then I call the same method on Jim, passing the message, and then Steve can respond by calling the respond method with the response content and the question object, effectively modeling the conversation. This all needs to happen without ActiveRecord and database thinking. I’ll use OpenStruct, which automatically provides accessors for the properties of objects.
00:12:21.600 To implement the method on the Cowboy class—responsible for receiving content in the form of a message—I encapsulate that message in a Message object and push it to a chat array, nothing more. To respond, I also need to implement the response method within the Cowboy class, which receives the message and the question. I instantiate a Response object to handle the message.
00:13:41.120 You might wonder, 'Okay, that’s great, but what about persistence?' In our application, we need users to browse their chat history. We can utilize several approaches for persistence, and I will introduce Data Mapper 2. This upcoming object-relational mapper is expected to be available soon. It completely changes how we use persistence in our models.
00:14:39.920 DataMapper allows you to include a resource model module and define persistent fields in your objects, which can be mapped to a database table. Unlike ActiveRecord, it isn’t connected to any specific database or table layout. The cool thing is you can manage your model separately, so your domain logic remains unobstructed.
00:15:32.560 Now, let’s talk about views. Let’s say we have a sidebar in our application. Who’s running a Rails app? Wow, that’s awesome! Who has sidebar elements or dashboards? Are you happy?
00:16:12.640 So, say we have a sidebar with two functional boxes. The top box shows a list of all recent messages from our chat, while the lower box displays chat activity. This works well for simple scenarios, allowing for partial renderings and simple controllers.
00:17:23.120 Now I want to create two different sidebars—a top box displaying recent messages and lower boxes showing the happiness of our chat. I want the pie chart sidebar on three controllers, and the happiness sidebar on one. The traditional approach involves adding logic to decide which sidebar to render. Should we have logic in our views? The answer is no!
00:18:37.520 To eliminate this decision logic, I would use a View Component. There’s a gem available for Ruby developers called Cells. It allows you to call render cells instead of render partial. I’ll handle the sidebar as a separate class, passing necessary values like the chat object. This encapsulates the partial into a cell.
00:20:07.920 We can further improve our views by applying object-oriented techniques to avoid conditional logic. This encapsulation leads to cleaner, more maintainable code. By utilizing inherited views, we can create a base view class and derive new classes from it, overriding only what needs to change in our sidebar functionality.
00:21:31.280 This makes the architecture easier to follow. For instance, if I wanted to represent the activities of chats, I can define a new cell; adjusting it is as simple as overriding what we need from the parent class. This approach helps manage complexity in the application.
00:23:11.280 Typically, a Rails controller can become a big monster. It often works well for smaller applications, but controllers can become overly complicated. Instead of aggregating all data in one controller, we can split logic using the Objectify gem. This gem allows us to break down the complexity of controllers into separate classes, following the single responsibility principle.
00:24:25.920 By defining a policy class for validating input URLs, a service class for processing data, and a responder class for rendering, we can handle complexity more gracefully. This leads to better organized and more testable code. While I haven’t implemented this solution in a project yet, the concept is intriguing.
00:26:10.780 Next, we'll discuss how to implement API endpoints. The campfire example could require an API for listing messages or sending new ones. Typically, APIs are designed to render documents through endpoints, and Rails provides several techniques to facilitate this.
00:28:16.720 By using the SJson for rendering, you can produce documents from models, although it often requires complex configurations. So Active Model Serializer was introduced, offering a more object-oriented style for rendering. Parsing can be done with parameter hashes that Rails automatically parses regard less of the format, simplifying handling of incoming documents.
00:30:41.800 However, there are limitations to separating rendering and parsing. The Representer pattern unifies this by encapsulating all the logic for handling documents. By creating a representer to define the document properties, a single interface can be used for both rendering and parsing.
00:32:14.840 Utilizing a Representer not only simplifies handling of data in both directions but also allows you to easily switch between media formats like JSON or XML without significant restructuring of the code base. This reduces the amount of configuration needed and integrates your APIs as a cohesive unit.
00:34:53.780 In conclusion, the key takeaways from this talk highlight the importance of separating domain logic from persistence through methods like PORO and Data Mapper, using object-oriented techniques in views with Cells, and simplifying controller logic with gems like Objectify. The Representer pattern is an effective way to unify rendering and parsing within APIs. Rails does not strictly force conventional approaches; instead, it allows for incremental changes to improve code architecture.
00:36:55.640 You shouldn't fear using more classes. Your code will improve as a result. Let's embrace the use of object-oriented principles in our Rails applications. Thank you!
Explore all talks recorded at RubyConf AU 2013
+25