My helpers need help

Helper methods in a rails app help us to be more abstract as programmers, and consequently more precise. For example, In a simple user authenification system, there are many different points at which you might want to check that a user is logged in.

You can check if a user is logged it like this:

session[:user_id].present?

This works fine. There's nothing wrong with it. But I don't like it (you don't like it either, just trust me).

First of all, it doesn't read very intuitively. I want my code to say what it means. It shouldn't cause anyone looking at this for the first time to wonder what it means. Secondly, even if it is only a few words, we can make it even shorter, cleaner and easier to use at every juncture in my app that requires a user to be logged in to complete an action.

We can do this by extracting this code into a method. Let's call that method logged_in? since that's exactly what it's checking.

def current_user
    @current_user ||= User.find(session[:user_id]) if 
    session[:user_id].present?
 
 def logged_in?
    !!current_user
  end

Let's walk through what just happened. The job of logged_in? is to determine if a session has been intiated with a user id. In other words, does an instance of my User class exist and is the session[:user_id] equal to that user's primary key id?

We can think of the current user as an object with cerain attributes. It is an instance of my user class so it likely has a name, email address and password to start with. Since I might like to address my current user by name (for example, display a "Welcome, Bob!" message after a user logs in), I've created a current_user method that assigns the current user's id to a variable "current_user".

Then, this method is called by the logged_in? method. The !! (double bang method) in front of the current_user call converts the outcome of that call into a boolean (true or false) value. If current_user exists (is not nil), logged_in will return true. If a current_user does not exist (is nil), it will return false.

Okay, so we have a nifty helper method to help us easily determine if a user is logged in. But, where should this helper method live? There are two options for housing your helper methods in rails application. The ApplicationController, from which all other controllers inherit. Or, in a module in the helpers directory of your app. How will we choose??

Helpers Directory

'Helpers' can be thought of as 'view helpers'. Any methods you define inside modules in the helper's directory (top level of your app) are automatically included in the views, but not in controllers or models.

If you find yourself writing a lot of loops, or if/else statements in a view, you may want to extract this logic into a method in a helper module.

For example, if a user is logged in to your site, you want a "log out" button/link available in your navbar. If a user is not logged in, you want a "log in" link available instead. This is likely true of every page in your site. So, instead of writing if/else statments on every view page to check if a user is logged in and display the corresponding link, we can extract this display logic, responsible for moderating how a navbar looks, to our helper module.

module ApplicationHelper

  def login_or_logout
   if logged_in?
     "Hello, #{current_user.name}! #{link_to "Log Out", 
     logout_path} #{link_to "New Post", 
     new_post_path}".html_safe
   else
     "#{link_to "Log In", login_path} #{link_to "Sign 
     Up", signup_path} Log in to edit or delete your 
     Post".html_safe
	end
 end

end

This logic deals only with what we want a user to view and when. It is specific to our views and therefore belongs in the ApplicationHelper. This means our login_or_logout method is available on all of the view pages in the entire application.

Helper Methods in the Application Controller

What about our logged_in? method from earlier? What might we use it for and where should it live? Here's an example:
You are viewing my super interesting and delightful coding blog and you decide you don't like Battlestar Gallactica (because you are crazy). You want to edit my clever Database Legos post and eradicate all of my clever BSG references. How can I stop you?

I can ensure that the edit action for a post is only available to user's who are signed up and logged in to my site. It is the post's responsibility to protect itself so the Post Controller will have to handle this one:

class PostsController < ApplicationController
  before_action :login_required, only: [:destroy, 
  :edit, :update, :new]

This will ensure that the login_required method is called before any attempts to destroy, edit or create a new post. The login_required, which we'll define next, will prevent user who isn't logged in from messing with my posts. Since I want this method to be available to my Posts Controller but potentially to other controller's as well and since it has server-side responsibilites (i.e. it is involved in requests to edit/create/delete a post), it belongs in the Application Controller:

class ApplicationController < ActionController::Base
 
  private

  def login_required
    if !logged_in?
      flash[:notice] = "Log in to edit or delete your 
      Post"
      redirect_to root_path
    end
  end


  def current_user
    @current_user ||= User.find(session[:user_id]) if 
    session[:user_id].present?
  end

  helper_method :current_user

  def logged_in?
    !!current_user
  end

  helper_method :logged_in?
end

Our login_required method checks to see if a user is logged in, using our previously defined helper methods. If a user is not signed it, it alerts them that they can't access that action and redirect them to the root page of my site.

By establishing helper_method :*method_name* underneath each method definition, we make the method available to all classes (other controllers) that inherit from application contoller.

So there you have it. Helper methods with server side responsibilies belong in the Application Controller, and helper methods that will only be needed in the views belong in modules in the helper directory.

subscribe and never miss a post!

Blog Logo

Sophie DeBenedetto

comments powered by Disqus
comments powered by Disqus