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.

  1. Creating the User Controller

    We are starting with something that you should already be familiar with:

    Exercise 4.1
    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.
    2. 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.
    3. 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.

  2. 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:string

    This 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
    end
    
    

    Quite 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.

  3. 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
    end
    
    

    Short 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 Conventions

    The 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.2

    Create 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.
  4. 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:migrate
    Creating a Database

    This 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:all

    If 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.

  5. 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"
      end
    
    

    And 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 Rails

    In 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.3

    Create a form relevant to your extended User model.

    Exercise 4.4

    Design 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>.

  6. 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
      end
    
    

    Wow! 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...
  7. Quick Check

    First, try out our register action and register two or three users.

    Now, use your Console Window and type:

    ruby script/console

    This 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_name

    Close the console with:

    >> exit

    Now, 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)
      end
    
    
    app/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.

  8. 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
    end
    
    

    Now 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.

  9. 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.

Exercise 4.8

Introduce flash functionality to your program.

Valid XHTML 1.0 Strict Valid CSS!