16: Virtual Attributes 

(view original Railscast)

Other translations: Cn It Es Fr

Below is a user registration form that accepts a first name, a last name and a password.

These fields are also in our database structure:

create_table "users", :force => true do |t|
  t.string   "first_name"
  t.string   "last_name"
  t.string   "password"

But what if we want to change the user interface so that we have a full_name field instead of fields for first_name and last_name? We can do this by using a virtual attribute. First, we’ll change our view code and combine the two name fields.

<% form_for @user do |form| %>
<ol class="formList">
    <%= form.label :full_name, 'Full Name' %>
    <%= form.text_field :full_name %>
    <%= form.label :password, 'Password' %>
    <%= form.password_field :password %>
<% end %>

new.html.erb with the full_name field in place.

Now, when we submit the form it will look for a full_name attribute in the User model, which it currently doesn’t have. We can create this by defining a virtual attribute for full_name.

class User < ActiveRecord::Base
  # Getter
  def full_name
    [first_name, last_name].join(' ')

  # Setter
  def full_name=(name)
    split = name.split(' ', 2)
    self.first_name = split.first
    self.last_name = split.last

The getter and setter methods defined in the User model.

The getter method gets the first_name and last_name values for the user and returns them, joined by a space. The setter method splits the passed value at the first space and sets the first_name and last_name to be the first and second parts of the split string.

Using virtual properties means that the user interface doesn’t have to have a field to map to each field in the corresponding database table. This is especially useful when you’re using Rails to connect to a legacy database and can’t alter the fields in its tables.

Seeing it in action

We'll try out our new form and see if it works. We’ll enter “John Smith” as the full name and “secret” as the password.

  Processing UsersController#create (for at 2009-01-10 21:55:44) [POST]
    Parameters: {"user"=>{"password"=>"secret", "full_name"=>" John Smith"}, "commit"=>"Add user", "authenticity_token"=>"6990f4ad21cb4f9c812a6f10ceef51faa4f46ce7"}
    User Create (0.4ms)   INSERT INTO "users" ("first_name", "last_name", "password") VALUES('John', 'Smith', 'secret')

Part of the development log showing the new user being added to the database.

We can see from the development log above that the full name was sent to the create action (and then on to the User model), but that the separate first and last names were passed to the INSERT statement passed to the database.

Virtual attributes provide a powerful and flexible way to allow you to customise your user interface