Rapid Prototyping with Rails: Lesson 3, part 2
24 Oct 2014Items Covererd:
- Creating/Updating User
- Login/Logout
- Memoization
Creating/Updating User
- Create resources resources :users, only: [:create]
- Create users_controller.rb (new, create, edit, update actions)
- since :password is the virtual attribute for :passworddigest, you have to include it in strongparams:
def users_params
params.require(:user).permit(:username, :password)
end
- Create users folder under views (new.html.erb, edit.html.erb, _form.html.erb)
- Add password validations
validates :password, presence: true, on: :create, length: {minimun: 3}
- Change users#new to register under routes file to add a register path '/register':
get '/register', to: 'users#new', as: 'register'
- Add register link to _navigation.html.erb
<li>
<%= link_to 'Register', register_path %>
Login/Logout
- Create routes - *resources are reserved for models, typcially custom routes are on-offs
get '/login', to: 'sessions#new'
post '/login', to: 'sessions#create'
get '/logout', to: 'sessions#destroy'
- Create sessions_controller.rb
class SessionsController < Application.rb
def new
end
def create
Let's write pseudo code:
What are we trying to do... we want to run user.authenticate('password')
How do we accomplish this?
1. Get user object
2. See if the password matches
3. If so, log in
4. If not, error message
user = User.find_by(username: params[:username])
# we can use a local variable bc we aren't using model backed forms so persistency isn't required.
if user && user.authenticate(params[:password])
session[:user_id] = user_id
flash[:notice] = "You're logged in!"
redirect_to root_path
#never pass an object into sessions ( like @user for example)
bc sessions only have 4KBs in bandwidth and will generate a "cookie overload error" after the user carries out
so many actions.
else
flash[:error] = "There was something wrong with your username and password"
#Avoid being too specific here, (i.e. found username but wrong password)... to prevent hacking
redirect_to register_path
end
end
#Login pages should be https://, if not, passwords submitted over an http:// server are not encrypted. The https://
is not a very high added expense. You just have to purchase SSL certificate.
def delete
session[:user_id] = nil
flash[:notice] = "You've logged out"
redirect_to root_path
end
end
Create a sessions folder under views, new.html.erb file
Add login/logout link to nav bar
#_navigation.html.erb
if logged_in?
<li>Hi<%= current_user.username %></li>
<li>
<%= link_to "Log Out", logout_path %>
</li>
<% else %>
<li>
<%= link_to "Register", register_path %>
</li>
<li>
<%= link_to "Log in", login_path %>
<% end %>
</li>
- We need to create a method called loggedin (referenced above) in the applicationcontroller.rb bc we want this method accessible across the application.
application_controller.rb
helper_method :current_user, :logged_in?
def current_user
#Wpseudo-code
#If current user, return the user obj
#else return nil
@current_user ||= User.find(session[user_id]) if session[:user_id]
end
end
def logged_in?
!!current_user
end
end
Memoization
For effecient performance, we want to hit the database only once per request. For methods that are being called multiple times, it is more effecient to save in an instance variable rather than continously hitting the database.
- We can now hide links based on whether a user is logged in or not. Let's hide the 'edit' link
#posts/index.html.erb
<h3> Welcome </h3>
<ul>
<% @posts.each do |post| %>
<li>
<%= link_to post.title, post_path(post)
<% if logged_in? %>
[<%= link_to 'Edit', edit_post_path(post) %>]
- We also want to make sure you can't go to the link via url
#posts_controller.rb
before_action :require_user,except: [:index, :show]
- We must create a require_user method
#application_controller.rb
def require_user
if !logged_in?
flash[:notice] = "Must be logged in to do that"
redirect_to root_path
end
- Set up beforeaction under the commentscontroller.rb, also.
#comments_controller.rb
class CommentsController < ApplicationController.rb
before_action :require_user
def create
@comment.creator = current_user #change from @comment_creator = User.first
end
end
- Define post.creator as current_user
#posts_controller.rb
def create
@post.creator = current_user
end
Remember - when you want to lock down a peice of functionality, you have to 1. Remove it from the user interface (links) 2. Set up a before_action