$ rails _3.2.13_ new user_profile -d mysql
$ cd user_profile
Part 2: Devise Users
In /Gemfile,
gem 'devise'
$ bundle install
$ rails generate devise:install
In /config/environment/development.rb,
config.action_mailer.default_url_options = { :host => 'localhost:3000' }
In /config/routes.rb,
root :to => "home#index"
In /app/views/layours/application.html.erb,
<body> <p class="notice"><%= notice %></p> <p class="alert"><%= alert %></p> <%= yield %>
In /config/application.rb,
# Enable the asset pipeline config.assets.enabled = true config.assets.initialize_on_precompile = false
$ rm public/index.html
Part 3: User Model
$ rails generate devise user
$ rake db:migrate
Part 4: Home Index Page
$ rails g controller home
In /app/controllers/home_controller.rb,
class HomeController < ApplicationController def index end end
Create your own index page at /app/views/home/index.html.erb.
Part 5: Sign In, Sign Out and Register Links
Create the new file on /app/views/common/_session.html.erb and include the code below into the file:
<%- if user_signed_in? %> <p>Hello <%= current_user.email %><br /> <%= link_to 'Sign out', destroy_user_session_path, :method => :delete %></p> <%- else %> <p> <%= link_to 'Register', new_user_registration_path %> | <%= link_to 'Sign in', new_user_session_path %> </p> <%- end %>
In /app/views/home/index.html.erb,
<div id="secondaryContent"> <%= render 'common/session' %> </div>
$ rails g devise:views
Part 6: Authorization
In /app/controllers/application_controller.rb,
class ApplicationController < ActionController::Base protect_from_forgery protected def authorize_user! if user_signed_in? return else flash[:notice] = 'You need to sign in first' redirect_to new_user_session_path end end end
In /app/controllers/home_controller.rb,
class HomeController < ApplicationController before_filter :authorize_user! def index end end
Part 7: Username
$ rails g migration add_username_to_users username:string
$ rake db:migrate
Add the code below into the 3 files as belows:
- /app/views/devise/registrations/edit.html.erb
- /app/views/devise/registrations/new.html.erb
- /app/views/devise/sessions/new.html.erb
<div><%= f.label :username %><br /> <%= f.text_field :username %></div>
In /config/initializers/devise.rb,
# ==> Configuration for any authentication mechanism # Configure which keys are used when authenticating a user. The default is # just :email. You can configure it to use [:username, :subdomain], so for # authenticating a user, both parameters are required. Remember that those # parameters are used only when authenticating and not when retrieving from # session. If you need permissions, you should implement that in a before filter. # You can also supply a hash where the value is a boolean determining whether # or not authentication should be aborted when the value is not present. config.authentication_keys = [ :username ]
In /app/models/user.rb,
class User < ActiveRecord::Base # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable and :omniauthable devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable # Setup accessible (or protected) attributes for your model attr_accessible :username, :email, :password, :password_confirmation, :remember_me end
Part 8: Name & Nickname
$ rails g migration add_name_to_users name:string
$ rails g migration add_nickname_to_users nickname:string
$ rake db:migrate
Add the code below into the 2 files as belows:
- /app/views/devise/registrations/edit.html.erb
- /app/views/devise/registrations/new.html.erb
<div><%= f.label :name %><br /> <%= f.text_field :name %></div> <div><%= f.label :nickname %><br /> <%= f.text_field :nickname %></div>
In /app/models/user.rb,
class User < ActiveRecord::Base # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable and :omniauthable devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable # Setup accessible (or protected) attributes for your model attr_accessible :username, :email, :password, :password_confirmation, :name, :nickname, :remember_me end
In /app/common/_session.html.erb,
<%- if user_signed_in? %> <p>Welcome, <%= current_user.nickname %>!<br /> <%= link_to 'Sign out', destroy_user_session_path, :method => :delete %></p> <%- else %> <p> <%= link_to 'Register', new_user_registration_path %> | <%= link_to 'Sign in', new_user_session_path %> </p> <%- end %>
Part 9: Carrierwave File Uploads
In /Gemfile,
gem 'carrierwave'
$ bundle install
$ rails g uploader photo
$ rails g migration add_photo_to_users photo:string
$ rake db:migrate
In /app/models/user.rb,
class User < ActiveRecord::Base # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable and :omniauthable devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable # Setup accessible (or protected) attributes for your model attr_accessible :username, :email, :password, :password_confirmation, :name, :nickname, :photo, :remote_photo_url, :remember_me mount_uploader :photo, PhotoUploader end
In /app/uploaders/photo_uploader.rb,
class PhotoUploader < CarrierWave::Uploader::Base def extension_white_list %w(jpg jpeg gif png) end end
Add the code below into the 2 files as belows:
- /app/views/devise/registrations/edit.html.erb
- /app/views/devise/registrations/new.html.erb
<div><%= f.label :photo %><br /> <%= f.file_field :photo %><br /> <%= f.label :remote_photo_url, "or photo URL" %> <%= f.text_field :remote_photo_url %> </div>
Part 10: User Authorization with Rolify and Cancan
In /Gemfile,
gem 'rolify' gem 'cancan'
$ bundle install
$ rails g rolify Role User
$ rake db:migrate
In /app/models/user.rb,
class User < ActiveRecord::Base rolify # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable and :omniauthable devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable # Setup accessible (or protected) attributes for your model attr_accessible :username, :email, :password, :password_confirmation, :name, :nickname, :photo, :remote_photo_url, :remember_me attr_accessor :current_role mount_uploader :photo, PhotoUploader end
In /app/models/role.rb,
class Role < ActiveRecord::Base has_and_belongs_to_many :users, :join_table => :users_roles belongs_to :resource, :polymorphic => true scopify attr_accessible :name end
$ rails g cancan:ability
In /app/models/ability.rb,
class Ability include CanCan::Ability def initialize(user) user ||= User.new if user.role? :admin can :manage, :all else cannot :create, User can :read, User can :update, User cannot :destroy, User
cannot :index, User end end end
Part 11: User with Roles
Add the code below into the 2 files as belows:
- /app/views/devise/registrations/edit.html.erb
- /app/views/devise/registrations/new.html.erb
<div><%= f.label :role %><br /> <% for role in Role.find(:all) %> <%= check_box_tag "user[role_ids][]", role.id, @user.roles.include?(role) %> <%= role.name %><br/> <% end %></div>
In /app/models/user.rb,
class User < ActiveRecord::Base rolify has_and_belongs_to_many :roles, :join_table => :users_roles # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable and :omniauthable devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable # Setup accessible (or protected) attributes for your model attr_accessible :username, :email, :password, :password_confirmation, :name, :nickname, :photo, :remote_photo_url, :role_ids, :remember_me attr_accessor :current_role mount_uploader :photo, PhotoUploader def role?(role_sym) roles.any? { |r| r.name.underscore.to_sym == role_sym } end end
Part 12: User Management via CRUD
In order to manage our users through a CRUD interface we have to create a users controller and its associated views. First use rails to generate the users controller.
$ rails g controller users
In our users controller we are going to define the seven basic actions to maintain the RESTful protocol, so open up app/controllers/users_controller.rb and make it look like the following:
class UsersController < ApplicationController before_filter :authorize_user! load_and_authorize_resource def index @users = User.all end def show @user = User.find(params[:id]) end def new @user = User.new end def edit @user = User.find(params[:id]) end def create @user = User.new(params[:user]) if @user.save redirect_to @user, :flash => { :success => 'User was successfully created.' } else render :action => 'new' end end def update @user = User.find(params[:id]) if @user.update_attributes(params[:user]) sign_in(@user, :bypass => true) if @user == current_user redirect_to @user, :flash => { :success => 'User was successfully updated.' } else render :action => 'edit' end end def destroy @user = User.find(params[:id]) @user.destroy redirect_to users_path, :flash => { :success => 'User was successfully deleted.' } end end
Now we need to create the associated views and a form partial.
$ cd app/views/users && touch _form.html.erb edit.html.erb index.html.erb new.html.erb show.html.erb
Make each of these files look as follows respectively:
In /app/views/users/_form.html.erb,
<%= form_for @user do |f| %> <% if @user.errors.any? %> <div class="error_explanation"> <h2><%= pluralize(@user.errors.count, "error") %> prohibited this user from being saved:</h2> <ul> <% @user.errors.full_messages.each do |msg| %> <li><%= msg %></li> <% end %> </ul> </div> <% end %> <div class="field"> <%= f.label :username %> <div class="controls"> <%= f.text_field :username %> </div> </div> <div class="field"> <%= f.label :email %> <div class="controls"> <%= f.email_field :email %> </div> </div> <div class="field"> <%= f.label :password, "Password" %> <div class="controls"> <%= f.password_field :password %> </div> </div> <div class="field"> <%= f.label :password_confirmation %> <div class="controls"> <%= f.password_field :password_confirmation %> </div> </div> <div class="field"> <%= f.label :name %> <div class="controls"> <%= f.text_field :name %> </div> </div> <div class="field"> <%= f.label :nickname %> <div class="controls"> <%= f.text_field :nickname %> </div> </div> <div class="field"> <%= f.label :photo %> <div class="controls"> <%= f.file_field :photo %> </div> <%= f.label :remote_photo_url, "or photo URL" %> <%= f.text_field :remote_photo_url %> </div> <% if can? :create, @user %> <div class="field"> <%= f.label :role %> <div class="controls"> <% for role in Role.find(:all) %> <%= check_box_tag "user[role_ids][]", role.id, @user.roles.include?(role) %> <%= role.name %><br/> <% end %> </div> </div> <% end %> <div class="actions"> <%= f.submit %> </div> <% end %>
In /app/views/users/edit.html.erb,
<h1>Edit User</h1> <%= render 'form' %> <%= link_to 'Show User', @user %> <% if can? :index, @user %> | <%= link_to 'All Users', users_path %> <% end %>
In /app/views/users/index.html.erb,
<h1>Users</h1> <table> <thead> <tr> <th>Username</th> <th>Email</th> <th>Name</th> <th>Nickname</th> <th>Role</th> <th>Actions</th> </tr> </thead> <tbody> <% @users.each do |user| %> <tr> <td><%= user.username %></td> <td><%= user.email %></td> <td><%= user.name %></td> <td><%= user.nickname %></td> <td><%= user.roles.map(&:name).join(", ") %></td> <td> <%= link_to 'Show', user %> <%= link_to 'Edit', edit_user_path(user) %> <%= link_to 'Delete', user_path(user), :method => 'delete', :confirm => 'Are you sure?' %> </td> </tr> <% end %> </tbody> </table> <%= link_to 'New User', new_user_path %>
In /app/views/users/new.html.erb,
<h1>New User</h1> <%= render 'form' %> <%= link_to 'All Users', users_path %>
In /app/views/users/show.html.erb,
<h1>User</h1> <p><strong>Username:</strong> <%= @user.username %></p> <p><strong>Email:</strong> <%= @user.email %></p> <p><strong>Name:</strong> <%= @user.name %></p> <p><strong>Nickname:</strong> <%= @user.nickname %></p> <p><strong>Photo:<strong><br /> <%= image_tag @user.photo_url %> </p> <p><strong>Role:</strong> <%= @user.roles.map(&:name).join(", ") %></p> <% if can? :index, @user %> <%= link_to 'All Users', users_path %> | <% end %> <%= link_to 'Edit User', edit_user_path(@user) %> <% if can? :destroy, @user %> | <%= link_to 'Delete User', user_path(@user), :method => 'delete', :confirm => 'Are you sure?' %> <% end %>
Next we need to set up some routes so that the routes for devise and our users controller don't have any conflicts. So, open up config/routes.rb and make it look as follows:
Userapp::Application.routes.draw do resources :users devise_for :users, :skip => [:registrations, :sessions] as :user do get "/login" => "devise/sessions#new", :as => :new_user_session post "/login" => "devise/sessions#create", :as => :user_session delete "/logout" => "devise/sessions#destroy", :as => :destroy_user_session end root :to => 'users#index' end
This routes.rb file first defines the RESTful routes for our users controller then tells devise to generate routes for users but to skip registrations and sessions. We skip the sessions routes because we will have to define them in a custom fashion so that they will not conflict with our RESTful users routes.
In /app/views/common/_session.html.erb,
<%- if user_signed_in? %> <p>Welcome, <%= current_user.nickname %>!<br /> <%= link_to 'Sign out', destroy_user_session_path, :method => :delete %></p> <%- else %> <p> <%= link_to 'Sign in', new_user_session_path %> </p> <%- end %>
In /app/controllers/application_controller.rb,
class ApplicationController < ActionController::Base protect_from_forgery def after_sign_in_path_for(resource) user_path(current_user) #your path end protected def authorize_user! if user_signed_in? return else flash[:notice] = 'You need to sign in first' redirect_to new_user_session_path end end end
In /app/views/devise/shared/_links.erb,
<%- if controller_name != 'sessions' %> <%#= link_to "Sign in", new_session_path(resource_name) %><!--<br />--> <% end -%> <%- if devise_mapping.registerable? && controller_name != 'registrations' %> <%#= link_to "Sign up", new_registration_path(resource_name) %><!--<br />--> <% end -%>
Now lets launch the application.
$ rails s
Navigate to http://localhost:3000/users/new in a web browser. Here we can create the first user of our application. Go ahead and create a user with an email address and password of your choice. Now go to http://localhost:3000 and click on the Log In link we made earlier. This brings us to the sign in page generated by devise but uses our custom /login route we specified in config/routes.rb. Authenticate with the account you just created. You should be greeted by the home page of the application with a success flash message and now a Log Out link. Congratulations! You can now create users and authenticate with devise. You can view a list of all the users by going to http://localhost:3000/users and from there you can edit or delete them.
Return to Internship Note (LoanStreet)
Previous Episode: User Authorization with Rolify and Cancan
Next Episode: Testing with RSpec
0 comments:
Post a Comment