Create a Generic Rails Base Project
This tutorial covers how to create a simple rails base project with user authentication using binarylogic’s authlogic gem. More advanced users might want to throw a few other gems in, but user authentication is good for a bare minimum, since practically every web app you code will have some sort of user authentication.
1. Create a new rails project
$ rails baseproject
2. Change directory into the new project’s folder
$ cd baseproject
3. Delete public/index.html so we can set our own landing page.
$ rm public/index.html
4. Create Home controller with the index action and view, which will be our landing page.
$ script/generate controller Home index
5. Install the authlogic gem if you haven’t done so yet.
$ gem install authlogic
6. Add the authlogic gem declaration to your environment.rb around line 22, under the section where there are similar commented out config.gem declarations.
# config.gem "hpricot", :version => '0.6', :source => "http://code.whytheluckystiff.net" # config.gem "sqlite3-ruby", :lib => "sqlite3" # config.gem "aws-s3", :lib => "aws/s3" config.gem "authlogic"
7. Scaffold the user model.
$ script/generate scaffold User login:string email:string crypted_password:string password_salt:string persistence_token:string
These fields are specified by authlogic. You simply create the fields in the db, and authlogic will use them accordingly. Once you understand authlogic better, you can add more fields to add functionality to the app.
8. Migrate your db.
$ rake db:migrate
9. Add the ‘acts_as_authentic’ declaration to app/models/user.rb so it looks like this:
class User < ActiveRecord::Base acts_as_authentic end
10. Erase the code in app/views/users/edit.html.erb and paste this code:
<h1>edit profile</h1> <% form_for @user do |f| %> <%= f.error_messages %><br /> <%= f.label 'edit password', f.object.new_record? ? nil : "change password" %><br /> <%= f.password_field :password %><br /> <br /> <%= f.label :password_confirmation %><br /> <%= f.password_field :password_confirmation %><br /> <br /> <%= f.submit "update" %> or <%= link_to "cancel", url_for(:controller => 'users', :action => 'show', :id => @user.id ) %> <% end %>
11. Erase the code in app/views/users/index.html.erb and paste this code:
<h1>Listing users</h1> <table> <tr> <th>Login</th> <th>Email</th> </tr> <% @users.each do |user| %> <tr> <td><%=h user.login %></td> <td><%=h user.email %></td> <td><%= link_to 'Show', user %></td> <td><%= link_to 'Edit', edit_user_path(user) %></td> <td><%= link_to 'Destroy', user, :confirm => 'Are you sure?', :method => :delete %></td> </tr> <% end %> </table> <br /> <%= link_to 'New user', new_user_path %>
12. Erase the code in app/views/users/new.html.erb and paste this code:
<h1>edit profile</h1> <% form_for @user do |f| %> <%= f.error_messages %><br /> <%= f.label 'edit password', f.object.new_record? ? nil : "change password" %><br /> <%= f.password_field :password %><br /> <br /> <%= f.label :password_confirmation %><br /> <%= f.password_field :password_confirmation %><br /> <br /> <%= f.submit "update" %> or <%= link_to "cancel", url_for(:controller => 'users', :action => 'show', :id => @user.id ) %> <% end %>
13. Erase the code in app/views/show.html.erb and paste this code:
<p> <b>Login:</b> <%=h @user.login %> </p> <p> <b>Email:</b> <%=h @user.email %> </p> <%= link_to 'Edit', edit_user_path(@user) %> | <%= link_to 'Back', users_path %>
14. Scaffold the user sessions:
$ script/generate scaffold UserSession --skip-migration
15. Erase the code in your UserSessions controller and paste this code:
class UserSessionsController < ApplicationController before_filter :require_no_user, :only => [:new, :create] before_filter :require_user, :only => :destroy def new @user_session = UserSession.new @user = User.new end def create @user_session = UserSession.new(params[:user_session]) if @user_session.save redirect_to root_url else flash[:notice]='Invalid username/password combination' redirect_to :back end end def destroy current_user_session.destroy redirect_to root_url end end
16. Erase the code in app/models/user_session.rb and paste this code:
class UserSession < Authlogic::Session::Base end
Notice it extends an authlogic base class, not a rails class.
17. Delete all the views in app/views/user_sessions/ other than new.html.erb, and replace the contents of new.html.erb with this:
<h1>login</h1> <% form_for @user_session, :url => url_for(:controller=>'user_sessions', :action=>'create') do |f| %> <%= f.error_messages %><br /> <%= f.label :login, 'username' %><br /> <%= f.text_field :login %><br /> <br /> <%= f.label :password, 'password' %><br /> <%= f.password_field :password %><br /> <br /> <%= f.check_box :remember_me %><%= f.label :remember_me, 'remember me' %><br /> <%= f.submit "login" %> <% end %>
18. Delete all views in app/views/layouts except for 1, and rename it application.html.erb to make it the master file. Replace its contents with this:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <meta http-equiv="content-type" content="text/html;charset=UTF-8" /> <title>Base Project</title> <%= stylesheet_link_tag 'scaffold' %> </head> <body> <p style="color: green"><%= notice %></p> <div> <% if current_user %> <%= link_to 'logout', logout_url %> <% else %> <%= link_to 'login', login_url %> <%= link_to 'signup', signup_url %> <% end %> </div> <%= yield %> </body> </html>
19. Update your config/routes.rb to look like this:
ActionController::Routing::Routes.draw do |map| map.resources :user_sessions map.resources :users map.resources :users # The priority is based upon order of creation: first created -> highest priority. # Sample of regular route: # map.connect 'products/:id', :controller => 'catalog', :action => 'view' # Keep in mind you can assign values other than :controller and :action map.logout 'logout', :controller => 'user_sessions', :action =>'destroy' map.login 'login', :controller => 'user_sessions', :action =>'new' map.signup 'signup', :controller => 'users', :action =>'new' # Sample of named route: # map.purchase 'products/:id/purchase', :controller => 'catalog', :action => 'purchase' # This route can be invoked with purchase_url(:id => product.id) # Sample resource route (maps HTTP verbs to controller actions automatically): # map.resources :products # Sample resource route with options: # map.resources :products, :member => { :short => :get, :toggle => :post }, :collection => { :sold => :get } # Sample resource route with sub-resources: # map.resources :products, :has_many => [ :comments, :sales ], :has_one => :seller # Sample resource route with more complex sub-resources # map.resources :products do |products| # products.resources :comments # products.resources :sales, :collection => { :recent => :get } # end # Sample resource route within a namespace: # map.namespace :admin do |admin| # # Directs /admin/products/* to Admin::ProductsController (app/controllers/admin/products_controller.rb) # admin.resources :products # end # You can have the root of your site routed with map.root -- just remember to delete public/index.html. map.root :controller => "home" # See how all your routes lay out with "rake routes" # Install the default routes as the lowest priority. # Note: These default routes make all actions in every controller accessible via GET requests. You should # consider removing or commenting them out if you're using named routes and resources. map.connect ':controller/:action/:id' map.connect ':controller/:action/:id.:format' end
20. Update your app/controllers/applicationcontroller.rb to look like this:
class ApplicationController < ActionController::Base helper :all # include all helpers, all the time helper_method :current_user_session, :current_user filter_parameter_logging :password, :password_confirmation private def current_user_session return @current_user_session if defined?(@current_user_session) @current_user_session = UserSession.find end def current_user return @current_user if defined?(@current_user) @current_user = current_user_session && current_user_session.record end def require_user unless current_user flash[:notice] = "You must be logged in to do that." respond_to do |format| format.html { redirect_to login_url } end return false end end def require_no_user if current_user redirect_to root_url return false end end def store_location session[:return_to] = request.request_uri end def redirect_back_or_default(default) redirect_to(session[:return_to] || default) session[:return_to] = nil end end
Your app should now run when you start your server with:
$ script/server
Starting your next projects with a copy of this project as a base rather than coding everything from scratch again will definitely save you some valuable time. Check out the demo app and the source code.