Helper Methods
By now we should all be familiar with the software design principle D.R.Y. or Don’t Repeat Yourself. Like Separation of Concerns, DRY helps us decide how to organize our code…Helper methods or Helpers, the focus of this post, help us do just that.
What exactly do helpers do? They help! Really! Helpers are just modules, and or model and controller methods, that help dry up our code by extracting complex logic and or duplicate code into reusable methods to be used in/by our views and controllers.
For my Flatiron Rails project I built a corporate social responsibility application, Giving Back, that allows businesses to showcase their various philanthropic initiatives and beneficiaries. The app is an opportunity to promote philanthropic giving as well as the support of local organizations or nonprofits.
As I worked my way through building the app I realized that much of what I was writing was repeated code and therefore could be extracted — cue helper methods. As previously stated, Rails helpers are defined in helper modules, models, and or controllers for use in these same controllers and view files. They keep us from having to repeat ourselves and help condense logic into fewer places. For the purposes of this project, I focused on controller and model helper methods. To begin I’ll cover a few of the controller methods defined in my application — the why and the how.
Controller Helpers
Controller helper methods are those that are tied to a given model and controller. They are concerned with logic more than presentation. In the Application Controller we define the methods that concern our entire application. Oftentimes what is queried and or is presented to the user depends on who is interacting with the application — i.e., a registered user or a guest…the App Controller is therefore a good place to define the logged in method and current user method. These helpers can then be used not only in the inheriting controllers but also in various views.
class ApplicationController < ActionController::Base
...
helper_method :current_business, :logged_in
def current_business
current_business = Business.find_by(id: session[:business_id])
end
def logged_in
unless current_business != nil
redirect_to root_path
end
end
end
Called in the businesses show page to allow access to certain information:
businesses/show.erb
<%if @business == current_business%>
<a href="<%=new_business_philanthropic_initiative_path(@business)%>" class="btn btn-secondary my-2 btn-sm">Add Initiative</a>
<br/>
<br/>
<br/>
<a href="<%=edit_business_path(@business)%>" class="btn btn-light my-2 btn-sm">Edit Business</a><br/>
<%=button_to "Delete", @business, method: :delete, class: "btn btn-light my-2 btn-sm" %>
<%end%>
Now only logged-in current users (i.e. account owners) have access to the edit/delete buttons to edit or delete their accounts.
Above I’ve defined two controller helpers to identify the current user/business and to know if they are logged in. Defining these in the application controller allows me to reference these methods through my entire application — in the other controllers (as I’ll show later) and in my views (as seen above).
Controller Helpers Cont’d
Similarly, we can create controller-specific methods that are shared by the methods in the controller to carry-out shared logic, helping us dry up our controller actions.
class BusinessesController < ApplicationController
before_action :set_business, :logged_in, only: [:index, :show, :edit, :update, :destroy]
before_action :correct_business, only: [:edit, :update, :destroy]
...
def update
@business.update(business_params)
if @business.save
redirect_to business_path(@business)
else
render 'edit'
end
end
def destroy
@business.destroy
end
private def business_params
params.require(:business).permit(:name, :sector, :city, :state, :website, :email, :password, :password_confirmation, philanthropic_initiatives_attributes: [:name, :pledged_amount, :goal, beneficiary_attributes: [:recipient, :city, :state]] )
end
def set_business
@business = Business.find_by(id: params[:id])
end
def correct_business
if current_business.id != params[:id].to_i
flash[:danger] = "Unauthorized access!"
redirect_to business_path(current_business)
end
end
end
Let’s break this down. Controller helper methods are generally defined as private methods — this means they are accessible to only the controller method in which they are defined (side note — we can access controller helpers in our views if we really want to but must first define them at the top of our controller as helper_methods). The set_business method above serves as a callback that defines the @business instance variable which is then passed in as an argument in the ActionController class method before_action which appends the callback before each action. What is a callback? A callback is a method that is called at a certain point of an object’s lifecycle. In this case, I want @business to be “set” before each action. Notice how doing this saves me the time and trouble of having to manually define or set @business multiple times throughout my controller.
Next, I extracted even more logic into yet another helper method: correct_business — here I compare the current_business.id (defined in my set_business method) against the id value of the parameters passed in with each request. If the values do not match, I redirect to the user to their home page with a flash message indicating unauthorized access. Once again, defining this helper method here allows me to use it in my before_action, now instead of having to call this method from inside the edit, update, and destroy actions I can just leave them blank. As my “user” model, the business controller creates first-time users, but you can imagine how such a method would apply to the other actions ( i.e. new, edit) in other controllers — after all, we do not want anyone but the correct business/user to hold such powers.
Model Helper Methods
Similar to controller helpers, model helpers help us abstract our code by condensing functionality into fewer reusable methods. Model helpers, for example, can help us alter or format our data before saving it our database. In the titleize method below, for example, I am reformatting multiple business attributes before saving them to my database. Now, instead of having to define specification validations to ensure only valid data is populated into my database I can just call the method using before_save. Similarly, my website_format method ensures that the website entered is in the desired format before validating the new business.
class Business < ApplicationRecord
has_many :philanthropic_initiatives
has_many :beneficiaries, through: :philanthropic_initiatives
...
before_save :titleize
validate :website_format
validates :state, length: { is: 2 }
...
scope :in_tech, -> { where(sector: "Technology") }
private
def website_format
unless website && website.match(/^[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&\/\/=]*)?$/)
errors.add(:website, "must be in a valid format, for example: learn.co")
end
end
def titleize
self.state = self.state.upcase
self.name = self.name.split.map(&:capitalize).join(' ')
self.city = self.city.split.map(&:capitalize).join(' ')
end
end
Now thanks to these model helpers and validations I am able to formulate custom queries to filter and further manipulate my data in a cleaner more attractive way. The in_tech scope method above, for example, focuses on extracting from the database those businesses that are in the technology sector or industry. Once defined, I can then use the in_tech method in my business controller to display in the business’ index view those business in technology.
scope :in_tech, -> { where(sector: "Technology") }
class BusinessesController < ApplicationController
...
def index
@businesses = Business.all
@in_tech = Business.in_tech
end
businesses/index.erb
<h6>Tech Companies:</h6>
<ul>
<%@in_tech.each do |t|%>
<li> <%=t.name%></li>
<%end%>
</ul>
Because of my titleize method I can also, just as easily, do something like: scope :big_apple_businesses, -> { where(state: "NY") }
to pull the business in New York.
These are just a few examples of how Helper methods help us adhere to the DRY principle of programming — helping us write code that is cleaner and easier to maintain. Thanks for the help, Helpers!
Originally published at https://mely07.github.io on June 14, 2020.