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.