Basic User Model with Rails (Part 1 of 3)
October 17th, 2007. Published under Programming, Ruby on Rails, Tutorials. 1 Comment.
Creating a registration page for an application is one of the most common things to any application. There are countless tutorials out there that document a registration process, but I will take you through basic registration, and then end with a login system that can be used for any application. This article will be a 3 part series, and maybe even more if I feel people will benefit from it. This article is aimed at the beginner looking to create a small application, or getting used to the basics of Ruby on Rails.
This article assumes the reader has knowledge in migrations and MVC. It also assumes the reader has MySQL or any other database of choice. For this article I will be using MySQL.
Let us begin by creating a new rails project by issuing the following command in console on linux, mac, or in cmd on windows.
rails mysite
You can replace “mysite” with any name you want for your application, for simplicity I named it mysite. Now cd into that directory: cd mysite
Application Controller
The first step to any rails project (after creating the actual project, of course) is creating a controller. The controller, as you may have guessed, is the C in MVC, the design architecture used by rails. Rails controllers act as a container for a collection of related pages. Let us create the application controller now. We will call our controller Web
1 | ruby script/generate controller Web index |
make sure you’re running generate from within your newly created rails project. This command creates a controller named web_controller.rb with an action named index. With each action created via generate, a new “Ruby HTML” file is created in your views/site/ called index.rhtml. These rhtml files are the template files for our site.
Let us take a look at the new controller we created by opening up web_controller.rb in your editor.
1 2 3 4 5 | class WebController < ApplicationController def index end end |
The index is a blank action. index is a ruby function defined by the def keyword. Eventually we will fill in this function, but for now leave it alone.
Importance of Rails URLs
So far we have created a rails project, generated a controller with an action called “index”. We are far from being complete (not really, but it may seem so right now), but you actually have a working site. Start up Webrick by typing script/server from inside the rails project, and point your web browser to http://localhost:3000/web/index. Notice that the URL contains the base of our site (web) and the action in the controller (index). Many rails URLs follow this form: http://localhost:3000/[controller]/[action]. Since almost every index page is usually the very root of the web site or application, we will change our routes.rb to allow us to go to http://localhost:3000 to get to our index action. Open config/routes.rb and uncomment the line
1 | # map.connect '', :controller => "welcome" |
and change it to this:
1 | # map.connect '', :controller => "web" |
Don’t try to navigate to http://localhost:3000 yet. We have to do one more thing. Rails; by default, looks for a file called index.html in the public directory. If we don’t remove it manually then rails will return to that page by default. It is a welcome page to any new rails project. It is no longer needed, so remove it.
1 | rm public/index.html |
Now go ahead and test out your routes by navigating to http://localhost:3000.
Views and Layouts
Let us change what the index page looks like with some generic text. Open up app/views/web/index.rhtml, which is a rails view. The V in MVC.
1 2 | <h1>Welcome to Our Rails Application</h1> <p>Have a look around or register for our application. If you have already created an account, you may login.</p> |
You may notice that there is no DOCTYPE or any meta information within this view. We will leave that to Rails layouts to handle. A layout is simply an .rhtml file. You can name it whatever you want. Let us create one called standard.rhtml and it will reside in app/views/layouts/ directory in your rails project.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html> <head> <meta http-equiv="Content-type" content="text/html; charset=utf-8"> <title><%= @title %></title> </head> <body> <ul> <li><%= link_to "Home", :action => "index" %></li> <li><%= link_to "Register", :action => "register" %></li> <li><%= link_to "Login", :action => "login" %></li> </ul> <%= @content_for_layout %> </body> </html> |
I’ll quickly go through this bit of code line by line, we will get back to this later in more detail when required.
Line 6: Is an “instance variable” defining the title of the page. This instance variable will be set in our controller shortly.
Line 10-13: is a rails function for HTML generation. The first parameter is the link name, the section is a hash defining the action. You can read more about link_to on the Rails API.
Line 15: This is where the real magic of the layout comes from. It inserts the value of the variable into the layout. But where does this come from? When rails processes the URL http://localhost:3000, it puts the results of the index.rhtml into the @contentforlayout. In our case the @contentforlayout contains
1 2 | <h1>Welcome to Our Rails Application</h1> <p>Have a look around or register for our application. If you have already created an account you may login.</p> |
Rails then processes the layout, thereby substituting the content into the appropriate place.
Now it is time to edit our controller to include our layout you just created. It should look like this:
1 2 3 4 5 6 | class WebController < ApplicationController layout 'standard' def index @title = "Welcome to Rails" end end |
Using the keyword layout followed by the filename without the extension tells our application to use standard.rhtml as our base file for our layouts
Setting up your Database
Since we want to store our users in a database, we need to set one up. I will assume that you have a database set up, if not go ahead and create a database now.
1 | mysqladmin create applicationdb_development -u root -p |
You just created the database called applicationdb_development.
We now have to tell rails to use our newly created database before we start building our User model. Open the file “database.yml” in “config/” folder. Focus on the part under development. Enter your username and password used to connect the database. Here is my config for development, we will get to what the others are for shortly:
1 2 3 4 5 6 | development: adapter: mysql database: applicationdb_development username: root password: mypassword socket: /tmp/mysql.sock |
User Model
Now that our database is created and we told rails how to use it, it’s time to create a data model for our users. We will use username, email, and password for the registration page, and username will be used to login. The email address will be used for the forgot password feature that will be in part 3.
Let us generate a new rails model and call it User:
ruby script/generate model User
Open up the newly created migration file in db/migration/001_create_users.rb and edit it to look like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 | class CreateUsers < ActiveRecord::Migration def self.up create_table :users do |t| t.column :username, :string t.column :email, :string t.column :password, :string end end def self.down drop_table :users end end |
and then run the rake db:migrate. Again, make sure you are running these commands from within your rails project. You should see something similar to this:
1 2 3 4 5 6 7 8 | tdela:~/projs/article/mysite Trevor$ rake db:migrate (in /Users/Trevor/projs/article/mysite) == CreateUsers: migrating ===================================================== -- create_table(:users) -> 0.0469s == CreateUsers: migrated (0.0471s) ============================================ tdela:~/projs/article/mysite Trevor$ |
This creates the user table in your database that you defined in the database.yml.
User Validations
First, I think a short recap of what we’ve done so far is needed. We created a rails project with 1 action named index. We edited our routes.rb and we created a view and a layout for our application. We generated a model named User and added 3 columns to our database. We are now ready to get to the core of this; start interacting with the database, and validate user registrations.
Let us start by first saving a user into our database by using the console. The reason I am using the console to do this is so we can see why we need to validate, and what type of validations we will need in our User model.
1 2 3 4 5 6 7 8 9 | tdela:~/projs/article/mysite Trevor$ script/console
Loading development environment.
>> newuser = User.new(:username => "trevor", :email => "trevor.example.com", :password => "xy")
=> #<User:0x34e525c @new_record=true, @attributes={"username"=>"trevor", "password"=>"xy", "email"=>"trevor.example.com"}>
>> newuser.username
=> "trevor"
>> newuser.save
=> true
>> |
There is some obvious problems with this. An invalid email, and the password is too short. We could of used anything for a password, even blank, and the same applies to the username. Let us edit our user.rb model located in app/models/ directory and add some validations to it:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | class User < ActiveRecord::Base validates_uniqueness_of :username, :email validates_length_of :username, :within => 4..15 validates_length_of :password, :with => 4..15 validates_format_of :username, :with => /^[A-Z0-9_]*$/i, :message => "must contain only letters, numbers, and underscores" validates_format_of :email, :with => /^[A-Z0-9._%-]+@([A-Z0-9-]+\.)+[A-Z]{2,4}$/i, :messages => "must be a valid email address" end |
Let us now try our new validations.
1 2 3 4 5 6 7 8 9 10 11 12 | tdela:~/projs/article/mysite Trevor$ script/console
Loading development environment.
>> newuser = User.new(:username => "trevor", :email => "abc.com", :password => "123")
=> #<User:0x34e4744 @new_record=true, @attributes={"username"=>"trevor", "password"=>"123", "email"=>"abc.com"}>
>> newuser.username
=> "trevor"
>> newuser.email
=> "abc.com"
>> newuser.password
=> "123"
>> newuser.save
=> false |
(*Note: Instead of closing the console, you could have just typed reload! to reload the model). newuser.save returned false this time, so let us see where our validations caught the errors.
1 2 3 4 5 6 7 | >> newuser.errors.on(:username) => "has already been taken" >> newuser.errors.on(:email) => "is invalid" >> newuser.errors.on(:password) => "is too short (minimum is 4 characters)" >> |
Using errors.on shows us where the errors were caught. This is much better but we have one more topic to discuss when validating.
Better validations using Rails validate Function
Our previous validations do not pick up spaces in usernames, or the “@” symbol in an email address. Rails doesn’t provide predefined validations for these cases. Rails has a built-in function name called validate. Let us create this now:
1 2 3 4 5 6 | def validate errors.add(:email, "Must be a valid email address.") unless email.include?("@") if username.include?(" ") errors.add(:username, "Username can not include spaces.") end end |
Let us test our additional validation:
1 2 3 4 5 6 7 8 9 | tdela:~/projs/article/mysite Trevor$ script/console
Loading development environment.
>> newuser = User.new(:username => "tr v", :email => "tr.example.com", :password => "123456")
=> #<User:0x34e3e98 @new_record=true, @attributes={"username"=>"tr v", "password"=>"123456", "email"=>"tr.example.com"}>
>> newuser.save
=> false
>> newuser.errors.full_messages
=> ["Username must contain only letters, numbers, and underscores", "Username Username cannot include spaces.", "Email is invalid", "Email must be a valid email address."]
>> |
As you can now see, our validate function disallowed us to save a username with spaces, and the email address must contain a “@”. You also see we can see our full error messages by typing newuser.errors.full_messages. Just for one last test, let us create a user that will pass our validations.
1 2 3 4 5 6 7 | tdela:~/projs/article/mysite Trevor$ script/console
Loading development environment.
>> newuser = User.new(:username => "newuser_1", :email => "newuser@email.com", :password => "somepassword")
=> #<User:0x34e354c @new_record=true, @attributes={"username"=>"newuser_1", "password"=>"somepassword", "email"=>"newuser@email.com"}>
>> newuser.save
=> true
>> |
newuser.save returns true, so the user is now saved. Our validations can be made better too, but we won’t get into that now. We have a pretty basic, but solid user model for our registration.
Part 2; User registration, using this User model, will be published within the next few days. You can download the code used in this article. Any questions, problems or comments can be left below.
e-velocity - Trevor Delamorandiere » Blog Archive » User Registration with Rails (Part 2 of 3) on October 25th, 2007
[...] Basic User Model with Rails (Part 1 of 3) Posted, 17/10/2007 [...]