#16 Virtual Attributes
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" end
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.
<h1>Register</h1> <% form_for @user do |form| %> <ol class="formList"> <li> <%= form.label :full_name, 'Full Name' %> <%= form.text_field :full_name %> </li> <li> <%= form.label :password, 'Password' %> <%= form.password_field :password %> </li> </ol> <% 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(' ') end # Setter def full_name=(name) split = name.split(' ', 2) self.first_name = split.first self.last_name = split.last end end
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 127.0.0.1 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