Chapter 4: User Registration
In this chapter, we will allow our users to register. This is an important first step towards the full login functionality. To achieve this, we will do some real web programming - far beyond the basics we did last week. We will do it in genuine rails style - we'll create working web forms with just a pinch of HTML, and we will make a database to work for us - without using any SQL.
Creating the User Controller
We are starting with something that you should already be familiar with:
Exercise 4.1- Create a new controller called User. Create the following pages for it: index, register, login and logout. It's just enough to use a generator script to acomplish this.
- Add new items into the main menu: Register and Login. The main menu is defined in the application layout file app\views\layouts\application.html.erb. Notice that we are not yet adding a menu link to the index page at this stage.
- Try out your application. If the new pages don't work, restart Mongrel web application.
If you successfully accomplished the exercise, we have now a UserController class to control functionality related to the user accounts, with a register action to give people a way to register themselves. We'll leave the other actions, index, login and logout for later use.
Creating the User Model
It's time to create a model object for the User account. The model is the object responsible for communicating with the back-end database. It may be created using a special form of the generator script (to be run from within your Ruby Console Window):
ruby script/generate model User screen_name:string e_mail:string password:stringThis command specified the name for the model class: User, together with a number of fields: screen_name, e_mail and password.
Let's have a look at the newly created User.rb file that holds the definition of our model class:
app/models/user.rb
class User < ActiveRecord::Base endQuite surprisingly, the class definition contains... nothing! We will be back to this file soon, but now a question arises: where are our data fields defined?
For an answer, progress to the next section.
Exploring the Database Migration
Look at the following file (notice that it is not in app directory):
db/migrate/001_create_users.rb
class CreateUsers < ActiveRecord::Migration def self.up create_table :users do |t| t.string :screen_name t.string :e_mail t.string :password t.timestamps end end def self.down drop_table :users end endShort scripts called migrations, as the one above, modify the application back-end database. They are not only necessary when you develop an application; they also help when you port (or migrate) this application to your production server or wherever.
This code will create a table named users which will be linked to the User model. You can easily spot the data fields we've generated. Besides, rails automatically add some timestamps (magically created two informative fields: created_at and updated_at) which track the date and time of creation of every database record.
Naming ConventionsThe name of the database table follows rails naming conventions: the model is singular (a model of user), but the table name is plural (table of users). Similarly, a model Car would be matched with a table cars, a model Person would be matched with a table people (yes! Rails can do such irregular inflection!).
Exercise 4.2Create your own version of the User model. Use the following, extended list of attributes:
- screen_name,
- e-mail address.
- password,
- first name,
- surname,
- your specific item relevant to the theme of your application, like favourite book, owned car etc.
Raking the Application
Having a migration is not enough. We also have to execute this migration. In rails we say that we rake the migration. There is a special helper command to do so. Just type in the Ruby Console Window:
rake db:migrateCreating a DatabaseThis note is important only under the condition that you are using MySQL database (or a number of others): before raking the migration, you have to rake the database, or create it:
rake db:create:allIf you follow this guide, most probably you did not change the default database configuration and you are using sqlite database. If this is the case a database is waiting for you ready to use: you don't need the command above.
Creating the User/Register View
Based on our controller and action names, the registration page will live at http://localhost:3000/user/register. This URL calls the register action in the UserController. The action then renders the template in the corresponding html.erb view template file That's where we'll put the registration form HTML code.
First, we have to setup the page title in the UserController class, similar to what we did with SiteController actions last week:
app/controllers/user_controller.rb - register action
### omitted other stuff, we are only interested in register action now ### def register @title = "Register" endAnd here is the view template for the registration form:
app/views/user/register.html.erb
<h1>Register</h1> <%= error_messages_for :user %> <% form_for :user do |f| %> <p> <%= f.label :screen_name %>: <%= f.text_field :screen_name %> </p> <p> <%= f.label :e_mail, "E-Mail" %>: <%= f.text_field :e_mail %> </p> <p> <%= f.label :password %>: <%= f.password_field :password %> </p> <p> <%= f.submit "Register" %> </p> <% end %>Now try accessing http://localhost:3000/user/register and you should see a user registration form.
This is a good example of creating HTML views without actually using too much of HTML. Here are explanations for all the ruby functions used above:
- error_messages_for :user
- This function emits messages about errors that could occur when entering data for the user. This will be important for us when we implement validations.
- form_for :user do |f|
end - This creates a web form for the user object. This function also incorporates a block of ruby instructions - these are everything between do and end. A nice goodie here is the f variable that will be available everywhere inside the block. It represents the web form itself!
- f.label, f.text_field, f.password_field, f.submit
- These functions emit HTML tags for creating various parts of the form, like <label> or <input>. It is important to notice that they are performed by the f object. Therefore we don't have to specify the user object with each item - the form already knows what the object it is for. We still have to say which data fields we have in mind in each line.
Humanized Names in RailsIn the code above Rails simply takes the names we have used in our User model, for example screen_name, and use them to automatically generate labels (captions)! They are nicely converted from programming names (identifiers) into human readable names like Screen name. Look also how we've changed this default functionality in case of e_mail (which would produce E mail).
To make it looking better, why not to add a style to the stylesheet:
public/stylesheets/default.css - adding more styles
label { width: 150px; text-align: right; float: left; margin-top: 4px; }Exercise 4.3Create a form relevant to your extended User model.
Exercise 4.4Design a better stylesheet for your form. Why not to add a nicely-looking frame around the frame?. You may experiment with special HTML tags <fieldset> and <legend>.
What to do with the Post
When the user submits the form, the control is routed back to our register action in UserController class. This happens when the user presses the register button - which is, by the way, not just a button, it's a submit control.
The same action is therefore used in two different cases: one is when the web form is about to be displayed, and the other is when it is submitted.
How to tell which is which? The HTTP requests come in two flavours: GET and POST. When the page is about to display, it's the GET. When the user submits the form results - it's POST.
We can look at the values of request.get? and request.post? to see whether the incoming request was GET or POST (Values ending in a question mark will always be either be true or false).
app/controllers/user_controller.rb - register action
### omitted other stuff, we are only interested in register now ### def register @title = "Register" if request.post? @user = User.new(params[:user]) if @user.save flash[:notice] = "User with login #{@user.screen_name} created successfully!" redirect_to :action => :index end end endWow! This is realy a nice snippet of ruby code! Let's have a closer look at it:
- if request.post?
- We do the stuff only in case of the POST data (or user submission)
- @user = User.new(params[:user])
- This line creates a new user. The params[:user] represents all the data submitted by the user web form.
- if @user.save
- That's the very important point - the save functions stores the user data into the database. We check if it's successful - wait until the next section to check why it could be failed.
- flash[:notice] = "User with login #{@user.screen_name} created!"
- This is a flash notice - it will be explained later.
- redirect_to :action => :index
- Redirection is like a flying carpet - we end up in another action, or page...
Quick Check
First, try out our register action and register two or three users.
Now, use your Console Window and type:
ruby script/consoleThis is a ruby console in which you can now test your ruby objects. For example, type the following:
>> User.find(:all)What you get looks like a mess - but whatch carefully and you'll see all the data you've entered into the form. You have just requested to find all users!
Now, type, replacing the "name" with some real screen name you entered:
>> user = User.find_by_screen_name("name")Remember that ruby names are case-sensitive!
Now, one of the users should now be stored in your user variable. You can, for example, check its e-mail:
>> user.e_nameClose the console with:
>> exitNow, you're ready for some checking code to be added to the index action:
app/controllers/user_controller.rb - index action
### now we only change something in index, other stuff omitted def index @title = "Temporary View" @users = User.find(:all) endapp/views/user/index.html.erb
<h1>Users</h1> <ol> <% @users.each do |user| %> <li><%= user.screen_name %></li> <% end %> </ol>@users.each iterates through all the registered users. Each of them is available inside the iteration as user. "Inside the iteration" means between do and end.
Check http://localhost:3000/user
Exercise 4.5.Implement user registration, as described above, and modify your program so that it not only displays the screen_name, but also full real name and e_mail address for each user.
User Model Validations
The registration page works, but a real production server would impose a whole series of additional validation tests. For example, a valid user:
- has a screen name, e-mail and password (not blank),
- has a unique screen name,
- has a unique e-mail address,
- has a screen name that is between 6 and 30 characters long,
- have a e-mail address that is between 6 and 50 characters long,
- have a password that is between 6 and 30 characters long.
Rails has some amazing built-in features such as validations which are merely function calls. They are instructions to the User class itself, telling it about the properties that it should have. Rails runs these validations when it tries to save the user to the database, and the save only succeeds if all the validations pass. If any validation fails, it gives an error message which can be returned to the user. We will see several examples of this shortly. More details can be found at http://wiki.rubyonrails.org/rails/pages/UnderstandingValidation
Let's start with something simple. Edit the User model file and add the validators directly into the class code - these are instructions for the whole User class so they don't go to any particular finction:
app/models/user.rb
class User < ActiveRecord::Base validates_presence_of :screen_name, :e_mail, :password validates_uniqueness_of :screen_name validates_uniqueness_of :e_mail, :case_sensitive => false validates_length_of :screen_name, :within => 6..30 validates_length_of :e_mail, :within => 6..50 validates_length_of :password, :within => 6..30 endNow go to http://localhost:3000/user/register and see what happens if you don't follow any of the rules while creating a new user.
Using the magic on rails, our application automatically adds the error messages to the page. We can even reinforce this magic by adding some special ingredients to our CSS stylesheet:
public/stylesheets/default.css - adding more styles
.fieldWithErrors { display: inline; border: solid 2px maroon; background-color: red; }Exercise 4.6.Apply validations to your model. Additionally, validation password confirmation. First, add a new text_field into the register template (app/views/user/register.html.erb) to provide the space for the user to confirm his/her password. Then use validates_confirmation_of function to provide this validation.
Hint: check http://api.rubyonrails.org/ for details.
Exercise 4.7 - Advanced!Valid e-mail address should:
- start with a sequence of one or more letters, digits or '-', '_', '.', '%' characters,
- followed by '@' character,
- followed by one or more letters, digits or '_' characters,
- followed two, three or four letters at the end of the string.
The validates_format function provides validation of the well-formatted strings using Regular Expressions. Add a validation for valid e-mail addresses.
For a good introduction to the Regular Expressions check the RubyBook (available in your Ruby installation kit, from Windows Start menu), look for Standard Types chapter and then Regular Expressions.
Flash
The standard Rails way to give user feedback after a successful event is to put a message in a special container called the flash. The flash acts essentially as a hash that lasts for only one request, so that we can put a notice on a page which, when the page is reloaded, disappears. Having a flash notice is so common that it has become conventional to add a snippet to the layout itself between the header and the content:
Actually, we have used a flash notice in the UserController code. Find the following line of code:
app/controllers/user_controller.rb - code snippet
flash[:notice] = "User with login #{@user.screen_name} created!"For the flash notice to be rendered we have to add a few lines of code in the application.html.erb layout file.
app/views/layouts/application.html.erb - code snippet
# locate this snippet of code and only add the missing lines: <div id='content'> <% if flash[:notice] -%> <div id='notice'><%= flash[:notice] %></div> <% end %> <%= yield %> </div>Now, you should see a notice in green colour when user registration is successful.
Introduce flash functionality to your program.
Copyright (C) 2009-2011 by Jaroslaw Francik

