Rails 3
Rails' Next Top Model: Using ActiveModel and ActiveRelation
Summarized using AI

Rails' Next Top Model: Using ActiveModel and ActiveRelation

by Adam Keys

Introduction

In the talk titled "Rails' Next Top Model: Using ActiveModel and ActiveRelation" presented by Adam Keys at LoneStarRuby Conf 2010, the speaker explores the significant enhancements introduced in Rails 3 through the extraction of ActiveRecord into two new components: ActiveModel and ActiveRelation. These innovations aim to simplify the development of clean and maintainable data layer code by addressing the diverse data interactions encountered in modern applications.

Key Points

- Active Model and Active Relation: The extraction helps decrease boilerplate code and facilitates integration with various data stores, including non-relational databases and APIs.

- Active Support: The foundation of Active Record and Rails, now modular in Rails 3, allows developers to use only the necessary parts, enhancing performance and organization.

- Simplification of Domain Logic: By leveraging Active Support, developers can remove boilerplate logic, streamline class structures, and define methods more elegantly.

- Caching Strategies: The speaker details the use of Active Support's Cache class to manage caching effectively, demonstrating how to develop a domain-specific language (DSL) to handle cache interactions seamlessly.

- Active Model Features: Active Model allows traditional Ruby objects to incorporate features such as validations and serialization, akin to those found in Active Record, making it easier to maintain compact models.

- Serialization: The process of encoding models as JSON or XML is simplified through Active Model, facilitating data handling, especially in API-centric applications.

- Active Relation for Queries: Moving away from traditional Active Record querying, Active Relation provides a structured API for data manipulation, supporting lazy loading and conforming to CRUD principles.

- Connecting Diverse Data Sources: The transition emphasizes the ability to interface with various data sources, enhancing the developer's ability to construct accessible data layers.

Examples and Illustrations

- Adam outlines an example of using caching methods with Active Support and illustrates how to build a DSL for pseudo-associations.

- He explains how to implement serialization through an attributes method, paving the way for simplified API handling.

- The concluding discussion highlights how Active Relation streamlines creating and managing queries through intuitive methods rather than complex direct SQL calls.

Conclusion

The session wraps up by encouraging developers to experiment with Active Model and Active Relation to reduce boilerplate code and foster more robust application architecture. Adam Keys concludes with an invitation for questions and emphasizes exploring these tools for enriched development practices. The speaker's insights provide a clear roadmap for harnessing the potential of Rails 3's new functionalities, making it a valuable resource for developers seeking to enhance their applications.

Takeaways

- Active Model and Active Relation allow for improved code organization, reduced boilerplate, and streamlined data handling.

- The modular approach of Active Support supports more efficient memory use and functionality.

- Developers are encouraged to create custom implementations utilizing these frameworks to meet their specific application needs.

00:00:10.080 Hello, I'm Adam Keys. I'm an expert typist at Gowalla and I'm also an internet astronaut. Today, I am going to talk about what I consider the most intriguing aspect of the reimagining of Rails, which is Rails 3. I want to explore how Active Record was extracted into two new libraries called Active Model and Active Relation.
00:00:22.880 These changes can make it significantly easier for us to write clean and maintainable data layer code. Throughout this talk, I will discuss four great paradigms for your application that can transform tasks that were challenging before Rails 3, requiring considerable boilerplate coding, into simpler, more aesthetically pleasing tasks. This newfound ease of use should make developers more enthusiastic about tackling them.
00:00:39.040 The extraction of Active Model and Active Relation reduces the friction in building small languages on top of various data stores. Increasingly, in the applications I encounter, we are not limited to just a relational database. We often need a caching layer or additional data stores for analytics, growth statistics, internal measurements, and other data structures that can be challenging to store in a traditional database. Furthermore, we increasingly utilize web APIs, whether they are external ones like Twitter or Gowalla, or internal APIs that a company uses. Sometimes, it could even be a Simple PeopleSoft database or an old Oracle database lingering around.
00:01:09.119 In any case, our applications are interacting with a wider variety of data sources than before, and thus, it would be beneficial if the integration with these different systems were easier. Implementing functionality akin to Active Record for non-relational databases often required cumbersome boilerplate code. By harnessing a data layer as refined as Active Record, we can streamline the process of adding elements like validations and associations.
00:01:28.959 Active Model and Active Relation help lessen the amount of boilerplate code required to set up a data layer. This allows you to get to the point where you can start enriching your data layer library, making it enjoyable to work with and engaging for other developers. You can shift from worrying about validations to focusing on how you want associations to work in your data layer or designing a nice-looking API, whether it resembles a hash or functions similarly to Active Record.
00:01:53.519 With Active Model and Active Relation, this becomes much easier. I would like to start further down the stack, specifically with Active Support, to help clean up your domain objects. Active Support, often viewed unfavorably, acts as a foundation upon which Active Record and Rails are built. In Rails 3, it's smaller and less cumbersome than in previous versions.
00:02:10.160 In Rails 2, utilizing Active Support meant that the moment you required it, your Ruby process would grow significantly—by 20 or 30 megabytes of resident memory. This could lead to unpleasant or inefficient experiences due to oddities with class loading. However, in Rails 3, you can cherry-pick the functionality you need from Active Support. So, if you only need core extensions to classes or fancy methods on hashes, you can pull those in by themselves.
00:02:38.560 The real advantage of being able to cherry-pick functionality is that you can utilize only the parts of Active Support you need, thereby avoiding two unappealing scenarios that were common in Active Support 2. I often encounter people who re-implement their versions of methods or, even worse, copy code from Active Support into their applications or libraries, which can lead to significant issues when trying to upgrade to new versions.
00:03:01.440 A key benefit of Active Support that excites me is how it facilitates tightening up your classes and domain logic by removing boilerplate logic from your class and placing it in a module. For example, let's consider a User class. This class is not particularly different from a standard user model in many applications. The implementation uses Rails 3 Active Support, where we begin by requiring Active Support and Active SupportCache.
00:03:27.919 Active Support's Cache class provides an abstraction over a variety of systems for caching data. As you can imagine, every user has a name, and I have a friends method which retrieves friends from Memcache. With a simple cache.fetch call, we attempt to retrieve data using a key in Memcache. If no data is found, the operation executes a block to fetch and populate that data from the source.
00:03:49.440 As I grow this application, the number of friends could increase, necessitating a more graceful solution to retrieve this data. I implemented an instance method called cache that returns a new connection to Memcache. To further streamline this, I plan to use Active Support's attribute accessors, which work for modules as well as classes. Everyone is likely familiar with attribute accessors as they are among the most appealing features of Ruby. They simplify property access significantly compared to writing those methods manually.
00:04:12.960 However, traditional accessors are generally instance-based only, so Active Support offers module and class-compatible versions and class inheritable versions. This feature is particularly useful when writing frameworks with class hierarchies, as it allows them to propagate properties down to all child classes.
00:04:27.840 Here, I have a class attribute accessor, and now when I call cache.fetch, it uses this connection shared among all instances of User. In practice, I am going to set up user.cache equals memcachedstore.new during application startup. Each User object then shares this connection to the cache.
00:04:46.720 I wish to refine this further as I foresee having many pseudo associations. Writing the same five lines of code for each would become tedious. Therefore, I analyze my implementation and identify creation variables: the association name, the key formatting for the cache, and the logic that generates values if no cache data exists.
00:05:06.560 To solve this, I developed a small domain-specific language (DSL) for handling caches. It uses a method cache_key, which takes a symbol to indicate the association being created. Using this, I can create a friends method that returns cached data and format the cache key accordingly. The logic for filling the cache when there is no data is included in the block.
00:05:29.440 The process of developing this DSL involves sketching what I think should be on paper and then experimenting with Ruby to make it executable, often returning to revise the code to ensure it's valid Ruby while defining the logic. This approach is how I typically refactor and improve code, a method inspired by Rich Kilmer, whom I would like to acknowledge.
00:05:57.360 The implementation of the DSL uses a class method for cache_key that defines methods in my class and manages all related logic, ensuring good encapsulation and organization. The user class retains a clean and understandable structure, while adapting caching behavior efficiently.
00:06:15.520 I've removed much of the boilerplate and streamlined my class structure. However, it has become elongated to the point that it could be a challenge to read for those seated further back at a conference presentation.
00:06:26.639 Fortunately, Active Support enables elegant refactoring through a feature called 'Concerns,' which provide a more organized structure. Tyra might find the current setup bulky, but with the right enhancements, we can simplify it.
00:06:41.760 Active Support Concern is a module you can extend into another module. It acts as syntactic sugar over the old Rails style plugin hack—if you include a module with an included hook, everything will work correctly. The ease of this approach makes it much more appealing than earlier methods of plugin implementation.
00:07:02.000 By utilizing this concern structure, you can cleanly extract the caching behavior from your model. Doing so organizes your code better, allowing users to comprehend where specific behaviors originate from without needing to sift through a dense User class.
00:07:27.200 Thus, the combination of accessor methods and concerns can enhance your code's clarity, streamlining domain logic and reducing redundancy. It paves the way for fewer lines of code while preserving the model's functionality.
00:07:42.360 In addition, where I once used class comment blocks for organization, I can now encapsulate code into modules. This allows for streamlined inclusion wherever needed, leading to clearer and more readable code.
00:08:07.360 Active Support also includes numerous other useful features, like message verifier classes for securely sending data and secure random number generators for creating API tokens, making it a worthy component of your toolkit.
00:08:24.640 Now, let's shift our focus to Active Model specifically. Active Model allows you to construct intuitive model classes that maintain many features present in Active Record, such as validations, callbacks, and dirty tracking.
00:08:39.120 If you are eager to implement these features in standard Ruby objects, you'll find this functionality very appealing. Moreover, Active Model follows the same cherry-picking approach as Active Support, allowing you to include only the components you need.
00:08:55.920 This ease of use is particularly beneficial for validations—by simply Including the Active Model validations module, you can use all of the familiar validations from Active Record as long as your model contains the appropriate methods.
00:09:10.560 You can also extract common validation logic into separate classes for shared use across various projects. By defining a validate method that interacts with the record's error attributes, it works functionally like the validations found in Rails 2, but now presented as a simpler hash.
00:09:27.040 Using validations is straightforward; you can easily include GhostbustersValidator or any custom validations. Your domain models can now maintain compact and efficient structures.
00:09:39.440 Next, let's consider serialization. If you wish to encode your object as JSON or XML, this functionality is invaluable, particularly when managing APIs. The process can become cumbersome if you have deeply nested models. Thankfully, with Active Model, you can reduce the complexity significantly.
00:09:54.880 Implementing serialization requires a minimal setup. You simply need to establish an attributes method that returns a hash of the attributes you wish to serialize. You can also implement attributes= to accept a hash for populating your model, facilitating a straightforward approach to working with incoming data.
00:10:11.680 Once you've included the appropriate JSON and XML serialization modules, you will gain methods for generating a hash from your models. This return value will be the foundation for any subsequent JSON or XML processing you implement, including methods for generating the serialized output.
00:10:32.560 The ease of leveraging these modules allows handling data effectively, especially important for applications creating or consuming APIs. This functionality speaks volumes about how we can streamline processes that were previously daunting.
00:10:54.080 Next up is persistence and querying. Rails 2 Active Record combined logic for forming queries along with operations speaking to various relational databases. Rails 3 has abstracted this functionality into Active Relation, allowing for simple CRUD operations and the manipulation aligning with relational algebra.
00:11:11.520 The neat aspect is that through these enhancements, you can streamline querying across your data sources—be they relational or alternative formats—using a standardized API.
00:11:29.440 The transition away from direct finds accompanied by condition hashes towards chainable methods like where and order elucidate how Active Relation has streamlined the interface. The beautiful part is that you can create your data storage systems that parallel Active Record behaviors.
00:11:45.920 To leverage this functionality, you merely need to include Active Relation in your model, adding necessary methods corresponding to the relational structure you're implementing. Be aware that operations vary slightly from Active Record, reflecting the nature of the underlying data layer.
00:12:07.440 Our goal is to maintain familiarity while harnessing the full power of Active Relation and Active Model. By introducing simple Savior methods, we ensure the ability to create new records in your database while harkening back to the conveniences of Active Record.
00:12:27.440 Active Relation supports lazy loading—a feature Active Record employs—facilitating queries that only execute once required data is actively sought after. The transformation from hash conditions to key insertion highlights the adaptability of this system.
00:12:42.720 Where Active Record expects complex builds, specific to a SQL environment, Active Relation permits a simpler approach, especially when directly interfacing with systems like Memcache. Even the overall logic structure adheres to the CRUD principles, resembling the typical object interaction flow.
00:13:02.920 Moreover, the internal workings of attributes allow for tunable data retrieval akin to Active Record's querying capabilities, enabling a high degree of customizability while still simplifying the user experience and interaction.
00:13:20.560 Thus, I would encourage all of you to explore building an Active Relation engine for your data, aiding in making the process accessible and straightforward without having to start from the ground up. Given the complex structures that Active Relation presents, you'll find the structure worth exploring.
00:13:39.440 So far, we have delineated numerous functionalities all present within this framework: declarative lazy caching, validation, serialization methods, persistence handling, and querying, showcasing the potent capabilities of a well-implemented Active Model and Active Relation.
00:13:55.680 Remember, using Active Model and Active Relation can help reduce dependencies on boilerplate code while allowing for more robust application architecture. It’s about becoming more efficient coders and reducing the hurdles that once impeded development.
00:14:13.440 I encourage you to give Active Support another look, even if you’ve previously encountered challenges. Experiment with Active Model to enrich your classes; they don't necessarily need persistence to benefit from these enhancements. If you're particularly adventurous, you might want to craft a custom data layer with Active Relation.
00:14:29.919 That concludes my talk. Thank you for your time. I’ll be posting the slides online and on GitHub. You can find me at therealadam.com or @therealadam on GitHub if you want to follow up.
00:14:45.440 If there are any questions, I have a few minutes to address them.
00:14:49.919 The question was about how easy it is to write custom serialization methods. Although I haven't attempted that yet, I believe it should be a manageable process.
00:15:01.280 You would access the data from the attributes method and manipulate it as necessary. The complexity would largely depend on the desired output format.
00:15:12.960 Thank you for your attention.
Explore all talks recorded at LoneStarRuby Conf 2010
+20