Customised User Management
Goldberg’s user and self-registration system is functional but fairly basic. Most real live sites would want to customise the information they collect about their users, as well as the appearance (the login page, the emails sent for self-registration etc.). Fortunately this is all possible, and without altering the code in RAILS_ROOT/vendor/plugins/goldberg.
The examples in this page are based on a recent project, where I helped a community group set up a site to allow parents to register their children for an event. These were the customisations we required:
- We decided to make another model to supplement Goldberg::User, to contain more detailed name and address information for the parents.
- The parents were named by their email addresses (for the purposes of logging in etc.). It was decided that the concept of a screen name would be too complex and unnecessary for random members of the public, especially considering they would only be using the site for one purpose.
- There were various aspects of the user-registration process that we wanted to customise, including the content of the self-registration emails.
Note that there’s always more than one way to do it. This is the approach that suited us at the time — the specific techniques you use might differ, but hopefully this will give you some ideas.
Extending the User Model
A good approach is to create a separate model in a belongs-to relationship with Goldberg::User, to contain any supplementary details and functionality that Goldberg::User doesn’t offer.
RAILS_ROOT/app/models/parent.rb
class Parent < ActiveRecord::Base
belongs_to :user, :class_name => 'Goldberg::User'
protected
def validate_on_update
# Check for empty fields
errors.add_on_empty %w(given_names surname address suburb postcode)
# Check that at least one phone number has been provided
(
(self.home_phone && self.home_phone.length > 0) ||
(self.mobile_phone && self.mobile_phone.length > 0)
) || errors.add_to_base("You need to provide either your home or mobile phone number")
end
end
Goldberg::User.class_eval do
has_one :parent, :dependent => :destroy
validates_format_of :name,
:with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i
end
One potential issue is that Goldberg::User’s association with Member won’t come into effect until this file is loaded. To avoid any problems it would be a good idea to require this model on startup. Add the following line to your environment.rb:
RAILS_ROOT/config/environment.rb
# Put this somewhere near the end:
require 'parent'
User Self-registration
We wanted to use self-registration, but to customise how it worked in terms of the views and the fields required. The approach we took was to manage the parents and their corresponding Goldberg::User records through the parents controller.
RAILS_ROOT/app/views/parents/register.rhtml
<% form_tag :controller => '/parents', :action => 'register_submit' do %>
<table>
<tr>
<th class="required">Email address</th>
<td><%= text_field 'user', 'email' %></td>
</tr>
<tr>
<th class="required">Password</th>
<td><%= password_field 'user', 'clear_password' %></td>
</tr>
<tr>
<th class="required">Password, again</th>
<td><%= password_field 'user', 'confirm_password' %></td>
</tr>
</table>
<br/>
<%= submit_tag 'Register' %>
<br/>
<% end %>
Note the use of form_tag instead of form_for. Not every Rails user is aware of the distinction because form_for is so ubiquitous, but these two methods offer two very different approaches to form design:
form_forcreates a form that is bound to a specific object. This is very convenient if you are only dealing with one model object because then you can use other methods such astext_fieldthat automatically populate the value of the form field.form_tagcreates a form that isn’t bound to a specific object. This approach offers a high degree of flexibility: you can create form fields for multiple different objects within the one form. For example you could create atext_field_tagcalled “user[fullname]”, and another called “parent[home_phone]”. Rails would unpack these into differnt parts of theparamshash: all theuserfields underparams[:user], and all theparentfields underparams[:parent].
So form_for is easy, but form_tag is highly flexible, allowing you to gather all the details for both the Goldberg::User and the other associated model all in one form. Then in the controller, you can create both the records and associate them.
Customising the Login Page
If you want to customise the the appearance of Goldberg’s login page, you can alter its templates. But rather than altering any files in vendor/plugins/goldberg/app, it’s recommended that you make a copy of Goldberg’s login templates and alter those.
Start by copying RAILS_ROOT/vendor/plugins/goldberg/app/views/goldberg/auth to RAILS_ROOT/app/views/goldberg/auth. But then how do you tell Goldberg to use your customised login templates, rather than the provided ones? Add the following to the end of your application.rb file:
RAILS_ROOT/app/controllers/application.rb
# Add this at the end:
Goldberg::AuthController.class_eval do
self.template_root = File.join("#{File.dirname(__FILE__)}/../views")
self.layout "layouts/application"
end
Customising the Self-registration Emails
Similarly to customising the login page, you can copy Goldberg’s templates into your application’s space and modify them.
For our project, we actually copied Goldbergs UserMailer templates and made a new mailer class called ParentMailer. This is because we not only wanted to customise the appearance of the emails: we also wanted to expand the functionality of the mailer model because there were other email messages we wanted to be able to send.
