Restful_authentication collects some basic information to get an account set up, like a username, email address, and password, which all gets stored in a User model. What if you have some additional information that you’d like to collect upon registration, and you don’t want to store it in the User model (addresses, for example)? Rails 2.3 makes this easy.
Let’s get an example registration app set up:
rails registration
ruby script/plugin install http://svn.techno-weenie.net/projects/plugins/restful_authentication
ruby script/generate authenticated user sessions
Cool, now you have your registration app set up with a Users model, controller, and view. If you start up your server and head to http://localhost:3000/users/new/ you should see a very basic registration form. Let’s add our Address model. For the sake of brevity, I’m just going to create a field that references the User model and one field for a zip code, but you get the idea.
ruby script/generate model Address user:references zipcode:string
rake db:migrate
With our Address model in place, we need to let the User model know that it is linked to the Address model. Here’s what the User model looks like (\app\models\user.rb):
require 'digest/sha1'
class User < ActiveRecord::Base
# Virtual attribute for the unencrypted password
attr_accessor :password
validates_presence_of :login, :email
# lots more stuff that we're not going to worry about...
We’re going to add three things:
require 'digest/sha1'
class User < ActiveRecord::Base
has_one :address
accepts_nested_attributes_for :address
attr_accessible :address_attributes
# Virtual attribute for the unencrypted password
attr_accessor :password
validates_presence_of :login, :email
# lots more stuff that we're not going to worry about...
We’ve told the User model that (1) it has one Address model, (2) that it should save data for Address automagically, and (3) that the address_attributes fields are permitted fields to receive data for (if you read through the code a bit further you’ll see another attr_accessible line, you could add address_attributes there too).
We need to make one change to the User controller (\controllers\users_controller.rb) to prevent an “Called id for nil” error later on. At line 6 you should have an empty new method. We’re going to create an instance variable for User:
# render new.rhtml
def new
@user = User.new
end
Let’s now take a look at the one User view that restful_auth created (\app\views\users\new.html.erb):
<%= error_messages_for :user %>
<% form_for :user, :url => users_path do |f| -%>
<p><label for="login">Login</label><br/>
<%= f.text_field :login %></p>
<p><label for="email">Email</label><br/>
<%= f.text_field :email %></p>
<p><label for="password">Password</label><br/>
<%= f.password_field :password %></p>
<p><label for="password_confirmation">Confirm Password</label><br/>
<%= f.password_field :password_confirmation %></p>
<p><%= submit_tag 'Sign up' %></p>
<% end -%>
First of all, we need to change the form_for :user to be form_for @user. Second, we get to add in our zipcode field:
<%= error_messages_for :user %>
<% form_for @user, :url => users_path do |f| -%>
<p><label for="login">Login</label><br/>
<%= f.text_field :login %></p>
<p><label for="email">Email</label><br/>
<%= f.text_field :email %></p>
<p><label for="password">Password</label><br/>
<%= f.password_field :password %></p>
<p><label for="password_confirmation">Confirm Password</label><br/>
<%= f.password_field :password_confirmation %></p>
<% @user.build_address unless @user.address %>
<% f.fields_for :address do |a| %>
<p>
<%= a.label :zipcode %>
<%= a.text_field :zipcode %>
</p>
<% end %>
<p><%= submit_tag 'Sign up' %></p>
<% end -%>
Here’s where I ran into the two gotcha’s. If you don’t switch form_for :user to form_for @user when you submit the form you’ll get a very unpleasant error that looks like this:
ActiveRecord::AssociationTypeMismatch in UsersController#create
Address(#46729050) expected, got HashWithIndifferentAccess(#23561230)
Second, if you don’t add in line 15, @user.build_address unless @user.address, you’ll get the following error when you try to view the form:
You have a nil object when you didn't expect it!
More info on why that error occurs here.
Anyway, with the above steps you should now have a registration form that creates records for both User and Address. If you found this helpful, leave a comment and let me know!
Recent Comments