How to use the Delegate Pattern in Ruby on Rails
Tags: ruby, rails, tutorial, showdev
Published: 2020-12-04 15:49:23

---
title: How to use the Delegate Pattern in Ruby on Rails
published: true
date: 2020-11-27 19:29:00 UTC
tags: ruby, rails, tutorial, showdev
canonical_url: https://web-crunch.com/posts/how-to-use-the-delegate-pattern-ruby-on-rails
---

{% youtube FRnXFjxS2LA %}

When working with more complex associations in a Ruby on Rails app you may find yourself in an object chaining problem upon rendering data to a view or helper. The delegate pattern is a handy built-in feature to Ruby on Rails to help with this issue.

### What is delegate exactly?

Taking a deeper look [at the Ruby on Rails API documentation](https://api.rubyonrails.org/classes/Module.html#method-i-delegate) you can see delegate defined as a Ruby class method used to easily expose contained objects' public methods as your own.

That sounds more complicated than it is and luckily it's pretty simple to configure.

In the example, you'll find in this tutorial I used a `User` model and a `Profile` model to demonstrate this pattern. You'll need something similar to follow along but pretty much anything with a relationship intact should suffice.

Side note: I reference my Rails application template called [Kickoff Tailwind](https://github.com/justalever/kickoff_tailwind) in the video. This template actually creates the `User` model automatically for us thanks to the handy `Devise` gem.

If you don't have the template or don't want to bother with it you can run this to get a basic user model added to your app:

```bash
rails generate model User first_name:string last_name:string email:string
```

I ran the following to generate our profile model:

```bash
rails generate Profile tagline user:references
rails db:migrate
```

With both models created we need some dummy data. I'll leverage the `rails console` to create some by hand.

```bash
rails console
```

Because [my template](https://github.com/justalever/kickoff_tailwind) I mentioned prior leverages [Devise](https://github.com/heartcombo/devise) I'll go ahead and create the necessary object data we need to create a new user.

```bash
# if you're using my template/devise
> User.create({name: "Andy Leverenz", email: "andy@web-crunch.com", password: "password", password_confirmation: "password"})

# if you're not using my template/devise
> User.create({first_name: "Andy", last_name: "Leverenz", email: "andy@web-crunch.com"})
```

And now some Profile data:

```bash
> Profile.create(tagline: "My awesome profile", user_id: User.first.id)
```

Here we reference the `User` we just created and/or the first user created in your database. You can more conveniently run that as follows and the built in ActiveRecord magic should account for things automatically.

```bash
> Profile.create(tagline: "My awesome profile", user: User.first)
```

### Relationships

With our models and data created we need to ensure our models are set up for success.

#### User model

Our User model should be taken care of thanks to our generator when it was first created. My file looks like this. Again I used my template so your mileage may vary if you aren't using it.

```ruby
# app/models/user.rb
class User < ApplicationRecord
  has_person_name
  has_one :profile

  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable
end

```

Each user would only ever have one profile in this app per account so we'll establish that with the line that reads `has_one :profile`.

#### Profile model

The `Profile` model is quite simple:

```ruby
class Profile < ApplicationRecord
  belongs_to :user
end
```

Because a `User` `has_one :profile` a `Profile` needs to `belong_to` a `User.

### Adding delegation

At this point, if we were to code away in our app using and say render some data on a Profile view we would need to access it like this `@profile.user.name`. That chaining effect isn't a huge deal but it would be nice to not have to have the `.user` in the middle and/or refer to the name as something completely different.

We can extend the profile model to include these things using delegate.

In the User model's case, we can delegate some methods to the profile.

```ruby
class User < ApplicationRecord
  has_person_name
  has_one :profile

  delegate :username, to: :profile
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable

  def username
    "#{first_name}_#{last_name}"
  end
end
```

Here I create a new `username` class method (a public one) that consists of the `first_name` and `last_name` attributes of the `User` table on the database layer. That method we can then pass to the `delegate` method and assign it through to the `Profile` model.

```ruby
class Profile < ApplicationRecord
  belongs_to :user

  delegate :username, :email, to: :user, allow_nil: true, prefix: :user
end
```

On the `Profile` side we use the `delegate` method to pass any class methods to the `User` model that we want access from our `User` model inside our `Profile` model.

The `delegate` method allows you to optionally pass `allow_nil` and a `prefix` as well.

Ultimately this allows us to query for data in custom ways.

```bash
rails c
> profile = Profile.first
> profile.email #= "andy@web-crunch.com"
> profile.username #= "Andy_Leverenz"
```

Pretty handy!

As an application scale, I can see this pattern being useful in terms of scoping and productivity.

I haven't seen it widely in use myself but I think it may be due to these _invented_ naming conventions that aren't always obvious from one developer to the next on a team. The next developer that comes along might be looking for a database column named after some custom public method you made for delegation purposes and spend a lot of time trying to pinpoint its origin.

It has its pros and cons but looks really appealing for some applications. You be the judge!


### Shameless plug!

Are you new to Ruby on Rails? Would you like an in-depth jumpstart to your learning experience? I made a comprehensive course to assist with that. 

**Find out more at [hellorails.io](https://hellorails.io)**