© Brady Somerville, Adam Gamble, Cloves Carneiro Jr and Rida Al Barazi 2020
B. Somerville et al.Beginning Rails 6https://doi.org/10.1007/978-1-4842-5716-6_6

6. Advanced Active Record: Enhancing Your Models

Brady Somerville1 , Adam Gamble2, Cloves Carneiro Jr.3 and Rida Al Barazi4
(1)
Bowling Green, KY, USA
(2)
Gardendale, AL, USA
(3)
Hollywood, FL, USA
(4)
FONTHILL, ON, Canada
 

Chapter 5 introduced the basics of Active Record and how to use it. This chapter delves more deeply into Active Record and teaches you how to enhance your models.

Model enhancement is a general term. It refers to endowing your models with attributes and capabilities that go beyond what you get from subclassing ActiveRecord::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 one another. In the real world, bank accounts have transactions, books belong to authors, and products have categories. These relationships are referred to 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 they’re more than just simple maps of your tables. To demonstrate the concepts, you build on the blog 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 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 want to wrap the users table, you create a subclass of ApplicationRecord (which is a subclass of ActiveRecord::Base) called User, like this:
class User < ApplicationRecord
end

That’s all you really need to have Active Record map the users table and get all the basic CRUD functionality described in Chapter 5. But few models are actually this bare.

So far, you’ve left your model 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 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 only simple case solutions and can’t handle complicated cases. That’s entirely untrue. Although 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 you enhance models is by adding methods to them. This is 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. 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 (e.g., find, save, or destroy). For the most part, though, this isn’t a problem.

Let’s look at a simple example. You often need to format data, rather than accessing a model attribute in its raw form. In the blog application, you want to be able to produce a formatted, long title that includes the title of the article and its date. To accomplish this, all you need to do is define a new instance method called long_title that performs the concatenation of those attributes and produces a formatted string. Update your copy of app/models/article.rb so that it matches the code shown in Listing 6-1 by adding the long_title method definition.
class Article < ApplicationRecord
  validates :title, :body, presence: true
  def long_title
     "#{title} - #{published_at}"
  end
end
Listing 6-1

Custom long_title Method, in app/models/article.rb: https://gist.github.com/nicedawg/0355af37c2e0375b004d4e0c12566b4b

You’ve just created an instance method on the model; that is, you’ve told the Article model that it’s now endowed with a new attribute called long_title . You can address long_title the same way you would any other method on the class. Open an irb session and try this on the console. From the terminal window, make sure you’re inside the blog application directory, and then start up the Rails console with the following command:
$ rails console
This should drop you at a simple irb prompt with two right arrows and a blinking cursor; this may look a bit different based on your environment. From here, you create a new article and use it to call the long_title method:
>> Article.create title: 'Advanced Active Record', published_at: Date.today,
body: 'Models need to relate to each other. In the real world, ...'
=> #<Article id: 1, title: "Advanced Active Record", ...>
>> Article.last.long_title
=> "Advanced Active Record - 2020-02-03 00:00:00 UTC"

There is no difference between the methods Active Record creates and those you define. Here, instead of asking the model for one of the attributes garnered from the database column names, you define your own method called long_title , which does a bit more than the standard title method.

The methods you add to your models can be as simple as returning true or false 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 section is that Active Record models are regular Ruby classes that can be augmented, modified, played with, poked, and turned inside out with sufficient Ruby-fu. Knowing this is extremely helpful in being able to pull back the curtain and understand the advanced features of Active Record.

Fat Models

Some might be nervous by the long_title method you just used. They may 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 means. 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. Instead of performing model-related logic in other places (i.e., in controllers or views), you keep it in the model, thus making it fat. This makes your models easier to work with and helps 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, it may be worth your time to take a moment and figure out if there is a better way to approach the problem. For instance, if you had kept the Article#long_title formatting outside the model, you might have needed to repeat the same basic string-formatting procedure every time you wanted a human-friendly representation of an article’s title. Then again, creating that method is a waste of time if you’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’ll find it easier and easier to figure out where stuff is supposed to go. If you’re 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, let’s 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 one another.

Using Associations

It’s a lowly application that has only one table. Most applications have many tables, and these tables typically need to relate to one another in one way or another. Associations are a common model enhancement that let you relate tables to one another.

Associations are natural constructs that you encounter all the time in the real world: articles have comments, stores have products, magazines have subscriptions, and so on. In a relational database system, you relate tables using a foreign key reference in one table to the primary key of another table.

Note

The terms relationship and association can be used pretty much interchangeably. However, when this book refers to associations, it generally means the association on the Active Record side, as opposed to the actual foreign key relationships at the database level.

Let’s take the example of articles and comments. In a situation where a given article can have any number of comments attached to it, each comment belongs to a particular article. Figure 6-1 demonstrates the association from the database’s point of view.
../images/314622_4_En_6_Chapter/314622_4_En_6_Fig1_HTML.jpg
Figure 6-1

The relationship between the articles and comments tables

The example in Figure 6-1 uses a column named article_id in the comments table to identify the related article in the articles table. In database speak, comments holds a foreign key reference to articles.

By Rails convention, the foreign key column is the singular, lowercase name of the target class with _id appended. So, for products that belong to a particular store, the foreign key is named store_id; for subscriptions that belong to magazines, the foreign key is named magazine_id; and so on. Here’s the pattern:
#{singular_name_of_parent_class}_id
Table 6-1 shows a few more examples, just to drive this concept home.
Table 6-1

Sample Foreign Key References

Model

Table

Foreign Key to Reference This Table

Article

articles

article_id

Person

people

person_id

Friend

friends

friend_id

Category

categories

category_id

Book

books

book_id

Whenever you need one table to reference another table, remember to create the foreign key column in the table doing the referencing. In other words, the model that contains the “belongs_to” needs to have the foreign key column in it. That’s all your table needs before you can put Active Record’s associations to work.

Declaring Associations

As you’ve probably come to expect by now, Active Record makes working with associations easy. You don’t need to get down to the bare metal of the database very often. As long as you understand the concept of primary and foreign keys and how to create basic relationships in your tables, Active Record does the proverbial heavy lifting, converting foreign key relationships into rich object associations. This means you get to access associated objects cleanly and naturally using Ruby:
article.comments
store.products
magazine.subscriptions
After the relationships are defined in your database tables, you use a set of macro-like class methods in your models to create associations. They look like this:
  • has_one

  • has_many

  • belongs_to

  • has_and_belongs_to_many

Here’s a quick example. The Message model declares a has_many relationship with Attachment; Attachment returns the favor by declaring that each of its objects belongs to a particular Message:
class Message < ApplicationRecord
  has_many :attachments
end
class Attachment < ApplicationRecord
  belongs_to :message
end

Given these instructions, Active Record expects to find a table called attachments that has a field in it called message_id (the foreign key reference). It uses this association to let you enter things like Message.first.attachments and get an array (or a collection) of Attachment objects that belongs to the first Message in the database. Moreover, you can work with your associations in both directions. So you can enter Attachment.first.message to access the Message to which the first Attachment belongs. It sounds like a mouthful, but when you get the hang of it, it’s quite intuitive.

Whenever you declare an association, Active Record automatically adds a set of methods to your model that makes dealing with the association easier. This is a lot like the way in which Active Record creates methods based on your column names. When it notices you’ve declared an association, it dynamically creates methods that enable you to work with that association. The following sections go through the different types of associations and describe how to work with them. You also learn about the various options you can use to fine-tune associations.

Creating One-to-One Associations

One-to-one associations describe a pattern where a row in one table is related to exactly one row in another table.

Suppose that in your blog application, you have users and profiles, and each user has exactly one profile. Assume you have User and Profile models, and the corresponding users and profiles tables have the appropriate columns. You can tell your User model that it has one Profile and your Profile model that it belongs to a User. Active Record takes care of the rest. The has_one and belongs_to macros are designed to read like regular English, so they sound natural in conversation and are easy to remember. Each represents a different side of the equation, working in tandem to make the association complete.

Note

Part of the Rails philosophy about development is that the gap between programmers and other project stakeholders should be bridged. Using natural language, such as has one and belongs to, in describing programmatic concepts helps bridge this gap, providing a construct that everyone can understand.

Adding the User and Profile Models

When you started the blog application, you decided to let anyone create new articles. This worked fine when only one person was using the system; but you want this to be a multiple-user application and let different people sign up, sign in, and start writing their own articles separately from one another.

Let’s fire up the generator and create the User model:
$ rails generate model User email:string password:string
Just as you saw in Chapter 3, the model generator creates, among other things, a model file in app/models and a migration in db/migrate. Open db/migrate/20200204011416_create_users.rb, and you should see the now-familiar code in Listing 6-2. (Remember that the timestamp in the migration file will differ.)
class CreateUsers < ActiveRecord::Migration[6.0]
  def change
    create_table :users do |t|
      t.string :email
      t.string :password
      t.timestamps
    end
  end
end
Listing 6-2

Migration to Create the users Table , db/migrate/20200204011416_create_users.rb

This is standard migration fare. In the change definition, you use the create_table method to create a new users table. The new table object is yielded to the block in the variable, t, on which you call the string method to create each column. Along with the standard email field, you specify a password field, which you use for authentication, as explained in the “Reviewing the Updated Models” section later in this chapter. The primary key, id, is created automatically, so there’s no need to specify it here.

As you probably noticed, the User model is extremely simple: it only contains information that allows the user to authenticate into the application. Some users may want to add a lot more detail about themselves and would love the ability to enter personal information such as their birthday, a biography, their favorite color, their Twitter account name, and so on. You can create a Profile model to hold such information outside the scope of the User model. Just as you did for the User model, use the generator again:
$ rails generate model Profile user:references name:string birthday:date
bio:text color:string twitter:string

You also have a migration file for the Profile model in db/migrate/20200204013911_create_profiles.rb—feel free to take a peek. Notice the existence of the foreign key for users in the profiles schema. Also recall that you don’t need to specify primary keys in migrations because they’re created automatically.

Now, all you need to do is run the migrations and create the new tables using the db:migrate Rails command . Run the migrations with the following command:
$ rails db:migrate
==  CreateUsers: migrating ==============================================
-- create_table(:users)
   -> 0.0019s
==  CreateUsers: migrated (0.0020s) ======================================
==  CreateProfiles: migrating ============================================
-- create_table(:profiles)
   -> 0.0027s
==  CreateProfiles: migrated (0.0035s) ===================================
With the table and foreign keys in place, Listings 6-3 and 6-4 show how to declare the one-to-one association on the User and Profile models , respectively. Please update your User model in app/models/user.rb to match Listing 6-3. However, we won’t need to update the Profile model to match Listing 6-4; the rails generate command added exactly what we needed!
class User < ApplicationRecord
  has_one :profile
end
class Profile < ApplicationRecord
  belongs_to :user
end

The has_one declaration on the User model tells Active Record that it can expect to find one record in the profiles table that has a user_id matching the primary key of a row in the users table. The Profile model, in turn, declares that each of its records belongs_to a particular User.

Telling the Profile model that it belongs_to :user is saying, in effect, that each Profile object references a particular User. You can even go so far as to say that User is the parent and Profile is the child. The child model is dependent on the parent and therefore references it. Figure 6-2 demonstrates the has_one relationship .
../images/314622_4_En_6_Chapter/314622_4_En_6_Fig2_HTML.jpg
Figure 6-2

The one-to-one relationship between users and profiles

Let’s get inside a console session (rails console) and see how this comes together. If you have a console session opened, run the reload! command in the console session to make sure it loads the newly generated models. Follow along to create objects and relate them to one another. First, create a user and a profile as follows:
>> reload!
Reloading...
>> user = User.create(email: "user@example.com", password: "secret")
=> #<User id: 1, email: "user@example.com", password: [FILTERED], created_at: "2020-02-04 01:42:21", updated_at: "2020-02-04 01:42:21">
>> profile = Profile.create(name: "John Doe",
bio: "Ruby developer trying to learn Rails")
=> #<Profile id: nil, user_id: nil, name: "John Doe", birthday: nil, bio: "Ruby developer trying to learn Rails", color: nil, twitter: nil, created_at: nil, updated_at: nil>
Note

The reload! method reloads the Rails application environment within your console session. You need to call it when you make changes to existing code. It’s exactly as if you had restarted your console session—all the variables you may have instantiated are lost.

Although you’ve successfully created a user, look closely and you’ll see the profile failed to save! (See how after calling Profile.create, a profile with a nil id was returned?) What happened?
>> profile.errors
=> #<ActiveModel::Errors:0x00007ffd53a87958 @base=#<Profile id: nil, user_id: nil, name: "John Doe", birthday: nil, bio: "Ruby developer trying to learn Rails", color: nil, twitter: nil, created_at: nil, updated_at: nil>, @messages={:user=>["must exist"]}, @details={:user=>[{:error=>:blank}]}>

The profile failed to save because its user is nil. The belongs_to association also adds a validation to ensure it is present. We could change the Profile class to optionally belong to a user—belongs_to :user, optional: true—but for our purposes, we don’t want profiles to exist without a user.

To successfully create the profile so that it is associated with the user, we can just assign it and call save, like this:
>> profile.user = user
=> #<User id: 1, email: "user@example.com", password: [FILTERED], created_at: "2020-02-04 01:42:21", updated_at: "2020-02-04 01:42:21">
>> profile.save
=> true
Assignment is assignment, whether it’s a name attribute to which you’re assigning the value Joe or an association method to which you’re assigning an object. Also notice that the profile’s user_id attribute is updated to the value of user.id: This is what bonds both objects together. Now, when you ask the user object for its profile, it happily responds with one:
>> user.profile
=> #<Profile id: 1, user_id: 1, name: "John Doe", birthday: nil, bio: "Ruby developer trying to learn Rails", color: nil, twitter: nil, created_at: "2020-02-04 01:52:51", updated_at: "2020-02-04 01:52:51">
That’s all there is to it. Although this is pretty good, you can do a bit better. You can create and save the profile in one shot and have it perform the association automatically, like this:
>> user.profile.destroy
=> #<Profile id: 1, user_id: 1, name: "John Doe", birthday: nil, bio: "Ruby developer trying to learn Rails", color: nil, twitter: nil, created_at: "2020-02-04 01:52:51", updated_at: "2020-02-04 01:52:51">
>> user.create_profile name: 'Jane Doe', color: 'pink'
=> #<Profile id: 2, user_id: 1, name: "Jane Doe", birthday: nil, bio: nil, color: "pink", twitter: nil, created_at: "2020-02-04 01:55:23", updated_at: "2020-02-04 01:55:23">

Using the create_profile method to create a new profile initializes the Profile object, sets its foreign key to user.id, and saves it to the database. This works for any has_one association, no matter what it’s named. Active Record automatically generates the create_#{association_name} method for you. So if you had an Employee model set up with an association like has_one :address, you would get the create_address method automatically.

These alternatives for doing the same thing may seem confusing, but they’re really variations on the same theme. In all cases, you’re creating two objects (the parent and the child) and telling each about the other. Whether you choose to do this in a multistep operation or all on one line is entirely up to you.

Earlier, you learned that declaring a has_one association causes Active Record to automatically add a suite of methods to make working with the association easier. Table 6-2 shows a summary of the methods that are added when you declare a has_one and belongs_to relationship between User and Profile, where user is a User instance.
Table 6-2

Methods Added by the has_one Association in the User/Profile Example

Method

Description

user.profile

Returns the associated (Profile) object; nil is returned if none is found.

user.profile=(profile)

Assigns the associated (Profile) object, extracts the primary key, and sets it as the foreign key.

user.profile.nil?

Returns true if there is no associated Profile object.

user.build_profile(attributes={})

Returns a new Profile object that has been instantiated with attributes and linked to user through a foreign key but hasn’t yet been saved.

user.create_profile(attributes={})

Returns a new Profile object that has been instantiated with attributes and linked to user through a foreign key and that has already been saved.

Although you’re using the User.has_one :profile example here, the rules work for any object associated to another using has_one. Here are some examples, along with sample return values:
user.profile
#=> #<Profile id: 2, user_id: 1, ...>
user.profile.nil?
#=> false
user.profile.destroy
#=> #<Profile id: 2, user_id: 1, ..,>
user.build_profile(bio: 'eats leaves')
#=> #<Profile id: nil, user_id: 1, ...>
user.create_profile(bio: 'eats leaves')
#=> #<Profile id: 3, user_id: 1, ...>
The has_one declaration can also include an options hash to specialize its behavior if necessary. Table 6-3 lists the most common options. For a complete list of all options, consult the Rails API documentation (https://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-has_one).
Table 6-3

Common has_one Options

Option

Description

Example

:class_name

Specifies the class name of the association. Used when the class name can’t be inferred from the association name.

has_one :profile, class_name: 'Account'

:foreign_key

Specifies the foreign key used for the association in the event that it doesn’t adhere to the convention of being the lowercase, singular name of the target class with _id appended.

has_one :profile, foreign_key: 'account_id'

:dependent

Specifies that the associated object should be removed when this object is. If set to :destroy, the associated object is deleted using the destroy method. If set to :delete, the associated object is deleted without calling its destroy method. If set to :nullify, the associated object’s foreign key is set to NULL.

has_one :profile, dependent: :destroy

Creating One-to-Many Associations

One-to-many associations describe a pattern where a row in one table is related to one or more rows in another table. Examples are an Email that has many Recipients or a Magazine that has many Subscriptions.

Up until now, your articles have been orphaned—they don’t belong to anyone. You remedy that now by associating users with articles. In your system, each article belongs to a user, and a user may have many articles. Figure 6-3 illustrates this association.
../images/314622_4_En_6_Chapter/314622_4_En_6_Fig3_HTML.jpg
Figure 6-3

The one-to-many relationship between users and articles

Associating User and Article Models

Just as you associated users and profiles, you want to have a similar relationship between users and articles. You need to add a foreign key user_id in the articles table that points to a record in the users table.

Fire up the migration generator:
$ rails g migration add_user_reference_to_articles user:references

Notice that we used rails g instead of rails generate. Some of the most commonly used Rails commands have a shortcut—rails generate, rails server, rails console, rails test, and rails dbconsole. We’ll generally use the full command in this book, but feel free to use the shortcuts:

Open db/migrate/20200204020148_add_user_reference_to_articles.rb, and update it by removing null: false so that it matches the code in Listing 6-5.
class AddUserReferenceToArticles < ActiveRecord::Migration[6.0]
  def change
    add_reference :articles, :user, foreign_key: true
  end
end
Listing 6-5

Migration to Add User to Articles 20200204020148_add_user_reference_to_articles

We needed to remove null: false because our existing articles don’t yet have a user_id and our migration would have failed.

Now, all you need to do is run the migration using the db:migrate task . Run the migration with the following command:
$ rails db:migrate
==  AddUserReferenceToArticles: migrating ================================
-- add_column(:articles, :user_id, :integer)
   -> 0.0012s
==  AddUserReferenceToArticles: migrated (0.0015s) =======================
With the foreign key in place, Listings 6-6 and 6-7 show how you declare the one-to-many association in your Article and User models, respectively. Add these to the relevant models.
class Article < ApplicationRecord
  validates :title, :body, presence: true
  belongs_to :user
  def long_title
    "#{title} - #{published_at}"
  end
end
Listing 6-6

The Article Model, belongs_to Declaration in app/models/article.rb:https://gist.github.com/nicedawg/53289dd2be8683975f28283761f06fa0

class User < ApplicationRecord
  has_one :profile
  has_many :articles
end
Listing 6-7

The User Model, has_many Declaration in app/models/user.rb:https://gist.github.com/nicedawg/48f1cdf5917a3322347642e6ada25016

That’s all there is to it. This bit of code has endowed your Article and User models with a lot of functionality.

Note

For has_one and has_many associations, adding a belongs_to on the other side of the association is always recommended. The rule of thumb is that the belongs_to declaration always goes in the class with the foreign key.

Creating a New Associated Object

Your associations are in place; so let’s get back into the code to put what you’ve learned to the test. Do this exercise on the console: either run rails console to start a new console session or type reload! if you still have a console window open from the previous section.

Let’s test whether the association between users and articles is set up correctly. If it is, you should be able to ask the user object for its associated articles, and it should respond with a collection. Even though you haven’t created any articles for this user yet, it should still work, returning an empty collection:
>> reload!
Reloading...
=> true
>> user = User.first
=> #<User id: 1, email: "user@example.com", password: [FILTERED], created_at: "2020-02-04 01:42:21", updated_at: "2020-02-04 01:42:21">
>> user.articles
=> #<ActiveRecord::Associations::CollectionProxy []>

Great! The has_many association is working correctly, and the User instance now has an articles method, which was created automatically by Active Record when it noticed the has_many declaration.

Let’s give this user some articles. Enter the following commands:
>> user.articles << Article.first
=> [#<Article id: 1, ..., user_id: 1>]
>> user.articles.size
=> 1
>> user.articles
=> [#<Article id: 1, ..., user_id: 1>]

By using the append (<<) operator, you attach Article.first onto your user object. When you use << with associations, it automatically saves the new association. Some things in Active Record don’t happen until you say save, but this is one of the examples where that part is done automatically.

What did that do exactly? Let’s look into the article and find out:
>> Article.first.user_id
=> 1
See how this article’s user_id points to the user with an id of 1? This means you’ve successfully related the two objects. You can even ask an Article instance for its user:
>> Article.first.user
=> #<User id: 1, email: "user@example.com", password: [FILTERED], created_at: "2020-02-04 01:42:21", updated_at: "2020-02-04 01:42:21">
Voilà! Your models can really start to express things now. The has_many and belongs_to declarations create more methods, as you did earlier with the long_title method. Let’s look at what else these happy little helpers brought along to the party. Table 6-4 shows a summary of the methods that are added when you declare a has_many and belongs_to relationship between User and Article (user represents a User instance).
Table 6-4

Methods Added by the has_many Association in the User and Article Models

Method

Description

user.articles

Returns an array of all the associated articles. An empty array is returned if no articles are found.

user.articles=(articles)

Replaces the articles collection with the one supplied.

user.articles << article

Adds the article to the user’s articles collection.

user.articles.delete(articles)

Removes one or more articles from the collection by setting their foreign keys to NULL.

user.articles.empty?

Returns true if there are no associated Article objects for this user.

user.articles.size

Returns the number of associated Article objects for this user.

user.article_ids

Returns an array of associated article ids.

user.articles.clear

Clears all associated objects from the association by setting their foreign keys to NULL.

user.articles.find

Performs a find that is automatically scoped off the association; that is, it finds only within items that belong to user.

user.articles.build(attributes={})

Returns a new Article object that has been instantiated with attributes and linked to user through a foreign key but hasn’t yet been saved. Here’s an example: user.articles.build(title: 'Ruby 1.9').

user.articles.create(attributes={})

Returns a new Article object that has been instantiated with attributes and linked to user through a foreign key and has already been saved. Here’s an example: user.articles.create(title: 'Hoedown').

You’re using the User.has_many :articles example here, but the rules work for any object associated with another using has_many. Here are some examples, along with sample return values:
>> user.articles
=> [#<Article id: 1, ...>]
>> user.articles << Article.new(title: 'One-to-many associations',
body: 'One-to-many associations describe a pattern ..')
=> [#<Article id: 1, ...>, #<Article id: 2, ...>]
>> user.article_ids
=> [1, 2]
>> user.articles.first
=> #<Article id: 1, ...>
>> user.articles.clear
=> []
>> user.articles.count
 => 0
>> Article.count
 => 2
>> user.articles.create title: 'Associations',
body: 'Active Record makes working with associations easy..'
=> #<Article id: 3, ...>
You can also pass in options to your association declaration to affect the way you work with those associations. Table 6-5 lists some of the most common options.
Table 6-5

Common has_many Options

Option

Description

Example

:class_name

Specifies the class name of the association. Used when the class name can’t be inferred from the association name.

has_many :articles, class_name: 'Post'

:foreign_key

Specifies the foreign key used for the association in the event that it doesn’t adhere to convention of being the lowercase, singular name of the target class with _id appended.

has_many :articles, foreign_key: 'post_id'

:dependent

Specifies that the associated objects should be removed when this object is. If set to :destroy, the associated objects are deleted using the destroy method. If set to :delete, the associated objects are deleted without calling their destroy method. If set to :nullify, the associated objects’ foreign keys are set to NULL.

has_many :articles, dependent: :destroy

There’s much more to has_many associations than can possibly be covered here, so be sure to check out the Rails API documentation (https://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-has_many) for the full scoop.

Applying Association Options

It’s time to apply what you’ve learned to your domain model. Specifically, you use the :order option to apply a default order to the User.has_many :articles declaration , and you use the :dependent option to make sure when you delete a user, all their articles are deleted as well.

Specifying a Default Order

When you access a user’s articles, you want to make sure they come back in the order in which they’ve been published. Specifically, you want the oldest to be at the bottom of the list and the newest to be at the top. You can do this by configuring the has_many association with a default order using the order method in a scope. (We’ll explain scopes in detail later in this chapter.) Add a scope block to specify the default order of the has_many :articles declaration, as shown in Listing 6-8.
class User < ApplicationRecord
  has_one :profile
  has_many :articles,  -> { order 'published_at DESC' }
end

You give the name of the field that you want to order by, and then you say either ASC (ascending) or DESC (descending) to indicate the order in which the results should be returned. Because time moves forward (to bigger numbers), you want to make sure you’re going back in time, so you use the DESC keyword here.

Note

ASC and DESC are SQL keywords. You’re actually specifying an SQL fragment here, as discussed in the “Advanced Finding” section later in this chapter.

You can also specify a secondary order by adding a comma between arguments. Let’s say you want to sort by the title of the article after you sort by the date. If two articles have been published on the same day, they are ordered first by the date and then by the lexical order of the title. Listing 6-9 shows the article title added to the :order option.
class User < ApplicationRecord
  has_one :profile
  has_many :articles, -> { order 'published_at DESC, title ASC' }
end
Listing 6-9

Adding the Title to the Default Order for has_many: https://gist.github.com/nicedawg/d2054b9db2cfbb105c41c60dd9134f4c

Notice that you use ASC for ordering on the title. This is because as letters go up in the alphabet, their value goes up. So, to sort alphabetically, use the ASC keyword.

Specifying Dependencies

Frequently, dependencies exist between models. For instance, in your blog application, if you delete users, you want to make sure they don’t have articles in the system. Said another way, an Article is dependent on its User . You can let Active Record take care of this for you automatically by specifying the :dependent option to your association. Listing 6-10 shows all the options to has_many :articles, including the :dependent option.
class User < ApplicationRecord
  has_one :profile
  has_many :articles, -> { order 'published_at DESC, title ASC' },
                   dependent: :destroy
end

By passing in the symbol :destroy, you declare not only that articles are dependent but also that when the owner is deleted, you want to call the destroy method on every related article. This ensures that any *_destroy callbacks on the Article instances are called (callbacks are discussed later, in the “Making Callbacks” section). If you want to skip the callbacks, you can use the :delete option instead of :destroy, which deletes the records directly via SQL.

Let’s say you want to set the foreign key column (user_id) to NULL in the articles table, instead of completely destroying the article. Doing so essentially orphans the articles. You can do this by using the :nullify option instead of :destroy. If you don’t use the :dependent option and you delete a user with associated articles, you break foreign key references in your articles table. For this application, you want to keep the :nullify option, as per Listing 6-11.
class User < ApplicationRecord
  has_one :profile
  has_many :articles, -> { order 'published_at DESC, title ASC' },
                    dependent: :nullify
end

Creating Many-to-Many Associations

Sometimes, the relationship between two models is many-to-many. This describes a pattern where two tables are connected to multiple rows on both sides. You use this in the blog application to add categories to articles. If you wanted to allow only one category to be selected for a given article, you could use has_many. But you want to be able to apply multiple categories.

Think about this for a minute: an article can have many categories, and a category can have many articles—where does the belongs_to go in this situation? Neither model belongs to the other in the traditional sense. In Active Record speak, this kind of association is has_and_belongs_to_many (often referred to as habtm for short).

The has_and_belongs_to_many association works by relying on a join table that keeps a reference to the foreign keys involved in the relationship. The join table sits between the tables you want to join: articles and categories. Not surprisingly, then, the join table in this case is called articles_categories . Pay particular attention to the table name. It’s formed from the names of each table in alphabetical order, separated by an underscore. In this case, the a in articles comes before the c in categories—hence, articles_categories. Figure 6-4 illustrates this relationship.
../images/314622_4_En_6_Chapter/314622_4_En_6_Fig4_HTML.jpg
Figure 6-4

The many-to-many relationship between articles and categories

Let’s start by adding the Category model. This is a simple matter of generating the model, consisting of just a name column. Run the following command inside your application root:
$ rails generate model Category name:string
Look at the generated migration in db/migrate/20200214004535_create_categories.rb; it’s pretty familiar territory at this point. You need another migration to create the join table. Do that now by running the following command:
$ rails generate migration CreateJoinTableArticlesCategories article category

By naming our migration this way and passing the singular names of the models involved, this gave the rails generate command a hint that we wanted to create a join table and populated the resulting migration file with the commands we need.

Remember that when you use create_table inside a migration, you don’t need to specify the primary key, because it’s created automatically. Well, in the case of a join table, you don’t want a primary key. This is because the join table isn’t a first-class entity in its own right. Creating tables without primary keys is the exception and not the rule, so you need to explicitly tell create_table that you don’t want to create an id. It’s easy to forget, so Rails has added a new migration method called create_join_table to take care of that for you.

Take a look at Listing 6-12 to see how we’ll create a join table for our articles and categories. Uncomment the t.index lines so that database indexes will be created which will make related database queries more efficient.
class CreateJoin TableArticlesCategories < ActiveRecord::Migration[6.0]
  def change
    create_join_table :articles, :categories do |t|
      t.index [:article_id, :category_id]
      t.index [:category_id, :article_id]
    end
  end
end
Listing 6-12

The db/migrate/20200214004646_create_join_table_articles_categories.rb: File

This migration will create the articles_categories table with the right fields to support the associations. It also adds indexes to make sure that querying the database for an article’s categories (or a category’s articles) is fast. Go ahead and run the migrations:
$ rails db:migrate
== 20200214004535 CreateCategories: migrating ============================
-- create_table(:categories)
   -> 0.0061s
== 20200214004535 CreateCategories: migrated (0.0063s) ===================
== 20200214004646 CreateJoinTableArticlesCategories: migrating ===========
-- create_join_table(:articles, :categories)
   -> 0.0051s
== 20200214004646 CreateJoinTableArticlesCategories: migrated (0.0052s) ================
With the Category model and the join table in place, you’re ready to let Active Record in on your association. Open the Article and Category models and add the has_and_belongs_to_many declarations to them, as shown in Listings 6-13 and 6-14, respectively.
class Article < ApplicationRecord
  validates :title, :body, presence: true
  belongs_to :user
  has_and_belongs_to_many :categories
  def long_title
    "#{title} - #{published_at}"
  end
end
Listing 6-13

Adding the has_and_belongs_to_many Declaration in the Article Model app/models/article.rb: https://gist.github.com/nicedawg/c4ddd9231b23014146f1e06efaee8999

class Category < ApplicationRecord
  has_and_belongs_to_many :articles
end
Listing 6-14

Adding the has_and_belongs_to_many Declaration in Category Model app/models/category.rb:https://gist.github.com/nicedawg/df770f7673069c0d2ba0b45b3d8cc54f

Seeding Data

As part of creating an application skeleton, Rails added a file called db/seeds.rb, which defines some data you always need in your database. The seeds file contains Ruby code, so you can use the classes and methods—including associations—available in your models, such as create and update . Open it and create one user and a few categories so that it looks like Listing 6-15.
User.create email: 'mary@example.com', password: 'guessit'
Category.create [
  {name: 'Programming'},
  {name: 'Event'},
  {name: 'Travel'},
  {name: 'Music'},
  {name: 'TV'}
]
That should do nicely. You can load your seed data using the Rails command db:seed:
$ rails db:seed

If you need to add more default categories later, you can append them to the seeds file and reload it. If you want to rerun the seed data, the trick lies in the fact that the seeds file doesn’t know whether the records already in the database have to be cleaned up; running rake db:seed again adds all records one more time, and you end up with duplicate user and categories. You should instead call rails db:setup, which recreates the database and adds the seed data as you may expect.

Let’s give this a test run. Get your console ready, reload!, and run the following commands:
>> article = Article.last
=> #<Article id: 3, title: "Associations", ...>
>> category = Category.find_by name: 'Programming'
=> #<Category id: 1, name: "Programming", ..>
>> article.categories << category
=> [#<Category id: 1, name: "Programming", ..>]
>> article.categories.any?
=> true
>> article.categories.size
=> 1
Here, you automatically associate a category with an article using the << operator. You can even do this from the category side of the association. Try the following:
>> category.articles.empty?
=> false
>> category.articles.size
=> 1
>> category.articles.first.title
=> "Associations"

You just did the opposite of the previous test: has_and_belongs_to_many works in both directions, right? So you found your category and asked it for its first article titled “Associations” because that’s what you associated in the other direction.

Using has_and_belongs_to_many is a very simple way to approach many-to-many associations. However, it has its limitations. Before you’re tempted to use it for more than associating categories with articles, note that it has no way of storing additional information on the join. What if you want to know when or why someone assigns a category to an article? This kind of data fits naturally in the join table. Rails includes another type of association called has_many :through , which allows you to create rich joins like this.

Creating Rich Many-to-Many Associations

Sometimes, when you’re modeling a many-to-many association, you need to put additional data on the join model. But because Active Record’s has_and_belongs_to_many uses a join table (for which there is no associated model), there’s no model on which to operate. For this type of situation, you can create rich many-to-many associations using has_many :through. This is really a combination of techniques that ends up performing a similar but more robust version of has_and_belongs_to_many.

The basic idea is that you build or use a full model to represent the join table. Think about the blog application: articles need to have comments, so you create a Comment model and associate it with Article in a one-to-many relationship using has_many and belongs_to. You also want to be able to retrieve all the comments added to users’ articles. You could say that users have many comments that belong to their articles or users have many comments through articles. Figure 6-5 illustrates this relationship.
../images/314622_4_En_6_Chapter/314622_4_En_6_Fig5_HTML.jpg
Figure 6-5

The rich many-to-many relationship between comments and users, through articles

Let’s generate the model and migration for the Comment model:
$ rails generate model comment article_id:integer name:string email:string body:text
      invoke      active_record
      create      db/migrate/20200214010834_create_comments.rb
      create      app/models/comment.rb
      invoke      test_unit
      create      test/unit/comment_test.rb
      create      test/fixtures/comments.yml
Migrate by issuing the rails db:migrate command:
$ rails db:migrate
== 20200214010834 CreateComments: migrating =============================
-- create_table(:comments)
   -> 0.0106s
== 20200214010834 CreateComments: migrated (0.0109s) ====================
Update your models to reflect the one-to-many association between comments and articles. Listings 6-16 and 6-17 show the updated Comment and Article models , respectively.
class Comment < ApplicationRecord
  belongs_to :article
end
Listing 6-16

The Comment Model in app/models/Comment.rb: https://gist.github.com/nicedawg/1704eb1a760eb147d802b92d20d2ad29

class Article < ApplicationRecord
  validates :title, :body, presence: true
  belongs_to :user
  has_and_belongs_to_many :categories
  has_many :comments
  def long_title
    "#{title} - #{published_at}"
  end
end
Listing 6-17

The Article Model in app/models/article.rb: https://gist.github.com/nicedawg/016592081e5ea73a4170af60afa7043a

Nothing is new here—what you implement is very similar to the users and articles relationship you saw earlier, but instead of a user having many articles, an article has many comments.

Let’s get back to the relationship between users and comments. You need to tell your User model that a user has many comments through its articles. Basically, you use the Article model as a join table between users and comments. You achieve the linking using the has_many :through method. Listing 6-18 shows the updated User model.
class User < ApplicationRecord
  has_one :profile
  has_many :articles, -> { order 'published_at DESC, title ASC' },
           dependent: :nullify
  has_many :replies, through: :articles, source: :comments
end
Listing 6-18

The Updated User Model, has_many :through Declarations in app/models/user.rb: https://gist.github.com/nicedawg/23d2377fd1ac4d87c992acc341c6b8ad

Notice that you rework how you name associations. One aspect of the Rails philosophy is that you should always be questioning and refactoring your code to work with best practices. In this incarnation, comments that users receive on their articles are called replies.

As an added benefit, has_many :through allows you to easily have nice names for your associations. The :source option lets you define the source name of the association. In this case, the replies are the articles’ comments, so you set the :source option accordingly.

Let’s play with this on the console to see how it works—don’t forget to reload!. You first find the first user, find the user’s first article, and create a comment on it. Then, you see that comment directly from the user object:
>> user = User.first
=> #<User id: 1, email: "user@example.com", ...>
>> user.replies.empty?
=> true
>> article = user.articles.first
=> #<Article id: 3, title: "Associations", ..., user_id: 1>
>> article.comments.create(name:  'Guest',
email: 'guest@example.com', body: 'Great article!')
=> #<Comment id: 1, article_id: 3, name: "Guest", ...>
>> user.replies
=> [#<Comment id: 1, article_id: 3, name: "Guest", ...>]
>> user.replies.size
=> 1

Advanced Finding

Chapter 5 covered use of the find class method in Active Record. This section expands on different find operations using the where method. Building advanced finder methods is one of the most important things you do with your models.

Using the where Method

The most basic condition style is the hash syntax. Active Record takes the Hash passed to the where method and turns the keys into column names and the values into parameters to match. The hash syntax is useful only if you’re trying to find an exact match. Run the following command in a console window to try out the hash syntax:
>> Article.where(title: 'Advanced Active Record')
=> [#<Article id: 1, title: "Advanced Active Record", ...>]

The hash syntax works well for straightforward where operations where you use only ANDs to join together the conditions (i.e., all conditions must match). However, sometimes you need more flexibility than exact matches.

Using an SQL Fragment

To specify conditions, you can pass in an SQL fragment as a string that is sent directly to the query. You need to have a pretty decent knowledge of SQL to use this kind of syntax; but it provides a lot of flexibility, and you can create arbitrarily complex SQL fragments if you’re an SQL ninja.

Try the same find operation as in the previous section, but use a pure SQL condition fragment:
>> Article.where("title = 'Advanced Active Record'")
=> [#<Article id: 1, title: "Advanced Active Record", ...>]
Let’s try something more complicated that only SQL is able to do:
>> Article.where("created_at > '2020-02-04' OR body NOT LIKE '%model%'")
=> [#<Article id: 1, title: "Advanced Active Record", ...>, #<Article id: 2, title: "One-to-many associations", ...>, #<Article id: 3, title: "Associations", ...>]
Instead of using the = sign, you use the greater-than (>) symbol to make sure the date occurs after February 4, 2020. This is followed by the SQL OR operator, which says “if this first part isn’t a match, then try the right-hand side and give it a second chance at matching.” Therefore, you check the right-hand side only if the left-hand side fails. If an item fails the created_at match, you check to see if the body is NOT LIKE code. You can think of OR as a more permissive joining operator. It only cares that one of the conditions is a match. OR has a sister named AND, which requires that both conditions are true:
>> Article.where("created_at > '2020-02-04' AND body NOT LIKE '%model%'")
=> [#<Article id: 2, title: "One-to-many associations"...>, #<Article id: 3, title: "Associations", ...>]

You also use the SQL LIKE (modified using NOT, for negation) operator, which allows you to make partial matches. Normally, when using =, SQL requires that the strings match perfectly. However, LIKE is more permissive and allows partial matches when used with the % wildcard. The % symbols are SQL wildcard characters that apply in LIKE clauses. A % at the beginning of a pattern says that the pattern must match at the end of the field (the beginning can be any sequence of characters); a % at the end means that the pattern must match at the beginning, where the end can be any sequence of characters. Using a % on both sides of the pattern means that it must match anywhere in the field. Using %model% means that the word model must occur somewhere (anywhere) in the body of the article. In the previous example, you don’t want articles that have the word model; therefore, an article with the sentence “I don’t have your match” is accepted as a match.

As you can see, this usage has all the flexibility of SQL, but it also has SQL’s natural limitations. For instance, you may need to find information based on what the user passes into the application via the request parameters in your application (Chapter 8 covers request parameters). If you aren’t careful, those data can be very dangerous to your application, because they are open to SQL injection attacks. In such an attack, a user submits malicious code that tricks your database server into doing far more than you intended. For more information about SQL injection, check out the Wikipedia article at https://en.wikipedia.org/wiki/SQL_injection. Fortunately, Rails gives you a way to avoid such threats by using the array condition syntax, which performs correctly quoted replacements.

Using an Array Condition Syntax

The array condition syntax gives you the ability to specify conditions on your database calls in a safer way than using SQL syntax. Also, you don’t need to worry as much about SQL specifics like quoting and other concerns, because it does automatic conversions for you on the inputs you give it. This is how it protects against SQL injection—it ensures that the substituted values are safely quoted, thereby preventing malicious users from injecting arbitrary SQL into your queries.

The following example requires the use of a nice little Ruby method called Time.now. Basically, it returns a Time object that is set to the current time. Let’s see if you can find all the articles that were published before today:
>> Article.where("published_at < ?", Time.now)
=> [#<Article id: 1, title: "Advanced Active Record", ...>]
Instead of writing in the date, you put a ? in the spot where you’d normally write the value you want to find. The where method takes the second element in the array, Time.now, and replaces it where the first ? appears. Additionally, the array syntax automatically takes your time and converts it into something your database likes. You can invoke the to_sql method after the where method to inspect the issued SQL statement:
>> Article.where("published_at < ?", Time.now).to_sql
=> "SELECT \"articles\".* FROM \"articles\" WHERE (published_at < '2020-02-14 01:26:23.278875')"
You give it a Time object, and it turns the object into the format that pleases your database. If you had passed it a string, it wouldn’t have converted. You can even pass some information from another model:
>> Article.where("created_at = ?", Article.last.created_at)
=> [#<Article id: 3, title: "Associations", ...>]
That condition returns all the articles created at the same moment as the last article. You can pass as many conditions as you want, as long as they occur in the same order as the question marks:
>> Article.where("created_at = ? OR body LIKE ?", Article.last.created_at, 'model')
=> [#<Article id: 3, title: "Associations", ...>]
Monitoring the Logs

You can see the SQL statements issued by your application in the file log/development.log. It’s often useful to monitor what the server is doing. You may have already noticed that when you run rails server, it tells you about what is going on in your application. However, different web servers (depending on what you’ve installed) give different outputs, some more descriptive than others.

Fortunately, Rails prints all of its activities to a log file. If you look in your log directory, you see log/development.log. This is the file where all the activities of your application are output. If you’re running in production mode, the log file is log/production.log.

This file is written to live by your server. Sometimes it’s useful (especially on a live server) to monitor the events occurring on your server. If you’re on a UNIX system, you can run the command tail -f log/development.log to get a live feed from your logs. If you’re on a Windows system, you can find several applications that behave like tail with a quick Google search.

During debugging, it can be useful to output messages to the log to see what’s going on with your application. Almost anywhere in your application, you can type this:
Rails.logger.debug "This will only show in development"
Rails.logger.warn "This will show in all environments"

Both of these messages print directly to the log file and can be extremely useful for figuring out what is happening with your server.

The main disadvantage with the array syntax is that it can become confusing to remember the order of the elements you’re passing in for the conditions.

Instead of adding a list of things at the end of the array, you can pass in a hash and change the question marks to actual named replacements. This can help you keep the order of your arguments straight:
>> Article.where("title LIKE :search OR body LIKE :search",
{search: '%association%'})
=> [#<Article id: 2, title: "One-to-many associations", ...>,
#<Article id: 3, title: "Associations", ...>]

As you can see, you can reuse the same term in multiple places in your condition. If you were using the regular array syntax, you’d have to pass the same value '%association%' twice. This is especially useful if you have many, many conditions.

Using Association Proxies

Association proxy is a fancy term for the ability to chain together multiple calls to Active Record. You’ve been using this technique throughout the book, but it hasn’t received special mention. Here is a basic example of association proxies:
>> User.first.articles.all
=> [#<Article id: 3, title: "Associations", ...>]

This code returns all the articles of the first user. The all method (off articles) is automatically scoped to the user, which is to say it finds articles that belong to that user. If you recall, articles is a has_many relationship on the User model.

Scoped finders are also more secure. Imagine a multiple-user system where data owned by one user shouldn’t be accessible by another user. Finding an associated object (say, an article) by its id doesn’t restrict it to articles owned by a particular user. You could pass in the article_id and the user_id as conditions, but that’s sloppy and prone to error. The correct way to do this is to scope all find operations off the user in question. For example, assuming you have a User object stored in the variable current_user, current_user.articles.find(1) ensures that the article with id 1 is returned only if it belongs to the current_user.

Anyone who has done database work will realize that this incredibly simple syntax is far easier than the SQL queries that need to be created to achieve similar results. If you play around with these chains, you can check out the log to see the SQL that’s generated—be happy that you didn’t have to write it yourself!

This technique doesn’t just apply to finding. You can use it to automatically assign ownership with build and create constructors by setting the appropriate foreign keys. Consider the following example, which creates a new article for the current_user. It automatically sets the article’s user_id to that of the current user:
current_user.articles.create(title: 'Private', body: ‘Body here..’)

This is much better than the alternative, which is to go through the Article model directly and set the user_id as an attribute (Article.create(user_id: current_user.id). As a rule, whenever you need to restrict find operations to an owner or if you’re assigning ownership, you should use the power of the association proxy.

Other Finder Methods

Active Record ships with other finder methods that complement the where method and can be used on their own as well. Table 6-6 lists some of those methods with a brief description and a quick example.
Table 6-6

Some Active Record Finder Methods

Method

Description

Example

where(conditions)

Specifies the conditions in which the records are returned as a WHERE SQL fragment.

Article.where("title = 'Advanced Active Record'")

order

Specifies the order in which the records are returned as an ORDER BY SQL fragment.

Article.order("published_at DESC")

limit

Specifies the number of records to be returned as a LIMIT SQL fragment.

Article.limit(1)

joins

Specifies associated tables to be joined in as a JOIN SQL fragment.

Article.joins(:comments)

includes

Specifies associated tables to be joined and loaded as Active Record objects in a JOIN SQL fragment.

Article.includes(:comments)

You’ve used the where method before. Let’s take the rest for a spin:
>> Article.all
=> [#<Article id: 1, title: "Advanced Active Record", ...>,
#<Article id: 2, title: "One-to-many associations", ...>,
#<Article id: 3, title: "Associations", ...>]
>> Article.order("title ASC")
=> [#<Article id: 1, title: "Advanced Active Record", ...>,
#<Article id: 3, title: "Associations", ...>,
#<Article id: 2, title: "One-to-many associations", ...>]
>> Article.limit(1)
=> [#<Article id: 1, title: "Advanced Active Record", ...>]
>> Article.order("title DESC").limit(2)
=> [#<Article id: 2, title: "One-to-many associations", ...>,
#<Article id: 3, title: "Associations", ...>]

You first retrieve a list of articles with all; then, you retrieve all articles ordered alphabetically by their title using the order method. After that, you retrieve a single article using the limit method. Finally, you chain the limit method to order to retrieve a couple of articles after sorting them. All methods listed in Table 6-6 are chainable; when you chain finder methods to one another, Rails combines their specifics to form a single query to the database.

Default Scope

As you write applications, you may notice that you repeat certain conditions many times throughout your code. For the blog application, it would make sense to display categories in alphabetical order, as the user would expect. Rails provides a technique called scope to encapsulate commonly used find operations. Rails doesn’t enforce a default order; it lets the database take care of sorting the results, which in most cases is done on the primary key id. Let’s look at how your Category records are returned now:
>> Category.all
=> [#<Category id: 1, name: "Programming", ...>, #<Category id: 2, name: "Event", ...>,
#<Category id: 3, name: "Travel", ...>, #<Category id: 4, name: "Music", ..>,
#<Category id: 5, name: "TV", ...>]
As you can see, categories are returned according to their primary key id. Let’s make sure categories are always listed alphabetically, regardless of the conditions you use for the query. The code in Listing 6-19 tells the Category class that you always want records to be ordered by the name field.
class Category < ApplicationRecord
  has_and_belongs_to_many :articles
  default_scope { order :name }
end
Listing 6-19

The default_scope Declaration in app/models/category.rb: https://gist.github.com/nicedawg/f16cbf50672545b569db91995dc1ee6c

As you may expect, you can pass any finder method to default_scope. Let’s see the order in which your categories are retrieved now:
>> reload!
Reloading...
>> Category.all
=> [#<Category id: 2, name: "Event", ...>, #<Category id: 4, name: "Music", ...>,
#<Category id: 1, name: "Programming", ...>, #<Category id: 5, name: "TV", ...>,
#<Category id: 3, name: "Travel", ...>]

As you can see, your categories are sorted alphabetically by default.

Named Scope

The default scope is useful. But in most cases, the only code you want to have there is default ordering for your application, because adding a condition to default_scope would cause that condition to be applied every time. For queries that you run often, you should create named scopes that make your code easier to read and maintain.

Let’s create two named scopes: the first one lists all the articles with a published_at date and is named :published; the second scope lists all the articles without a published_at date and is named :draft. You create both scopes using the scope method, which takes the name of the scope as its first parameter and a finder method call as its second. Listing 6-20 shows the updated Article model.
class Article < ApplicationRecord
  validates :title, :body, presence: true
  belongs_to :user
  has_and_belongs_to_many :categories
  has_many :comments
  scope :published, -> { where.not(published_at: nil) }
  scope :draft, -> { where(published_at: nil) }
  def long_title
    "#{title} - #{published_at}"
  end
end
Listing 6-20

Named Scope Declarations in app/models/article.rb: https://gist.github.com/nicedawg/322f265fd4499310098e37b80bc66fef

As in a regular where method, you can use arrays as parameters. In fact, you can chain finder methods with other named scopes. You define the recent scope to give you articles recently published: first, you use the published named scope, and then you chain to it a where call (Listing 6-21).
class Article < ApplicationRecord
  validates :title, :body, presence: true
  belongs_to :user
  has_and_belongs_to_many :categories
  has_many :comments
  scope :published, -> { where.not(published_at: nil) }
  scope :draft, -> { where(published_at: nil) }
  scope :recent, -> { where('articles.published_at > ?', 1.week.ago.to_date) }
  def long_title
    "#{title} - #{published_at}"
  end
end
Listing 6-21

Recent Named Scope Declaration in app/models/article.rb: https://gist.github.com/nicedawg/aac8a3412e80ea6f3398dfc5817a2f14

Note

Wondering what the strange -> { } syntax is which we use for scopes? It’s a shorthand syntax for generating a lambda—a self-contained standalone method in Ruby, which is executed only when you invoke it. You must use a lambda or some other object that responds to call when defining a scope. By wrapping our code in a lambda, we’re assured it will be reevaluated every time the scope is used. Without using a lambda, it would only be evaluated once, meaning, for example, calling Article.draft may end up returning stale data—including some articles which are no longer drafts—and perhaps omitting new draft articles which were created since the first usage of Article.draft.

To make scopes even more useful, you can define scopes that can receive parameters, instead of hardcoding the values you want to query with. You need search functionality that allows the end user to look up articles by title; so let’s add another scope called where_title that accepts an argument and searches by it (Listing 6-22).
class Article < ApplicationRecord
  validates :title, :body, presence: true
  belongs_to :user
  has_and_belongs_to_many :categories
  has_many :comments
  scope :published, -> { where.not(published_at: nil) }
  scope :draft, -> { where(published_at: nil) }
  scope :recent, -> { where('articles.published_at > ?', 1.week.ago.to_date) }
  scope :where_title, -> (term) { where("articles.title LIKE ?", "%#{term}%") }
  def long_title
    "#{title} - #{published_at}"
  end
end
Listing 6-22

The where_title Named Scope Declaration in app/models/article.rb: https://gist.github.com/nicedawg/271672430205b48c7ffb068582991ca8

Now that you’ve added those scopes, let’s see them in action in a console session. When you look at the results of running the methods, you get an English-like syntax that makes the code easy to read and expand. Pay special attention to the line that uses Article.draft.where_title("one"), which shows how you chain scopes to get the exact data you want:
>> reload!
Reloading...
>> Article.published
=> [#<Article id: 1, title: "Advanced Active Record", ...>]
>> Article.draft
=> [#<Article id: 2, title: "One-to-many associations", ...>,
#<Article id: 3, title: "Associations", ...>]
>> Article.recent
=> [#<Article id: 1, title: "Advanced Active Record", ...>]
>> Article.draft.where_title("one")
=> [#<Article id: 2, title: "One-to-many associations", ...>]
>> Article.where_title("Active")
=> [#<Article id: 1, title: "Advanced Active Record", ...>]

Applying Validations

It’s probably a safe bet that you don’t want every field in your tables to be optional. Certain fields need to be required , terms of service agreements need to be accepted, and passwords need to be confirmed. That’s just the way it is when you’re building web applications, and Rails understands this. Consider this example of an Account model :
class Account < ApplicationRecord
  validates :login, presence: true
  validates :password, confirmation: true
  validates :terms_of_service, acceptance: true
end

Like associations, validations are sets of high-level macros that let you selectively apply common validation requirements to your model’s attributes. In this section, you create a full set of validations for your blog application, and you see firsthand how easy it is to perform basic validations with Active Record. You start by applying some of the built-in validations, and then you build a couple custom validation methods.

Using Built-in Validations

Rails has myriad built-in validators, all of which are accessible through the validates method. Here you will learn about some of the options the validates method accepts as you apply them to your blog application. Check the API for details of all the Rails validators (https://api.rubyonrails.org/classes/ActiveModel/Validations/ClassMethods.html).

As a reference to get you started, you can pass two common options into any built-in validator. These are described in Table 6-7.
Table 6-7

Default Options for All Validators

Option

Description

Example

:message

Specifies the error message shown if validation fails.

message: 'too long'

:on

Specifies when this validation happens. The default is :save. Other options are :create and :update.

on: :create

Validating That a Value Has Been Entered

You can use the :presence option to make sure a user has entered something into a field. This is very useful in many cases. You have those validations in the Article model for the title and body fields, as shown in Listing 6-23.
class Article < ApplicationRecord
  validates :title, :body, presence: true
  belongs_to :user
  has_and_belongs_to_many :categories
  has_many :comments
  scope :published, -> { where.not(published_at: nil) }
  scope :draft, -> { where(published_at: nil) }
  scope :recent, -> { where('articles.published_at > ?', 1.week.ago.to_date) }
  scope :where_title, -> (term) { where("articles.title LIKE ?", "%#{term}%") }
  def long_title
    "#{title} - #{published_at}"
  end
end
Listing 6-23

The Article Model, Validating Presence in app/models/article.rb https://gist.github.com/nicedawg/869fe075987f4a8f0d20a1bb1ac1632c

The default message is “can’t be blank.”

Validating That a Value Is Unique

Often, you want to ensure that a certain field is unique. The :uniqueness option validates whether the value of the specified attribute is unique across the system. You use this method in the User model to make sure each email is unique, as shown in Listing 6-24.
class User < ApplicationRecord
  validates :email, uniqueness: true
  has_one :profile
  has_many :articles, -> { order 'published_at DESC, title ASC' },
           dependent: :nullify
  has_many :replies, through: :articles, source: :comments
end
Listing 6-24

The validates_uniqueness_of Method in app/models/user.rb

When the record is created, a check is performed to ensure no record exists in the database with the given value for the specified attribute email (that maps to a column). When the record is updated, the same check is made, disregarding the record itself. The default error message is “#{value} has already been taken.”

The :scope option can also validate whether the value of the specified attributes is unique based on multiple parameters. For example, you can use it to ensure that a teacher is on the schedule only once per semester for a particular class:
class Schedule < ApplicationRecord
  valdates :teacher_id, uniqueness: { scope: [:semester_id, :class_id] }
end

Validating Length or Size

Sometimes you want to validate the length, or size, of a field entry. You can do this by using the :length option. You use this method in the User model to specify a valid number of characters for an email address, as shown in Listing 6-25. The option for specifying a size range is :within.
class User < ApplicationRecord
  validates :email, uniqueness: true
  validates :email, length: { in: 5..50 }
  has_one :profile
  has_many :articles, -> { order 'published_at DESC, title ASC' },
           dependent: :nullify
  has_many :replies, through: :articles, source: :comments
end
Listing 6-25

The validates_length_of Method in app/models/user.rb

If you want to ensure only the minimum or maximum, you can use the :minimum or :maximum option. Table 6-8 lists the most common :length validator’s options.
Table 6-8

Options for Validating :length

Option

Description

:minimum

Specifies the minimum size of the attribute.

:maximum

Specifies the maximum size of the attribute.

:is

Specifies the exact size of the attribute.

:in

Specifies the valid range (as a Ruby Range object) of values acceptable for the attribute.

:allow_nil

Specifies that the attribute may be nil; if so, the validation is skipped.

:too_long

Specifies the error message to add if the attribute exceeds the maximum.

:too_short

Specifies the error message to add if the attribute is below the minimum.

:wrong_length

Specifies the error message to add if the attribute is of the wrong size.

:message

Specifies the error message to add if :minimum, :maximum, or :is is violated.

Validating the Format of an Attribute

The :format option checks whether a value is in the correct format. Using this method requires familiarity with regular expressions (regex) or being able to steal other people’s regular expressions. The classic example (and the one you need) is email. Update the validates method as shown in Listing 6-26.
class User < ApplicationRecord
  validates :email, uniqueness: true
  validates :email, length: { in: 5..50 }
  validates :email, format: { with:  /\A[^@][\w.-]+@[\w.-]+[.][a-z]{2,4}\z/i }
  has_one :profile
  has_many :articles, -> { order 'published_at DESC, title ASC' },
           dependent: :nullify
  has_many :replies, through: :articles, source: :comments
end
Listing 6-26

Update validates :format Method in app/models/user.rb: https://gist.github.com/nicedawg/7814013e0aa05613ea1e8f6a0b6ba89f

Don’t be put off by how complicated this looks. You pass in the :with option and a regex object to say what patterns you want to match.

Tip

If you want to learn more about using regular expressions, you can find many tutorials and books on the subject. One good reference is Nathan Good’s Regular Expression Recipes (Apress, 2005).

Validating Confirmation

Whenever a user changes an important piece of data (especially the password), you may want the user to confirm that entry by typing it again. This is the purpose of the :confirmation validation helper. When you use this helper, you create a new virtual attribute called #{field_name}_confirmation. Add this to the User model for password confirmation, as shown in Listing 6-27.
class User < ApplicationRecord
  validates :email, uniqueness: true
  validates :email, length: { in: 5..50 }
  validates :email, format: { with:  /\A[^@][\w.-]+@[\w.-]+[.][a-z]{2,4}\z/i }
  validates :password, confirmation: true
  has_one :profile
  has_many :articles, -> { order 'published_at DESC, title ASC' },
           dependent: :nullify
  has_many :replies, through: :articles, source: :comments
end
Listing 6-27

The validates :confirmation Method in app/models/user.rb: https://gist.github.com/nicedawg/d5398b7f716bfa919a233e6cb68b6925

The password attribute is a column in the users table, but the password_confirmation attribute is virtual. It exists only as an in-memory variable for validating the password. This check is performed only if password_confirmation isn’t nil and runs whenever the object is saved.

Other Validations

There is one other important validation helper, :acceptance, which validates the acceptance of a Boolean field.

Building Custom Validation Methods

In the blog application, you’d like to make sure no one creates a comment for an article that hasn’t been published yet. First, you need to create a method so you can ask an Article whether its published_at field is null by using the present? method, which returns true if a value exists and false otherwise . This method is useful outside validations, because you may want to indicate on the administration interface later whether an article has been published. Let’s add that method now and call it published?. Add the code shown in Listing 6-28 to the Article model.
class Article < ApplicationRecord
  validates :title, :body, presence: true
  belongs_to :user
  has_and_belongs_to_many :categories
  has_many :comments
  scope :published, -> { where.not(published_at: nil) }
  scope :draft, -> { where(published_at: nil) }
  scope :recent, -> { where('articles.published_at > ?', 1.week.ago.to_date) }
  scope :where_title, -> (term) { where("articles.title LIKE ?", "%#{term}%") }
  def long_title
    "#{title} - #{published_at}"
  end
  def published?
    published_at.present?
  end
end
Listing 6-28

Adding the published? Method in app/models/article.rb: https://gist.github.com/nicedawg/9751e165bf7106c97044f4b133d1e322

This gets you a step closer to your goal. When building validations, Active Record gives you nice objects called errors to use. Whenever you want to add a validation error to the list of errors, you just type errors.add(column_name, error_message). So let’s implement a method called article_should_be_published in the Comment class that uses this functionality, as shown in Listing 6-29.
class Comment < ApplicationRecord
  belongs_to :article
  def article_should_be_published
    errors.add(:article_id, 'is not published yet') if article && !article.published?
  end
end
Listing 6-29

Adding the article_should_be_published Method in app/models/comment.rb

This checks whether you should apply the error by evaluating the if statement. If that if statement is true, you want to add an error into the errors object. Note that before you test whether the article is published, you make sure article isn’t nil. This is so your test doesn’t throw an error. If article is nil, that should be handled by another validator: the validates_presence_of method .

How do you tell Active Record that this method should be run before a save? You use the validate class method and pass it a symbol with the name of the method. At the top of your Comment class, add the code shown in Listing 6-30. Note that we also expect comments to have values for name, email, and body; so we add a presence validation call.
class Comment < ApplicationRecord
  belongs_to :article
  validates :name, :email, :body, presence: true
  validate :article_should_be_published
  def article_should_be_published
    errors.add(:article_id, 'is not published yet') if article && !article.published?
  end
end
Listing 6-30

The validate Method in app/models/comment.rb

This advises Active Record to pay attention to your new article_should_be_published method . In Chapter 16, you write tests to make sure this is working. But you can also go to the console—if you have it open already, don’t forget to reload!—and try to create an invalid object to see if it reports errors for you. The easiest way to get to errors in an Active Record object is with comment.errors.full_messages, as shown here:
>> article = Article.draft.first
=> #<Article id: 2, title: "One-to-many associations", ...>
>> comment = article.comments.create name: 'Dude',
email: 'dude@example.com', body: 'Great article!'
=> #<Comment id: nil, article_id: 2, name: "Dude", email: "dude@example.com",
body: "Great article!", created_at: nil, updated_at: nil>
>> comment.errors.full_messages
=> ["Article is not published yet"]

Making Callbacks

You often want to have certain things happen during the lifecycle of the model. Certain actions need to happen during certain events pertaining to a particular model. For instance, what if you want to send an email to your administrator whenever someone cancels an account? Or perhaps you want to make sure to create a new model because some other model was also created. Sometimes, certain actions in the life of a model should execute associated actions.

To implement this, Active Record has callbacks. Six callbacks are commonly used in Active Record models:
  • before_create

  • after_create

  • before_save

  • after_save

  • before_destroy

  • after_destroy

As you can see, the names of the Rails callbacks describe their purpose. When you create a method with any of these names in your model, the method is called automatically by the model during the time the name suggests. For instance, if you make a before_save method , that method is called right before the model object is saved.

Any callback that starts with before_ can stop the execution chain if it returns false. For instance, if you define before_create, you ensure that this model object will never be created:
def before_create
  false
end

This can be a gotcha later if you’re doing something like an assignment of false to a variable. If you’re ever confused why a model won’t save, check your before_ filters.

In the blog application, you’d like to make sure that when a user creates a comment, an email is automatically sent to the article author. Although you don’t send an email here, this chapter goes over the steps required to put together code to eventually send the email in Chapter 12. To set this up, you add an after_create method to the Comment class that will eventually have the code to send an email. Add the method shown in Listing 6-31 to the Comment model.
class Comment < ApplicationRecord
  belongs_to :article
  validates :name, :email, :body, presence: true
  validate :article_should_be_published
  def article_should_be_published
    errors.add(:article_id, "is not published yet") if article
    && !article.published?
  end
  def after_create
    puts "We will notify the author in Chapter 12"
  end
end
Listing 6-31

Adding after_create Method in app/models/comment.rb

You use the code you want to be executed directly in the code of the after_create method . This is nice and simple, but you should probably use the pattern as you did for validate in Listing 6-30, where you pass in a symbol that references the method to run when the validation is performed. This helps keep the code readable and easier to augment in the future, because you can supply an arbitrary number of methods to run on a callback, separated by a comma. Name the method email_article_author, and tell Active Record to run it after a record is created, as shown in Listing 6-32.
class Comment < ApplicationRecord
  belongs_to :article
  validates :name, :email, :body, presence: true
  validate :article_should_be_published
  after_create :email_article_author
  def article_should_be_published
    errors.add(:article_id, 'is not published yet') if article && !article.published?
  end
  def email_article_author
    puts "We will notify #{article.user.email} in Chapter 12" if article.user
  end
end
Listing 6-32

The email_article_author Method Specified as an after_create Callback in app/models/comment.rb: https://gist.github.com/nicedawg/03868d69e4b318cfcd73d87fe495dd23

Active Record provides many more callbacks than are mentioned here, but those listed at the beginning of this section are the ones you’ll find yourself using often. Some of the others are used in extremely rare cases (for instance, after_initialize, which is called after an object is initialized). These callbacks can help you with just about anything you need to do during the lifecycle of a model. They’re part of smart models, which know how to deal with their own birth, life, and death. See https://guides.rubyonrails.org/active_record_callbacks.html for more information.

Updating the User Model

You still need to do a little work on your User model. You can apply many of the techniques described in this chapter, such as custom methods to allow you to perform user authentication and validation methods to make sure your data stay clean.

When you created the user migration (Listing 6-2), you added a field called password. This field stores a plain-text password, which, if you think about it, isn’t very secure. It’s always a good idea to encrypt any sensitive data so they can’t be easily read by would-be intruders. You deal with the encryption in the User model itself, but the first thing you do is rename the field in the database from password to hashed_password. This is so you can create a custom accessor called password with which to set the password while maintaining a field to store the encrypted version in the database. The plain-text password is never saved.

To accomplish this, you create a migration. From the terminal, issue the following command to create the new migration:
$ rails generate migration rename_password_to_hashed_password
Next, fill in the migration as shown in Listing 6-33.
class RenamePasswordToHashedPassword < ActiveRecord::Migration[6.0]
  def change
    rename_column :users, :password, :hashed_password
  end
end
Listing 6-33

Migration to Rename password to hashed_password in db/migrate/20200214024558_rename_password_to_hashed_password.rb: https://gist.github.com/nicedawg/1b40a825d44fb4a99393fec8884f4043

Run the migration using the rails db:migrate command, as follows:
$ rails db:migrate
== 20200214024558 RenamePasswordToHashedPassword: migrating ==============
-- rename_column(:users, :password, :hashed_password)
   -> 0.0220s
== 20200214024558 RenamePasswordToHashedPassword: migrated (0.0222s) ==========
Next, update your User model so it looks like that in Listing 6-34. You program all the user authentication methods you need for allowing users to log in. Let’s look at the code first and then see in detail what you’ve done.
require 'digest'
class User < ApplicationRecord
  attr_accessor :password
  validates :email, uniqueness: true
  validates :email, length: { in: 5..50 }
  validates :email, format: { with:  /\A[^@][\w.-]+@[\w.-]+[.][a-z]{2,4}\z/i }
  validates :password, confirmation: true, if: :password_required?
  validates :password, length: { in: 4..20 }, if: :password_required?
  validates :password, presence: true, if: :password_required?
  has_one :profile
  has_many :articles, -> { order 'published_at DESC, title ASC' },
           dependent: :nullify
  has_many :replies, through: :articles, source: :comments
  before_save :encrypt_new_password
  def self.authenticate(email, password)
    user = find_by email: email
    return user if user && user.authenticated?(password)
  end
  def authenticated?(password)
    self.hashed_password == encrypt(password)
  end
  protected
  def encrypt_new_password
    return if password.blank?
    self.hashed_password = encrypt(password)
  end
  def password_required?
    hashed_password.blank? || password.present?
  end
  def encrypt(string)
    Digest::SHA1.hexdigest(string)
  end
end
Listing 6-34

Current User Model in app/models/user.rb: https://gist.github.com/nicedawg/fd5a29e943c06b7e93824a0b71cfd16c

Note

The SHA1 hashing algorithm used in this example is weak and was only used for an example. For production websites, you should take a look at the bcrypt gem (https://github.com/codahale/bcrypt-ruby).

Whenever you store something sensitive like a password, you should encrypt it. To encrypt the password in your User model, you use a simple algorithm called a hash that creates a random-looking string from the provided input. This hashed output can’t be turned back into the original string easily, so even if someone steals your database, they will have a prohibitively difficult time discovering your users’ passwords. Ruby has a built-in library called Digest that includes many hashing algorithms.

Let’s go through the additions to the User model:
  • require 'digest': You start by requiring the Digest library you use for encrypting the passwords. This loads the needed library and makes it available to work within your class.

  • attr_accessor :password: This defines an accessor attribute, password, at the top of the class body. It tells Ruby to create reader and writer methods for password. Because the password column doesn’t exist in your table anymore, a password method isn’t created automatically by Active Record. Still, you need a way to set the password before it’s encrypted, so you make your own attribute to use. This works like any model attribute, except that it isn’t persisted to the database when the model is saved.

  • before_save :encrypt_new_password: This before_save callback tells Active Record to run the encrypt_new_password method before it saves a record. That means it applies to all operations that trigger a save, including create and update.

  • encrypt_new_password: This method should perform encryption only if the password attribute contains a value, because you don’t want it to happen unless a user is changing their password. If the password attribute is blank, you return from the method, and the hash_password value is never set. If the password value isn’t blank, you have some work to do. You set the hashed_password attribute to the encrypted version of the password by laundering it through the encrypt method.

  • encrypt: This method is fairly simple. It uses Ruby’s Digest library, which you included on the first line, to create an SHA1 digest of whatever you pass it. Because methods in Ruby always return the last thing evaluated, encrypt returns the encrypted string.

  • password_required?: When you perform validations, you want to make sure you’re validating the presence, length, and confirmation of the password only if validation is required. And it’s required only if this is a new record (the hashed_password attribute is blank) or if the password accessor you created has been used to set a new password (password.present?). To make this easy, you create the password_required? predicate method, which returns true if a password is required or false if it’s not. You then apply this method as an :if condition on all your password validators.

  • self.authenticate: You can tell this is a class method because it’s prefixed with self (it’s defined on the class itself). That means you don’t access it via an instance; you access it directly off the class, just as you would find, new, or create (User.authenticate, not @user = User.new; @user.authenticate). The authenticate method accepts an email address and an unencrypted password. It uses a dynamic finder (find_by_email) to fetch the user with a matching email address. If the user is found, the user variable contains a User object; if not, it’s nil. Knowing this, you can return the value of user if, and only if, it isn’t nil and the authenticated? method returns true for the given password (user && user.authenticated?(password)).

  • authenticated?: This is a simple predicate method that checks to make sure the stored hashed_password matches the given password after it has been encrypted (via encrypt). If it matches, true is returned.

Let’s play with these new methods from the console so you can get a better idea of how this comes together:
>> reload!
Reloading...
=> true
>> user = User.first
=> #<User id: 1, email: "user@example.com", ..>
>> user.password = 'secret'
=> "secret"
>> user.password_confirmation = 'secret'
=> "secret"
>> user.save
=> true
>> user.hashed_password
=> "e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4"
>> User.authenticate('user@example.com', 'secret')
=> #<User id: 1, email: "user@example.com", ...>
>> User.authenticate('user@example.com', 'secret2')
=> nil
>> second_user = User.last
=> #<User id: 2, email: "mary@example.com", ...>
>> second_user.update(password: 'secret',
password_confirmation: 'secret')
=> true
>> User.authenticate('mary@example.com', 'secret')
=> #<User id: 2, email: "mary@example.com", ...>

When you ask the User model to authenticate someone, you pass in the email address and the plain-text password. The authenticate method hashes the given password and then compares it to the stored (hashed) password in the database. If the passwords match, the User object is returned, and authentication was successful. When you try to use an incorrect password, nil is returned. In Chapter 8, you write code in your controller to use these model methods and allow users to log in to the site. For now, you have a properly built and secure backend for the way users authenticate.

With the validation in the User model, the db/seeds.rb file also needs to be updated to make sure it follows the rules expected in the model. While we are at it, we also add some code to create a few articles. Update your db/seeds.rb file so that it looks like Listing 6-35.
user = User.create email: 'mary@example.com', password: 'guessit', password_confirmation: 'guessit'
Category.create [
  {name: 'Programming'},
  {name: 'Event'},
  {name: 'Travel'},
  {name: 'Music'},
  {name: 'TV'}
]
user.articles.create([
  {
    title: 'Advanced Active Record',
    body: "Models need to relate to each other. In the real world, ..",
    published_at: Date.today,
  },
  {
    title: 'One-to-many associations',
    body: "One-to-many associations describe a pattern ..",
    published_at: Date.today
  },
  {
    title: 'Associations',
    body: "Active Record makes working with associations easy..",
    published_at: Date.today
  },
])
Listing 6-35

Current Seeds File in db/seeds.rb: https://gist.github.com/nicedawg/86d1950f400c39eadd23067a3f26bd5e

Reviewing the Updated Models

You’ve made a lot of changes to your models, so let’s make sure we’re on the same page before you move on. Look at the Article , Category, and Comment models in Listings 6-36, 6-37, and 6-38, respectively, and make sure yours match.
class Article < ApplicationRecord
  validates :title, :body, presence: true
  belongs_to :user
  has_and_belongs_to_many :categories
  has_many :comments
  scope :published, -> { where.not(published_at: nil) }
  scope :draft, -> { where(published_at: nil) }
  scope :recent, -> { where('articles.published_at > ?', 1.week.ago.to_date) }
  scope :where_title, -> (term) { where("articles.title LIKE ?", "%#{term}%") }
  def long_title
    "#{title} - #{published_at}"
  end
  def published?
    published_at.present?
  end
end
Listing 6-36

Current Article Model in app/models/article.rb

class Category < ApplicationRecord
  has_and_belongs_to_many :articles
  default_scope { order :name }
end
Listing 6-37

Current Category Model in app/models/category.rb

class Comment < ApplicationRecord
  belongs_to :article
  validates :name, :email, :body, presence: true
  validate :article_should_be_published
  after_create :email_article_author
  def article_should_be_published
    errors.add(:article_id, 'is not published yet') if article && !article.published?
  end
  def email_article_author
    puts "We will notify #{article.user.email} in Chapter 12"
  end
end
Listing 6-38

Current Comment Model in app/models/comment.rb

Summary

After reading this chapter, you should have a complete understanding of Active Record models. The chapter covered associations, conditions, validations, and callbacks at breakneck speed. Now the fun part starts. In the next chapter, you get to use all the groundwork established in this chapter to produce the web interface for the data structures you’ve created. This is when you get to reap the benefits of your hard work.