If you're ready to go beyond the basics of the Active Record on Rails, this five-part series is for you. You'll learn how to use it to enhance your models. This article is excerpted from chapter five of the book Beginning Rails: From Novice to Professional, written by Jeffrey Allan Hardy, Cloves Carneiro Jr. and Hampton Catlin (Apress; ISBN: 1590596862).
Advanced Active Record: Enhancing Your Models (Page 1 of 4 )
In the previous chapter, we introduced the basics of Active Record and how to use it. In this chapter, we’re going to delve more deeply into Active Record and teach you how to enhance your models.
Model enhancement is a rather general term. It refers to endowing your models with attributes and capabilities that go beyond what you get from simply subclassingActiveRecord::Base. A model contains all the logic that governs its citizenship in the world of your application. In the model, you can define how it interacts with other models, what a model should accept as a minimum amount of information for it to be considered valid, and other abilities and responsibilities.
Models need to relate to each other. In the real world, bank accounts have transactions, books belong to authors, and products have categories. We refer to these relationships as associations, and Active Record makes them easy to work with. Models also have requirements. For instance, you can’t have a transaction without an amount—it might break your system if someone tried to have an empty transaction. So, Active Record gives you easy ways to tell a model what it should expect in order to be saved to the database.
This chapter will teach you how to programmatically enhance your models, so that they’re more than just simple maps of your tables. To demonstrate the concepts, we’ll be building on the events application you started in Chapter 3, so keep it handy if you want to follow along with the examples.
Adding Methods
Let’s begin with a brief review of the Active Record basics. At the simplest level, Active Record works by automatically wrapping database tables whose names match the plural, underscored version of any classes that inherit from ActiveRecord::Base. For example, if you wanted to wrap the userstable, you would simply create a subclass ofActiveRecord::BasecalledUser, like this:
class User < ActiveRecord::Base end
That’s all you really need to have Active Record map theuserstable and get all the basic CRUD functionality we described in Chapter 4. But few models are actually this bare.
So far, we’ve been leaving our models classes unchanged. That’s a good thing, and it speaks to the power and simplicity of Active Record. However, it leaves something to be desired. Most of the time, your models will need to do a lot more than just wrap a table.
■Note If you’re familiar with SQL, you’re probably feeling that Active Record provides you with only simple-case solutions and it can’t handle complicated cases. That’s entirely untrue. While SQL is useful for highly customized database queries, most Rails projects rarely need to touch SQL, thanks to some clever tricks in Active Record.
The primary way in which we enhance models is by adding methods to them. This is what’s referred to as adding domain logic. With Active Record, all the logic for a particular table is contained in one place: the model. This is why the model is said to encapsulate all the domain logic. This logic includes access rules, validations, relationships, and, well, just about anything else you feel like adding.
In addition to all the column-based reader and writer methods you get by wrapping a table, you’re free to define your own methods on the class. In fact, an Active Record subclass isn’t much different from a regular Ruby class. About the only difference is that you need to make sure you don’t unintentionally overwrite any of Active Record’s methods (find,save, ordestroy, for example). For the most part, though, this isn’t a problem.
Let’s look at a simple example. We’re often faced with the problem of having to format data, rather than accessing a model attribute in its raw form. In our events application, we would like to be able to produce a formatted, long title that includes the name of the event, its location, and its date. To accomplish this, all we need to do is define a new instance method that performs the concatenation of those attributes and produces a formatted string. We’ll call the methodlong_title. Add the code shown in Listing 5-1 just before the lastendstatement in theapp/models/event.rbfile.
Listing 5-1. Custom long_title Method, in app/models/event.rb
class Event < ActiveRecord::Base #...
def long_title "#{title} - #{location} - #{occurs_on}" end
end
We’ve just created an instance method on the model; that is, we’ve told theEventmodel that it’s now endowed with a new attribute calledlong_title. We can addresslong_titlein the same way as we would any other method on the class. Let’s open anirbsession and try this on the console. From your terminal window, start up the Rails console with the following command:
$ ./script/console
This should drop you at a simpleirbprompt with two right arrows and a blinking cursor. From here, we’ll find an event and call thelong_titlemethod.
>> Event.find(:first).long_title => "Tiki Party - Hampton's Apartment - 2007-09-02"
So you see, there is no difference between the methods that Active Record creates and those we define ourselves. Here, instead of just asking the model for one of the attributes garnered from the database column names, we’ve defined our own method calledlong_title, which does a bit more than the standardtitlemethod.
The methods you add to your models can be as simple as returningtrueorfalse, or as complicated as doing major calculations and formatting on the object. The full power of Ruby is in your hands to do with as you please.
Don’t worry if you don’t feel comfortable adding your own methods to models just yet. The important part to note from this first section is that Active Record models are regular Ruby objects that can be augmented, modified, played with, poked, and turned inside out with sufficient Ruby-fu. Knowing this will be extremely helpful in being able to pull back the curtain and understand the advanced features of Active Record.
FAT MODELS
Some people might be made nervous by the long_title method we just created. They might see it as a violation of the MVC paradigm. They might ask, "Isn’t formatting code supposed to be in the view?" In general, the answer is yes. However, it often helps to have models that act as intelligent objects. If you ask a model for some information about itself, it’s natural to assume that it can give you a decent answer that doesn’t require a large amount of work later on to figure out what it meant. So, small formatted strings and basic data types that faithfully represent the data in the model are good things to have in your code.
An intelligent model like this is often called "fat." This means that instead of performing model-related logic in other places (that is, in controllers or views), you keep it in the model, thus making it fat. This makes your models easier to work with and will help your code stay DRY.
A basic rule of thumb while trying to stay DRY is that if you find yourself copying and pasting a bit of code, then it might be worth your time to take a moment and figure out if there is a better way to approach the problem. For instance, if we had kept the Event#long_title formatting outside the model, we may have needed to repeat the same basic string-formatting procedure every time we wanted a human-friendly representation of an event’s title. Then again, creating that method is a waste of time if we’re going to use it in only one place in the application and never again.
This is where programmer experience comes in. As you learn and mature in your Rails programming, you will find it easier and easier to figure out where stuff is supposed to go. If you are always aiming for a goal of having the most maintainable and beautiful code you can possibly write, your projects will naturally become easier to maintain.
Next, we will look at another common form of model enhancement: associations. Active Record’s associations give you the ability to define in simple terms how models relate to and interact with each other.