Keep Controllers Organized: The Shared Helper method

Have you ever felt like your controller was getting a lot of private methods to maintain its code clean? Better yet, have you ever felt that those chunks of code could be used elsewhere in your application, helping keep your code DRY?

I was hit by this thoughts a while back when i was starting developing with rails and the method i used back then seems to be pretty straightforward to me, even now (one year later). The principle is simple: Take reusable chunks of your code that can be used on different controllers, glue them up in a helper module and include that module on every controller that uses the code.

I usually split the methods into subjects, like UserSharedHelper for user-related methods, PurchaseSharedHelper for purchase-related methods and so on…

Let’s see an example of this practice in action:

module UserSharedHelper
   # Require an user to be logged in.
   def require_current_user
      # if there is a current_user, then the user is logged in and
      # the action can proceed
      return true if current_user.present?
      # if the method hasn't returned, there is no user logged in!
      # flash a warning and redirect the user to the log in page
      flash[:warning] = "You must log in before proceeding!"
      redirect_to new_user_session_path
   end
end
 
class StoreCheckoutsController < ApplicationController
   include UserSharedHelper
   # require users to be logged in on actions under this controller
   before_filter :require_current_user
 
   def new; end
   [...]
end

It’s pretty simple and helps you to keep your code DRY. There are some drawbacks though that need to be considered:

  • The controller scope gets methods from the SharedHelper, which may lead to method conflicts;
  • As your SharedHelpers grow larger, you gotta be careful with injecting a lot of unnecessary code into your controllers;
  • Methods from the SharedHelper make their way into the view, which may lead to further conflicts.

Being used with careful, i don’t see a reason not to use it. What about you? How do you keep your controllers DRY? Comment below!

Edit:

Guillermo commented with a pretty straightforward way to make this approach even better:

Defining the module:
And in the controller:

  • Guest

    Why not use the ApplicationController for this purpose?

    • http://andyjeffries.co.uk/ Andy Jeffries

      Because not all controllers require the UserSharedHelper functionality.

      • http://rubynoobie.wordpress.com/ Lucas d’Acampora Prim

        exactly!

  • Drhenner

    YEah that looks like an example for the ApplicationController… Sometimes I create BaseController and inherit from it. For example, Create a namespace “blog”

    Now blog/base_controller.rb holds share methods. and blog/posts_controller.rb inherits from blog/base_controller.rb

    • http://rubynoobie.wordpress.com/ Lucas d’Acampora Prim

      I prefer the SharedHelper approach as it doesn’t pollute the ApplicationController scope once you just include whatever you need, but i think it’s all a matter of taste :)

  • http://twitter.com/thiagopradi Thiago Pradi

    Howdy Son!

    Nice Approach! My thought is if you have a amount of code that is shared inside a Namespace, I prefer the BaseController approach, but if the code is used along your entire application, this composition approach is better.

    Correction:

    redirect_to new_user_session_path and return

    “and return” is unnecessary here ;-)

    • http://rubynoobie.wordpress.com/ Lucas d’Acampora Prim

      howdy mr. octopuss ! :P

      it’s just an addiction i have to make every method return! Gonna edit it!

  • Guillermo

    Maybe another aproach can be fun:

    module UserSharedHelper
    def require_logged_user!
    before_filter do
    return true if current_user.present?
    # if the method hasn’t returned, there is no user logged in!
    # flash a warning and redirect the user to the log in page
    flash[:warning] = “You must log in before proceeding!”
    redirect_to new_user_session_path and return

    end
    end

    class StoreCheckoutsController < ApplicationController
    require_logged_user!

    end

  • Guillermo

    Sorry, i want to say:

    class StoreCheckoutsController < ApplicationController
    extend UserSharedHelper
    require_logged_user!

    end

  • Guillermo

    Nice in a gist, and another refactor:

    https://gist.github.com/869582

    Maybe not so good.

    • http://rubynoobie.wordpress.com/ Lucas d’Acampora Prim

      Wow! This is really straightforward! Really liked your approach!
      Gonna add it to the post!

  • http://btucker.org btucker

    Not that many of us are still using default routes, but making the methods like require_current_user protected is probably a good practice.