Quite often when writing Rails applications we need to do some kind of user input sanitization. We usually end up doing some dull checks across multiple lines. Have a look at this hidden ActiveSupport #presence_in method that provides a convenient way to do whitelisting in just one line.
Let’s say your app presents a list of articles and offers multiple ways of sorting them: by upload date, by the number of upvotes and number of comments. One of the possible techniques to support this behavior is defining three types of scopes on your Article model:
class Article < ActiveRecord::Base
scope :newest, -> { order(updated_at: :desc) }
scope :top, -> { order(upvotes_count: :desc) }
scope :most_commented, -> { order(comments_count: :desc) }
end
You’ll want to call those scopes depending on the query parameter passed to your controller, e.g. /articles?sort_by=newest
.
The easiest way to implement this in our controller would be:
class ArticlesController < ApplicationController
def index
@articles = Article.public_send(params[:sort_by])
end
end
However, you don’t want to send those user inputs directly to your model because you would leave yourself vulnerable to remote code execution
. An evildoer could send you a request such as /articles?sort_by=destroy_all
and destroy your records.
As a responsible developer, you sanitize that input:
class ArticlesController < ApplicationController
def index
@articles = Article.public_send(sorting_technique)
end
private
def sorting_technique
if %w(newest most_commented top).include? params[:sort_by]
params[:sort_by]
else
:newest
end
end
end
Although this code provides safety, it’s still not very nice. I’ve written similar code many times and have never felt happy about it because I believed it could be shorter.
The #presence_in method to the rescue
That’s where the lesser-known method from the ActiveSupport module called Object#presence_in comes in.
Description:
Returns the receiver if it’s included in the argument. Otherwise, it returns nil. The argument has to be any object which responds to #include?.
Source:
def presence_in(another_object)
self.in?(another_object) ? self : nil
end
Usage:
With the presence_in
method we can write our sorting_technique
method with a nice one-liner:
def sorting_technique
params[:sort_by].presence_in(%w(newest most_commented top)) || :newest
end
Its usage doesn’t stop here, but I’ve found it to be most useful when sanitizing user input.