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
  endCreate 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
endMemoization
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
  endRemember - 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